added snapshot support for BD engine
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void 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_WALL                             ? EL_BD_WALL :
1848              element == EL_BDX_STEELWALL                        ? EL_STEELWALL :
1849              element == EL_BDX_ROCK                             ? EL_BD_ROCK :
1850              element == EL_BDX_DIAMOND                          ? EL_BD_DIAMOND :
1851              element == EL_BDX_AMOEBA_1                         ? EL_BD_AMOEBA :
1852              element == EL_BDX_MAGIC_WALL                       ? EL_BD_MAGIC_WALL :
1853              element == EL_BDX_BUTTERFLY_1_RIGHT                ? EL_BD_BUTTERFLY_RIGHT :
1854              element == EL_BDX_BUTTERFLY_1_UP                   ? EL_BD_BUTTERFLY_UP :
1855              element == EL_BDX_BUTTERFLY_1_LEFT                 ? EL_BD_BUTTERFLY_LEFT :
1856              element == EL_BDX_BUTTERFLY_1_DOWN                 ? EL_BD_BUTTERFLY_DOWN :
1857              element == EL_BDX_BUTTERFLY_1                      ? EL_BD_BUTTERFLY :
1858              element == EL_BDX_FIREFLY_1_RIGHT                  ? EL_BD_FIREFLY_RIGHT :
1859              element == EL_BDX_FIREFLY_1_UP                     ? EL_BD_FIREFLY_UP :
1860              element == EL_BDX_FIREFLY_1_LEFT                   ? EL_BD_FIREFLY_LEFT :
1861              element == EL_BDX_FIREFLY_1_DOWN                   ? EL_BD_FIREFLY_DOWN :
1862              element == EL_BDX_FIREFLY_1                        ? EL_BD_FIREFLY :
1863              element == EL_BDX_EXPANDABLE_WALL_HORIZONTAL       ? EL_BD_EXPANDABLE_WALL :
1864              element == EL_BDX_WALL_DIAMOND                     ? EL_WALL_BD_DIAMOND :
1865              element == EL_BDX_EXIT_CLOSED                      ? EL_EXIT_CLOSED :
1866              element == EL_BDX_EXIT_OPEN                        ? EL_EXIT_OPEN :
1867              element);
1868
1869   Tile[x][y] = element;
1870 }
1871
1872 static void InitFieldForEngine(int x, int y)
1873 {
1874   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1875     InitFieldForEngine_RND(x, y);
1876 }
1877
1878 static void InitField(int x, int y, boolean init_game)
1879 {
1880   int element = Tile[x][y];
1881
1882   switch (element)
1883   {
1884     case EL_SP_MURPHY:
1885     case EL_PLAYER_1:
1886     case EL_PLAYER_2:
1887     case EL_PLAYER_3:
1888     case EL_PLAYER_4:
1889       InitPlayerField(x, y, element, init_game);
1890       break;
1891
1892     case EL_SOKOBAN_FIELD_PLAYER:
1893       element = Tile[x][y] = EL_PLAYER_1;
1894       InitField(x, y, init_game);
1895
1896       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1897       InitField(x, y, init_game);
1898       break;
1899
1900     case EL_SOKOBAN_FIELD_EMPTY:
1901       IncrementSokobanFieldsNeeded();
1902       break;
1903
1904     case EL_SOKOBAN_OBJECT:
1905       IncrementSokobanObjectsNeeded();
1906       break;
1907
1908     case EL_STONEBLOCK:
1909       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1910         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1911       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1912         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1913       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1914         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1915       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1916         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1917       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1918         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1919       break;
1920
1921     case EL_BUG:
1922     case EL_BUG_RIGHT:
1923     case EL_BUG_UP:
1924     case EL_BUG_LEFT:
1925     case EL_BUG_DOWN:
1926     case EL_SPACESHIP:
1927     case EL_SPACESHIP_RIGHT:
1928     case EL_SPACESHIP_UP:
1929     case EL_SPACESHIP_LEFT:
1930     case EL_SPACESHIP_DOWN:
1931     case EL_BD_BUTTERFLY:
1932     case EL_BD_BUTTERFLY_RIGHT:
1933     case EL_BD_BUTTERFLY_UP:
1934     case EL_BD_BUTTERFLY_LEFT:
1935     case EL_BD_BUTTERFLY_DOWN:
1936     case EL_BD_FIREFLY:
1937     case EL_BD_FIREFLY_RIGHT:
1938     case EL_BD_FIREFLY_UP:
1939     case EL_BD_FIREFLY_LEFT:
1940     case EL_BD_FIREFLY_DOWN:
1941     case EL_PACMAN_RIGHT:
1942     case EL_PACMAN_UP:
1943     case EL_PACMAN_LEFT:
1944     case EL_PACMAN_DOWN:
1945     case EL_YAMYAM:
1946     case EL_YAMYAM_LEFT:
1947     case EL_YAMYAM_RIGHT:
1948     case EL_YAMYAM_UP:
1949     case EL_YAMYAM_DOWN:
1950     case EL_DARK_YAMYAM:
1951     case EL_ROBOT:
1952     case EL_PACMAN:
1953     case EL_SP_SNIKSNAK:
1954     case EL_SP_ELECTRON:
1955     case EL_MOLE:
1956     case EL_MOLE_LEFT:
1957     case EL_MOLE_RIGHT:
1958     case EL_MOLE_UP:
1959     case EL_MOLE_DOWN:
1960     case EL_SPRING_LEFT:
1961     case EL_SPRING_RIGHT:
1962       InitMovDir(x, y);
1963       break;
1964
1965     case EL_AMOEBA_FULL:
1966     case EL_BD_AMOEBA:
1967       InitAmoebaNr(x, y);
1968       break;
1969
1970     case EL_AMOEBA_DROP:
1971       if (y == lev_fieldy - 1)
1972       {
1973         Tile[x][y] = EL_AMOEBA_GROWING;
1974         Store[x][y] = EL_AMOEBA_WET;
1975       }
1976       break;
1977
1978     case EL_DYNAMITE_ACTIVE:
1979     case EL_SP_DISK_RED_ACTIVE:
1980     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1981     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1982     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1983     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1984       MovDelay[x][y] = 96;
1985       break;
1986
1987     case EL_EM_DYNAMITE_ACTIVE:
1988       MovDelay[x][y] = 32;
1989       break;
1990
1991     case EL_LAMP:
1992       game.lights_still_needed++;
1993       break;
1994
1995     case EL_PENGUIN:
1996       game.friends_still_needed++;
1997       break;
1998
1999     case EL_PIG:
2000     case EL_DRAGON:
2001       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
2002       break;
2003
2004     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
2005     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
2006     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
2007     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
2008     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
2009     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
2010     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
2011     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
2012     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
2013     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
2014     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
2015     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
2016       if (init_game)
2017       {
2018         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2019         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2020         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2021
2022         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2023         {
2024           game.belt_dir[belt_nr] = belt_dir;
2025           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2026         }
2027         else    // more than one switch -- set it like the first switch
2028         {
2029           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2030         }
2031       }
2032       break;
2033
2034     case EL_LIGHT_SWITCH_ACTIVE:
2035       if (init_game)
2036         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2037       break;
2038
2039     case EL_INVISIBLE_STEELWALL:
2040     case EL_INVISIBLE_WALL:
2041     case EL_INVISIBLE_SAND:
2042       if (game.light_time_left > 0 ||
2043           game.lenses_time_left > 0)
2044         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2045       break;
2046
2047     case EL_EMC_MAGIC_BALL:
2048       if (game.ball_active)
2049         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2050       break;
2051
2052     case EL_EMC_MAGIC_BALL_SWITCH:
2053       if (game.ball_active)
2054         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2055       break;
2056
2057     case EL_TRIGGER_PLAYER:
2058     case EL_TRIGGER_ELEMENT:
2059     case EL_TRIGGER_CE_VALUE:
2060     case EL_TRIGGER_CE_SCORE:
2061     case EL_SELF:
2062     case EL_ANY_ELEMENT:
2063     case EL_CURRENT_CE_VALUE:
2064     case EL_CURRENT_CE_SCORE:
2065     case EL_PREV_CE_1:
2066     case EL_PREV_CE_2:
2067     case EL_PREV_CE_3:
2068     case EL_PREV_CE_4:
2069     case EL_PREV_CE_5:
2070     case EL_PREV_CE_6:
2071     case EL_PREV_CE_7:
2072     case EL_PREV_CE_8:
2073     case EL_NEXT_CE_1:
2074     case EL_NEXT_CE_2:
2075     case EL_NEXT_CE_3:
2076     case EL_NEXT_CE_4:
2077     case EL_NEXT_CE_5:
2078     case EL_NEXT_CE_6:
2079     case EL_NEXT_CE_7:
2080     case EL_NEXT_CE_8:
2081       // reference elements should not be used on the playfield
2082       Tile[x][y] = EL_EMPTY;
2083       break;
2084
2085     default:
2086       if (IS_CUSTOM_ELEMENT(element))
2087       {
2088         if (CAN_MOVE(element))
2089           InitMovDir(x, y);
2090
2091         if (!element_info[element].use_last_ce_value || init_game)
2092           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2093       }
2094       else if (IS_GROUP_ELEMENT(element))
2095       {
2096         Tile[x][y] = GetElementFromGroupElement(element);
2097
2098         InitField(x, y, init_game);
2099       }
2100       else if (IS_EMPTY_ELEMENT(element))
2101       {
2102         GfxElementEmpty[x][y] = element;
2103         Tile[x][y] = EL_EMPTY;
2104
2105         if (element_info[element].use_gfx_element)
2106           game.use_masked_elements = TRUE;
2107       }
2108
2109       break;
2110   }
2111
2112   if (!init_game)
2113     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2114 }
2115
2116 static void InitField_WithBug1(int x, int y, boolean init_game)
2117 {
2118   InitField(x, y, init_game);
2119
2120   // not needed to call InitMovDir() -- already done by InitField()!
2121   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2122       CAN_MOVE(Tile[x][y]))
2123     InitMovDir(x, y);
2124 }
2125
2126 static void InitField_WithBug2(int x, int y, boolean init_game)
2127 {
2128   int old_element = Tile[x][y];
2129
2130   InitField(x, y, init_game);
2131
2132   // not needed to call InitMovDir() -- already done by InitField()!
2133   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2134       CAN_MOVE(old_element) &&
2135       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2136     InitMovDir(x, y);
2137
2138   /* this case is in fact a combination of not less than three bugs:
2139      first, it calls InitMovDir() for elements that can move, although this is
2140      already done by InitField(); then, it checks the element that was at this
2141      field _before_ the call to InitField() (which can change it); lastly, it
2142      was not called for "mole with direction" elements, which were treated as
2143      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2144   */
2145 }
2146
2147 static int get_key_element_from_nr(int key_nr)
2148 {
2149   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2150                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2151                           EL_EM_KEY_1 : EL_KEY_1);
2152
2153   return key_base_element + key_nr;
2154 }
2155
2156 static int get_next_dropped_element(struct PlayerInfo *player)
2157 {
2158   return (player->inventory_size > 0 ?
2159           player->inventory_element[player->inventory_size - 1] :
2160           player->inventory_infinite_element != EL_UNDEFINED ?
2161           player->inventory_infinite_element :
2162           player->dynabombs_left > 0 ?
2163           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2164           EL_UNDEFINED);
2165 }
2166
2167 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2168 {
2169   // pos >= 0: get element from bottom of the stack;
2170   // pos <  0: get element from top of the stack
2171
2172   if (pos < 0)
2173   {
2174     int min_inventory_size = -pos;
2175     int inventory_pos = player->inventory_size - min_inventory_size;
2176     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2177
2178     return (player->inventory_size >= min_inventory_size ?
2179             player->inventory_element[inventory_pos] :
2180             player->inventory_infinite_element != EL_UNDEFINED ?
2181             player->inventory_infinite_element :
2182             player->dynabombs_left >= min_dynabombs_left ?
2183             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2184             EL_UNDEFINED);
2185   }
2186   else
2187   {
2188     int min_dynabombs_left = pos + 1;
2189     int min_inventory_size = pos + 1 - player->dynabombs_left;
2190     int inventory_pos = pos - player->dynabombs_left;
2191
2192     return (player->inventory_infinite_element != EL_UNDEFINED ?
2193             player->inventory_infinite_element :
2194             player->dynabombs_left >= min_dynabombs_left ?
2195             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2196             player->inventory_size >= min_inventory_size ?
2197             player->inventory_element[inventory_pos] :
2198             EL_UNDEFINED);
2199   }
2200 }
2201
2202 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2203 {
2204   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2205   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2206   int compare_result;
2207
2208   if (gpo1->sort_priority != gpo2->sort_priority)
2209     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2210   else
2211     compare_result = gpo1->nr - gpo2->nr;
2212
2213   return compare_result;
2214 }
2215
2216 int getPlayerInventorySize(int player_nr)
2217 {
2218   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2219     return game_em.ply[player_nr]->dynamite;
2220   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2221     return game_sp.red_disk_count;
2222   else
2223     return stored_player[player_nr].inventory_size;
2224 }
2225
2226 static void InitGameControlValues(void)
2227 {
2228   int i;
2229
2230   for (i = 0; game_panel_controls[i].nr != -1; i++)
2231   {
2232     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2233     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2234     struct TextPosInfo *pos = gpc->pos;
2235     int nr = gpc->nr;
2236     int type = gpc->type;
2237
2238     if (nr != i)
2239     {
2240       Error("'game_panel_controls' structure corrupted at %d", i);
2241
2242       Fail("this should not happen -- please debug");
2243     }
2244
2245     // force update of game controls after initialization
2246     gpc->value = gpc->last_value = -1;
2247     gpc->frame = gpc->last_frame = -1;
2248     gpc->gfx_frame = -1;
2249
2250     // determine panel value width for later calculation of alignment
2251     if (type == TYPE_INTEGER || type == TYPE_STRING)
2252     {
2253       pos->width = pos->size * getFontWidth(pos->font);
2254       pos->height = getFontHeight(pos->font);
2255     }
2256     else if (type == TYPE_ELEMENT)
2257     {
2258       pos->width = pos->size;
2259       pos->height = pos->size;
2260     }
2261
2262     // fill structure for game panel draw order
2263     gpo->nr = gpc->nr;
2264     gpo->sort_priority = pos->sort_priority;
2265   }
2266
2267   // sort game panel controls according to sort_priority and control number
2268   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2269         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2270 }
2271
2272 static void UpdatePlayfieldElementCount(void)
2273 {
2274   boolean use_element_count = FALSE;
2275   int i, j, x, y;
2276
2277   // first check if it is needed at all to calculate playfield element count
2278   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2279     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2280       use_element_count = TRUE;
2281
2282   if (!use_element_count)
2283     return;
2284
2285   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2286     element_info[i].element_count = 0;
2287
2288   SCAN_PLAYFIELD(x, y)
2289   {
2290     element_info[Tile[x][y]].element_count++;
2291   }
2292
2293   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2294     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2295       if (IS_IN_GROUP(j, i))
2296         element_info[EL_GROUP_START + i].element_count +=
2297           element_info[j].element_count;
2298 }
2299
2300 static void UpdateGameControlValues(void)
2301 {
2302   int i, k;
2303   int time = (game.LevelSolved ?
2304               game.LevelSolved_CountingTime :
2305               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2306               game_bd.time_left :
2307               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2308               game_em.lev->time :
2309               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2310               game_sp.time_played :
2311               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2312               game_mm.energy_left :
2313               game.no_level_time_limit ? TimePlayed : TimeLeft);
2314   int score = (game.LevelSolved ?
2315                game.LevelSolved_CountingScore :
2316                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2317                game_bd.score :
2318                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2319                game_em.lev->score :
2320                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2321                game_sp.score :
2322                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2323                game_mm.score :
2324                game.score);
2325   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2326               game_bd.gems_still_needed :
2327               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2328               game_em.lev->gems_needed :
2329               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2330               game_sp.infotrons_still_needed :
2331               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2332               game_mm.kettles_still_needed :
2333               game.gems_still_needed);
2334   int gems_needed = level.gems_needed;
2335   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2336                         game_bd.game->cave->diamonds_collected :
2337                         gems_needed - gems);
2338   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2339                     game_bd.game->cave->diamond_value :
2340                     level.score[SC_EMERALD]);
2341   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2342                      game_bd.gems_still_needed > 0 :
2343                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2344                      game_em.lev->gems_needed > 0 :
2345                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2346                      game_sp.infotrons_still_needed > 0 :
2347                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2348                      game_mm.kettles_still_needed > 0 ||
2349                      game_mm.lights_still_needed > 0 :
2350                      game.gems_still_needed > 0 ||
2351                      game.sokoban_fields_still_needed > 0 ||
2352                      game.sokoban_objects_still_needed > 0 ||
2353                      game.lights_still_needed > 0);
2354   int health = (game.LevelSolved ?
2355                 game.LevelSolved_CountingHealth :
2356                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2357                 MM_HEALTH(game_mm.laser_overload_value) :
2358                 game.health);
2359   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2360
2361   UpdatePlayfieldElementCount();
2362
2363   // update game panel control values
2364
2365   // used instead of "level_nr" (for network games)
2366   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2367   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2368   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2369   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2370   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2371
2372   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2373   for (i = 0; i < MAX_NUM_KEYS; i++)
2374     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2375   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2376   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2377
2378   if (game.centered_player_nr == -1)
2379   {
2380     for (i = 0; i < MAX_PLAYERS; i++)
2381     {
2382       // only one player in Supaplex game engine
2383       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2384         break;
2385
2386       for (k = 0; k < MAX_NUM_KEYS; k++)
2387       {
2388         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2389         {
2390           if (game_em.ply[i]->keys & (1 << k))
2391             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2392               get_key_element_from_nr(k);
2393         }
2394         else if (stored_player[i].key[k])
2395           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2396             get_key_element_from_nr(k);
2397       }
2398
2399       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2400         getPlayerInventorySize(i);
2401
2402       if (stored_player[i].num_white_keys > 0)
2403         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2404           EL_DC_KEY_WHITE;
2405
2406       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2407         stored_player[i].num_white_keys;
2408     }
2409   }
2410   else
2411   {
2412     int player_nr = game.centered_player_nr;
2413
2414     for (k = 0; k < MAX_NUM_KEYS; k++)
2415     {
2416       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2417       {
2418         if (game_em.ply[player_nr]->keys & (1 << k))
2419           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2420             get_key_element_from_nr(k);
2421       }
2422       else if (stored_player[player_nr].key[k])
2423         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2424           get_key_element_from_nr(k);
2425     }
2426
2427     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2428       getPlayerInventorySize(player_nr);
2429
2430     if (stored_player[player_nr].num_white_keys > 0)
2431       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2432
2433     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2434       stored_player[player_nr].num_white_keys;
2435   }
2436
2437   // re-arrange keys on game panel, if needed or if defined by style settings
2438   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2439   {
2440     int nr = GAME_PANEL_KEY_1 + i;
2441     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2442     struct TextPosInfo *pos = gpc->pos;
2443
2444     // skip check if key is not in the player's inventory
2445     if (gpc->value == EL_EMPTY)
2446       continue;
2447
2448     // check if keys should be arranged on panel from left to right
2449     if (pos->style == STYLE_LEFTMOST_POSITION)
2450     {
2451       // check previous key positions (left from current key)
2452       for (k = 0; k < i; k++)
2453       {
2454         int nr_new = GAME_PANEL_KEY_1 + k;
2455
2456         if (game_panel_controls[nr_new].value == EL_EMPTY)
2457         {
2458           game_panel_controls[nr_new].value = gpc->value;
2459           gpc->value = EL_EMPTY;
2460
2461           break;
2462         }
2463       }
2464     }
2465
2466     // check if "undefined" keys can be placed at some other position
2467     if (pos->x == -1 && pos->y == -1)
2468     {
2469       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2470
2471       // 1st try: display key at the same position as normal or EM keys
2472       if (game_panel_controls[nr_new].value == EL_EMPTY)
2473       {
2474         game_panel_controls[nr_new].value = gpc->value;
2475       }
2476       else
2477       {
2478         // 2nd try: display key at the next free position in the key panel
2479         for (k = 0; k < STD_NUM_KEYS; k++)
2480         {
2481           nr_new = GAME_PANEL_KEY_1 + k;
2482
2483           if (game_panel_controls[nr_new].value == EL_EMPTY)
2484           {
2485             game_panel_controls[nr_new].value = gpc->value;
2486
2487             break;
2488           }
2489         }
2490       }
2491     }
2492   }
2493
2494   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2495   {
2496     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2497       get_inventory_element_from_pos(local_player, i);
2498     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2499       get_inventory_element_from_pos(local_player, -i - 1);
2500   }
2501
2502   game_panel_controls[GAME_PANEL_SCORE].value = score;
2503   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2504
2505   game_panel_controls[GAME_PANEL_TIME].value = time;
2506
2507   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2508   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2509   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2510
2511   if (level.time == 0)
2512     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2513   else
2514     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2515
2516   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2517   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2518
2519   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2520
2521   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2522     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2523      EL_EMPTY);
2524   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2525     local_player->shield_normal_time_left;
2526   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2527     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2528      EL_EMPTY);
2529   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2530     local_player->shield_deadly_time_left;
2531
2532   game_panel_controls[GAME_PANEL_EXIT].value =
2533     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2534
2535   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2536     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2537   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2538     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2539      EL_EMC_MAGIC_BALL_SWITCH);
2540
2541   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2542     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2543   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2544     game.light_time_left;
2545
2546   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2547     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2548   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2549     game.timegate_time_left;
2550
2551   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2552     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2553
2554   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2555     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2556   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2557     game.lenses_time_left;
2558
2559   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2560     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2561   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2562     game.magnify_time_left;
2563
2564   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2565     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2566      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2567      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2568      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2569      EL_BALLOON_SWITCH_NONE);
2570
2571   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2572     local_player->dynabomb_count;
2573   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2574     local_player->dynabomb_size;
2575   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2576     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2577
2578   game_panel_controls[GAME_PANEL_PENGUINS].value =
2579     game.friends_still_needed;
2580
2581   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2582     game.sokoban_objects_still_needed;
2583   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2584     game.sokoban_fields_still_needed;
2585
2586   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2587     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2588
2589   for (i = 0; i < NUM_BELTS; i++)
2590   {
2591     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2592       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2593        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2594     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2595       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2596   }
2597
2598   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2599     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2600   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2601     game.magic_wall_time_left;
2602
2603   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2604     local_player->gravity;
2605
2606   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2607     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2608
2609   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2610     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2611       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2612        game.panel.element[i].id : EL_UNDEFINED);
2613
2614   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2615     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2616       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2617        element_info[game.panel.element_count[i].id].element_count : 0);
2618
2619   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2620     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2621       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2622        element_info[game.panel.ce_score[i].id].collect_score : 0);
2623
2624   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2625     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2626       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2627        element_info[game.panel.ce_score_element[i].id].collect_score :
2628        EL_UNDEFINED);
2629
2630   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2631   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2632   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2633
2634   // update game panel control frames
2635
2636   for (i = 0; game_panel_controls[i].nr != -1; i++)
2637   {
2638     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2639
2640     if (gpc->type == TYPE_ELEMENT)
2641     {
2642       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2643       {
2644         int last_anim_random_frame = gfx.anim_random_frame;
2645         int element = gpc->value;
2646         int graphic = el2panelimg(element);
2647         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2648                                sync_random_frame :
2649                                graphic_info[graphic].anim_global_anim_sync ?
2650                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2651
2652         if (gpc->value != gpc->last_value)
2653         {
2654           gpc->gfx_frame = 0;
2655           gpc->gfx_random = init_gfx_random;
2656         }
2657         else
2658         {
2659           gpc->gfx_frame++;
2660
2661           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2662               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2663             gpc->gfx_random = init_gfx_random;
2664         }
2665
2666         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2667           gfx.anim_random_frame = gpc->gfx_random;
2668
2669         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2670           gpc->gfx_frame = element_info[element].collect_score;
2671
2672         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2673
2674         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2675           gfx.anim_random_frame = last_anim_random_frame;
2676       }
2677     }
2678     else if (gpc->type == TYPE_GRAPHIC)
2679     {
2680       if (gpc->graphic != IMG_UNDEFINED)
2681       {
2682         int last_anim_random_frame = gfx.anim_random_frame;
2683         int graphic = gpc->graphic;
2684         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2685                                sync_random_frame :
2686                                graphic_info[graphic].anim_global_anim_sync ?
2687                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2688
2689         if (gpc->value != gpc->last_value)
2690         {
2691           gpc->gfx_frame = 0;
2692           gpc->gfx_random = init_gfx_random;
2693         }
2694         else
2695         {
2696           gpc->gfx_frame++;
2697
2698           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2699               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2700             gpc->gfx_random = init_gfx_random;
2701         }
2702
2703         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2704           gfx.anim_random_frame = gpc->gfx_random;
2705
2706         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2707
2708         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2709           gfx.anim_random_frame = last_anim_random_frame;
2710       }
2711     }
2712   }
2713 }
2714
2715 static void DisplayGameControlValues(void)
2716 {
2717   boolean redraw_panel = FALSE;
2718   int i;
2719
2720   for (i = 0; game_panel_controls[i].nr != -1; i++)
2721   {
2722     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2723
2724     if (PANEL_DEACTIVATED(gpc->pos))
2725       continue;
2726
2727     if (gpc->value == gpc->last_value &&
2728         gpc->frame == gpc->last_frame)
2729       continue;
2730
2731     redraw_panel = TRUE;
2732   }
2733
2734   if (!redraw_panel)
2735     return;
2736
2737   // copy default game door content to main double buffer
2738
2739   // !!! CHECK AGAIN !!!
2740   SetPanelBackground();
2741   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2742   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2743
2744   // redraw game control buttons
2745   RedrawGameButtons();
2746
2747   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2748
2749   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2750   {
2751     int nr = game_panel_order[i].nr;
2752     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2753     struct TextPosInfo *pos = gpc->pos;
2754     int type = gpc->type;
2755     int value = gpc->value;
2756     int frame = gpc->frame;
2757     int size = pos->size;
2758     int font = pos->font;
2759     boolean draw_masked = pos->draw_masked;
2760     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2761
2762     if (PANEL_DEACTIVATED(pos))
2763       continue;
2764
2765     if (pos->class == get_hash_from_string("extra_panel_items") &&
2766         !setup.prefer_extra_panel_items)
2767       continue;
2768
2769     gpc->last_value = value;
2770     gpc->last_frame = frame;
2771
2772     if (type == TYPE_INTEGER)
2773     {
2774       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2775           nr == GAME_PANEL_INVENTORY_COUNT ||
2776           nr == GAME_PANEL_SCORE ||
2777           nr == GAME_PANEL_HIGHSCORE ||
2778           nr == GAME_PANEL_TIME)
2779       {
2780         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2781
2782         if (use_dynamic_size)           // use dynamic number of digits
2783         {
2784           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2785                               nr == GAME_PANEL_INVENTORY_COUNT ||
2786                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2787           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2788                           nr == GAME_PANEL_INVENTORY_COUNT ||
2789                           nr == GAME_PANEL_TIME ? 1 : 2);
2790           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2791                        nr == GAME_PANEL_INVENTORY_COUNT ||
2792                        nr == GAME_PANEL_TIME ? 3 : 5);
2793           int size2 = size1 + size_add;
2794           int font1 = pos->font;
2795           int font2 = pos->font_alt;
2796
2797           size = (value < value_change ? size1 : size2);
2798           font = (value < value_change ? font1 : font2);
2799         }
2800       }
2801
2802       // correct text size if "digits" is zero or less
2803       if (size <= 0)
2804         size = strlen(int2str(value, size));
2805
2806       // dynamically correct text alignment
2807       pos->width = size * getFontWidth(font);
2808
2809       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2810                   int2str(value, size), font, mask_mode);
2811     }
2812     else if (type == TYPE_ELEMENT)
2813     {
2814       int element, graphic;
2815       Bitmap *src_bitmap;
2816       int src_x, src_y;
2817       int width, height;
2818       int dst_x = PANEL_XPOS(pos);
2819       int dst_y = PANEL_YPOS(pos);
2820
2821       if (value != EL_UNDEFINED && value != EL_EMPTY)
2822       {
2823         element = value;
2824         graphic = el2panelimg(value);
2825
2826 #if 0
2827         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2828               element, EL_NAME(element), size);
2829 #endif
2830
2831         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2832           size = TILESIZE;
2833
2834         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2835                               &src_x, &src_y);
2836
2837         width  = graphic_info[graphic].width  * size / TILESIZE;
2838         height = graphic_info[graphic].height * size / TILESIZE;
2839
2840         if (draw_masked)
2841           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2842                            dst_x, dst_y);
2843         else
2844           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2845                      dst_x, dst_y);
2846       }
2847     }
2848     else if (type == TYPE_GRAPHIC)
2849     {
2850       int graphic        = gpc->graphic;
2851       int graphic_active = gpc->graphic_active;
2852       Bitmap *src_bitmap;
2853       int src_x, src_y;
2854       int width, height;
2855       int dst_x = PANEL_XPOS(pos);
2856       int dst_y = PANEL_YPOS(pos);
2857       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2858                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2859
2860       if (graphic != IMG_UNDEFINED && !skip)
2861       {
2862         if (pos->style == STYLE_REVERSE)
2863           value = 100 - value;
2864
2865         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2866
2867         if (pos->direction & MV_HORIZONTAL)
2868         {
2869           width  = graphic_info[graphic_active].width * value / 100;
2870           height = graphic_info[graphic_active].height;
2871
2872           if (pos->direction == MV_LEFT)
2873           {
2874             src_x += graphic_info[graphic_active].width - width;
2875             dst_x += graphic_info[graphic_active].width - width;
2876           }
2877         }
2878         else
2879         {
2880           width  = graphic_info[graphic_active].width;
2881           height = graphic_info[graphic_active].height * value / 100;
2882
2883           if (pos->direction == MV_UP)
2884           {
2885             src_y += graphic_info[graphic_active].height - height;
2886             dst_y += graphic_info[graphic_active].height - height;
2887           }
2888         }
2889
2890         if (draw_masked)
2891           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2892                            dst_x, dst_y);
2893         else
2894           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2895                      dst_x, dst_y);
2896
2897         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2898
2899         if (pos->direction & MV_HORIZONTAL)
2900         {
2901           if (pos->direction == MV_RIGHT)
2902           {
2903             src_x += width;
2904             dst_x += width;
2905           }
2906           else
2907           {
2908             dst_x = PANEL_XPOS(pos);
2909           }
2910
2911           width = graphic_info[graphic].width - width;
2912         }
2913         else
2914         {
2915           if (pos->direction == MV_DOWN)
2916           {
2917             src_y += height;
2918             dst_y += height;
2919           }
2920           else
2921           {
2922             dst_y = PANEL_YPOS(pos);
2923           }
2924
2925           height = graphic_info[graphic].height - height;
2926         }
2927
2928         if (draw_masked)
2929           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2930                            dst_x, dst_y);
2931         else
2932           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2933                      dst_x, dst_y);
2934       }
2935     }
2936     else if (type == TYPE_STRING)
2937     {
2938       boolean active = (value != 0);
2939       char *state_normal = "off";
2940       char *state_active = "on";
2941       char *state = (active ? state_active : state_normal);
2942       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2943                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2944                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2945                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2946
2947       if (nr == GAME_PANEL_GRAVITY_STATE)
2948       {
2949         int font1 = pos->font;          // (used for normal state)
2950         int font2 = pos->font_alt;      // (used for active state)
2951
2952         font = (active ? font2 : font1);
2953       }
2954
2955       if (s != NULL)
2956       {
2957         char *s_cut;
2958
2959         if (size <= 0)
2960         {
2961           // don't truncate output if "chars" is zero or less
2962           size = strlen(s);
2963
2964           // dynamically correct text alignment
2965           pos->width = size * getFontWidth(font);
2966         }
2967
2968         s_cut = getStringCopyN(s, size);
2969
2970         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2971                     s_cut, font, mask_mode);
2972
2973         free(s_cut);
2974       }
2975     }
2976
2977     redraw_mask |= REDRAW_DOOR_1;
2978   }
2979
2980   SetGameStatus(GAME_MODE_PLAYING);
2981 }
2982
2983 void UpdateAndDisplayGameControlValues(void)
2984 {
2985   if (tape.deactivate_display)
2986     return;
2987
2988   UpdateGameControlValues();
2989   DisplayGameControlValues();
2990 }
2991
2992 void UpdateGameDoorValues(void)
2993 {
2994   UpdateGameControlValues();
2995 }
2996
2997 void DrawGameDoorValues(void)
2998 {
2999   DisplayGameControlValues();
3000 }
3001
3002
3003 // ============================================================================
3004 // InitGameEngine()
3005 // ----------------------------------------------------------------------------
3006 // initialize game engine due to level / tape version number
3007 // ============================================================================
3008
3009 static void InitGameEngine(void)
3010 {
3011   int i, j, k, l, x, y;
3012
3013   // set game engine from tape file when re-playing, else from level file
3014   game.engine_version = (tape.playing ? tape.engine_version :
3015                          level.game_version);
3016
3017   // set single or multi-player game mode (needed for re-playing tapes)
3018   game.team_mode = setup.team_mode;
3019
3020   if (tape.playing)
3021   {
3022     int num_players = 0;
3023
3024     for (i = 0; i < MAX_PLAYERS; i++)
3025       if (tape.player_participates[i])
3026         num_players++;
3027
3028     // multi-player tapes contain input data for more than one player
3029     game.team_mode = (num_players > 1);
3030   }
3031
3032 #if 0
3033   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3034         level.game_version);
3035   Debug("game:init:level", "          tape.file_version   == %06d",
3036         tape.file_version);
3037   Debug("game:init:level", "          tape.game_version   == %06d",
3038         tape.game_version);
3039   Debug("game:init:level", "          tape.engine_version == %06d",
3040         tape.engine_version);
3041   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3042         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3043 #endif
3044
3045   // --------------------------------------------------------------------------
3046   // set flags for bugs and changes according to active game engine version
3047   // --------------------------------------------------------------------------
3048
3049   /*
3050     Summary of bugfix:
3051     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3052
3053     Bug was introduced in version:
3054     2.0.1
3055
3056     Bug was fixed in version:
3057     4.2.0.0
3058
3059     Description:
3060     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3061     but the property "can fall" was missing, which caused some levels to be
3062     unsolvable. This was fixed in version 4.2.0.0.
3063
3064     Affected levels/tapes:
3065     An example for a tape that was fixed by this bugfix is tape 029 from the
3066     level set "rnd_sam_bateman".
3067     The wrong behaviour will still be used for all levels or tapes that were
3068     created/recorded with it. An example for this is tape 023 from the level
3069     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3070   */
3071
3072   boolean use_amoeba_dropping_cannot_fall_bug =
3073     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3074       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3075      (tape.playing &&
3076       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3077       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3078
3079   /*
3080     Summary of bugfix/change:
3081     Fixed move speed of elements entering or leaving magic wall.
3082
3083     Fixed/changed in version:
3084     2.0.1
3085
3086     Description:
3087     Before 2.0.1, move speed of elements entering or leaving magic wall was
3088     twice as fast as it is now.
3089     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3090
3091     Affected levels/tapes:
3092     The first condition is generally needed for all levels/tapes before version
3093     2.0.1, which might use the old behaviour before it was changed; known tapes
3094     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3095     The second condition is an exception from the above case and is needed for
3096     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3097     above, but before it was known that this change would break tapes like the
3098     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3099     although the engine version while recording maybe was before 2.0.1. There
3100     are a lot of tapes that are affected by this exception, like tape 006 from
3101     the level set "rnd_conor_mancone".
3102   */
3103
3104   boolean use_old_move_stepsize_for_magic_wall =
3105     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3106      !(tape.playing &&
3107        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3108        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3109
3110   /*
3111     Summary of bugfix/change:
3112     Fixed handling for custom elements that change when pushed by the player.
3113
3114     Fixed/changed in version:
3115     3.1.0
3116
3117     Description:
3118     Before 3.1.0, custom elements that "change when pushing" changed directly
3119     after the player started pushing them (until then handled in "DigField()").
3120     Since 3.1.0, these custom elements are not changed until the "pushing"
3121     move of the element is finished (now handled in "ContinueMoving()").
3122
3123     Affected levels/tapes:
3124     The first condition is generally needed for all levels/tapes before version
3125     3.1.0, which might use the old behaviour before it was changed; known tapes
3126     that are affected are some tapes from the level set "Walpurgis Gardens" by
3127     Jamie Cullen.
3128     The second condition is an exception from the above case and is needed for
3129     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3130     above (including some development versions of 3.1.0), but before it was
3131     known that this change would break tapes like the above and was fixed in
3132     3.1.1, so that the changed behaviour was active although the engine version
3133     while recording maybe was before 3.1.0. There is at least one tape that is
3134     affected by this exception, which is the tape for the one-level set "Bug
3135     Machine" by Juergen Bonhagen.
3136   */
3137
3138   game.use_change_when_pushing_bug =
3139     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3140      !(tape.playing &&
3141        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3142        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3143
3144   /*
3145     Summary of bugfix/change:
3146     Fixed handling for blocking the field the player leaves when moving.
3147
3148     Fixed/changed in version:
3149     3.1.1
3150
3151     Description:
3152     Before 3.1.1, when "block last field when moving" was enabled, the field
3153     the player is leaving when moving was blocked for the time of the move,
3154     and was directly unblocked afterwards. This resulted in the last field
3155     being blocked for exactly one less than the number of frames of one player
3156     move. Additionally, even when blocking was disabled, the last field was
3157     blocked for exactly one frame.
3158     Since 3.1.1, due to changes in player movement handling, the last field
3159     is not blocked at all when blocking is disabled. When blocking is enabled,
3160     the last field is blocked for exactly the number of frames of one player
3161     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3162     last field is blocked for exactly one more than the number of frames of
3163     one player move.
3164
3165     Affected levels/tapes:
3166     (!!! yet to be determined -- probably many !!!)
3167   */
3168
3169   game.use_block_last_field_bug =
3170     (game.engine_version < VERSION_IDENT(3,1,1,0));
3171
3172   /* various special flags and settings for native Emerald Mine game engine */
3173
3174   game_em.use_single_button =
3175     (game.engine_version > VERSION_IDENT(4,0,0,2));
3176
3177   game_em.use_push_delay =
3178     (game.engine_version > VERSION_IDENT(4,3,7,1));
3179
3180   game_em.use_snap_key_bug =
3181     (game.engine_version < VERSION_IDENT(4,0,1,0));
3182
3183   game_em.use_random_bug =
3184     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3185
3186   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3187
3188   game_em.use_old_explosions            = use_old_em_engine;
3189   game_em.use_old_android               = use_old_em_engine;
3190   game_em.use_old_push_elements         = use_old_em_engine;
3191   game_em.use_old_push_into_acid        = use_old_em_engine;
3192
3193   game_em.use_wrap_around               = !use_old_em_engine;
3194
3195   // --------------------------------------------------------------------------
3196
3197   // set maximal allowed number of custom element changes per game frame
3198   game.max_num_changes_per_frame = 1;
3199
3200   // default scan direction: scan playfield from top/left to bottom/right
3201   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3202
3203   // dynamically adjust element properties according to game engine version
3204   InitElementPropertiesEngine(game.engine_version);
3205
3206   // ---------- initialize special element properties -------------------------
3207
3208   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3209   if (use_amoeba_dropping_cannot_fall_bug)
3210     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3211
3212   // ---------- initialize player's initial move delay ------------------------
3213
3214   // dynamically adjust player properties according to level information
3215   for (i = 0; i < MAX_PLAYERS; i++)
3216     game.initial_move_delay_value[i] =
3217       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3218
3219   // dynamically adjust player properties according to game engine version
3220   for (i = 0; i < MAX_PLAYERS; i++)
3221     game.initial_move_delay[i] =
3222       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3223        game.initial_move_delay_value[i] : 0);
3224
3225   // ---------- initialize player's initial push delay ------------------------
3226
3227   // dynamically adjust player properties according to game engine version
3228   game.initial_push_delay_value =
3229     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3230
3231   // ---------- initialize changing elements ----------------------------------
3232
3233   // initialize changing elements information
3234   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3235   {
3236     struct ElementInfo *ei = &element_info[i];
3237
3238     // this pointer might have been changed in the level editor
3239     ei->change = &ei->change_page[0];
3240
3241     if (!IS_CUSTOM_ELEMENT(i))
3242     {
3243       ei->change->target_element = EL_EMPTY_SPACE;
3244       ei->change->delay_fixed = 0;
3245       ei->change->delay_random = 0;
3246       ei->change->delay_frames = 1;
3247     }
3248
3249     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3250     {
3251       ei->has_change_event[j] = FALSE;
3252
3253       ei->event_page_nr[j] = 0;
3254       ei->event_page[j] = &ei->change_page[0];
3255     }
3256   }
3257
3258   // add changing elements from pre-defined list
3259   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3260   {
3261     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3262     struct ElementInfo *ei = &element_info[ch_delay->element];
3263
3264     ei->change->target_element       = ch_delay->target_element;
3265     ei->change->delay_fixed          = ch_delay->change_delay;
3266
3267     ei->change->pre_change_function  = ch_delay->pre_change_function;
3268     ei->change->change_function      = ch_delay->change_function;
3269     ei->change->post_change_function = ch_delay->post_change_function;
3270
3271     ei->change->can_change = TRUE;
3272     ei->change->can_change_or_has_action = TRUE;
3273
3274     ei->has_change_event[CE_DELAY] = TRUE;
3275
3276     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3277     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3278   }
3279
3280   // ---------- initialize if element can trigger global animations -----------
3281
3282   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3283   {
3284     struct ElementInfo *ei = &element_info[i];
3285
3286     ei->has_anim_event = FALSE;
3287   }
3288
3289   InitGlobalAnimEventsForCustomElements();
3290
3291   // ---------- initialize internal run-time variables ------------------------
3292
3293   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3294   {
3295     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3296
3297     for (j = 0; j < ei->num_change_pages; j++)
3298     {
3299       ei->change_page[j].can_change_or_has_action =
3300         (ei->change_page[j].can_change |
3301          ei->change_page[j].has_action);
3302     }
3303   }
3304
3305   // add change events from custom element configuration
3306   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3307   {
3308     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3309
3310     for (j = 0; j < ei->num_change_pages; j++)
3311     {
3312       if (!ei->change_page[j].can_change_or_has_action)
3313         continue;
3314
3315       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3316       {
3317         // only add event page for the first page found with this event
3318         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3319         {
3320           ei->has_change_event[k] = TRUE;
3321
3322           ei->event_page_nr[k] = j;
3323           ei->event_page[k] = &ei->change_page[j];
3324         }
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize reference elements in change conditions ------------
3330
3331   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3332   {
3333     int element = EL_CUSTOM_START + i;
3334     struct ElementInfo *ei = &element_info[element];
3335
3336     for (j = 0; j < ei->num_change_pages; j++)
3337     {
3338       int trigger_element = ei->change_page[j].initial_trigger_element;
3339
3340       if (trigger_element >= EL_PREV_CE_8 &&
3341           trigger_element <= EL_NEXT_CE_8)
3342         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3343
3344       ei->change_page[j].trigger_element = trigger_element;
3345     }
3346   }
3347
3348   // ---------- initialize run-time trigger player and element ----------------
3349
3350   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3351   {
3352     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3353
3354     for (j = 0; j < ei->num_change_pages; j++)
3355     {
3356       struct ElementChangeInfo *change = &ei->change_page[j];
3357
3358       change->actual_trigger_element = EL_EMPTY;
3359       change->actual_trigger_player = EL_EMPTY;
3360       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3361       change->actual_trigger_side = CH_SIDE_NONE;
3362       change->actual_trigger_ce_value = 0;
3363       change->actual_trigger_ce_score = 0;
3364       change->actual_trigger_x = -1;
3365       change->actual_trigger_y = -1;
3366     }
3367   }
3368
3369   // ---------- initialize trigger events -------------------------------------
3370
3371   // initialize trigger events information
3372   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3374       trigger_events[i][j] = FALSE;
3375
3376   // add trigger events from element change event properties
3377   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3378   {
3379     struct ElementInfo *ei = &element_info[i];
3380
3381     for (j = 0; j < ei->num_change_pages; j++)
3382     {
3383       struct ElementChangeInfo *change = &ei->change_page[j];
3384
3385       if (!change->can_change_or_has_action)
3386         continue;
3387
3388       if (change->has_event[CE_BY_OTHER_ACTION])
3389       {
3390         int trigger_element = change->trigger_element;
3391
3392         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3393         {
3394           if (change->has_event[k])
3395           {
3396             if (IS_GROUP_ELEMENT(trigger_element))
3397             {
3398               struct ElementGroupInfo *group =
3399                 element_info[trigger_element].group;
3400
3401               for (l = 0; l < group->num_elements_resolved; l++)
3402                 trigger_events[group->element_resolved[l]][k] = TRUE;
3403             }
3404             else if (trigger_element == EL_ANY_ELEMENT)
3405               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3406                 trigger_events[l][k] = TRUE;
3407             else
3408               trigger_events[trigger_element][k] = TRUE;
3409           }
3410         }
3411       }
3412     }
3413   }
3414
3415   // ---------- initialize push delay -----------------------------------------
3416
3417   // initialize push delay values to default
3418   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3419   {
3420     if (!IS_CUSTOM_ELEMENT(i))
3421     {
3422       // set default push delay values (corrected since version 3.0.7-1)
3423       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3424       {
3425         element_info[i].push_delay_fixed = 2;
3426         element_info[i].push_delay_random = 8;
3427       }
3428       else
3429       {
3430         element_info[i].push_delay_fixed = 8;
3431         element_info[i].push_delay_random = 8;
3432       }
3433     }
3434   }
3435
3436   // set push delay value for certain elements from pre-defined list
3437   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3438   {
3439     int e = push_delay_list[i].element;
3440
3441     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3442     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3443   }
3444
3445   // set push delay value for Supaplex elements for newer engine versions
3446   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3447   {
3448     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3449     {
3450       if (IS_SP_ELEMENT(i))
3451       {
3452         // set SP push delay to just enough to push under a falling zonk
3453         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3454
3455         element_info[i].push_delay_fixed  = delay;
3456         element_info[i].push_delay_random = 0;
3457       }
3458     }
3459   }
3460
3461   // ---------- initialize move stepsize --------------------------------------
3462
3463   // initialize move stepsize values to default
3464   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3465     if (!IS_CUSTOM_ELEMENT(i))
3466       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3467
3468   // set move stepsize value for certain elements from pre-defined list
3469   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3470   {
3471     int e = move_stepsize_list[i].element;
3472
3473     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3474
3475     // set move stepsize value for certain elements for older engine versions
3476     if (use_old_move_stepsize_for_magic_wall)
3477     {
3478       if (e == EL_MAGIC_WALL_FILLING ||
3479           e == EL_MAGIC_WALL_EMPTYING ||
3480           e == EL_BD_MAGIC_WALL_FILLING ||
3481           e == EL_BD_MAGIC_WALL_EMPTYING)
3482         element_info[e].move_stepsize *= 2;
3483     }
3484   }
3485
3486   // ---------- initialize collect score --------------------------------------
3487
3488   // initialize collect score values for custom elements from initial value
3489   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3490     if (IS_CUSTOM_ELEMENT(i))
3491       element_info[i].collect_score = element_info[i].collect_score_initial;
3492
3493   // ---------- initialize collect count --------------------------------------
3494
3495   // initialize collect count values for non-custom elements
3496   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3497     if (!IS_CUSTOM_ELEMENT(i))
3498       element_info[i].collect_count_initial = 0;
3499
3500   // add collect count values for all elements from pre-defined list
3501   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3502     element_info[collect_count_list[i].element].collect_count_initial =
3503       collect_count_list[i].count;
3504
3505   // ---------- initialize access direction -----------------------------------
3506
3507   // initialize access direction values to default (access from every side)
3508   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3509     if (!IS_CUSTOM_ELEMENT(i))
3510       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3511
3512   // set access direction value for certain elements from pre-defined list
3513   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3514     element_info[access_direction_list[i].element].access_direction =
3515       access_direction_list[i].direction;
3516
3517   // ---------- initialize explosion content ----------------------------------
3518   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3519   {
3520     if (IS_CUSTOM_ELEMENT(i))
3521       continue;
3522
3523     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3524     {
3525       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3526
3527       element_info[i].content.e[x][y] =
3528         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3529          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3530          i == EL_PLAYER_3 ? EL_EMERALD :
3531          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3532          i == EL_MOLE ? EL_EMERALD_RED :
3533          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3534          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3535          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3536          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3537          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3538          i == EL_WALL_EMERALD ? EL_EMERALD :
3539          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3540          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3541          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3542          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3543          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3544          i == EL_WALL_PEARL ? EL_PEARL :
3545          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3546          EL_EMPTY);
3547     }
3548   }
3549
3550   // ---------- initialize recursion detection --------------------------------
3551   recursion_loop_depth = 0;
3552   recursion_loop_detected = FALSE;
3553   recursion_loop_element = EL_UNDEFINED;
3554
3555   // ---------- initialize graphics engine ------------------------------------
3556   game.scroll_delay_value =
3557     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3558      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3559      !setup.forced_scroll_delay           ? 0 :
3560      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3561   if (game.forced_scroll_delay_value == -1)
3562     game.scroll_delay_value =
3563       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3564
3565   // ---------- initialize game engine snapshots ------------------------------
3566   for (i = 0; i < MAX_PLAYERS; i++)
3567     game.snapshot.last_action[i] = 0;
3568   game.snapshot.changed_action = FALSE;
3569   game.snapshot.collected_item = FALSE;
3570   game.snapshot.mode =
3571     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3572      SNAPSHOT_MODE_EVERY_STEP :
3573      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3574      SNAPSHOT_MODE_EVERY_MOVE :
3575      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3576      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3577   game.snapshot.save_snapshot = FALSE;
3578
3579   // ---------- initialize level time for Supaplex engine ---------------------
3580   // Supaplex levels with time limit currently unsupported -- should be added
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3582     level.time = 0;
3583
3584   // ---------- initialize flags for handling game actions --------------------
3585
3586   // set flags for game actions to default values
3587   game.use_key_actions = TRUE;
3588   game.use_mouse_actions = FALSE;
3589
3590   // when using Mirror Magic game engine, handle mouse events only
3591   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3592   {
3593     game.use_key_actions = FALSE;
3594     game.use_mouse_actions = TRUE;
3595   }
3596
3597   // check for custom elements with mouse click events
3598   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3599   {
3600     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3601     {
3602       int element = EL_CUSTOM_START + i;
3603
3604       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3605           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3606           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3607           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3608         game.use_mouse_actions = TRUE;
3609     }
3610   }
3611 }
3612
3613 static int get_num_special_action(int element, int action_first,
3614                                   int action_last)
3615 {
3616   int num_special_action = 0;
3617   int i, j;
3618
3619   for (i = action_first; i <= action_last; i++)
3620   {
3621     boolean found = FALSE;
3622
3623     for (j = 0; j < NUM_DIRECTIONS; j++)
3624       if (el_act_dir2img(element, i, j) !=
3625           el_act_dir2img(element, ACTION_DEFAULT, j))
3626         found = TRUE;
3627
3628     if (found)
3629       num_special_action++;
3630     else
3631       break;
3632   }
3633
3634   return num_special_action;
3635 }
3636
3637
3638 // ============================================================================
3639 // InitGame()
3640 // ----------------------------------------------------------------------------
3641 // initialize and start new game
3642 // ============================================================================
3643
3644 #if DEBUG_INIT_PLAYER
3645 static void DebugPrintPlayerStatus(char *message)
3646 {
3647   int i;
3648
3649   if (!options.debug)
3650     return;
3651
3652   Debug("game:init:player", "%s:", message);
3653
3654   for (i = 0; i < MAX_PLAYERS; i++)
3655   {
3656     struct PlayerInfo *player = &stored_player[i];
3657
3658     Debug("game:init:player",
3659           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3660           i + 1,
3661           player->present,
3662           player->connected,
3663           player->connected_locally,
3664           player->connected_network,
3665           player->active,
3666           (local_player == player ? " (local player)" : ""));
3667   }
3668 }
3669 #endif
3670
3671 void InitGame(void)
3672 {
3673   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3674   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3675   int fade_mask = REDRAW_FIELD;
3676   boolean restarting = (game_status == GAME_MODE_PLAYING);
3677   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3678   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3679   int initial_move_dir = MV_DOWN;
3680   int i, j, x, y;
3681
3682   // required here to update video display before fading (FIX THIS)
3683   DrawMaskedBorder(REDRAW_DOOR_2);
3684
3685   if (!game.restart_level)
3686     CloseDoor(DOOR_CLOSE_1);
3687
3688   if (restarting)
3689   {
3690     // force fading out global animations displayed during game play
3691     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3692   }
3693   else
3694   {
3695     SetGameStatus(GAME_MODE_PLAYING);
3696
3697     // do not cover screen before fading out when starting from main menu
3698     game_bd.cover_screen = FALSE;
3699   }
3700
3701   if (level_editor_test_game)
3702     FadeSkipNextFadeOut();
3703   else
3704     FadeSetEnterScreen();
3705
3706   if (CheckFadeAll())
3707     fade_mask = REDRAW_ALL;
3708
3709   FadeLevelSoundsAndMusic();
3710
3711   ExpireSoundLoops(TRUE);
3712
3713   FadeOut(fade_mask);
3714
3715   if (restarting)
3716   {
3717     // force restarting global animations displayed during game play
3718     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3719
3720     // this is required for "transforming" fade modes like cross-fading
3721     // (else global animations will be stopped, but not restarted here)
3722     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3723
3724     SetGameStatus(GAME_MODE_PLAYING);
3725   }
3726
3727   if (level_editor_test_game)
3728     FadeSkipNextFadeIn();
3729
3730   // needed if different viewport properties defined for playing
3731   ChangeViewportPropertiesIfNeeded();
3732
3733   ClearField();
3734
3735   DrawCompleteVideoDisplay();
3736
3737   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3738
3739   InitGameEngine();
3740   InitGameControlValues();
3741
3742   if (tape.recording)
3743   {
3744     // initialize tape actions from game when recording tape
3745     tape.use_key_actions   = game.use_key_actions;
3746     tape.use_mouse_actions = game.use_mouse_actions;
3747
3748     // initialize visible playfield size when recording tape (for team mode)
3749     tape.scr_fieldx = SCR_FIELDX;
3750     tape.scr_fieldy = SCR_FIELDY;
3751   }
3752
3753   // don't play tapes over network
3754   network_playing = (network.enabled && !tape.playing);
3755
3756   for (i = 0; i < MAX_PLAYERS; i++)
3757   {
3758     struct PlayerInfo *player = &stored_player[i];
3759
3760     player->index_nr = i;
3761     player->index_bit = (1 << i);
3762     player->element_nr = EL_PLAYER_1 + i;
3763
3764     player->present = FALSE;
3765     player->active = FALSE;
3766     player->mapped = FALSE;
3767
3768     player->killed = FALSE;
3769     player->reanimated = FALSE;
3770     player->buried = FALSE;
3771
3772     player->action = 0;
3773     player->effective_action = 0;
3774     player->programmed_action = 0;
3775     player->snap_action = 0;
3776
3777     player->mouse_action.lx = 0;
3778     player->mouse_action.ly = 0;
3779     player->mouse_action.button = 0;
3780     player->mouse_action.button_hint = 0;
3781
3782     player->effective_mouse_action.lx = 0;
3783     player->effective_mouse_action.ly = 0;
3784     player->effective_mouse_action.button = 0;
3785     player->effective_mouse_action.button_hint = 0;
3786
3787     for (j = 0; j < MAX_NUM_KEYS; j++)
3788       player->key[j] = FALSE;
3789
3790     player->num_white_keys = 0;
3791
3792     player->dynabomb_count = 0;
3793     player->dynabomb_size = 1;
3794     player->dynabombs_left = 0;
3795     player->dynabomb_xl = FALSE;
3796
3797     player->MovDir = initial_move_dir;
3798     player->MovPos = 0;
3799     player->GfxPos = 0;
3800     player->GfxDir = initial_move_dir;
3801     player->GfxAction = ACTION_DEFAULT;
3802     player->Frame = 0;
3803     player->StepFrame = 0;
3804
3805     player->initial_element = player->element_nr;
3806     player->artwork_element =
3807       (level.use_artwork_element[i] ? level.artwork_element[i] :
3808        player->element_nr);
3809     player->use_murphy = FALSE;
3810
3811     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3812     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3813
3814     player->gravity = level.initial_player_gravity[i];
3815
3816     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3817
3818     player->actual_frame_counter.count = 0;
3819     player->actual_frame_counter.value = 1;
3820
3821     player->step_counter = 0;
3822
3823     player->last_move_dir = initial_move_dir;
3824
3825     player->is_active = FALSE;
3826
3827     player->is_waiting = FALSE;
3828     player->is_moving = FALSE;
3829     player->is_auto_moving = FALSE;
3830     player->is_digging = FALSE;
3831     player->is_snapping = FALSE;
3832     player->is_collecting = FALSE;
3833     player->is_pushing = FALSE;
3834     player->is_switching = FALSE;
3835     player->is_dropping = FALSE;
3836     player->is_dropping_pressed = FALSE;
3837
3838     player->is_bored = FALSE;
3839     player->is_sleeping = FALSE;
3840
3841     player->was_waiting = TRUE;
3842     player->was_moving = FALSE;
3843     player->was_snapping = FALSE;
3844     player->was_dropping = FALSE;
3845
3846     player->force_dropping = FALSE;
3847
3848     player->frame_counter_bored = -1;
3849     player->frame_counter_sleeping = -1;
3850
3851     player->anim_delay_counter = 0;
3852     player->post_delay_counter = 0;
3853
3854     player->dir_waiting = initial_move_dir;
3855     player->action_waiting = ACTION_DEFAULT;
3856     player->last_action_waiting = ACTION_DEFAULT;
3857     player->special_action_bored = ACTION_DEFAULT;
3858     player->special_action_sleeping = ACTION_DEFAULT;
3859
3860     player->switch_x = -1;
3861     player->switch_y = -1;
3862
3863     player->drop_x = -1;
3864     player->drop_y = -1;
3865
3866     player->show_envelope = 0;
3867
3868     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3869
3870     player->push_delay       = -1;      // initialized when pushing starts
3871     player->push_delay_value = game.initial_push_delay_value;
3872
3873     player->drop_delay = 0;
3874     player->drop_pressed_delay = 0;
3875
3876     player->last_jx = -1;
3877     player->last_jy = -1;
3878     player->jx = -1;
3879     player->jy = -1;
3880
3881     player->shield_normal_time_left = 0;
3882     player->shield_deadly_time_left = 0;
3883
3884     player->last_removed_element = EL_UNDEFINED;
3885
3886     player->inventory_infinite_element = EL_UNDEFINED;
3887     player->inventory_size = 0;
3888
3889     if (level.use_initial_inventory[i])
3890     {
3891       for (j = 0; j < level.initial_inventory_size[i]; j++)
3892       {
3893         int element = level.initial_inventory_content[i][j];
3894         int collect_count = element_info[element].collect_count_initial;
3895         int k;
3896
3897         if (!IS_CUSTOM_ELEMENT(element))
3898           collect_count = 1;
3899
3900         if (collect_count == 0)
3901           player->inventory_infinite_element = element;
3902         else
3903           for (k = 0; k < collect_count; k++)
3904             if (player->inventory_size < MAX_INVENTORY_SIZE)
3905               player->inventory_element[player->inventory_size++] = element;
3906       }
3907     }
3908
3909     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3910     SnapField(player, 0, 0);
3911
3912     map_player_action[i] = i;
3913   }
3914
3915   network_player_action_received = FALSE;
3916
3917   // initial null action
3918   if (network_playing)
3919     SendToServer_MovePlayer(MV_NONE);
3920
3921   FrameCounter = 0;
3922   TimeFrames = 0;
3923   TimePlayed = 0;
3924   TimeLeft = level.time;
3925
3926   TapeTimeFrames = 0;
3927   TapeTime = 0;
3928
3929   ScreenMovDir = MV_NONE;
3930   ScreenMovPos = 0;
3931   ScreenGfxPos = 0;
3932
3933   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3934
3935   game.robot_wheel_x = -1;
3936   game.robot_wheel_y = -1;
3937
3938   game.exit_x = -1;
3939   game.exit_y = -1;
3940
3941   game.all_players_gone = FALSE;
3942
3943   game.LevelSolved = FALSE;
3944   game.GameOver = FALSE;
3945
3946   game.GamePlayed = !tape.playing;
3947
3948   game.LevelSolved_GameWon = FALSE;
3949   game.LevelSolved_GameEnd = FALSE;
3950   game.LevelSolved_SaveTape = FALSE;
3951   game.LevelSolved_SaveScore = FALSE;
3952
3953   game.LevelSolved_CountingTime = 0;
3954   game.LevelSolved_CountingScore = 0;
3955   game.LevelSolved_CountingHealth = 0;
3956
3957   game.RestartGameRequested = FALSE;
3958
3959   game.panel.active = TRUE;
3960
3961   game.no_level_time_limit = (level.time == 0);
3962   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3963
3964   game.yamyam_content_nr = 0;
3965   game.robot_wheel_active = FALSE;
3966   game.magic_wall_active = FALSE;
3967   game.magic_wall_time_left = 0;
3968   game.light_time_left = 0;
3969   game.timegate_time_left = 0;
3970   game.switchgate_pos = 0;
3971   game.wind_direction = level.wind_direction_initial;
3972
3973   game.time_final = 0;
3974   game.score_time_final = 0;
3975
3976   game.score = 0;
3977   game.score_final = 0;
3978
3979   game.health = MAX_HEALTH;
3980   game.health_final = MAX_HEALTH;
3981
3982   game.gems_still_needed = level.gems_needed;
3983   game.sokoban_fields_still_needed = 0;
3984   game.sokoban_objects_still_needed = 0;
3985   game.lights_still_needed = 0;
3986   game.players_still_needed = 0;
3987   game.friends_still_needed = 0;
3988
3989   game.lenses_time_left = 0;
3990   game.magnify_time_left = 0;
3991
3992   game.ball_active = level.ball_active_initial;
3993   game.ball_content_nr = 0;
3994
3995   game.explosions_delayed = TRUE;
3996
3997   // special case: set custom artwork setting to initial value
3998   game.use_masked_elements = game.use_masked_elements_initial;
3999
4000   for (i = 0; i < NUM_BELTS; i++)
4001   {
4002     game.belt_dir[i] = MV_NONE;
4003     game.belt_dir_nr[i] = 3;            // not moving, next moving left
4004   }
4005
4006   for (i = 0; i < MAX_NUM_AMOEBA; i++)
4007     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
4008
4009 #if DEBUG_INIT_PLAYER
4010   DebugPrintPlayerStatus("Player status at level initialization");
4011 #endif
4012
4013   SCAN_PLAYFIELD(x, y)
4014   {
4015     Tile[x][y] = Last[x][y] = level.field[x][y];
4016     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
4017     ChangeDelay[x][y] = 0;
4018     ChangePage[x][y] = -1;
4019     CustomValue[x][y] = 0;              // initialized in InitField()
4020     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4021     AmoebaNr[x][y] = 0;
4022     WasJustMoving[x][y] = 0;
4023     WasJustFalling[x][y] = 0;
4024     CheckCollision[x][y] = 0;
4025     CheckImpact[x][y] = 0;
4026     Stop[x][y] = FALSE;
4027     Pushed[x][y] = FALSE;
4028
4029     ChangeCount[x][y] = 0;
4030     ChangeEvent[x][y] = -1;
4031
4032     ExplodePhase[x][y] = 0;
4033     ExplodeDelay[x][y] = 0;
4034     ExplodeField[x][y] = EX_TYPE_NONE;
4035
4036     RunnerVisit[x][y] = 0;
4037     PlayerVisit[x][y] = 0;
4038
4039     GfxFrame[x][y] = 0;
4040     GfxRandom[x][y] = INIT_GFX_RANDOM();
4041     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4042     GfxElement[x][y] = EL_UNDEFINED;
4043     GfxElementEmpty[x][y] = EL_EMPTY;
4044     GfxAction[x][y] = ACTION_DEFAULT;
4045     GfxDir[x][y] = MV_NONE;
4046     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4047   }
4048
4049   SCAN_PLAYFIELD(x, y)
4050   {
4051     InitFieldForEngine(x, y);
4052
4053     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4054       emulate_bd = FALSE;
4055     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4056       emulate_sp = FALSE;
4057
4058     InitField(x, y, TRUE);
4059
4060     ResetGfxAnimation(x, y);
4061   }
4062
4063   InitBeltMovement();
4064
4065   // required if level does not contain any "empty space" element
4066   if (element_info[EL_EMPTY].use_gfx_element)
4067     game.use_masked_elements = TRUE;
4068
4069   for (i = 0; i < MAX_PLAYERS; i++)
4070   {
4071     struct PlayerInfo *player = &stored_player[i];
4072
4073     // set number of special actions for bored and sleeping animation
4074     player->num_special_action_bored =
4075       get_num_special_action(player->artwork_element,
4076                              ACTION_BORING_1, ACTION_BORING_LAST);
4077     player->num_special_action_sleeping =
4078       get_num_special_action(player->artwork_element,
4079                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4080   }
4081
4082   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4083                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4084
4085   // initialize type of slippery elements
4086   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4087   {
4088     if (!IS_CUSTOM_ELEMENT(i))
4089     {
4090       // default: elements slip down either to the left or right randomly
4091       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4092
4093       // SP style elements prefer to slip down on the left side
4094       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4095         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4096
4097       // BD style elements prefer to slip down on the left side
4098       if (game.emulation == EMU_BOULDERDASH)
4099         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4100     }
4101   }
4102
4103   // initialize explosion and ignition delay
4104   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4105   {
4106     if (!IS_CUSTOM_ELEMENT(i))
4107     {
4108       int num_phase = 8;
4109       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4110                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4111                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4112       int last_phase = (num_phase + 1) * delay;
4113       int half_phase = (num_phase / 2) * delay;
4114
4115       element_info[i].explosion_delay = last_phase - 1;
4116       element_info[i].ignition_delay = half_phase;
4117
4118       if (i == EL_BLACK_ORB)
4119         element_info[i].ignition_delay = 1;
4120     }
4121   }
4122
4123   // correct non-moving belts to start moving left
4124   for (i = 0; i < NUM_BELTS; i++)
4125     if (game.belt_dir[i] == MV_NONE)
4126       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4127
4128 #if USE_NEW_PLAYER_ASSIGNMENTS
4129   // use preferred player also in local single-player mode
4130   if (!network.enabled && !game.team_mode)
4131   {
4132     int new_index_nr = setup.network_player_nr;
4133
4134     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4135     {
4136       for (i = 0; i < MAX_PLAYERS; i++)
4137         stored_player[i].connected_locally = FALSE;
4138
4139       stored_player[new_index_nr].connected_locally = TRUE;
4140     }
4141   }
4142
4143   for (i = 0; i < MAX_PLAYERS; i++)
4144   {
4145     stored_player[i].connected = FALSE;
4146
4147     // in network game mode, the local player might not be the first player
4148     if (stored_player[i].connected_locally)
4149       local_player = &stored_player[i];
4150   }
4151
4152   if (!network.enabled)
4153     local_player->connected = TRUE;
4154
4155   if (tape.playing)
4156   {
4157     for (i = 0; i < MAX_PLAYERS; i++)
4158       stored_player[i].connected = tape.player_participates[i];
4159   }
4160   else if (network.enabled)
4161   {
4162     // add team mode players connected over the network (needed for correct
4163     // assignment of player figures from level to locally playing players)
4164
4165     for (i = 0; i < MAX_PLAYERS; i++)
4166       if (stored_player[i].connected_network)
4167         stored_player[i].connected = TRUE;
4168   }
4169   else if (game.team_mode)
4170   {
4171     // try to guess locally connected team mode players (needed for correct
4172     // assignment of player figures from level to locally playing players)
4173
4174     for (i = 0; i < MAX_PLAYERS; i++)
4175       if (setup.input[i].use_joystick ||
4176           setup.input[i].key.left != KSYM_UNDEFINED)
4177         stored_player[i].connected = TRUE;
4178   }
4179
4180 #if DEBUG_INIT_PLAYER
4181   DebugPrintPlayerStatus("Player status after level initialization");
4182 #endif
4183
4184 #if DEBUG_INIT_PLAYER
4185   Debug("game:init:player", "Reassigning players ...");
4186 #endif
4187
4188   // check if any connected player was not found in playfield
4189   for (i = 0; i < MAX_PLAYERS; i++)
4190   {
4191     struct PlayerInfo *player = &stored_player[i];
4192
4193     if (player->connected && !player->present)
4194     {
4195       struct PlayerInfo *field_player = NULL;
4196
4197 #if DEBUG_INIT_PLAYER
4198       Debug("game:init:player",
4199             "- looking for field player for player %d ...", i + 1);
4200 #endif
4201
4202       // assign first free player found that is present in the playfield
4203
4204       // first try: look for unmapped playfield player that is not connected
4205       for (j = 0; j < MAX_PLAYERS; j++)
4206         if (field_player == NULL &&
4207             stored_player[j].present &&
4208             !stored_player[j].mapped &&
4209             !stored_player[j].connected)
4210           field_player = &stored_player[j];
4211
4212       // second try: look for *any* unmapped playfield player
4213       for (j = 0; j < MAX_PLAYERS; j++)
4214         if (field_player == NULL &&
4215             stored_player[j].present &&
4216             !stored_player[j].mapped)
4217           field_player = &stored_player[j];
4218
4219       if (field_player != NULL)
4220       {
4221         int jx = field_player->jx, jy = field_player->jy;
4222
4223 #if DEBUG_INIT_PLAYER
4224         Debug("game:init:player", "- found player %d",
4225               field_player->index_nr + 1);
4226 #endif
4227
4228         player->present = FALSE;
4229         player->active = FALSE;
4230
4231         field_player->present = TRUE;
4232         field_player->active = TRUE;
4233
4234         /*
4235         player->initial_element = field_player->initial_element;
4236         player->artwork_element = field_player->artwork_element;
4237
4238         player->block_last_field       = field_player->block_last_field;
4239         player->block_delay_adjustment = field_player->block_delay_adjustment;
4240         */
4241
4242         StorePlayer[jx][jy] = field_player->element_nr;
4243
4244         field_player->jx = field_player->last_jx = jx;
4245         field_player->jy = field_player->last_jy = jy;
4246
4247         if (local_player == player)
4248           local_player = field_player;
4249
4250         map_player_action[field_player->index_nr] = i;
4251
4252         field_player->mapped = TRUE;
4253
4254 #if DEBUG_INIT_PLAYER
4255         Debug("game:init:player", "- map_player_action[%d] == %d",
4256               field_player->index_nr + 1, i + 1);
4257 #endif
4258       }
4259     }
4260
4261     if (player->connected && player->present)
4262       player->mapped = TRUE;
4263   }
4264
4265 #if DEBUG_INIT_PLAYER
4266   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4267 #endif
4268
4269 #else
4270
4271   // check if any connected player was not found in playfield
4272   for (i = 0; i < MAX_PLAYERS; i++)
4273   {
4274     struct PlayerInfo *player = &stored_player[i];
4275
4276     if (player->connected && !player->present)
4277     {
4278       for (j = 0; j < MAX_PLAYERS; j++)
4279       {
4280         struct PlayerInfo *field_player = &stored_player[j];
4281         int jx = field_player->jx, jy = field_player->jy;
4282
4283         // assign first free player found that is present in the playfield
4284         if (field_player->present && !field_player->connected)
4285         {
4286           player->present = TRUE;
4287           player->active = TRUE;
4288
4289           field_player->present = FALSE;
4290           field_player->active = FALSE;
4291
4292           player->initial_element = field_player->initial_element;
4293           player->artwork_element = field_player->artwork_element;
4294
4295           player->block_last_field       = field_player->block_last_field;
4296           player->block_delay_adjustment = field_player->block_delay_adjustment;
4297
4298           StorePlayer[jx][jy] = player->element_nr;
4299
4300           player->jx = player->last_jx = jx;
4301           player->jy = player->last_jy = jy;
4302
4303           break;
4304         }
4305       }
4306     }
4307   }
4308 #endif
4309
4310 #if 0
4311   Debug("game:init:player", "local_player->present == %d",
4312         local_player->present);
4313 #endif
4314
4315   // set focus to local player for network games, else to all players
4316   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4317   game.centered_player_nr_next = game.centered_player_nr;
4318   game.set_centered_player = FALSE;
4319   game.set_centered_player_wrap = FALSE;
4320
4321   if (network_playing && tape.recording)
4322   {
4323     // store client dependent player focus when recording network games
4324     tape.centered_player_nr_next = game.centered_player_nr_next;
4325     tape.set_centered_player = TRUE;
4326   }
4327
4328   if (tape.playing)
4329   {
4330     // when playing a tape, eliminate all players who do not participate
4331
4332 #if USE_NEW_PLAYER_ASSIGNMENTS
4333
4334     if (!game.team_mode)
4335     {
4336       for (i = 0; i < MAX_PLAYERS; i++)
4337       {
4338         if (stored_player[i].active &&
4339             !tape.player_participates[map_player_action[i]])
4340         {
4341           struct PlayerInfo *player = &stored_player[i];
4342           int jx = player->jx, jy = player->jy;
4343
4344 #if DEBUG_INIT_PLAYER
4345           Debug("game:init:player", "Removing player %d at (%d, %d)",
4346                 i + 1, jx, jy);
4347 #endif
4348
4349           player->active = FALSE;
4350           StorePlayer[jx][jy] = 0;
4351           Tile[jx][jy] = EL_EMPTY;
4352         }
4353       }
4354     }
4355
4356 #else
4357
4358     for (i = 0; i < MAX_PLAYERS; i++)
4359     {
4360       if (stored_player[i].active &&
4361           !tape.player_participates[i])
4362       {
4363         struct PlayerInfo *player = &stored_player[i];
4364         int jx = player->jx, jy = player->jy;
4365
4366         player->active = FALSE;
4367         StorePlayer[jx][jy] = 0;
4368         Tile[jx][jy] = EL_EMPTY;
4369       }
4370     }
4371 #endif
4372   }
4373   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4374   {
4375     // when in single player mode, eliminate all but the local player
4376
4377     for (i = 0; i < MAX_PLAYERS; i++)
4378     {
4379       struct PlayerInfo *player = &stored_player[i];
4380
4381       if (player->active && player != local_player)
4382       {
4383         int jx = player->jx, jy = player->jy;
4384
4385         player->active = FALSE;
4386         player->present = FALSE;
4387
4388         StorePlayer[jx][jy] = 0;
4389         Tile[jx][jy] = EL_EMPTY;
4390       }
4391     }
4392   }
4393
4394   for (i = 0; i < MAX_PLAYERS; i++)
4395     if (stored_player[i].active)
4396       game.players_still_needed++;
4397
4398   if (level.solved_by_one_player)
4399     game.players_still_needed = 1;
4400
4401   // when recording the game, store which players take part in the game
4402   if (tape.recording)
4403   {
4404 #if USE_NEW_PLAYER_ASSIGNMENTS
4405     for (i = 0; i < MAX_PLAYERS; i++)
4406       if (stored_player[i].connected)
4407         tape.player_participates[i] = TRUE;
4408 #else
4409     for (i = 0; i < MAX_PLAYERS; i++)
4410       if (stored_player[i].active)
4411         tape.player_participates[i] = TRUE;
4412 #endif
4413   }
4414
4415 #if DEBUG_INIT_PLAYER
4416   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4417 #endif
4418
4419   if (BorderElement == EL_EMPTY)
4420   {
4421     SBX_Left = 0;
4422     SBX_Right = lev_fieldx - SCR_FIELDX;
4423     SBY_Upper = 0;
4424     SBY_Lower = lev_fieldy - SCR_FIELDY;
4425   }
4426   else
4427   {
4428     SBX_Left = -1;
4429     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4430     SBY_Upper = -1;
4431     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4432   }
4433
4434   if (full_lev_fieldx <= SCR_FIELDX)
4435     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4436   if (full_lev_fieldy <= SCR_FIELDY)
4437     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4438
4439   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4440     SBX_Left--;
4441   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4442     SBY_Upper--;
4443
4444   // if local player not found, look for custom element that might create
4445   // the player (make some assumptions about the right custom element)
4446   if (!local_player->present)
4447   {
4448     int start_x = 0, start_y = 0;
4449     int found_rating = 0;
4450     int found_element = EL_UNDEFINED;
4451     int player_nr = local_player->index_nr;
4452
4453     SCAN_PLAYFIELD(x, y)
4454     {
4455       int element = Tile[x][y];
4456       int content;
4457       int xx, yy;
4458       boolean is_player;
4459
4460       if (level.use_start_element[player_nr] &&
4461           level.start_element[player_nr] == element &&
4462           found_rating < 4)
4463       {
4464         start_x = x;
4465         start_y = y;
4466
4467         found_rating = 4;
4468         found_element = element;
4469       }
4470
4471       if (!IS_CUSTOM_ELEMENT(element))
4472         continue;
4473
4474       if (CAN_CHANGE(element))
4475       {
4476         for (i = 0; i < element_info[element].num_change_pages; i++)
4477         {
4478           // check for player created from custom element as single target
4479           content = element_info[element].change_page[i].target_element;
4480           is_player = IS_PLAYER_ELEMENT(content);
4481
4482           if (is_player && (found_rating < 3 ||
4483                             (found_rating == 3 && element < found_element)))
4484           {
4485             start_x = x;
4486             start_y = y;
4487
4488             found_rating = 3;
4489             found_element = element;
4490           }
4491         }
4492       }
4493
4494       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4495       {
4496         // check for player created from custom element as explosion content
4497         content = element_info[element].content.e[xx][yy];
4498         is_player = IS_PLAYER_ELEMENT(content);
4499
4500         if (is_player && (found_rating < 2 ||
4501                           (found_rating == 2 && element < found_element)))
4502         {
4503           start_x = x + xx - 1;
4504           start_y = y + yy - 1;
4505
4506           found_rating = 2;
4507           found_element = element;
4508         }
4509
4510         if (!CAN_CHANGE(element))
4511           continue;
4512
4513         for (i = 0; i < element_info[element].num_change_pages; i++)
4514         {
4515           // check for player created from custom element as extended target
4516           content =
4517             element_info[element].change_page[i].target_content.e[xx][yy];
4518
4519           is_player = IS_PLAYER_ELEMENT(content);
4520
4521           if (is_player && (found_rating < 1 ||
4522                             (found_rating == 1 && element < found_element)))
4523           {
4524             start_x = x + xx - 1;
4525             start_y = y + yy - 1;
4526
4527             found_rating = 1;
4528             found_element = element;
4529           }
4530         }
4531       }
4532     }
4533
4534     scroll_x = SCROLL_POSITION_X(start_x);
4535     scroll_y = SCROLL_POSITION_Y(start_y);
4536   }
4537   else
4538   {
4539     scroll_x = SCROLL_POSITION_X(local_player->jx);
4540     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4541   }
4542
4543   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4544     scroll_x = game.forced_scroll_x;
4545   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4546     scroll_y = game.forced_scroll_y;
4547
4548   // !!! FIX THIS (START) !!!
4549   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4550   {
4551     InitGameEngine_BD();
4552   }
4553   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4554   {
4555     InitGameEngine_EM();
4556   }
4557   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4558   {
4559     InitGameEngine_SP();
4560   }
4561   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4562   {
4563     InitGameEngine_MM();
4564   }
4565   else
4566   {
4567     DrawLevel(REDRAW_FIELD);
4568     DrawAllPlayers();
4569
4570     // after drawing the level, correct some elements
4571     if (game.timegate_time_left == 0)
4572       CloseAllOpenTimegates();
4573   }
4574
4575   // blit playfield from scroll buffer to normal back buffer for fading in
4576   BlitScreenToBitmap(backbuffer);
4577   // !!! FIX THIS (END) !!!
4578
4579   DrawMaskedBorder(fade_mask);
4580
4581   FadeIn(fade_mask);
4582
4583 #if 1
4584   // full screen redraw is required at this point in the following cases:
4585   // - special editor door undrawn when game was started from level editor
4586   // - drawing area (playfield) was changed and has to be removed completely
4587   redraw_mask = REDRAW_ALL;
4588   BackToFront();
4589 #endif
4590
4591   if (!game.restart_level)
4592   {
4593     // copy default game door content to main double buffer
4594
4595     // !!! CHECK AGAIN !!!
4596     SetPanelBackground();
4597     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4598     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4599   }
4600
4601   SetPanelBackground();
4602   SetDrawBackgroundMask(REDRAW_DOOR_1);
4603
4604   UpdateAndDisplayGameControlValues();
4605
4606   if (!game.restart_level)
4607   {
4608     UnmapGameButtons();
4609     UnmapTapeButtons();
4610
4611     FreeGameButtons();
4612     CreateGameButtons();
4613
4614     MapGameButtons();
4615     MapTapeButtons();
4616
4617     // copy actual game door content to door double buffer for OpenDoor()
4618     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4619
4620     OpenDoor(DOOR_OPEN_ALL);
4621
4622     KeyboardAutoRepeatOffUnlessAutoplay();
4623
4624 #if DEBUG_INIT_PLAYER
4625     DebugPrintPlayerStatus("Player status (final)");
4626 #endif
4627   }
4628
4629   UnmapAllGadgets();
4630
4631   MapGameButtons();
4632   MapTapeButtons();
4633
4634   if (!game.restart_level && !tape.playing)
4635   {
4636     LevelStats_incPlayed(level_nr);
4637
4638     SaveLevelSetup_SeriesInfo();
4639   }
4640
4641   game.restart_level = FALSE;
4642   game.request_active = FALSE;
4643   game.envelope_active = FALSE;
4644   game.any_door_active = FALSE;
4645
4646   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4647     InitGameActions_MM();
4648
4649   SaveEngineSnapshotToListInitial();
4650
4651   if (!game.restart_level)
4652   {
4653     PlaySound(SND_GAME_STARTING);
4654
4655     if (setup.sound_music)
4656       PlayLevelMusic();
4657   }
4658
4659   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4660 }
4661
4662 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4663                         int actual_player_x, int actual_player_y)
4664 {
4665   // this is used for non-R'n'D game engines to update certain engine values
4666
4667   // needed to determine if sounds are played within the visible screen area
4668   scroll_x = actual_scroll_x;
4669   scroll_y = actual_scroll_y;
4670
4671   // needed to get player position for "follow finger" playing input method
4672   local_player->jx = actual_player_x;
4673   local_player->jy = actual_player_y;
4674 }
4675
4676 void InitMovDir(int x, int y)
4677 {
4678   int i, element = Tile[x][y];
4679   static int xy[4][2] =
4680   {
4681     {  0, +1 },
4682     { +1,  0 },
4683     {  0, -1 },
4684     { -1,  0 }
4685   };
4686   static int direction[3][4] =
4687   {
4688     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4689     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4690     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4691   };
4692
4693   switch (element)
4694   {
4695     case EL_BUG_RIGHT:
4696     case EL_BUG_UP:
4697     case EL_BUG_LEFT:
4698     case EL_BUG_DOWN:
4699       Tile[x][y] = EL_BUG;
4700       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4701       break;
4702
4703     case EL_SPACESHIP_RIGHT:
4704     case EL_SPACESHIP_UP:
4705     case EL_SPACESHIP_LEFT:
4706     case EL_SPACESHIP_DOWN:
4707       Tile[x][y] = EL_SPACESHIP;
4708       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4709       break;
4710
4711     case EL_BD_BUTTERFLY_RIGHT:
4712     case EL_BD_BUTTERFLY_UP:
4713     case EL_BD_BUTTERFLY_LEFT:
4714     case EL_BD_BUTTERFLY_DOWN:
4715       Tile[x][y] = EL_BD_BUTTERFLY;
4716       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4717       break;
4718
4719     case EL_BD_FIREFLY_RIGHT:
4720     case EL_BD_FIREFLY_UP:
4721     case EL_BD_FIREFLY_LEFT:
4722     case EL_BD_FIREFLY_DOWN:
4723       Tile[x][y] = EL_BD_FIREFLY;
4724       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4725       break;
4726
4727     case EL_PACMAN_RIGHT:
4728     case EL_PACMAN_UP:
4729     case EL_PACMAN_LEFT:
4730     case EL_PACMAN_DOWN:
4731       Tile[x][y] = EL_PACMAN;
4732       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4733       break;
4734
4735     case EL_YAMYAM_LEFT:
4736     case EL_YAMYAM_RIGHT:
4737     case EL_YAMYAM_UP:
4738     case EL_YAMYAM_DOWN:
4739       Tile[x][y] = EL_YAMYAM;
4740       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4741       break;
4742
4743     case EL_SP_SNIKSNAK:
4744       MovDir[x][y] = MV_UP;
4745       break;
4746
4747     case EL_SP_ELECTRON:
4748       MovDir[x][y] = MV_LEFT;
4749       break;
4750
4751     case EL_MOLE_LEFT:
4752     case EL_MOLE_RIGHT:
4753     case EL_MOLE_UP:
4754     case EL_MOLE_DOWN:
4755       Tile[x][y] = EL_MOLE;
4756       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4757       break;
4758
4759     case EL_SPRING_LEFT:
4760     case EL_SPRING_RIGHT:
4761       Tile[x][y] = EL_SPRING;
4762       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4763       break;
4764
4765     default:
4766       if (IS_CUSTOM_ELEMENT(element))
4767       {
4768         struct ElementInfo *ei = &element_info[element];
4769         int move_direction_initial = ei->move_direction_initial;
4770         int move_pattern = ei->move_pattern;
4771
4772         if (move_direction_initial == MV_START_PREVIOUS)
4773         {
4774           if (MovDir[x][y] != MV_NONE)
4775             return;
4776
4777           move_direction_initial = MV_START_AUTOMATIC;
4778         }
4779
4780         if (move_direction_initial == MV_START_RANDOM)
4781           MovDir[x][y] = 1 << RND(4);
4782         else if (move_direction_initial & MV_ANY_DIRECTION)
4783           MovDir[x][y] = move_direction_initial;
4784         else if (move_pattern == MV_ALL_DIRECTIONS ||
4785                  move_pattern == MV_TURNING_LEFT ||
4786                  move_pattern == MV_TURNING_RIGHT ||
4787                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4788                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4789                  move_pattern == MV_TURNING_RANDOM)
4790           MovDir[x][y] = 1 << RND(4);
4791         else if (move_pattern == MV_HORIZONTAL)
4792           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4793         else if (move_pattern == MV_VERTICAL)
4794           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4795         else if (move_pattern & MV_ANY_DIRECTION)
4796           MovDir[x][y] = element_info[element].move_pattern;
4797         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4798                  move_pattern == MV_ALONG_RIGHT_SIDE)
4799         {
4800           // use random direction as default start direction
4801           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4802             MovDir[x][y] = 1 << RND(4);
4803
4804           for (i = 0; i < NUM_DIRECTIONS; i++)
4805           {
4806             int x1 = x + xy[i][0];
4807             int y1 = y + xy[i][1];
4808
4809             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4810             {
4811               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4812                 MovDir[x][y] = direction[0][i];
4813               else
4814                 MovDir[x][y] = direction[1][i];
4815
4816               break;
4817             }
4818           }
4819         }                
4820       }
4821       else
4822       {
4823         MovDir[x][y] = 1 << RND(4);
4824
4825         if (element != EL_BUG &&
4826             element != EL_SPACESHIP &&
4827             element != EL_BD_BUTTERFLY &&
4828             element != EL_BD_FIREFLY)
4829           break;
4830
4831         for (i = 0; i < NUM_DIRECTIONS; i++)
4832         {
4833           int x1 = x + xy[i][0];
4834           int y1 = y + xy[i][1];
4835
4836           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4837           {
4838             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4839             {
4840               MovDir[x][y] = direction[0][i];
4841               break;
4842             }
4843             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4844                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4845             {
4846               MovDir[x][y] = direction[1][i];
4847               break;
4848             }
4849           }
4850         }
4851       }
4852       break;
4853   }
4854
4855   GfxDir[x][y] = MovDir[x][y];
4856 }
4857
4858 void InitAmoebaNr(int x, int y)
4859 {
4860   int i;
4861   int group_nr = AmoebaNeighbourNr(x, y);
4862
4863   if (group_nr == 0)
4864   {
4865     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4866     {
4867       if (AmoebaCnt[i] == 0)
4868       {
4869         group_nr = i;
4870         break;
4871       }
4872     }
4873   }
4874
4875   AmoebaNr[x][y] = group_nr;
4876   AmoebaCnt[group_nr]++;
4877   AmoebaCnt2[group_nr]++;
4878 }
4879
4880 static void LevelSolved_SetFinalGameValues(void)
4881 {
4882   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_left :
4883                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4884   game.score_time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.frames_played :
4885                            level.use_step_counter ? TimePlayed :
4886                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4887
4888   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4889                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4890                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4891                       game.score);
4892
4893   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4894                        MM_HEALTH(game_mm.laser_overload_value) :
4895                        game.health);
4896
4897   game.LevelSolved_CountingTime = game.time_final;
4898   game.LevelSolved_CountingScore = game.score_final;
4899   game.LevelSolved_CountingHealth = game.health_final;
4900 }
4901
4902 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4903 {
4904   game.LevelSolved_CountingTime = time;
4905   game.LevelSolved_CountingScore = score;
4906   game.LevelSolved_CountingHealth = health;
4907
4908   game_panel_controls[GAME_PANEL_TIME].value = time;
4909   game_panel_controls[GAME_PANEL_SCORE].value = score;
4910   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4911
4912   DisplayGameControlValues();
4913 }
4914
4915 static void LevelSolved(void)
4916 {
4917   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4918       game.players_still_needed > 0)
4919     return;
4920
4921   game.LevelSolved = TRUE;
4922   game.GameOver = TRUE;
4923
4924   tape.solved = TRUE;
4925
4926   // needed here to display correct panel values while player walks into exit
4927   LevelSolved_SetFinalGameValues();
4928 }
4929
4930 static boolean AdvanceToNextLevel(void)
4931 {
4932   if (setup.increment_levels &&
4933       level_nr < leveldir_current->last_level &&
4934       !network_playing)
4935   {
4936     level_nr++;         // advance to next level
4937     TapeErase();        // start with empty tape
4938
4939     if (setup.auto_play_next_level)
4940     {
4941       scores.continue_playing = TRUE;
4942       scores.next_level_nr = level_nr;
4943
4944       LoadLevel(level_nr);
4945
4946       SaveLevelSetup_SeriesInfo();
4947     }
4948
4949     return TRUE;
4950   }
4951
4952   return FALSE;
4953 }
4954
4955 void GameWon(void)
4956 {
4957   static int time_count_steps;
4958   static int time, time_final;
4959   static float score, score_final; // needed for time score < 10 for 10 seconds
4960   static int health, health_final;
4961   static int game_over_delay_1 = 0;
4962   static int game_over_delay_2 = 0;
4963   static int game_over_delay_3 = 0;
4964   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4965   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4966
4967   if (!game.LevelSolved_GameWon)
4968   {
4969     int i;
4970
4971     // do not start end game actions before the player stops moving (to exit)
4972     if (local_player->active && local_player->MovPos)
4973       return;
4974
4975     // calculate final game values after player finished walking into exit
4976     LevelSolved_SetFinalGameValues();
4977
4978     game.LevelSolved_GameWon = TRUE;
4979     game.LevelSolved_SaveTape = tape.recording;
4980     game.LevelSolved_SaveScore = !tape.playing;
4981
4982     if (!tape.playing)
4983     {
4984       LevelStats_incSolved(level_nr);
4985
4986       SaveLevelSetup_SeriesInfo();
4987     }
4988
4989     if (tape.auto_play)         // tape might already be stopped here
4990       tape.auto_play_level_solved = TRUE;
4991
4992     TapeStop();
4993
4994     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4995     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4996     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4997
4998     time = time_final = game.time_final;
4999     score = score_final = game.score_final;
5000     health = health_final = game.health_final;
5001
5002     // update game panel values before (delayed) counting of score (if any)
5003     LevelSolved_DisplayFinalGameValues(time, score, health);
5004
5005     // if level has time score defined, calculate new final game values
5006     if (time_score > 0)
5007     {
5008       int time_final_max = 999;
5009       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
5010       int time_frames = 0;
5011       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
5012       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
5013
5014       if (TimeLeft > 0)
5015       {
5016         time_final = 0;
5017         time_frames = time_frames_left;
5018       }
5019       else if (game.no_level_time_limit && TimePlayed < time_final_max)
5020       {
5021         time_final = time_final_max;
5022         time_frames = time_frames_final_max - time_frames_played;
5023       }
5024
5025       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5026
5027       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5028
5029       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5030       {
5031         // keep previous values (final values already processed here)
5032         time_final = time;
5033         score_final = score;
5034       }
5035       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5036       {
5037         health_final = 0;
5038         score_final += health * time_score;
5039       }
5040
5041       game.score_final = score_final;
5042       game.health_final = health_final;
5043     }
5044
5045     // if not counting score after game, immediately update game panel values
5046     if (level_editor_test_game || !setup.count_score_after_game ||
5047         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5048     {
5049       time = time_final;
5050       score = score_final;
5051
5052       LevelSolved_DisplayFinalGameValues(time, score, health);
5053     }
5054
5055     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5056     {
5057       // check if last player has left the level
5058       if (game.exit_x >= 0 &&
5059           game.exit_y >= 0)
5060       {
5061         int x = game.exit_x;
5062         int y = game.exit_y;
5063         int element = Tile[x][y];
5064
5065         // close exit door after last player
5066         if ((game.all_players_gone &&
5067              (element == EL_EXIT_OPEN ||
5068               element == EL_SP_EXIT_OPEN ||
5069               element == EL_STEEL_EXIT_OPEN)) ||
5070             element == EL_EM_EXIT_OPEN ||
5071             element == EL_EM_STEEL_EXIT_OPEN)
5072         {
5073
5074           Tile[x][y] =
5075             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5076              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5077              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5078              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5079              EL_EM_STEEL_EXIT_CLOSING);
5080
5081           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5082         }
5083
5084         // player disappears
5085         DrawLevelField(x, y);
5086       }
5087
5088       for (i = 0; i < MAX_PLAYERS; i++)
5089       {
5090         struct PlayerInfo *player = &stored_player[i];
5091
5092         if (player->present)
5093         {
5094           RemovePlayer(player);
5095
5096           // player disappears
5097           DrawLevelField(player->jx, player->jy);
5098         }
5099       }
5100     }
5101
5102     PlaySound(SND_GAME_WINNING);
5103   }
5104
5105   if (setup.count_score_after_game)
5106   {
5107     if (time != time_final)
5108     {
5109       if (game_over_delay_1 > 0)
5110       {
5111         game_over_delay_1--;
5112
5113         return;
5114       }
5115
5116       int time_to_go = ABS(time_final - time);
5117       int time_count_dir = (time < time_final ? +1 : -1);
5118
5119       if (time_to_go < time_count_steps)
5120         time_count_steps = 1;
5121
5122       time  += time_count_steps * time_count_dir;
5123       score += time_count_steps * time_score;
5124
5125       // set final score to correct rounding differences after counting score
5126       if (time == time_final)
5127         score = score_final;
5128
5129       LevelSolved_DisplayFinalGameValues(time, score, health);
5130
5131       if (time == time_final)
5132         StopSound(SND_GAME_LEVELTIME_BONUS);
5133       else if (setup.sound_loops)
5134         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5135       else
5136         PlaySound(SND_GAME_LEVELTIME_BONUS);
5137
5138       return;
5139     }
5140
5141     if (health != health_final)
5142     {
5143       if (game_over_delay_2 > 0)
5144       {
5145         game_over_delay_2--;
5146
5147         return;
5148       }
5149
5150       int health_count_dir = (health < health_final ? +1 : -1);
5151
5152       health += health_count_dir;
5153       score  += time_score;
5154
5155       LevelSolved_DisplayFinalGameValues(time, score, health);
5156
5157       if (health == health_final)
5158         StopSound(SND_GAME_LEVELTIME_BONUS);
5159       else if (setup.sound_loops)
5160         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5161       else
5162         PlaySound(SND_GAME_LEVELTIME_BONUS);
5163
5164       return;
5165     }
5166   }
5167
5168   game.panel.active = FALSE;
5169
5170   if (game_over_delay_3 > 0)
5171   {
5172     game_over_delay_3--;
5173
5174     return;
5175   }
5176
5177   GameEnd();
5178 }
5179
5180 void GameEnd(void)
5181 {
5182   // used instead of "level_nr" (needed for network games)
5183   int last_level_nr = levelset.level_nr;
5184   boolean tape_saved = FALSE;
5185   boolean game_over = checkGameFailed();
5186
5187   // Important note: This function is not only called after "GameWon()", but also after
5188   // "game over" (if automatically asking for restarting the game is disabled in setup)
5189
5190   // do not handle game end if game over and automatically asking for game restart
5191   if (game_over && setup.ask_on_game_over)
5192   {
5193     // (this is a special case: player pressed "return" key or fire button shortly before
5194     // automatically asking to restart the game, so skip asking and restart right away)
5195
5196     CloseDoor(DOOR_CLOSE_1);
5197
5198     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5199
5200     return;
5201   }
5202
5203   // do not handle game end if request dialog is already active
5204   if (checkRequestActive())
5205     return;
5206
5207   if (game.LevelSolved)
5208     game.LevelSolved_GameEnd = TRUE;
5209
5210   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5211   {
5212     // make sure that request dialog to save tape does not open door again
5213     if (!global.use_envelope_request)
5214       CloseDoor(DOOR_CLOSE_1);
5215
5216     // ask to save tape
5217     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5218
5219     // set unique basename for score tape (also saved in high score table)
5220     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5221   }
5222
5223   // if no tape is to be saved, close both doors simultaneously
5224   CloseDoor(DOOR_CLOSE_ALL);
5225
5226   if (level_editor_test_game || score_info_tape_play)
5227   {
5228     SetGameStatus(GAME_MODE_MAIN);
5229
5230     DrawMainMenu();
5231
5232     return;
5233   }
5234
5235   if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
5236   {
5237     SetGameStatus(GAME_MODE_MAIN);
5238
5239     DrawMainMenu();
5240
5241     return;
5242   }
5243
5244   if (level_nr == leveldir_current->handicap_level)
5245   {
5246     leveldir_current->handicap_level++;
5247
5248     SaveLevelSetup_SeriesInfo();
5249   }
5250
5251   // save score and score tape before potentially erasing tape below
5252   if (game.LevelSolved_SaveScore)
5253     NewHighScore(last_level_nr, tape_saved);
5254
5255   // increment and load next level (if possible and not configured otherwise)
5256   AdvanceToNextLevel();
5257
5258   if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
5259   {
5260     SetGameStatus(GAME_MODE_SCORES);
5261
5262     DrawHallOfFame(last_level_nr);
5263   }
5264   else if (scores.continue_playing)
5265   {
5266     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5267   }
5268   else
5269   {
5270     SetGameStatus(GAME_MODE_MAIN);
5271
5272     DrawMainMenu();
5273   }
5274 }
5275
5276 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5277                          boolean one_score_entry_per_name)
5278 {
5279   int i;
5280
5281   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5282     return -1;
5283
5284   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5285   {
5286     struct ScoreEntry *entry = &list->entry[i];
5287     boolean score_is_better = (new_entry->score >  entry->score);
5288     boolean score_is_equal  = (new_entry->score == entry->score);
5289     boolean time_is_better  = (new_entry->time  <  entry->time);
5290     boolean time_is_equal   = (new_entry->time  == entry->time);
5291     boolean better_by_score = (score_is_better ||
5292                                (score_is_equal && time_is_better));
5293     boolean better_by_time  = (time_is_better ||
5294                                (time_is_equal && score_is_better));
5295     boolean is_better = (level.rate_time_over_score ? better_by_time :
5296                          better_by_score);
5297     boolean entry_is_empty = (entry->score == 0 &&
5298                               entry->time == 0);
5299
5300     // prevent adding server score entries if also existing in local score file
5301     // (special case: historic score entries have an empty tape basename entry)
5302     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5303         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5304     {
5305       // add fields from server score entry not stored in local score entry
5306       // (currently, this means setting platform, version and country fields;
5307       // in rare cases, this may also correct an invalid score value, as
5308       // historic scores might have been truncated to 16-bit values locally)
5309       *entry = *new_entry;
5310
5311       return -1;
5312     }
5313
5314     if (is_better || entry_is_empty)
5315     {
5316       // player has made it to the hall of fame
5317
5318       if (i < MAX_SCORE_ENTRIES - 1)
5319       {
5320         int m = MAX_SCORE_ENTRIES - 1;
5321         int l;
5322
5323         if (one_score_entry_per_name)
5324         {
5325           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5326             if (strEqual(list->entry[l].name, new_entry->name))
5327               m = l;
5328
5329           if (m == i)   // player's new highscore overwrites his old one
5330             goto put_into_list;
5331         }
5332
5333         for (l = m; l > i; l--)
5334           list->entry[l] = list->entry[l - 1];
5335       }
5336
5337       put_into_list:
5338
5339       *entry = *new_entry;
5340
5341       return i;
5342     }
5343     else if (one_score_entry_per_name &&
5344              strEqual(entry->name, new_entry->name))
5345     {
5346       // player already in high score list with better score or time
5347
5348       return -1;
5349     }
5350   }
5351
5352   // special case: new score is beyond the last high score list position
5353   return MAX_SCORE_ENTRIES;
5354 }
5355
5356 void NewHighScore(int level_nr, boolean tape_saved)
5357 {
5358   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5359   boolean one_per_name = FALSE;
5360
5361   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5362   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5363
5364   new_entry.score = game.score_final;
5365   new_entry.time = game.score_time_final;
5366
5367   LoadScore(level_nr);
5368
5369   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5370
5371   if (scores.last_added >= MAX_SCORE_ENTRIES)
5372   {
5373     scores.last_added = MAX_SCORE_ENTRIES - 1;
5374     scores.force_last_added = TRUE;
5375
5376     scores.entry[scores.last_added] = new_entry;
5377
5378     // store last added local score entry (before merging server scores)
5379     scores.last_added_local = scores.last_added;
5380
5381     return;
5382   }
5383
5384   if (scores.last_added < 0)
5385     return;
5386
5387   SaveScore(level_nr);
5388
5389   // store last added local score entry (before merging server scores)
5390   scores.last_added_local = scores.last_added;
5391
5392   if (!game.LevelSolved_SaveTape)
5393     return;
5394
5395   SaveScoreTape(level_nr);
5396
5397   if (setup.ask_for_using_api_server)
5398   {
5399     setup.use_api_server =
5400       Request("Upload your score and tape to the high score server?", REQ_ASK);
5401
5402     if (!setup.use_api_server)
5403       Request("Not using high score server! Use setup menu to enable again!",
5404               REQ_CONFIRM);
5405
5406     runtime.use_api_server = setup.use_api_server;
5407
5408     // after asking for using API server once, do not ask again
5409     setup.ask_for_using_api_server = FALSE;
5410
5411     SaveSetup_ServerSetup();
5412   }
5413
5414   SaveServerScore(level_nr, tape_saved);
5415 }
5416
5417 void MergeServerScore(void)
5418 {
5419   struct ScoreEntry last_added_entry;
5420   boolean one_per_name = FALSE;
5421   int i;
5422
5423   if (scores.last_added >= 0)
5424     last_added_entry = scores.entry[scores.last_added];
5425
5426   for (i = 0; i < server_scores.num_entries; i++)
5427   {
5428     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5429
5430     if (pos >= 0 && pos <= scores.last_added)
5431       scores.last_added++;
5432   }
5433
5434   if (scores.last_added >= MAX_SCORE_ENTRIES)
5435   {
5436     scores.last_added = MAX_SCORE_ENTRIES - 1;
5437     scores.force_last_added = TRUE;
5438
5439     scores.entry[scores.last_added] = last_added_entry;
5440   }
5441 }
5442
5443 static int getElementMoveStepsizeExt(int x, int y, int direction)
5444 {
5445   int element = Tile[x][y];
5446   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5447   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5448   int horiz_move = (dx != 0);
5449   int sign = (horiz_move ? dx : dy);
5450   int step = sign * element_info[element].move_stepsize;
5451
5452   // special values for move stepsize for spring and things on conveyor belt
5453   if (horiz_move)
5454   {
5455     if (CAN_FALL(element) &&
5456         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5457       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5458     else if (element == EL_SPRING)
5459       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5460   }
5461
5462   return step;
5463 }
5464
5465 static int getElementMoveStepsize(int x, int y)
5466 {
5467   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5468 }
5469
5470 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5471 {
5472   if (player->GfxAction != action || player->GfxDir != dir)
5473   {
5474     player->GfxAction = action;
5475     player->GfxDir = dir;
5476     player->Frame = 0;
5477     player->StepFrame = 0;
5478   }
5479 }
5480
5481 static void ResetGfxFrame(int x, int y)
5482 {
5483   // profiling showed that "autotest" spends 10~20% of its time in this function
5484   if (DrawingDeactivatedField())
5485     return;
5486
5487   int element = Tile[x][y];
5488   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5489
5490   if (graphic_info[graphic].anim_global_sync)
5491     GfxFrame[x][y] = FrameCounter;
5492   else if (graphic_info[graphic].anim_global_anim_sync)
5493     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5494   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5495     GfxFrame[x][y] = CustomValue[x][y];
5496   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5497     GfxFrame[x][y] = element_info[element].collect_score;
5498   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5499     GfxFrame[x][y] = ChangeDelay[x][y];
5500 }
5501
5502 static void ResetGfxAnimation(int x, int y)
5503 {
5504   GfxAction[x][y] = ACTION_DEFAULT;
5505   GfxDir[x][y] = MovDir[x][y];
5506   GfxFrame[x][y] = 0;
5507
5508   ResetGfxFrame(x, y);
5509 }
5510
5511 static void ResetRandomAnimationValue(int x, int y)
5512 {
5513   GfxRandom[x][y] = INIT_GFX_RANDOM();
5514 }
5515
5516 static void InitMovingField(int x, int y, int direction)
5517 {
5518   int element = Tile[x][y];
5519   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5520   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5521   int newx = x + dx;
5522   int newy = y + dy;
5523   boolean is_moving_before, is_moving_after;
5524
5525   // check if element was/is moving or being moved before/after mode change
5526   is_moving_before = (WasJustMoving[x][y] != 0);
5527   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5528
5529   // reset animation only for moving elements which change direction of moving
5530   // or which just started or stopped moving
5531   // (else CEs with property "can move" / "not moving" are reset each frame)
5532   if (is_moving_before != is_moving_after ||
5533       direction != MovDir[x][y])
5534     ResetGfxAnimation(x, y);
5535
5536   MovDir[x][y] = direction;
5537   GfxDir[x][y] = direction;
5538
5539   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5540                      direction == MV_DOWN && CAN_FALL(element) ?
5541                      ACTION_FALLING : ACTION_MOVING);
5542
5543   // this is needed for CEs with property "can move" / "not moving"
5544
5545   if (is_moving_after)
5546   {
5547     if (Tile[newx][newy] == EL_EMPTY)
5548       Tile[newx][newy] = EL_BLOCKED;
5549
5550     MovDir[newx][newy] = MovDir[x][y];
5551
5552     CustomValue[newx][newy] = CustomValue[x][y];
5553
5554     GfxFrame[newx][newy] = GfxFrame[x][y];
5555     GfxRandom[newx][newy] = GfxRandom[x][y];
5556     GfxAction[newx][newy] = GfxAction[x][y];
5557     GfxDir[newx][newy] = GfxDir[x][y];
5558   }
5559 }
5560
5561 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5562 {
5563   int direction = MovDir[x][y];
5564   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5565   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5566
5567   *goes_to_x = newx;
5568   *goes_to_y = newy;
5569 }
5570
5571 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5572 {
5573   int direction = MovDir[x][y];
5574   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5575   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5576
5577   *comes_from_x = oldx;
5578   *comes_from_y = oldy;
5579 }
5580
5581 static int MovingOrBlocked2Element(int x, int y)
5582 {
5583   int element = Tile[x][y];
5584
5585   if (element == EL_BLOCKED)
5586   {
5587     int oldx, oldy;
5588
5589     Blocked2Moving(x, y, &oldx, &oldy);
5590
5591     return Tile[oldx][oldy];
5592   }
5593
5594   return element;
5595 }
5596
5597 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5598 {
5599   // like MovingOrBlocked2Element(), but if element is moving
5600   // and (x, y) is the field the moving element is just leaving,
5601   // return EL_BLOCKED instead of the element value
5602   int element = Tile[x][y];
5603
5604   if (IS_MOVING(x, y))
5605   {
5606     if (element == EL_BLOCKED)
5607     {
5608       int oldx, oldy;
5609
5610       Blocked2Moving(x, y, &oldx, &oldy);
5611       return Tile[oldx][oldy];
5612     }
5613     else
5614       return EL_BLOCKED;
5615   }
5616   else
5617     return element;
5618 }
5619
5620 static void RemoveField(int x, int y)
5621 {
5622   Tile[x][y] = EL_EMPTY;
5623
5624   MovPos[x][y] = 0;
5625   MovDir[x][y] = 0;
5626   MovDelay[x][y] = 0;
5627
5628   CustomValue[x][y] = 0;
5629
5630   AmoebaNr[x][y] = 0;
5631   ChangeDelay[x][y] = 0;
5632   ChangePage[x][y] = -1;
5633   Pushed[x][y] = FALSE;
5634
5635   GfxElement[x][y] = EL_UNDEFINED;
5636   GfxAction[x][y] = ACTION_DEFAULT;
5637   GfxDir[x][y] = MV_NONE;
5638 }
5639
5640 static void RemoveMovingField(int x, int y)
5641 {
5642   int oldx = x, oldy = y, newx = x, newy = y;
5643   int element = Tile[x][y];
5644   int next_element = EL_UNDEFINED;
5645
5646   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5647     return;
5648
5649   if (IS_MOVING(x, y))
5650   {
5651     Moving2Blocked(x, y, &newx, &newy);
5652
5653     if (Tile[newx][newy] != EL_BLOCKED)
5654     {
5655       // element is moving, but target field is not free (blocked), but
5656       // already occupied by something different (example: acid pool);
5657       // in this case, only remove the moving field, but not the target
5658
5659       RemoveField(oldx, oldy);
5660
5661       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5662
5663       TEST_DrawLevelField(oldx, oldy);
5664
5665       return;
5666     }
5667   }
5668   else if (element == EL_BLOCKED)
5669   {
5670     Blocked2Moving(x, y, &oldx, &oldy);
5671     if (!IS_MOVING(oldx, oldy))
5672       return;
5673   }
5674
5675   if (element == EL_BLOCKED &&
5676       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5677        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5678        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5679        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5680        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5681        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5682     next_element = get_next_element(Tile[oldx][oldy]);
5683
5684   RemoveField(oldx, oldy);
5685   RemoveField(newx, newy);
5686
5687   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5688
5689   if (next_element != EL_UNDEFINED)
5690     Tile[oldx][oldy] = next_element;
5691
5692   TEST_DrawLevelField(oldx, oldy);
5693   TEST_DrawLevelField(newx, newy);
5694 }
5695
5696 void DrawDynamite(int x, int y)
5697 {
5698   int sx = SCREENX(x), sy = SCREENY(y);
5699   int graphic = el2img(Tile[x][y]);
5700   int frame;
5701
5702   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5703     return;
5704
5705   if (IS_WALKABLE_INSIDE(Back[x][y]))
5706     return;
5707
5708   if (Back[x][y])
5709     DrawLevelElement(x, y, Back[x][y]);
5710   else if (Store[x][y])
5711     DrawLevelElement(x, y, Store[x][y]);
5712   else if (game.use_masked_elements)
5713     DrawLevelElement(x, y, EL_EMPTY);
5714
5715   frame = getGraphicAnimationFrameXY(graphic, x, y);
5716
5717   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5718     DrawGraphicThruMask(sx, sy, graphic, frame);
5719   else
5720     DrawGraphic(sx, sy, graphic, frame);
5721 }
5722
5723 static void CheckDynamite(int x, int y)
5724 {
5725   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5726   {
5727     MovDelay[x][y]--;
5728
5729     if (MovDelay[x][y] != 0)
5730     {
5731       DrawDynamite(x, y);
5732       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5733
5734       return;
5735     }
5736   }
5737
5738   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5739
5740   Bang(x, y);
5741 }
5742
5743 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5744 {
5745   boolean num_checked_players = 0;
5746   int i;
5747
5748   for (i = 0; i < MAX_PLAYERS; i++)
5749   {
5750     if (stored_player[i].active)
5751     {
5752       int sx = stored_player[i].jx;
5753       int sy = stored_player[i].jy;
5754
5755       if (num_checked_players == 0)
5756       {
5757         *sx1 = *sx2 = sx;
5758         *sy1 = *sy2 = sy;
5759       }
5760       else
5761       {
5762         *sx1 = MIN(*sx1, sx);
5763         *sy1 = MIN(*sy1, sy);
5764         *sx2 = MAX(*sx2, sx);
5765         *sy2 = MAX(*sy2, sy);
5766       }
5767
5768       num_checked_players++;
5769     }
5770   }
5771 }
5772
5773 static boolean checkIfAllPlayersFitToScreen_RND(void)
5774 {
5775   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5776
5777   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5778
5779   return (sx2 - sx1 < SCR_FIELDX &&
5780           sy2 - sy1 < SCR_FIELDY);
5781 }
5782
5783 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5784 {
5785   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5786
5787   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5788
5789   *sx = (sx1 + sx2) / 2;
5790   *sy = (sy1 + sy2) / 2;
5791 }
5792
5793 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5794                                boolean center_screen, boolean quick_relocation)
5795 {
5796   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5797   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5798   boolean no_delay = (tape.warp_forward);
5799   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5800   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5801   int new_scroll_x, new_scroll_y;
5802
5803   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5804   {
5805     // case 1: quick relocation inside visible screen (without scrolling)
5806
5807     RedrawPlayfield();
5808
5809     return;
5810   }
5811
5812   if (!level.shifted_relocation || center_screen)
5813   {
5814     // relocation _with_ centering of screen
5815
5816     new_scroll_x = SCROLL_POSITION_X(x);
5817     new_scroll_y = SCROLL_POSITION_Y(y);
5818   }
5819   else
5820   {
5821     // relocation _without_ centering of screen
5822
5823     // apply distance between old and new player position to scroll position
5824     int shifted_scroll_x = scroll_x + (x - old_x);
5825     int shifted_scroll_y = scroll_y + (y - old_y);
5826
5827     // make sure that shifted scroll position does not scroll beyond screen
5828     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5829     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5830
5831     // special case for teleporting from one end of the playfield to the other
5832     // (this kludge prevents the destination area to be shifted by half a tile
5833     // against the source destination for even screen width or screen height;
5834     // probably most useful when used with high "game.forced_scroll_delay_value"
5835     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5836     if (quick_relocation)
5837     {
5838       if (EVEN(SCR_FIELDX))
5839       {
5840         // relocate (teleport) between left and right border (half or full)
5841         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5842           new_scroll_x = SBX_Right;
5843         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5844           new_scroll_x = SBX_Right - 1;
5845         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5846           new_scroll_x = SBX_Left;
5847         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5848           new_scroll_x = SBX_Left + 1;
5849       }
5850
5851       if (EVEN(SCR_FIELDY))
5852       {
5853         // relocate (teleport) between top and bottom border (half or full)
5854         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5855           new_scroll_y = SBY_Lower;
5856         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5857           new_scroll_y = SBY_Lower - 1;
5858         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5859           new_scroll_y = SBY_Upper;
5860         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5861           new_scroll_y = SBY_Upper + 1;
5862       }
5863     }
5864   }
5865
5866   if (quick_relocation)
5867   {
5868     // case 2: quick relocation (redraw without visible scrolling)
5869
5870     scroll_x = new_scroll_x;
5871     scroll_y = new_scroll_y;
5872
5873     RedrawPlayfield();
5874
5875     return;
5876   }
5877
5878   // case 3: visible relocation (with scrolling to new position)
5879
5880   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5881
5882   SetVideoFrameDelay(wait_delay_value);
5883
5884   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5885   {
5886     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5887     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5888
5889     if (dx == 0 && dy == 0)             // no scrolling needed at all
5890       break;
5891
5892     scroll_x -= dx;
5893     scroll_y -= dy;
5894
5895     // set values for horizontal/vertical screen scrolling (half tile size)
5896     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5897     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5898     int pos_x = dx * TILEX / 2;
5899     int pos_y = dy * TILEY / 2;
5900     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5901     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5902
5903     ScrollLevel(dx, dy);
5904     DrawAllPlayers();
5905
5906     // scroll in two steps of half tile size to make things smoother
5907     BlitScreenToBitmapExt_RND(window, fx, fy);
5908
5909     // scroll second step to align at full tile size
5910     BlitScreenToBitmap(window);
5911   }
5912
5913   DrawAllPlayers();
5914   BackToFront();
5915
5916   SetVideoFrameDelay(frame_delay_value_old);
5917 }
5918
5919 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5920 {
5921   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5922   int player_nr = GET_PLAYER_NR(el_player);
5923   struct PlayerInfo *player = &stored_player[player_nr];
5924   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5925   boolean no_delay = (tape.warp_forward);
5926   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5927   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5928   int old_jx = player->jx;
5929   int old_jy = player->jy;
5930   int old_element = Tile[old_jx][old_jy];
5931   int element = Tile[jx][jy];
5932   boolean player_relocated = (old_jx != jx || old_jy != jy);
5933
5934   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5935   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5936   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5937   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5938   int leave_side_horiz = move_dir_horiz;
5939   int leave_side_vert  = move_dir_vert;
5940   int enter_side = enter_side_horiz | enter_side_vert;
5941   int leave_side = leave_side_horiz | leave_side_vert;
5942
5943   if (player->buried)           // do not reanimate dead player
5944     return;
5945
5946   if (!player_relocated)        // no need to relocate the player
5947     return;
5948
5949   if (IS_PLAYER(jx, jy))        // player already placed at new position
5950   {
5951     RemoveField(jx, jy);        // temporarily remove newly placed player
5952     DrawLevelField(jx, jy);
5953   }
5954
5955   if (player->present)
5956   {
5957     while (player->MovPos)
5958     {
5959       ScrollPlayer(player, SCROLL_GO_ON);
5960       ScrollScreen(NULL, SCROLL_GO_ON);
5961
5962       AdvanceFrameAndPlayerCounters(player->index_nr);
5963
5964       DrawPlayer(player);
5965
5966       BackToFront_WithFrameDelay(wait_delay_value);
5967     }
5968
5969     DrawPlayer(player);         // needed here only to cleanup last field
5970     DrawLevelField(player->jx, player->jy);     // remove player graphic
5971
5972     player->is_moving = FALSE;
5973   }
5974
5975   if (IS_CUSTOM_ELEMENT(old_element))
5976     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5977                                CE_LEFT_BY_PLAYER,
5978                                player->index_bit, leave_side);
5979
5980   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5981                                       CE_PLAYER_LEAVES_X,
5982                                       player->index_bit, leave_side);
5983
5984   Tile[jx][jy] = el_player;
5985   InitPlayerField(jx, jy, el_player, TRUE);
5986
5987   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5988      possible that the relocation target field did not contain a player element,
5989      but a walkable element, to which the new player was relocated -- in this
5990      case, restore that (already initialized!) element on the player field */
5991   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5992   {
5993     Tile[jx][jy] = element;     // restore previously existing element
5994   }
5995
5996   // only visually relocate centered player
5997   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5998                      FALSE, level.instant_relocation);
5999
6000   TestIfPlayerTouchesBadThing(jx, jy);
6001   TestIfPlayerTouchesCustomElement(jx, jy);
6002
6003   if (IS_CUSTOM_ELEMENT(element))
6004     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
6005                                player->index_bit, enter_side);
6006
6007   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
6008                                       player->index_bit, enter_side);
6009
6010   if (player->is_switching)
6011   {
6012     /* ensure that relocation while still switching an element does not cause
6013        a new element to be treated as also switched directly after relocation
6014        (this is important for teleporter switches that teleport the player to
6015        a place where another teleporter switch is in the same direction, which
6016        would then incorrectly be treated as immediately switched before the
6017        direction key that caused the switch was released) */
6018
6019     player->switch_x += jx - old_jx;
6020     player->switch_y += jy - old_jy;
6021   }
6022 }
6023
6024 static void Explode(int ex, int ey, int phase, int mode)
6025 {
6026   int x, y;
6027   int last_phase;
6028   int border_element;
6029
6030   if (game.explosions_delayed)
6031   {
6032     ExplodeField[ex][ey] = mode;
6033     return;
6034   }
6035
6036   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
6037   {
6038     int center_element = Tile[ex][ey];
6039     int ce_value = CustomValue[ex][ey];
6040     int ce_score = element_info[center_element].collect_score;
6041     int artwork_element, explosion_element;     // set these values later
6042
6043     // remove things displayed in background while burning dynamite
6044     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
6045       Back[ex][ey] = 0;
6046
6047     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6048     {
6049       // put moving element to center field (and let it explode there)
6050       center_element = MovingOrBlocked2Element(ex, ey);
6051       RemoveMovingField(ex, ey);
6052       Tile[ex][ey] = center_element;
6053     }
6054
6055     // now "center_element" is finally determined -- set related values now
6056     artwork_element = center_element;           // for custom player artwork
6057     explosion_element = center_element;         // for custom player artwork
6058
6059     if (IS_PLAYER(ex, ey))
6060     {
6061       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6062
6063       artwork_element = stored_player[player_nr].artwork_element;
6064
6065       if (level.use_explosion_element[player_nr])
6066       {
6067         explosion_element = level.explosion_element[player_nr];
6068         artwork_element = explosion_element;
6069       }
6070     }
6071
6072     if (mode == EX_TYPE_NORMAL ||
6073         mode == EX_TYPE_CENTER ||
6074         mode == EX_TYPE_CROSS)
6075       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6076
6077     last_phase = element_info[explosion_element].explosion_delay + 1;
6078
6079     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6080     {
6081       int xx = x - ex + 1;
6082       int yy = y - ey + 1;
6083       int element;
6084
6085       if (!IN_LEV_FIELD(x, y) ||
6086           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6087           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6088         continue;
6089
6090       element = Tile[x][y];
6091
6092       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6093       {
6094         element = MovingOrBlocked2Element(x, y);
6095
6096         if (!IS_EXPLOSION_PROOF(element))
6097           RemoveMovingField(x, y);
6098       }
6099
6100       // indestructible elements can only explode in center (but not flames)
6101       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6102                                            mode == EX_TYPE_BORDER)) ||
6103           element == EL_FLAMES)
6104         continue;
6105
6106       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6107          behaviour, for example when touching a yamyam that explodes to rocks
6108          with active deadly shield, a rock is created under the player !!! */
6109       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6110 #if 0
6111       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6112           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6113            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6114 #else
6115       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6116 #endif
6117       {
6118         if (IS_ACTIVE_BOMB(element))
6119         {
6120           // re-activate things under the bomb like gate or penguin
6121           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6122           Back[x][y] = 0;
6123         }
6124
6125         continue;
6126       }
6127
6128       // save walkable background elements while explosion on same tile
6129       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6130           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6131         Back[x][y] = element;
6132
6133       // ignite explodable elements reached by other explosion
6134       if (element == EL_EXPLOSION)
6135         element = Store2[x][y];
6136
6137       if (AmoebaNr[x][y] &&
6138           (element == EL_AMOEBA_FULL ||
6139            element == EL_BD_AMOEBA ||
6140            element == EL_AMOEBA_GROWING))
6141       {
6142         AmoebaCnt[AmoebaNr[x][y]]--;
6143         AmoebaCnt2[AmoebaNr[x][y]]--;
6144       }
6145
6146       RemoveField(x, y);
6147
6148       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6149       {
6150         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6151
6152         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6153
6154         if (PLAYERINFO(ex, ey)->use_murphy)
6155           Store[x][y] = EL_EMPTY;
6156       }
6157
6158       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6159       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6160       else if (IS_PLAYER_ELEMENT(center_element))
6161         Store[x][y] = EL_EMPTY;
6162       else if (center_element == EL_YAMYAM)
6163         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6164       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6165         Store[x][y] = element_info[center_element].content.e[xx][yy];
6166 #if 1
6167       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6168       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6169       // otherwise) -- FIX THIS !!!
6170       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6171         Store[x][y] = element_info[element].content.e[1][1];
6172 #else
6173       else if (!CAN_EXPLODE(element))
6174         Store[x][y] = element_info[element].content.e[1][1];
6175 #endif
6176       else
6177         Store[x][y] = EL_EMPTY;
6178
6179       if (IS_CUSTOM_ELEMENT(center_element))
6180         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6181                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6182                        Store[x][y] >= EL_PREV_CE_8 &&
6183                        Store[x][y] <= EL_NEXT_CE_8 ?
6184                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6185                        Store[x][y]);
6186
6187       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6188           center_element == EL_AMOEBA_TO_DIAMOND)
6189         Store2[x][y] = element;
6190
6191       Tile[x][y] = EL_EXPLOSION;
6192       GfxElement[x][y] = artwork_element;
6193
6194       ExplodePhase[x][y] = 1;
6195       ExplodeDelay[x][y] = last_phase;
6196
6197       Stop[x][y] = TRUE;
6198     }
6199
6200     if (center_element == EL_YAMYAM)
6201       game.yamyam_content_nr =
6202         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6203
6204     return;
6205   }
6206
6207   if (Stop[ex][ey])
6208     return;
6209
6210   x = ex;
6211   y = ey;
6212
6213   if (phase == 1)
6214     GfxFrame[x][y] = 0;         // restart explosion animation
6215
6216   last_phase = ExplodeDelay[x][y];
6217
6218   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6219
6220   // this can happen if the player leaves an explosion just in time
6221   if (GfxElement[x][y] == EL_UNDEFINED)
6222     GfxElement[x][y] = EL_EMPTY;
6223
6224   border_element = Store2[x][y];
6225   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6226     border_element = StorePlayer[x][y];
6227
6228   if (phase == element_info[border_element].ignition_delay ||
6229       phase == last_phase)
6230   {
6231     boolean border_explosion = FALSE;
6232
6233     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6234         !PLAYER_EXPLOSION_PROTECTED(x, y))
6235     {
6236       KillPlayerUnlessExplosionProtected(x, y);
6237       border_explosion = TRUE;
6238     }
6239     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6240     {
6241       Tile[x][y] = Store2[x][y];
6242       Store2[x][y] = 0;
6243       Bang(x, y);
6244       border_explosion = TRUE;
6245     }
6246     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6247     {
6248       AmoebaToDiamond(x, y);
6249       Store2[x][y] = 0;
6250       border_explosion = TRUE;
6251     }
6252
6253     // if an element just explodes due to another explosion (chain-reaction),
6254     // do not immediately end the new explosion when it was the last frame of
6255     // the explosion (as it would be done in the following "if"-statement!)
6256     if (border_explosion && phase == last_phase)
6257       return;
6258   }
6259
6260   // this can happen if the player was just killed by an explosion
6261   if (GfxElement[x][y] == EL_UNDEFINED)
6262     GfxElement[x][y] = EL_EMPTY;
6263
6264   if (phase == last_phase)
6265   {
6266     int element;
6267
6268     element = Tile[x][y] = Store[x][y];
6269     Store[x][y] = Store2[x][y] = 0;
6270     GfxElement[x][y] = EL_UNDEFINED;
6271
6272     // player can escape from explosions and might therefore be still alive
6273     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6274         element <= EL_PLAYER_IS_EXPLODING_4)
6275     {
6276       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6277       int explosion_element = EL_PLAYER_1 + player_nr;
6278       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6279       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6280
6281       if (level.use_explosion_element[player_nr])
6282         explosion_element = level.explosion_element[player_nr];
6283
6284       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6285                     element_info[explosion_element].content.e[xx][yy]);
6286     }
6287
6288     // restore probably existing indestructible background element
6289     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6290       element = Tile[x][y] = Back[x][y];
6291     Back[x][y] = 0;
6292
6293     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6294     GfxDir[x][y] = MV_NONE;
6295     ChangeDelay[x][y] = 0;
6296     ChangePage[x][y] = -1;
6297
6298     CustomValue[x][y] = 0;
6299
6300     InitField_WithBug2(x, y, FALSE);
6301
6302     TEST_DrawLevelField(x, y);
6303
6304     TestIfElementTouchesCustomElement(x, y);
6305
6306     if (GFX_CRUMBLED(element))
6307       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6308
6309     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6310       StorePlayer[x][y] = 0;
6311
6312     if (IS_PLAYER_ELEMENT(element))
6313       RelocatePlayer(x, y, element);
6314   }
6315   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6316   {
6317     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6318     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6319
6320     if (phase == 1)
6321       TEST_DrawLevelFieldCrumbled(x, y);
6322
6323     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6324     {
6325       DrawLevelElement(x, y, Back[x][y]);
6326       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6327     }
6328     else if (IS_WALKABLE_UNDER(Back[x][y]))
6329     {
6330       DrawLevelGraphic(x, y, graphic, frame);
6331       DrawLevelElementThruMask(x, y, Back[x][y]);
6332     }
6333     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6334       DrawLevelGraphic(x, y, graphic, frame);
6335   }
6336 }
6337
6338 static void DynaExplode(int ex, int ey)
6339 {
6340   int i, j;
6341   int dynabomb_element = Tile[ex][ey];
6342   int dynabomb_size = 1;
6343   boolean dynabomb_xl = FALSE;
6344   struct PlayerInfo *player;
6345   struct XY *xy = xy_topdown;
6346
6347   if (IS_ACTIVE_BOMB(dynabomb_element))
6348   {
6349     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6350     dynabomb_size = player->dynabomb_size;
6351     dynabomb_xl = player->dynabomb_xl;
6352     player->dynabombs_left++;
6353   }
6354
6355   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6356
6357   for (i = 0; i < NUM_DIRECTIONS; i++)
6358   {
6359     for (j = 1; j <= dynabomb_size; j++)
6360     {
6361       int x = ex + j * xy[i].x;
6362       int y = ey + j * xy[i].y;
6363       int element;
6364
6365       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6366         break;
6367
6368       element = Tile[x][y];
6369
6370       // do not restart explosions of fields with active bombs
6371       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6372         continue;
6373
6374       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6375
6376       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6377           !IS_DIGGABLE(element) && !dynabomb_xl)
6378         break;
6379     }
6380   }
6381 }
6382
6383 void Bang(int x, int y)
6384 {
6385   int element = MovingOrBlocked2Element(x, y);
6386   int explosion_type = EX_TYPE_NORMAL;
6387
6388   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6389   {
6390     struct PlayerInfo *player = PLAYERINFO(x, y);
6391
6392     element = Tile[x][y] = player->initial_element;
6393
6394     if (level.use_explosion_element[player->index_nr])
6395     {
6396       int explosion_element = level.explosion_element[player->index_nr];
6397
6398       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6399         explosion_type = EX_TYPE_CROSS;
6400       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6401         explosion_type = EX_TYPE_CENTER;
6402     }
6403   }
6404
6405   switch (element)
6406   {
6407     case EL_BUG:
6408     case EL_SPACESHIP:
6409     case EL_BD_BUTTERFLY:
6410     case EL_BD_FIREFLY:
6411     case EL_YAMYAM:
6412     case EL_DARK_YAMYAM:
6413     case EL_ROBOT:
6414     case EL_PACMAN:
6415     case EL_MOLE:
6416       RaiseScoreElement(element);
6417       break;
6418
6419     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6420     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6421     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6422     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6423     case EL_DYNABOMB_INCREASE_NUMBER:
6424     case EL_DYNABOMB_INCREASE_SIZE:
6425     case EL_DYNABOMB_INCREASE_POWER:
6426       explosion_type = EX_TYPE_DYNA;
6427       break;
6428
6429     case EL_DC_LANDMINE:
6430       explosion_type = EX_TYPE_CENTER;
6431       break;
6432
6433     case EL_PENGUIN:
6434     case EL_LAMP:
6435     case EL_LAMP_ACTIVE:
6436     case EL_AMOEBA_TO_DIAMOND:
6437       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6438         explosion_type = EX_TYPE_CENTER;
6439       break;
6440
6441     default:
6442       if (element_info[element].explosion_type == EXPLODES_CROSS)
6443         explosion_type = EX_TYPE_CROSS;
6444       else if (element_info[element].explosion_type == EXPLODES_1X1)
6445         explosion_type = EX_TYPE_CENTER;
6446       break;
6447   }
6448
6449   if (explosion_type == EX_TYPE_DYNA)
6450     DynaExplode(x, y);
6451   else
6452     Explode(x, y, EX_PHASE_START, explosion_type);
6453
6454   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6455 }
6456
6457 static void SplashAcid(int x, int y)
6458 {
6459   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6460       (!IN_LEV_FIELD(x - 1, y - 2) ||
6461        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6462     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6463
6464   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6465       (!IN_LEV_FIELD(x + 1, y - 2) ||
6466        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6467     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6468
6469   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6470 }
6471
6472 static void InitBeltMovement(void)
6473 {
6474   static int belt_base_element[4] =
6475   {
6476     EL_CONVEYOR_BELT_1_LEFT,
6477     EL_CONVEYOR_BELT_2_LEFT,
6478     EL_CONVEYOR_BELT_3_LEFT,
6479     EL_CONVEYOR_BELT_4_LEFT
6480   };
6481   static int belt_base_active_element[4] =
6482   {
6483     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6484     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6485     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6486     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6487   };
6488
6489   int x, y, i, j;
6490
6491   // set frame order for belt animation graphic according to belt direction
6492   for (i = 0; i < NUM_BELTS; i++)
6493   {
6494     int belt_nr = i;
6495
6496     for (j = 0; j < NUM_BELT_PARTS; j++)
6497     {
6498       int element = belt_base_active_element[belt_nr] + j;
6499       int graphic_1 = el2img(element);
6500       int graphic_2 = el2panelimg(element);
6501
6502       if (game.belt_dir[i] == MV_LEFT)
6503       {
6504         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6505         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6506       }
6507       else
6508       {
6509         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6510         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6511       }
6512     }
6513   }
6514
6515   SCAN_PLAYFIELD(x, y)
6516   {
6517     int element = Tile[x][y];
6518
6519     for (i = 0; i < NUM_BELTS; i++)
6520     {
6521       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6522       {
6523         int e_belt_nr = getBeltNrFromBeltElement(element);
6524         int belt_nr = i;
6525
6526         if (e_belt_nr == belt_nr)
6527         {
6528           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6529
6530           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6531         }
6532       }
6533     }
6534   }
6535 }
6536
6537 static void ToggleBeltSwitch(int x, int y)
6538 {
6539   static int belt_base_element[4] =
6540   {
6541     EL_CONVEYOR_BELT_1_LEFT,
6542     EL_CONVEYOR_BELT_2_LEFT,
6543     EL_CONVEYOR_BELT_3_LEFT,
6544     EL_CONVEYOR_BELT_4_LEFT
6545   };
6546   static int belt_base_active_element[4] =
6547   {
6548     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6549     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6550     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6551     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6552   };
6553   static int belt_base_switch_element[4] =
6554   {
6555     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6556     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6557     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6558     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6559   };
6560   static int belt_move_dir[4] =
6561   {
6562     MV_LEFT,
6563     MV_NONE,
6564     MV_RIGHT,
6565     MV_NONE,
6566   };
6567
6568   int element = Tile[x][y];
6569   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6570   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6571   int belt_dir = belt_move_dir[belt_dir_nr];
6572   int xx, yy, i;
6573
6574   if (!IS_BELT_SWITCH(element))
6575     return;
6576
6577   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6578   game.belt_dir[belt_nr] = belt_dir;
6579
6580   if (belt_dir_nr == 3)
6581     belt_dir_nr = 1;
6582
6583   // set frame order for belt animation graphic according to belt direction
6584   for (i = 0; i < NUM_BELT_PARTS; i++)
6585   {
6586     int element = belt_base_active_element[belt_nr] + i;
6587     int graphic_1 = el2img(element);
6588     int graphic_2 = el2panelimg(element);
6589
6590     if (belt_dir == MV_LEFT)
6591     {
6592       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6593       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6594     }
6595     else
6596     {
6597       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6598       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6599     }
6600   }
6601
6602   SCAN_PLAYFIELD(xx, yy)
6603   {
6604     int element = Tile[xx][yy];
6605
6606     if (IS_BELT_SWITCH(element))
6607     {
6608       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6609
6610       if (e_belt_nr == belt_nr)
6611       {
6612         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6613         TEST_DrawLevelField(xx, yy);
6614       }
6615     }
6616     else if (IS_BELT(element) && belt_dir != MV_NONE)
6617     {
6618       int e_belt_nr = getBeltNrFromBeltElement(element);
6619
6620       if (e_belt_nr == belt_nr)
6621       {
6622         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6623
6624         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6625         TEST_DrawLevelField(xx, yy);
6626       }
6627     }
6628     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6629     {
6630       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6631
6632       if (e_belt_nr == belt_nr)
6633       {
6634         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6635
6636         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6637         TEST_DrawLevelField(xx, yy);
6638       }
6639     }
6640   }
6641 }
6642
6643 static void ToggleSwitchgateSwitch(void)
6644 {
6645   int xx, yy;
6646
6647   game.switchgate_pos = !game.switchgate_pos;
6648
6649   SCAN_PLAYFIELD(xx, yy)
6650   {
6651     int element = Tile[xx][yy];
6652
6653     if (element == EL_SWITCHGATE_SWITCH_UP)
6654     {
6655       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6656       TEST_DrawLevelField(xx, yy);
6657     }
6658     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6659     {
6660       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6661       TEST_DrawLevelField(xx, yy);
6662     }
6663     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6664     {
6665       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6666       TEST_DrawLevelField(xx, yy);
6667     }
6668     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6669     {
6670       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6671       TEST_DrawLevelField(xx, yy);
6672     }
6673     else if (element == EL_SWITCHGATE_OPEN ||
6674              element == EL_SWITCHGATE_OPENING)
6675     {
6676       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6677
6678       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6679     }
6680     else if (element == EL_SWITCHGATE_CLOSED ||
6681              element == EL_SWITCHGATE_CLOSING)
6682     {
6683       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6684
6685       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6686     }
6687   }
6688 }
6689
6690 static int getInvisibleActiveFromInvisibleElement(int element)
6691 {
6692   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6693           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6694           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6695           element);
6696 }
6697
6698 static int getInvisibleFromInvisibleActiveElement(int element)
6699 {
6700   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6701           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6702           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6703           element);
6704 }
6705
6706 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6707 {
6708   int x, y;
6709
6710   SCAN_PLAYFIELD(x, y)
6711   {
6712     int element = Tile[x][y];
6713
6714     if (element == EL_LIGHT_SWITCH &&
6715         game.light_time_left > 0)
6716     {
6717       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6718       TEST_DrawLevelField(x, y);
6719     }
6720     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6721              game.light_time_left == 0)
6722     {
6723       Tile[x][y] = EL_LIGHT_SWITCH;
6724       TEST_DrawLevelField(x, y);
6725     }
6726     else if (element == EL_EMC_DRIPPER &&
6727              game.light_time_left > 0)
6728     {
6729       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6730       TEST_DrawLevelField(x, y);
6731     }
6732     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6733              game.light_time_left == 0)
6734     {
6735       Tile[x][y] = EL_EMC_DRIPPER;
6736       TEST_DrawLevelField(x, y);
6737     }
6738     else if (element == EL_INVISIBLE_STEELWALL ||
6739              element == EL_INVISIBLE_WALL ||
6740              element == EL_INVISIBLE_SAND)
6741     {
6742       if (game.light_time_left > 0)
6743         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6744
6745       TEST_DrawLevelField(x, y);
6746
6747       // uncrumble neighbour fields, if needed
6748       if (element == EL_INVISIBLE_SAND)
6749         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6750     }
6751     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6752              element == EL_INVISIBLE_WALL_ACTIVE ||
6753              element == EL_INVISIBLE_SAND_ACTIVE)
6754     {
6755       if (game.light_time_left == 0)
6756         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6757
6758       TEST_DrawLevelField(x, y);
6759
6760       // re-crumble neighbour fields, if needed
6761       if (element == EL_INVISIBLE_SAND)
6762         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6763     }
6764   }
6765 }
6766
6767 static void RedrawAllInvisibleElementsForLenses(void)
6768 {
6769   int x, y;
6770
6771   SCAN_PLAYFIELD(x, y)
6772   {
6773     int element = Tile[x][y];
6774
6775     if (element == EL_EMC_DRIPPER &&
6776         game.lenses_time_left > 0)
6777     {
6778       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6779       TEST_DrawLevelField(x, y);
6780     }
6781     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6782              game.lenses_time_left == 0)
6783     {
6784       Tile[x][y] = EL_EMC_DRIPPER;
6785       TEST_DrawLevelField(x, y);
6786     }
6787     else if (element == EL_INVISIBLE_STEELWALL ||
6788              element == EL_INVISIBLE_WALL ||
6789              element == EL_INVISIBLE_SAND)
6790     {
6791       if (game.lenses_time_left > 0)
6792         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6793
6794       TEST_DrawLevelField(x, y);
6795
6796       // uncrumble neighbour fields, if needed
6797       if (element == EL_INVISIBLE_SAND)
6798         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6799     }
6800     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6801              element == EL_INVISIBLE_WALL_ACTIVE ||
6802              element == EL_INVISIBLE_SAND_ACTIVE)
6803     {
6804       if (game.lenses_time_left == 0)
6805         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6806
6807       TEST_DrawLevelField(x, y);
6808
6809       // re-crumble neighbour fields, if needed
6810       if (element == EL_INVISIBLE_SAND)
6811         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6812     }
6813   }
6814 }
6815
6816 static void RedrawAllInvisibleElementsForMagnifier(void)
6817 {
6818   int x, y;
6819
6820   SCAN_PLAYFIELD(x, y)
6821   {
6822     int element = Tile[x][y];
6823
6824     if (element == EL_EMC_FAKE_GRASS &&
6825         game.magnify_time_left > 0)
6826     {
6827       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6828       TEST_DrawLevelField(x, y);
6829     }
6830     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6831              game.magnify_time_left == 0)
6832     {
6833       Tile[x][y] = EL_EMC_FAKE_GRASS;
6834       TEST_DrawLevelField(x, y);
6835     }
6836     else if (IS_GATE_GRAY(element) &&
6837              game.magnify_time_left > 0)
6838     {
6839       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6840                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6841                     IS_EM_GATE_GRAY(element) ?
6842                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6843                     IS_EMC_GATE_GRAY(element) ?
6844                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6845                     IS_DC_GATE_GRAY(element) ?
6846                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6847                     element);
6848       TEST_DrawLevelField(x, y);
6849     }
6850     else if (IS_GATE_GRAY_ACTIVE(element) &&
6851              game.magnify_time_left == 0)
6852     {
6853       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6854                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6855                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6856                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6857                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6858                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6859                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6860                     EL_DC_GATE_WHITE_GRAY :
6861                     element);
6862       TEST_DrawLevelField(x, y);
6863     }
6864   }
6865 }
6866
6867 static void ToggleLightSwitch(int x, int y)
6868 {
6869   int element = Tile[x][y];
6870
6871   game.light_time_left =
6872     (element == EL_LIGHT_SWITCH ?
6873      level.time_light * FRAMES_PER_SECOND : 0);
6874
6875   RedrawAllLightSwitchesAndInvisibleElements();
6876 }
6877
6878 static void ActivateTimegateSwitch(int x, int y)
6879 {
6880   int xx, yy;
6881
6882   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6883
6884   SCAN_PLAYFIELD(xx, yy)
6885   {
6886     int element = Tile[xx][yy];
6887
6888     if (element == EL_TIMEGATE_CLOSED ||
6889         element == EL_TIMEGATE_CLOSING)
6890     {
6891       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6892       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6893     }
6894
6895     /*
6896     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6897     {
6898       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6899       TEST_DrawLevelField(xx, yy);
6900     }
6901     */
6902
6903   }
6904
6905   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6906                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6907 }
6908
6909 static void Impact(int x, int y)
6910 {
6911   boolean last_line = (y == lev_fieldy - 1);
6912   boolean object_hit = FALSE;
6913   boolean impact = (last_line || object_hit);
6914   int element = Tile[x][y];
6915   int smashed = EL_STEELWALL;
6916
6917   if (!last_line)       // check if element below was hit
6918   {
6919     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6920       return;
6921
6922     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6923                                          MovDir[x][y + 1] != MV_DOWN ||
6924                                          MovPos[x][y + 1] <= TILEY / 2));
6925
6926     // do not smash moving elements that left the smashed field in time
6927     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6928         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6929       object_hit = FALSE;
6930
6931 #if USE_QUICKSAND_IMPACT_BUGFIX
6932     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6933     {
6934       RemoveMovingField(x, y + 1);
6935       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6936       Tile[x][y + 2] = EL_ROCK;
6937       TEST_DrawLevelField(x, y + 2);
6938
6939       object_hit = TRUE;
6940     }
6941
6942     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6943     {
6944       RemoveMovingField(x, y + 1);
6945       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6946       Tile[x][y + 2] = EL_ROCK;
6947       TEST_DrawLevelField(x, y + 2);
6948
6949       object_hit = TRUE;
6950     }
6951 #endif
6952
6953     if (object_hit)
6954       smashed = MovingOrBlocked2Element(x, y + 1);
6955
6956     impact = (last_line || object_hit);
6957   }
6958
6959   if (!last_line && smashed == EL_ACID) // element falls into acid
6960   {
6961     SplashAcid(x, y + 1);
6962     return;
6963   }
6964
6965   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6966   // only reset graphic animation if graphic really changes after impact
6967   if (impact &&
6968       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6969   {
6970     ResetGfxAnimation(x, y);
6971     TEST_DrawLevelField(x, y);
6972   }
6973
6974   if (impact && CAN_EXPLODE_IMPACT(element))
6975   {
6976     Bang(x, y);
6977     return;
6978   }
6979   else if (impact && element == EL_PEARL &&
6980            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6981   {
6982     ResetGfxAnimation(x, y);
6983
6984     Tile[x][y] = EL_PEARL_BREAKING;
6985     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6986     return;
6987   }
6988   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6989   {
6990     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6991
6992     return;
6993   }
6994
6995   if (impact && element == EL_AMOEBA_DROP)
6996   {
6997     if (object_hit && IS_PLAYER(x, y + 1))
6998       KillPlayerUnlessEnemyProtected(x, y + 1);
6999     else if (object_hit && smashed == EL_PENGUIN)
7000       Bang(x, y + 1);
7001     else
7002     {
7003       Tile[x][y] = EL_AMOEBA_GROWING;
7004       Store[x][y] = EL_AMOEBA_WET;
7005
7006       ResetRandomAnimationValue(x, y);
7007     }
7008     return;
7009   }
7010
7011   if (object_hit)               // check which object was hit
7012   {
7013     if ((CAN_PASS_MAGIC_WALL(element) && 
7014          (smashed == EL_MAGIC_WALL ||
7015           smashed == EL_BD_MAGIC_WALL)) ||
7016         (CAN_PASS_DC_MAGIC_WALL(element) &&
7017          smashed == EL_DC_MAGIC_WALL))
7018     {
7019       int xx, yy;
7020       int activated_magic_wall =
7021         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
7022          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
7023          EL_DC_MAGIC_WALL_ACTIVE);
7024
7025       // activate magic wall / mill
7026       SCAN_PLAYFIELD(xx, yy)
7027       {
7028         if (Tile[xx][yy] == smashed)
7029           Tile[xx][yy] = activated_magic_wall;
7030       }
7031
7032       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
7033       game.magic_wall_active = TRUE;
7034
7035       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7036                             SND_MAGIC_WALL_ACTIVATING :
7037                             smashed == EL_BD_MAGIC_WALL ?
7038                             SND_BD_MAGIC_WALL_ACTIVATING :
7039                             SND_DC_MAGIC_WALL_ACTIVATING));
7040     }
7041
7042     if (IS_PLAYER(x, y + 1))
7043     {
7044       if (CAN_SMASH_PLAYER(element))
7045       {
7046         KillPlayerUnlessEnemyProtected(x, y + 1);
7047         return;
7048       }
7049     }
7050     else if (smashed == EL_PENGUIN)
7051     {
7052       if (CAN_SMASH_PLAYER(element))
7053       {
7054         Bang(x, y + 1);
7055         return;
7056       }
7057     }
7058     else if (element == EL_BD_DIAMOND)
7059     {
7060       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7061       {
7062         Bang(x, y + 1);
7063         return;
7064       }
7065     }
7066     else if (((element == EL_SP_INFOTRON ||
7067                element == EL_SP_ZONK) &&
7068               (smashed == EL_SP_SNIKSNAK ||
7069                smashed == EL_SP_ELECTRON ||
7070                smashed == EL_SP_DISK_ORANGE)) ||
7071              (element == EL_SP_INFOTRON &&
7072               smashed == EL_SP_DISK_YELLOW))
7073     {
7074       Bang(x, y + 1);
7075       return;
7076     }
7077     else if (CAN_SMASH_EVERYTHING(element))
7078     {
7079       if (IS_CLASSIC_ENEMY(smashed) ||
7080           CAN_EXPLODE_SMASHED(smashed))
7081       {
7082         Bang(x, y + 1);
7083         return;
7084       }
7085       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7086       {
7087         if (smashed == EL_LAMP ||
7088             smashed == EL_LAMP_ACTIVE)
7089         {
7090           Bang(x, y + 1);
7091           return;
7092         }
7093         else if (smashed == EL_NUT)
7094         {
7095           Tile[x][y + 1] = EL_NUT_BREAKING;
7096           PlayLevelSound(x, y, SND_NUT_BREAKING);
7097           RaiseScoreElement(EL_NUT);
7098           return;
7099         }
7100         else if (smashed == EL_PEARL)
7101         {
7102           ResetGfxAnimation(x, y);
7103
7104           Tile[x][y + 1] = EL_PEARL_BREAKING;
7105           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7106           return;
7107         }
7108         else if (smashed == EL_DIAMOND)
7109         {
7110           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7111           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7112           return;
7113         }
7114         else if (IS_BELT_SWITCH(smashed))
7115         {
7116           ToggleBeltSwitch(x, y + 1);
7117         }
7118         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7119                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7120                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7121                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7122         {
7123           ToggleSwitchgateSwitch();
7124         }
7125         else if (smashed == EL_LIGHT_SWITCH ||
7126                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7127         {
7128           ToggleLightSwitch(x, y + 1);
7129         }
7130         else
7131         {
7132           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7133
7134           CheckElementChangeBySide(x, y + 1, smashed, element,
7135                                    CE_SWITCHED, CH_SIDE_TOP);
7136           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7137                                             CH_SIDE_TOP);
7138         }
7139       }
7140       else
7141       {
7142         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7143       }
7144     }
7145   }
7146
7147   // play sound of magic wall / mill
7148   if (!last_line &&
7149       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7150        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7151        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7152   {
7153     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7154       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7155     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7156       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7157     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7158       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7159
7160     return;
7161   }
7162
7163   // play sound of object that hits the ground
7164   if (last_line || object_hit)
7165     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7166 }
7167
7168 static void TurnRoundExt(int x, int y)
7169 {
7170   static struct
7171   {
7172     int dx, dy;
7173   } move_xy[] =
7174   {
7175     {  0,  0 },
7176     { -1,  0 },
7177     { +1,  0 },
7178     {  0,  0 },
7179     {  0, -1 },
7180     {  0,  0 }, { 0, 0 }, { 0, 0 },
7181     {  0, +1 }
7182   };
7183   static struct
7184   {
7185     int left, right, back;
7186   } turn[] =
7187   {
7188     { 0,        0,              0        },
7189     { MV_DOWN,  MV_UP,          MV_RIGHT },
7190     { MV_UP,    MV_DOWN,        MV_LEFT  },
7191     { 0,        0,              0        },
7192     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7193     { 0,        0,              0        },
7194     { 0,        0,              0        },
7195     { 0,        0,              0        },
7196     { MV_RIGHT, MV_LEFT,        MV_UP    }
7197   };
7198
7199   int element = Tile[x][y];
7200   int move_pattern = element_info[element].move_pattern;
7201
7202   int old_move_dir = MovDir[x][y];
7203   int left_dir  = turn[old_move_dir].left;
7204   int right_dir = turn[old_move_dir].right;
7205   int back_dir  = turn[old_move_dir].back;
7206
7207   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7208   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7209   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7210   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7211
7212   int left_x  = x + left_dx,  left_y  = y + left_dy;
7213   int right_x = x + right_dx, right_y = y + right_dy;
7214   int move_x  = x + move_dx,  move_y  = y + move_dy;
7215
7216   int xx, yy;
7217
7218   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7219   {
7220     TestIfBadThingTouchesOtherBadThing(x, y);
7221
7222     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7223       MovDir[x][y] = right_dir;
7224     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7225       MovDir[x][y] = left_dir;
7226
7227     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7228       MovDelay[x][y] = 9;
7229     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7230       MovDelay[x][y] = 1;
7231   }
7232   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7233   {
7234     TestIfBadThingTouchesOtherBadThing(x, y);
7235
7236     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7237       MovDir[x][y] = left_dir;
7238     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7239       MovDir[x][y] = right_dir;
7240
7241     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7242       MovDelay[x][y] = 9;
7243     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7244       MovDelay[x][y] = 1;
7245   }
7246   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7247   {
7248     TestIfBadThingTouchesOtherBadThing(x, y);
7249
7250     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7251       MovDir[x][y] = left_dir;
7252     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7253       MovDir[x][y] = right_dir;
7254
7255     if (MovDir[x][y] != old_move_dir)
7256       MovDelay[x][y] = 9;
7257   }
7258   else if (element == EL_YAMYAM)
7259   {
7260     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7261     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7262
7263     if (can_turn_left && can_turn_right)
7264       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7265     else if (can_turn_left)
7266       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7267     else if (can_turn_right)
7268       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7269     else
7270       MovDir[x][y] = back_dir;
7271
7272     MovDelay[x][y] = 16 + 16 * RND(3);
7273   }
7274   else if (element == EL_DARK_YAMYAM)
7275   {
7276     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7277                                                          left_x, left_y);
7278     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7279                                                          right_x, right_y);
7280
7281     if (can_turn_left && can_turn_right)
7282       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7283     else if (can_turn_left)
7284       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7285     else if (can_turn_right)
7286       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7287     else
7288       MovDir[x][y] = back_dir;
7289
7290     MovDelay[x][y] = 16 + 16 * RND(3);
7291   }
7292   else if (element == EL_PACMAN)
7293   {
7294     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7295     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7296
7297     if (can_turn_left && can_turn_right)
7298       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7299     else if (can_turn_left)
7300       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7301     else if (can_turn_right)
7302       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7303     else
7304       MovDir[x][y] = back_dir;
7305
7306     MovDelay[x][y] = 6 + RND(40);
7307   }
7308   else if (element == EL_PIG)
7309   {
7310     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7311     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7312     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7313     boolean should_turn_left, should_turn_right, should_move_on;
7314     int rnd_value = 24;
7315     int rnd = RND(rnd_value);
7316
7317     should_turn_left = (can_turn_left &&
7318                         (!can_move_on ||
7319                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7320                                                    y + back_dy + left_dy)));
7321     should_turn_right = (can_turn_right &&
7322                          (!can_move_on ||
7323                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7324                                                     y + back_dy + right_dy)));
7325     should_move_on = (can_move_on &&
7326                       (!can_turn_left ||
7327                        !can_turn_right ||
7328                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7329                                                  y + move_dy + left_dy) ||
7330                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7331                                                  y + move_dy + right_dy)));
7332
7333     if (should_turn_left || should_turn_right || should_move_on)
7334     {
7335       if (should_turn_left && should_turn_right && should_move_on)
7336         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7337                         rnd < 2 * rnd_value / 3 ? right_dir :
7338                         old_move_dir);
7339       else if (should_turn_left && should_turn_right)
7340         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7341       else if (should_turn_left && should_move_on)
7342         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7343       else if (should_turn_right && should_move_on)
7344         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7345       else if (should_turn_left)
7346         MovDir[x][y] = left_dir;
7347       else if (should_turn_right)
7348         MovDir[x][y] = right_dir;
7349       else if (should_move_on)
7350         MovDir[x][y] = old_move_dir;
7351     }
7352     else if (can_move_on && rnd > rnd_value / 8)
7353       MovDir[x][y] = old_move_dir;
7354     else if (can_turn_left && can_turn_right)
7355       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7356     else if (can_turn_left && rnd > rnd_value / 8)
7357       MovDir[x][y] = left_dir;
7358     else if (can_turn_right && rnd > rnd_value/8)
7359       MovDir[x][y] = right_dir;
7360     else
7361       MovDir[x][y] = back_dir;
7362
7363     xx = x + move_xy[MovDir[x][y]].dx;
7364     yy = y + move_xy[MovDir[x][y]].dy;
7365
7366     if (!IN_LEV_FIELD(xx, yy) ||
7367         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7368       MovDir[x][y] = old_move_dir;
7369
7370     MovDelay[x][y] = 0;
7371   }
7372   else if (element == EL_DRAGON)
7373   {
7374     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7375     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7376     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7377     int rnd_value = 24;
7378     int rnd = RND(rnd_value);
7379
7380     if (can_move_on && rnd > rnd_value / 8)
7381       MovDir[x][y] = old_move_dir;
7382     else if (can_turn_left && can_turn_right)
7383       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7384     else if (can_turn_left && rnd > rnd_value / 8)
7385       MovDir[x][y] = left_dir;
7386     else if (can_turn_right && rnd > rnd_value / 8)
7387       MovDir[x][y] = right_dir;
7388     else
7389       MovDir[x][y] = back_dir;
7390
7391     xx = x + move_xy[MovDir[x][y]].dx;
7392     yy = y + move_xy[MovDir[x][y]].dy;
7393
7394     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7395       MovDir[x][y] = old_move_dir;
7396
7397     MovDelay[x][y] = 0;
7398   }
7399   else if (element == EL_MOLE)
7400   {
7401     boolean can_move_on =
7402       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7403                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7404                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7405     if (!can_move_on)
7406     {
7407       boolean can_turn_left =
7408         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7409                               IS_AMOEBOID(Tile[left_x][left_y])));
7410
7411       boolean can_turn_right =
7412         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7413                               IS_AMOEBOID(Tile[right_x][right_y])));
7414
7415       if (can_turn_left && can_turn_right)
7416         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7417       else if (can_turn_left)
7418         MovDir[x][y] = left_dir;
7419       else
7420         MovDir[x][y] = right_dir;
7421     }
7422
7423     if (MovDir[x][y] != old_move_dir)
7424       MovDelay[x][y] = 9;
7425   }
7426   else if (element == EL_BALLOON)
7427   {
7428     MovDir[x][y] = game.wind_direction;
7429     MovDelay[x][y] = 0;
7430   }
7431   else if (element == EL_SPRING)
7432   {
7433     if (MovDir[x][y] & MV_HORIZONTAL)
7434     {
7435       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7436           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7437       {
7438         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7439         ResetGfxAnimation(move_x, move_y);
7440         TEST_DrawLevelField(move_x, move_y);
7441
7442         MovDir[x][y] = back_dir;
7443       }
7444       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7445                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7446         MovDir[x][y] = MV_NONE;
7447     }
7448
7449     MovDelay[x][y] = 0;
7450   }
7451   else if (element == EL_ROBOT ||
7452            element == EL_SATELLITE ||
7453            element == EL_PENGUIN ||
7454            element == EL_EMC_ANDROID)
7455   {
7456     int attr_x = -1, attr_y = -1;
7457
7458     if (game.all_players_gone)
7459     {
7460       attr_x = game.exit_x;
7461       attr_y = game.exit_y;
7462     }
7463     else
7464     {
7465       int i;
7466
7467       for (i = 0; i < MAX_PLAYERS; i++)
7468       {
7469         struct PlayerInfo *player = &stored_player[i];
7470         int jx = player->jx, jy = player->jy;
7471
7472         if (!player->active)
7473           continue;
7474
7475         if (attr_x == -1 ||
7476             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7477         {
7478           attr_x = jx;
7479           attr_y = jy;
7480         }
7481       }
7482     }
7483
7484     if (element == EL_ROBOT &&
7485         game.robot_wheel_x >= 0 &&
7486         game.robot_wheel_y >= 0 &&
7487         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7488          game.engine_version < VERSION_IDENT(3,1,0,0)))
7489     {
7490       attr_x = game.robot_wheel_x;
7491       attr_y = game.robot_wheel_y;
7492     }
7493
7494     if (element == EL_PENGUIN)
7495     {
7496       int i;
7497       struct XY *xy = xy_topdown;
7498
7499       for (i = 0; i < NUM_DIRECTIONS; i++)
7500       {
7501         int ex = x + xy[i].x;
7502         int ey = y + xy[i].y;
7503
7504         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7505                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7506                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7507                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7508         {
7509           attr_x = ex;
7510           attr_y = ey;
7511           break;
7512         }
7513       }
7514     }
7515
7516     MovDir[x][y] = MV_NONE;
7517     if (attr_x < x)
7518       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7519     else if (attr_x > x)
7520       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7521     if (attr_y < y)
7522       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7523     else if (attr_y > y)
7524       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7525
7526     if (element == EL_ROBOT)
7527     {
7528       int newx, newy;
7529
7530       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7531         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7532       Moving2Blocked(x, y, &newx, &newy);
7533
7534       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7535         MovDelay[x][y] = 8 + 8 * !RND(3);
7536       else
7537         MovDelay[x][y] = 16;
7538     }
7539     else if (element == EL_PENGUIN)
7540     {
7541       int newx, newy;
7542
7543       MovDelay[x][y] = 1;
7544
7545       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7546       {
7547         boolean first_horiz = RND(2);
7548         int new_move_dir = MovDir[x][y];
7549
7550         MovDir[x][y] =
7551           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7552         Moving2Blocked(x, y, &newx, &newy);
7553
7554         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7555           return;
7556
7557         MovDir[x][y] =
7558           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7559         Moving2Blocked(x, y, &newx, &newy);
7560
7561         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7562           return;
7563
7564         MovDir[x][y] = old_move_dir;
7565         return;
7566       }
7567     }
7568     else if (element == EL_SATELLITE)
7569     {
7570       int newx, newy;
7571
7572       MovDelay[x][y] = 1;
7573
7574       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7575       {
7576         boolean first_horiz = RND(2);
7577         int new_move_dir = MovDir[x][y];
7578
7579         MovDir[x][y] =
7580           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7581         Moving2Blocked(x, y, &newx, &newy);
7582
7583         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7584           return;
7585
7586         MovDir[x][y] =
7587           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7588         Moving2Blocked(x, y, &newx, &newy);
7589
7590         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7591           return;
7592
7593         MovDir[x][y] = old_move_dir;
7594         return;
7595       }
7596     }
7597     else if (element == EL_EMC_ANDROID)
7598     {
7599       static int check_pos[16] =
7600       {
7601         -1,             //  0 => (invalid)
7602         7,              //  1 => MV_LEFT
7603         3,              //  2 => MV_RIGHT
7604         -1,             //  3 => (invalid)
7605         1,              //  4 =>            MV_UP
7606         0,              //  5 => MV_LEFT  | MV_UP
7607         2,              //  6 => MV_RIGHT | MV_UP
7608         -1,             //  7 => (invalid)
7609         5,              //  8 =>            MV_DOWN
7610         6,              //  9 => MV_LEFT  | MV_DOWN
7611         4,              // 10 => MV_RIGHT | MV_DOWN
7612         -1,             // 11 => (invalid)
7613         -1,             // 12 => (invalid)
7614         -1,             // 13 => (invalid)
7615         -1,             // 14 => (invalid)
7616         -1,             // 15 => (invalid)
7617       };
7618       static struct
7619       {
7620         int dx, dy;
7621         int dir;
7622       } check_xy[8] =
7623       {
7624         { -1, -1,       MV_LEFT  | MV_UP   },
7625         {  0, -1,                  MV_UP   },
7626         { +1, -1,       MV_RIGHT | MV_UP   },
7627         { +1,  0,       MV_RIGHT           },
7628         { +1, +1,       MV_RIGHT | MV_DOWN },
7629         {  0, +1,                  MV_DOWN },
7630         { -1, +1,       MV_LEFT  | MV_DOWN },
7631         { -1,  0,       MV_LEFT            },
7632       };
7633       int start_pos, check_order;
7634       boolean can_clone = FALSE;
7635       int i;
7636
7637       // check if there is any free field around current position
7638       for (i = 0; i < 8; i++)
7639       {
7640         int newx = x + check_xy[i].dx;
7641         int newy = y + check_xy[i].dy;
7642
7643         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7644         {
7645           can_clone = TRUE;
7646
7647           break;
7648         }
7649       }
7650
7651       if (can_clone)            // randomly find an element to clone
7652       {
7653         can_clone = FALSE;
7654
7655         start_pos = check_pos[RND(8)];
7656         check_order = (RND(2) ? -1 : +1);
7657
7658         for (i = 0; i < 8; i++)
7659         {
7660           int pos_raw = start_pos + i * check_order;
7661           int pos = (pos_raw + 8) % 8;
7662           int newx = x + check_xy[pos].dx;
7663           int newy = y + check_xy[pos].dy;
7664
7665           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7666           {
7667             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7668             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7669
7670             Store[x][y] = Tile[newx][newy];
7671
7672             can_clone = TRUE;
7673
7674             break;
7675           }
7676         }
7677       }
7678
7679       if (can_clone)            // randomly find a direction to move
7680       {
7681         can_clone = FALSE;
7682
7683         start_pos = check_pos[RND(8)];
7684         check_order = (RND(2) ? -1 : +1);
7685
7686         for (i = 0; i < 8; i++)
7687         {
7688           int pos_raw = start_pos + i * check_order;
7689           int pos = (pos_raw + 8) % 8;
7690           int newx = x + check_xy[pos].dx;
7691           int newy = y + check_xy[pos].dy;
7692           int new_move_dir = check_xy[pos].dir;
7693
7694           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7695           {
7696             MovDir[x][y] = new_move_dir;
7697             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7698
7699             can_clone = TRUE;
7700
7701             break;
7702           }
7703         }
7704       }
7705
7706       if (can_clone)            // cloning and moving successful
7707         return;
7708
7709       // cannot clone -- try to move towards player
7710
7711       start_pos = check_pos[MovDir[x][y] & 0x0f];
7712       check_order = (RND(2) ? -1 : +1);
7713
7714       for (i = 0; i < 3; i++)
7715       {
7716         // first check start_pos, then previous/next or (next/previous) pos
7717         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7718         int pos = (pos_raw + 8) % 8;
7719         int newx = x + check_xy[pos].dx;
7720         int newy = y + check_xy[pos].dy;
7721         int new_move_dir = check_xy[pos].dir;
7722
7723         if (IS_PLAYER(newx, newy))
7724           break;
7725
7726         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7727         {
7728           MovDir[x][y] = new_move_dir;
7729           MovDelay[x][y] = level.android_move_time * 8 + 1;
7730
7731           break;
7732         }
7733       }
7734     }
7735   }
7736   else if (move_pattern == MV_TURNING_LEFT ||
7737            move_pattern == MV_TURNING_RIGHT ||
7738            move_pattern == MV_TURNING_LEFT_RIGHT ||
7739            move_pattern == MV_TURNING_RIGHT_LEFT ||
7740            move_pattern == MV_TURNING_RANDOM ||
7741            move_pattern == MV_ALL_DIRECTIONS)
7742   {
7743     boolean can_turn_left =
7744       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7745     boolean can_turn_right =
7746       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7747
7748     if (element_info[element].move_stepsize == 0)       // "not moving"
7749       return;
7750
7751     if (move_pattern == MV_TURNING_LEFT)
7752       MovDir[x][y] = left_dir;
7753     else if (move_pattern == MV_TURNING_RIGHT)
7754       MovDir[x][y] = right_dir;
7755     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7756       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7757     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7758       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7759     else if (move_pattern == MV_TURNING_RANDOM)
7760       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7761                       can_turn_right && !can_turn_left ? right_dir :
7762                       RND(2) ? left_dir : right_dir);
7763     else if (can_turn_left && can_turn_right)
7764       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7765     else if (can_turn_left)
7766       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7767     else if (can_turn_right)
7768       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7769     else
7770       MovDir[x][y] = back_dir;
7771
7772     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7773   }
7774   else if (move_pattern == MV_HORIZONTAL ||
7775            move_pattern == MV_VERTICAL)
7776   {
7777     if (move_pattern & old_move_dir)
7778       MovDir[x][y] = back_dir;
7779     else if (move_pattern == MV_HORIZONTAL)
7780       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7781     else if (move_pattern == MV_VERTICAL)
7782       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7783
7784     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7785   }
7786   else if (move_pattern & MV_ANY_DIRECTION)
7787   {
7788     MovDir[x][y] = move_pattern;
7789     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7790   }
7791   else if (move_pattern & MV_WIND_DIRECTION)
7792   {
7793     MovDir[x][y] = game.wind_direction;
7794     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7795   }
7796   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7797   {
7798     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7799       MovDir[x][y] = left_dir;
7800     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7801       MovDir[x][y] = right_dir;
7802
7803     if (MovDir[x][y] != old_move_dir)
7804       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7805   }
7806   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7807   {
7808     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7809       MovDir[x][y] = right_dir;
7810     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7811       MovDir[x][y] = left_dir;
7812
7813     if (MovDir[x][y] != old_move_dir)
7814       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7815   }
7816   else if (move_pattern == MV_TOWARDS_PLAYER ||
7817            move_pattern == MV_AWAY_FROM_PLAYER)
7818   {
7819     int attr_x = -1, attr_y = -1;
7820     int newx, newy;
7821     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7822
7823     if (game.all_players_gone)
7824     {
7825       attr_x = game.exit_x;
7826       attr_y = game.exit_y;
7827     }
7828     else
7829     {
7830       int i;
7831
7832       for (i = 0; i < MAX_PLAYERS; i++)
7833       {
7834         struct PlayerInfo *player = &stored_player[i];
7835         int jx = player->jx, jy = player->jy;
7836
7837         if (!player->active)
7838           continue;
7839
7840         if (attr_x == -1 ||
7841             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7842         {
7843           attr_x = jx;
7844           attr_y = jy;
7845         }
7846       }
7847     }
7848
7849     MovDir[x][y] = MV_NONE;
7850     if (attr_x < x)
7851       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7852     else if (attr_x > x)
7853       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7854     if (attr_y < y)
7855       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7856     else if (attr_y > y)
7857       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7858
7859     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7860
7861     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7862     {
7863       boolean first_horiz = RND(2);
7864       int new_move_dir = MovDir[x][y];
7865
7866       if (element_info[element].move_stepsize == 0)     // "not moving"
7867       {
7868         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7869         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7870
7871         return;
7872       }
7873
7874       MovDir[x][y] =
7875         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7876       Moving2Blocked(x, y, &newx, &newy);
7877
7878       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7879         return;
7880
7881       MovDir[x][y] =
7882         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7883       Moving2Blocked(x, y, &newx, &newy);
7884
7885       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7886         return;
7887
7888       MovDir[x][y] = old_move_dir;
7889     }
7890   }
7891   else if (move_pattern == MV_WHEN_PUSHED ||
7892            move_pattern == MV_WHEN_DROPPED)
7893   {
7894     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7895       MovDir[x][y] = MV_NONE;
7896
7897     MovDelay[x][y] = 0;
7898   }
7899   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7900   {
7901     struct XY *test_xy = xy_topdown;
7902     static int test_dir[4] =
7903     {
7904       MV_UP,
7905       MV_LEFT,
7906       MV_RIGHT,
7907       MV_DOWN
7908     };
7909     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7910     int move_preference = -1000000;     // start with very low preference
7911     int new_move_dir = MV_NONE;
7912     int start_test = RND(4);
7913     int i;
7914
7915     for (i = 0; i < NUM_DIRECTIONS; i++)
7916     {
7917       int j = (start_test + i) % 4;
7918       int move_dir = test_dir[j];
7919       int move_dir_preference;
7920
7921       xx = x + test_xy[j].x;
7922       yy = y + test_xy[j].y;
7923
7924       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7925           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7926       {
7927         new_move_dir = move_dir;
7928
7929         break;
7930       }
7931
7932       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7933         continue;
7934
7935       move_dir_preference = -1 * RunnerVisit[xx][yy];
7936       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7937         move_dir_preference = PlayerVisit[xx][yy];
7938
7939       if (move_dir_preference > move_preference)
7940       {
7941         // prefer field that has not been visited for the longest time
7942         move_preference = move_dir_preference;
7943         new_move_dir = move_dir;
7944       }
7945       else if (move_dir_preference == move_preference &&
7946                move_dir == old_move_dir)
7947       {
7948         // prefer last direction when all directions are preferred equally
7949         move_preference = move_dir_preference;
7950         new_move_dir = move_dir;
7951       }
7952     }
7953
7954     MovDir[x][y] = new_move_dir;
7955     if (old_move_dir != new_move_dir)
7956       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7957   }
7958 }
7959
7960 static void TurnRound(int x, int y)
7961 {
7962   int direction = MovDir[x][y];
7963
7964   TurnRoundExt(x, y);
7965
7966   GfxDir[x][y] = MovDir[x][y];
7967
7968   if (direction != MovDir[x][y])
7969     GfxFrame[x][y] = 0;
7970
7971   if (MovDelay[x][y])
7972     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7973
7974   ResetGfxFrame(x, y);
7975 }
7976
7977 static boolean JustBeingPushed(int x, int y)
7978 {
7979   int i;
7980
7981   for (i = 0; i < MAX_PLAYERS; i++)
7982   {
7983     struct PlayerInfo *player = &stored_player[i];
7984
7985     if (player->active && player->is_pushing && player->MovPos)
7986     {
7987       int next_jx = player->jx + (player->jx - player->last_jx);
7988       int next_jy = player->jy + (player->jy - player->last_jy);
7989
7990       if (x == next_jx && y == next_jy)
7991         return TRUE;
7992     }
7993   }
7994
7995   return FALSE;
7996 }
7997
7998 static void StartMoving(int x, int y)
7999 {
8000   boolean started_moving = FALSE;       // some elements can fall _and_ move
8001   int element = Tile[x][y];
8002
8003   if (Stop[x][y])
8004     return;
8005
8006   if (MovDelay[x][y] == 0)
8007     GfxAction[x][y] = ACTION_DEFAULT;
8008
8009   if (CAN_FALL(element) && y < lev_fieldy - 1)
8010   {
8011     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
8012         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
8013       if (JustBeingPushed(x, y))
8014         return;
8015
8016     if (element == EL_QUICKSAND_FULL)
8017     {
8018       if (IS_FREE(x, y + 1))
8019       {
8020         InitMovingField(x, y, MV_DOWN);
8021         started_moving = TRUE;
8022
8023         Tile[x][y] = EL_QUICKSAND_EMPTYING;
8024 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8025         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8026           Store[x][y] = EL_ROCK;
8027 #else
8028         Store[x][y] = EL_ROCK;
8029 #endif
8030
8031         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8032       }
8033       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8034       {
8035         if (!MovDelay[x][y])
8036         {
8037           MovDelay[x][y] = TILEY + 1;
8038
8039           ResetGfxAnimation(x, y);
8040           ResetGfxAnimation(x, y + 1);
8041         }
8042
8043         if (MovDelay[x][y])
8044         {
8045           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8046           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8047
8048           MovDelay[x][y]--;
8049           if (MovDelay[x][y])
8050             return;
8051         }
8052
8053         Tile[x][y] = EL_QUICKSAND_EMPTY;
8054         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8055         Store[x][y + 1] = Store[x][y];
8056         Store[x][y] = 0;
8057
8058         PlayLevelSoundAction(x, y, ACTION_FILLING);
8059       }
8060       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8061       {
8062         if (!MovDelay[x][y])
8063         {
8064           MovDelay[x][y] = TILEY + 1;
8065
8066           ResetGfxAnimation(x, y);
8067           ResetGfxAnimation(x, y + 1);
8068         }
8069
8070         if (MovDelay[x][y])
8071         {
8072           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8073           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8074
8075           MovDelay[x][y]--;
8076           if (MovDelay[x][y])
8077             return;
8078         }
8079
8080         Tile[x][y] = EL_QUICKSAND_EMPTY;
8081         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8082         Store[x][y + 1] = Store[x][y];
8083         Store[x][y] = 0;
8084
8085         PlayLevelSoundAction(x, y, ACTION_FILLING);
8086       }
8087     }
8088     else if (element == EL_QUICKSAND_FAST_FULL)
8089     {
8090       if (IS_FREE(x, y + 1))
8091       {
8092         InitMovingField(x, y, MV_DOWN);
8093         started_moving = TRUE;
8094
8095         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8096 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8097         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8098           Store[x][y] = EL_ROCK;
8099 #else
8100         Store[x][y] = EL_ROCK;
8101 #endif
8102
8103         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8104       }
8105       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8106       {
8107         if (!MovDelay[x][y])
8108         {
8109           MovDelay[x][y] = TILEY + 1;
8110
8111           ResetGfxAnimation(x, y);
8112           ResetGfxAnimation(x, y + 1);
8113         }
8114
8115         if (MovDelay[x][y])
8116         {
8117           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8118           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8119
8120           MovDelay[x][y]--;
8121           if (MovDelay[x][y])
8122             return;
8123         }
8124
8125         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8126         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8127         Store[x][y + 1] = Store[x][y];
8128         Store[x][y] = 0;
8129
8130         PlayLevelSoundAction(x, y, ACTION_FILLING);
8131       }
8132       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8133       {
8134         if (!MovDelay[x][y])
8135         {
8136           MovDelay[x][y] = TILEY + 1;
8137
8138           ResetGfxAnimation(x, y);
8139           ResetGfxAnimation(x, y + 1);
8140         }
8141
8142         if (MovDelay[x][y])
8143         {
8144           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8145           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8146
8147           MovDelay[x][y]--;
8148           if (MovDelay[x][y])
8149             return;
8150         }
8151
8152         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8153         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8154         Store[x][y + 1] = Store[x][y];
8155         Store[x][y] = 0;
8156
8157         PlayLevelSoundAction(x, y, ACTION_FILLING);
8158       }
8159     }
8160     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8161              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8162     {
8163       InitMovingField(x, y, MV_DOWN);
8164       started_moving = TRUE;
8165
8166       Tile[x][y] = EL_QUICKSAND_FILLING;
8167       Store[x][y] = element;
8168
8169       PlayLevelSoundAction(x, y, ACTION_FILLING);
8170     }
8171     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8172              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8173     {
8174       InitMovingField(x, y, MV_DOWN);
8175       started_moving = TRUE;
8176
8177       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8178       Store[x][y] = element;
8179
8180       PlayLevelSoundAction(x, y, ACTION_FILLING);
8181     }
8182     else if (element == EL_MAGIC_WALL_FULL)
8183     {
8184       if (IS_FREE(x, y + 1))
8185       {
8186         InitMovingField(x, y, MV_DOWN);
8187         started_moving = TRUE;
8188
8189         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8190         Store[x][y] = EL_CHANGED(Store[x][y]);
8191       }
8192       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8193       {
8194         if (!MovDelay[x][y])
8195           MovDelay[x][y] = TILEY / 4 + 1;
8196
8197         if (MovDelay[x][y])
8198         {
8199           MovDelay[x][y]--;
8200           if (MovDelay[x][y])
8201             return;
8202         }
8203
8204         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8205         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8206         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8207         Store[x][y] = 0;
8208       }
8209     }
8210     else if (element == EL_BD_MAGIC_WALL_FULL)
8211     {
8212       if (IS_FREE(x, y + 1))
8213       {
8214         InitMovingField(x, y, MV_DOWN);
8215         started_moving = TRUE;
8216
8217         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8218         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8219       }
8220       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8221       {
8222         if (!MovDelay[x][y])
8223           MovDelay[x][y] = TILEY / 4 + 1;
8224
8225         if (MovDelay[x][y])
8226         {
8227           MovDelay[x][y]--;
8228           if (MovDelay[x][y])
8229             return;
8230         }
8231
8232         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8233         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8234         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8235         Store[x][y] = 0;
8236       }
8237     }
8238     else if (element == EL_DC_MAGIC_WALL_FULL)
8239     {
8240       if (IS_FREE(x, y + 1))
8241       {
8242         InitMovingField(x, y, MV_DOWN);
8243         started_moving = TRUE;
8244
8245         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8246         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8247       }
8248       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8249       {
8250         if (!MovDelay[x][y])
8251           MovDelay[x][y] = TILEY / 4 + 1;
8252
8253         if (MovDelay[x][y])
8254         {
8255           MovDelay[x][y]--;
8256           if (MovDelay[x][y])
8257             return;
8258         }
8259
8260         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8261         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8262         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8263         Store[x][y] = 0;
8264       }
8265     }
8266     else if ((CAN_PASS_MAGIC_WALL(element) &&
8267               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8268                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8269              (CAN_PASS_DC_MAGIC_WALL(element) &&
8270               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8271
8272     {
8273       InitMovingField(x, y, MV_DOWN);
8274       started_moving = TRUE;
8275
8276       Tile[x][y] =
8277         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8278          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8279          EL_DC_MAGIC_WALL_FILLING);
8280       Store[x][y] = element;
8281     }
8282     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8283     {
8284       SplashAcid(x, y + 1);
8285
8286       InitMovingField(x, y, MV_DOWN);
8287       started_moving = TRUE;
8288
8289       Store[x][y] = EL_ACID;
8290     }
8291     else if (
8292              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8293               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8294              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8295               CAN_FALL(element) && WasJustFalling[x][y] &&
8296               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8297
8298              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8299               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8300               (Tile[x][y + 1] == EL_BLOCKED)))
8301     {
8302       /* this is needed for a special case not covered by calling "Impact()"
8303          from "ContinueMoving()": if an element moves to a tile directly below
8304          another element which was just falling on that tile (which was empty
8305          in the previous frame), the falling element above would just stop
8306          instead of smashing the element below (in previous version, the above
8307          element was just checked for "moving" instead of "falling", resulting
8308          in incorrect smashes caused by horizontal movement of the above
8309          element; also, the case of the player being the element to smash was
8310          simply not covered here... :-/ ) */
8311
8312       CheckCollision[x][y] = 0;
8313       CheckImpact[x][y] = 0;
8314
8315       Impact(x, y);
8316     }
8317     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8318     {
8319       if (MovDir[x][y] == MV_NONE)
8320       {
8321         InitMovingField(x, y, MV_DOWN);
8322         started_moving = TRUE;
8323       }
8324     }
8325     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8326     {
8327       if (WasJustFalling[x][y]) // prevent animation from being restarted
8328         MovDir[x][y] = MV_DOWN;
8329
8330       InitMovingField(x, y, MV_DOWN);
8331       started_moving = TRUE;
8332     }
8333     else if (element == EL_AMOEBA_DROP)
8334     {
8335       Tile[x][y] = EL_AMOEBA_GROWING;
8336       Store[x][y] = EL_AMOEBA_WET;
8337     }
8338     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8339               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8340              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8341              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8342     {
8343       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8344                                 (IS_FREE(x - 1, y + 1) ||
8345                                  Tile[x - 1][y + 1] == EL_ACID));
8346       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8347                                 (IS_FREE(x + 1, y + 1) ||
8348                                  Tile[x + 1][y + 1] == EL_ACID));
8349       boolean can_fall_any  = (can_fall_left || can_fall_right);
8350       boolean can_fall_both = (can_fall_left && can_fall_right);
8351       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8352
8353       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8354       {
8355         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8356           can_fall_right = FALSE;
8357         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8358           can_fall_left = FALSE;
8359         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8360           can_fall_right = FALSE;
8361         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8362           can_fall_left = FALSE;
8363
8364         can_fall_any  = (can_fall_left || can_fall_right);
8365         can_fall_both = FALSE;
8366       }
8367
8368       if (can_fall_both)
8369       {
8370         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8371           can_fall_right = FALSE;       // slip down on left side
8372         else
8373           can_fall_left = !(can_fall_right = RND(2));
8374
8375         can_fall_both = FALSE;
8376       }
8377
8378       if (can_fall_any)
8379       {
8380         // if not determined otherwise, prefer left side for slipping down
8381         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8382         started_moving = TRUE;
8383       }
8384     }
8385     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8386     {
8387       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8388       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8389       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8390       int belt_dir = game.belt_dir[belt_nr];
8391
8392       if ((belt_dir == MV_LEFT  && left_is_free) ||
8393           (belt_dir == MV_RIGHT && right_is_free))
8394       {
8395         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8396
8397         InitMovingField(x, y, belt_dir);
8398         started_moving = TRUE;
8399
8400         Pushed[x][y] = TRUE;
8401         Pushed[nextx][y] = TRUE;
8402
8403         GfxAction[x][y] = ACTION_DEFAULT;
8404       }
8405       else
8406       {
8407         MovDir[x][y] = 0;       // if element was moving, stop it
8408       }
8409     }
8410   }
8411
8412   // not "else if" because of elements that can fall and move (EL_SPRING)
8413   if (CAN_MOVE(element) && !started_moving)
8414   {
8415     int move_pattern = element_info[element].move_pattern;
8416     int newx, newy;
8417
8418     Moving2Blocked(x, y, &newx, &newy);
8419
8420     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8421       return;
8422
8423     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8424         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8425     {
8426       WasJustMoving[x][y] = 0;
8427       CheckCollision[x][y] = 0;
8428
8429       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8430
8431       if (Tile[x][y] != element)        // element has changed
8432         return;
8433     }
8434
8435     if (!MovDelay[x][y])        // start new movement phase
8436     {
8437       // all objects that can change their move direction after each step
8438       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8439
8440       if (element != EL_YAMYAM &&
8441           element != EL_DARK_YAMYAM &&
8442           element != EL_PACMAN &&
8443           !(move_pattern & MV_ANY_DIRECTION) &&
8444           move_pattern != MV_TURNING_LEFT &&
8445           move_pattern != MV_TURNING_RIGHT &&
8446           move_pattern != MV_TURNING_LEFT_RIGHT &&
8447           move_pattern != MV_TURNING_RIGHT_LEFT &&
8448           move_pattern != MV_TURNING_RANDOM)
8449       {
8450         TurnRound(x, y);
8451
8452         if (MovDelay[x][y] && (element == EL_BUG ||
8453                                element == EL_SPACESHIP ||
8454                                element == EL_SP_SNIKSNAK ||
8455                                element == EL_SP_ELECTRON ||
8456                                element == EL_MOLE))
8457           TEST_DrawLevelField(x, y);
8458       }
8459     }
8460
8461     if (MovDelay[x][y])         // wait some time before next movement
8462     {
8463       MovDelay[x][y]--;
8464
8465       if (element == EL_ROBOT ||
8466           element == EL_YAMYAM ||
8467           element == EL_DARK_YAMYAM)
8468       {
8469         DrawLevelElementAnimationIfNeeded(x, y, element);
8470         PlayLevelSoundAction(x, y, ACTION_WAITING);
8471       }
8472       else if (element == EL_SP_ELECTRON)
8473         DrawLevelElementAnimationIfNeeded(x, y, element);
8474       else if (element == EL_DRAGON)
8475       {
8476         int i;
8477         int dir = MovDir[x][y];
8478         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8479         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8480         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8481                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8482                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8483                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8484         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8485
8486         GfxAction[x][y] = ACTION_ATTACKING;
8487
8488         if (IS_PLAYER(x, y))
8489           DrawPlayerField(x, y);
8490         else
8491           TEST_DrawLevelField(x, y);
8492
8493         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8494
8495         for (i = 1; i <= 3; i++)
8496         {
8497           int xx = x + i * dx;
8498           int yy = y + i * dy;
8499           int sx = SCREENX(xx);
8500           int sy = SCREENY(yy);
8501           int flame_graphic = graphic + (i - 1);
8502
8503           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8504             break;
8505
8506           if (MovDelay[x][y])
8507           {
8508             int flamed = MovingOrBlocked2Element(xx, yy);
8509
8510             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8511               Bang(xx, yy);
8512             else
8513               RemoveMovingField(xx, yy);
8514
8515             ChangeDelay[xx][yy] = 0;
8516
8517             Tile[xx][yy] = EL_FLAMES;
8518
8519             if (IN_SCR_FIELD(sx, sy))
8520             {
8521               TEST_DrawLevelFieldCrumbled(xx, yy);
8522               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8523             }
8524           }
8525           else
8526           {
8527             if (Tile[xx][yy] == EL_FLAMES)
8528               Tile[xx][yy] = EL_EMPTY;
8529             TEST_DrawLevelField(xx, yy);
8530           }
8531         }
8532       }
8533
8534       if (MovDelay[x][y])       // element still has to wait some time
8535       {
8536         PlayLevelSoundAction(x, y, ACTION_WAITING);
8537
8538         return;
8539       }
8540     }
8541
8542     // now make next step
8543
8544     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8545
8546     if (DONT_COLLIDE_WITH(element) &&
8547         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8548         !PLAYER_ENEMY_PROTECTED(newx, newy))
8549     {
8550       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8551
8552       return;
8553     }
8554
8555     else if (CAN_MOVE_INTO_ACID(element) &&
8556              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8557              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8558              (MovDir[x][y] == MV_DOWN ||
8559               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8560     {
8561       SplashAcid(newx, newy);
8562       Store[x][y] = EL_ACID;
8563     }
8564     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8565     {
8566       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8567           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8568           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8569           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8570       {
8571         RemoveField(x, y);
8572         TEST_DrawLevelField(x, y);
8573
8574         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8575         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8576           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8577
8578         game.friends_still_needed--;
8579         if (!game.friends_still_needed &&
8580             !game.GameOver &&
8581             game.all_players_gone)
8582           LevelSolved();
8583
8584         return;
8585       }
8586       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8587       {
8588         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8589           TEST_DrawLevelField(newx, newy);
8590         else
8591           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8592       }
8593       else if (!IS_FREE(newx, newy))
8594       {
8595         GfxAction[x][y] = ACTION_WAITING;
8596
8597         if (IS_PLAYER(x, y))
8598           DrawPlayerField(x, y);
8599         else
8600           TEST_DrawLevelField(x, y);
8601
8602         return;
8603       }
8604     }
8605     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8606     {
8607       if (IS_FOOD_PIG(Tile[newx][newy]))
8608       {
8609         if (IS_MOVING(newx, newy))
8610           RemoveMovingField(newx, newy);
8611         else
8612         {
8613           Tile[newx][newy] = EL_EMPTY;
8614           TEST_DrawLevelField(newx, newy);
8615         }
8616
8617         PlayLevelSound(x, y, SND_PIG_DIGGING);
8618       }
8619       else if (!IS_FREE(newx, newy))
8620       {
8621         if (IS_PLAYER(x, y))
8622           DrawPlayerField(x, y);
8623         else
8624           TEST_DrawLevelField(x, y);
8625
8626         return;
8627       }
8628     }
8629     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8630     {
8631       if (Store[x][y] != EL_EMPTY)
8632       {
8633         boolean can_clone = FALSE;
8634         int xx, yy;
8635
8636         // check if element to clone is still there
8637         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8638         {
8639           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8640           {
8641             can_clone = TRUE;
8642
8643             break;
8644           }
8645         }
8646
8647         // cannot clone or target field not free anymore -- do not clone
8648         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8649           Store[x][y] = EL_EMPTY;
8650       }
8651
8652       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8653       {
8654         if (IS_MV_DIAGONAL(MovDir[x][y]))
8655         {
8656           int diagonal_move_dir = MovDir[x][y];
8657           int stored = Store[x][y];
8658           int change_delay = 8;
8659           int graphic;
8660
8661           // android is moving diagonally
8662
8663           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8664
8665           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8666           GfxElement[x][y] = EL_EMC_ANDROID;
8667           GfxAction[x][y] = ACTION_SHRINKING;
8668           GfxDir[x][y] = diagonal_move_dir;
8669           ChangeDelay[x][y] = change_delay;
8670
8671           if (Store[x][y] == EL_EMPTY)
8672             Store[x][y] = GfxElementEmpty[x][y];
8673
8674           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8675                                    GfxDir[x][y]);
8676
8677           DrawLevelGraphicAnimation(x, y, graphic);
8678           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8679
8680           if (Tile[newx][newy] == EL_ACID)
8681           {
8682             SplashAcid(newx, newy);
8683
8684             return;
8685           }
8686
8687           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8688
8689           Store[newx][newy] = EL_EMC_ANDROID;
8690           GfxElement[newx][newy] = EL_EMC_ANDROID;
8691           GfxAction[newx][newy] = ACTION_GROWING;
8692           GfxDir[newx][newy] = diagonal_move_dir;
8693           ChangeDelay[newx][newy] = change_delay;
8694
8695           graphic = el_act_dir2img(GfxElement[newx][newy],
8696                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8697
8698           DrawLevelGraphicAnimation(newx, newy, graphic);
8699           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8700
8701           return;
8702         }
8703         else
8704         {
8705           Tile[newx][newy] = EL_EMPTY;
8706           TEST_DrawLevelField(newx, newy);
8707
8708           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8709         }
8710       }
8711       else if (!IS_FREE(newx, newy))
8712       {
8713         return;
8714       }
8715     }
8716     else if (IS_CUSTOM_ELEMENT(element) &&
8717              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8718     {
8719       if (!DigFieldByCE(newx, newy, element))
8720         return;
8721
8722       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8723       {
8724         RunnerVisit[x][y] = FrameCounter;
8725         PlayerVisit[x][y] /= 8;         // expire player visit path
8726       }
8727     }
8728     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8729     {
8730       if (!IS_FREE(newx, newy))
8731       {
8732         if (IS_PLAYER(x, y))
8733           DrawPlayerField(x, y);
8734         else
8735           TEST_DrawLevelField(x, y);
8736
8737         return;
8738       }
8739       else
8740       {
8741         boolean wanna_flame = !RND(10);
8742         int dx = newx - x, dy = newy - y;
8743         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8744         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8745         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8746                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8747         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8748                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8749
8750         if ((wanna_flame ||
8751              IS_CLASSIC_ENEMY(element1) ||
8752              IS_CLASSIC_ENEMY(element2)) &&
8753             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8754             element1 != EL_FLAMES && element2 != EL_FLAMES)
8755         {
8756           ResetGfxAnimation(x, y);
8757           GfxAction[x][y] = ACTION_ATTACKING;
8758
8759           if (IS_PLAYER(x, y))
8760             DrawPlayerField(x, y);
8761           else
8762             TEST_DrawLevelField(x, y);
8763
8764           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8765
8766           MovDelay[x][y] = 50;
8767
8768           Tile[newx][newy] = EL_FLAMES;
8769           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8770             Tile[newx1][newy1] = EL_FLAMES;
8771           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8772             Tile[newx2][newy2] = EL_FLAMES;
8773
8774           return;
8775         }
8776       }
8777     }
8778     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8779              Tile[newx][newy] == EL_DIAMOND)
8780     {
8781       if (IS_MOVING(newx, newy))
8782         RemoveMovingField(newx, newy);
8783       else
8784       {
8785         Tile[newx][newy] = EL_EMPTY;
8786         TEST_DrawLevelField(newx, newy);
8787       }
8788
8789       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8790     }
8791     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8792              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8793     {
8794       if (AmoebaNr[newx][newy])
8795       {
8796         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8797         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8798             Tile[newx][newy] == EL_BD_AMOEBA)
8799           AmoebaCnt[AmoebaNr[newx][newy]]--;
8800       }
8801
8802       if (IS_MOVING(newx, newy))
8803       {
8804         RemoveMovingField(newx, newy);
8805       }
8806       else
8807       {
8808         Tile[newx][newy] = EL_EMPTY;
8809         TEST_DrawLevelField(newx, newy);
8810       }
8811
8812       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8813     }
8814     else if ((element == EL_PACMAN || element == EL_MOLE)
8815              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8816     {
8817       if (AmoebaNr[newx][newy])
8818       {
8819         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8820         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8821             Tile[newx][newy] == EL_BD_AMOEBA)
8822           AmoebaCnt[AmoebaNr[newx][newy]]--;
8823       }
8824
8825       if (element == EL_MOLE)
8826       {
8827         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8828         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8829
8830         ResetGfxAnimation(x, y);
8831         GfxAction[x][y] = ACTION_DIGGING;
8832         TEST_DrawLevelField(x, y);
8833
8834         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8835
8836         return;                         // wait for shrinking amoeba
8837       }
8838       else      // element == EL_PACMAN
8839       {
8840         Tile[newx][newy] = EL_EMPTY;
8841         TEST_DrawLevelField(newx, newy);
8842         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8843       }
8844     }
8845     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8846              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8847               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8848     {
8849       // wait for shrinking amoeba to completely disappear
8850       return;
8851     }
8852     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8853     {
8854       // object was running against a wall
8855
8856       TurnRound(x, y);
8857
8858       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8859         DrawLevelElementAnimation(x, y, element);
8860
8861       if (DONT_TOUCH(element))
8862         TestIfBadThingTouchesPlayer(x, y);
8863
8864       return;
8865     }
8866
8867     InitMovingField(x, y, MovDir[x][y]);
8868
8869     PlayLevelSoundAction(x, y, ACTION_MOVING);
8870   }
8871
8872   if (MovDir[x][y])
8873     ContinueMoving(x, y);
8874 }
8875
8876 void ContinueMoving(int x, int y)
8877 {
8878   int element = Tile[x][y];
8879   struct ElementInfo *ei = &element_info[element];
8880   int direction = MovDir[x][y];
8881   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8882   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8883   int newx = x + dx, newy = y + dy;
8884   int stored = Store[x][y];
8885   int stored_new = Store[newx][newy];
8886   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8887   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8888   boolean last_line = (newy == lev_fieldy - 1);
8889   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8890
8891   if (pushed_by_player)         // special case: moving object pushed by player
8892   {
8893     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8894   }
8895   else if (use_step_delay)      // special case: moving object has step delay
8896   {
8897     if (!MovDelay[x][y])
8898       MovPos[x][y] += getElementMoveStepsize(x, y);
8899
8900     if (MovDelay[x][y])
8901       MovDelay[x][y]--;
8902     else
8903       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8904
8905     if (MovDelay[x][y])
8906     {
8907       TEST_DrawLevelField(x, y);
8908
8909       return;   // element is still waiting
8910     }
8911   }
8912   else                          // normal case: generically moving object
8913   {
8914     MovPos[x][y] += getElementMoveStepsize(x, y);
8915   }
8916
8917   if (ABS(MovPos[x][y]) < TILEX)
8918   {
8919     TEST_DrawLevelField(x, y);
8920
8921     return;     // element is still moving
8922   }
8923
8924   // element reached destination field
8925
8926   Tile[x][y] = EL_EMPTY;
8927   Tile[newx][newy] = element;
8928   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8929
8930   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8931   {
8932     element = Tile[newx][newy] = EL_ACID;
8933   }
8934   else if (element == EL_MOLE)
8935   {
8936     Tile[x][y] = EL_SAND;
8937
8938     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8939   }
8940   else if (element == EL_QUICKSAND_FILLING)
8941   {
8942     element = Tile[newx][newy] = get_next_element(element);
8943     Store[newx][newy] = Store[x][y];
8944   }
8945   else if (element == EL_QUICKSAND_EMPTYING)
8946   {
8947     Tile[x][y] = get_next_element(element);
8948     element = Tile[newx][newy] = Store[x][y];
8949   }
8950   else if (element == EL_QUICKSAND_FAST_FILLING)
8951   {
8952     element = Tile[newx][newy] = get_next_element(element);
8953     Store[newx][newy] = Store[x][y];
8954   }
8955   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8956   {
8957     Tile[x][y] = get_next_element(element);
8958     element = Tile[newx][newy] = Store[x][y];
8959   }
8960   else if (element == EL_MAGIC_WALL_FILLING)
8961   {
8962     element = Tile[newx][newy] = get_next_element(element);
8963     if (!game.magic_wall_active)
8964       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8965     Store[newx][newy] = Store[x][y];
8966   }
8967   else if (element == EL_MAGIC_WALL_EMPTYING)
8968   {
8969     Tile[x][y] = get_next_element(element);
8970     if (!game.magic_wall_active)
8971       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8972     element = Tile[newx][newy] = Store[x][y];
8973
8974     InitField(newx, newy, FALSE);
8975   }
8976   else if (element == EL_BD_MAGIC_WALL_FILLING)
8977   {
8978     element = Tile[newx][newy] = get_next_element(element);
8979     if (!game.magic_wall_active)
8980       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8981     Store[newx][newy] = Store[x][y];
8982   }
8983   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8984   {
8985     Tile[x][y] = get_next_element(element);
8986     if (!game.magic_wall_active)
8987       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8988     element = Tile[newx][newy] = Store[x][y];
8989
8990     InitField(newx, newy, FALSE);
8991   }
8992   else if (element == EL_DC_MAGIC_WALL_FILLING)
8993   {
8994     element = Tile[newx][newy] = get_next_element(element);
8995     if (!game.magic_wall_active)
8996       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8997     Store[newx][newy] = Store[x][y];
8998   }
8999   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
9000   {
9001     Tile[x][y] = get_next_element(element);
9002     if (!game.magic_wall_active)
9003       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
9004     element = Tile[newx][newy] = Store[x][y];
9005
9006     InitField(newx, newy, FALSE);
9007   }
9008   else if (element == EL_AMOEBA_DROPPING)
9009   {
9010     Tile[x][y] = get_next_element(element);
9011     element = Tile[newx][newy] = Store[x][y];
9012   }
9013   else if (element == EL_SOKOBAN_OBJECT)
9014   {
9015     if (Back[x][y])
9016       Tile[x][y] = Back[x][y];
9017
9018     if (Back[newx][newy])
9019       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
9020
9021     Back[x][y] = Back[newx][newy] = 0;
9022   }
9023
9024   Store[x][y] = EL_EMPTY;
9025   MovPos[x][y] = 0;
9026   MovDir[x][y] = 0;
9027   MovDelay[x][y] = 0;
9028
9029   MovDelay[newx][newy] = 0;
9030
9031   if (CAN_CHANGE_OR_HAS_ACTION(element))
9032   {
9033     // copy element change control values to new field
9034     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9035     ChangePage[newx][newy]  = ChangePage[x][y];
9036     ChangeCount[newx][newy] = ChangeCount[x][y];
9037     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9038   }
9039
9040   CustomValue[newx][newy] = CustomValue[x][y];
9041
9042   ChangeDelay[x][y] = 0;
9043   ChangePage[x][y] = -1;
9044   ChangeCount[x][y] = 0;
9045   ChangeEvent[x][y] = -1;
9046
9047   CustomValue[x][y] = 0;
9048
9049   // copy animation control values to new field
9050   GfxFrame[newx][newy]  = GfxFrame[x][y];
9051   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9052   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9053   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9054
9055   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9056
9057   // some elements can leave other elements behind after moving
9058   if (ei->move_leave_element != EL_EMPTY &&
9059       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9060       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9061   {
9062     int move_leave_element = ei->move_leave_element;
9063
9064     // this makes it possible to leave the removed element again
9065     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9066       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9067
9068     Tile[x][y] = move_leave_element;
9069
9070     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9071       MovDir[x][y] = direction;
9072
9073     InitField(x, y, FALSE);
9074
9075     if (GFX_CRUMBLED(Tile[x][y]))
9076       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9077
9078     if (IS_PLAYER_ELEMENT(move_leave_element))
9079       RelocatePlayer(x, y, move_leave_element);
9080   }
9081
9082   // do this after checking for left-behind element
9083   ResetGfxAnimation(x, y);      // reset animation values for old field
9084
9085   if (!CAN_MOVE(element) ||
9086       (CAN_FALL(element) && direction == MV_DOWN &&
9087        (element == EL_SPRING ||
9088         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9089         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9090     GfxDir[x][y] = MovDir[newx][newy] = 0;
9091
9092   TEST_DrawLevelField(x, y);
9093   TEST_DrawLevelField(newx, newy);
9094
9095   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9096
9097   // prevent pushed element from moving on in pushed direction
9098   if (pushed_by_player && CAN_MOVE(element) &&
9099       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9100       !(element_info[element].move_pattern & direction))
9101     TurnRound(newx, newy);
9102
9103   // prevent elements on conveyor belt from moving on in last direction
9104   if (pushed_by_conveyor && CAN_FALL(element) &&
9105       direction & MV_HORIZONTAL)
9106     MovDir[newx][newy] = 0;
9107
9108   if (!pushed_by_player)
9109   {
9110     int nextx = newx + dx, nexty = newy + dy;
9111     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9112
9113     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9114
9115     if (CAN_FALL(element) && direction == MV_DOWN)
9116       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9117
9118     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9119       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9120
9121     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9122       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9123   }
9124
9125   if (DONT_TOUCH(element))      // object may be nasty to player or others
9126   {
9127     TestIfBadThingTouchesPlayer(newx, newy);
9128     TestIfBadThingTouchesFriend(newx, newy);
9129
9130     if (!IS_CUSTOM_ELEMENT(element))
9131       TestIfBadThingTouchesOtherBadThing(newx, newy);
9132   }
9133   else if (element == EL_PENGUIN)
9134     TestIfFriendTouchesBadThing(newx, newy);
9135
9136   if (DONT_GET_HIT_BY(element))
9137   {
9138     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9139   }
9140
9141   // give the player one last chance (one more frame) to move away
9142   if (CAN_FALL(element) && direction == MV_DOWN &&
9143       (last_line || (!IS_FREE(x, newy + 1) &&
9144                      (!IS_PLAYER(x, newy + 1) ||
9145                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9146     Impact(x, newy);
9147
9148   if (pushed_by_player && !game.use_change_when_pushing_bug)
9149   {
9150     int push_side = MV_DIR_OPPOSITE(direction);
9151     struct PlayerInfo *player = PLAYERINFO(x, y);
9152
9153     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9154                                player->index_bit, push_side);
9155     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9156                                         player->index_bit, push_side);
9157   }
9158
9159   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9160     MovDelay[newx][newy] = 1;
9161
9162   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9163
9164   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9165   TestIfElementHitsCustomElement(newx, newy, direction);
9166   TestIfPlayerTouchesCustomElement(newx, newy);
9167   TestIfElementTouchesCustomElement(newx, newy);
9168
9169   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9170       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9171     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9172                              MV_DIR_OPPOSITE(direction));
9173 }
9174
9175 int AmoebaNeighbourNr(int ax, int ay)
9176 {
9177   int i;
9178   int element = Tile[ax][ay];
9179   int group_nr = 0;
9180   struct XY *xy = xy_topdown;
9181
9182   for (i = 0; i < NUM_DIRECTIONS; i++)
9183   {
9184     int x = ax + xy[i].x;
9185     int y = ay + xy[i].y;
9186
9187     if (!IN_LEV_FIELD(x, y))
9188       continue;
9189
9190     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9191       group_nr = AmoebaNr[x][y];
9192   }
9193
9194   return group_nr;
9195 }
9196
9197 static void AmoebaMerge(int ax, int ay)
9198 {
9199   int i, x, y, xx, yy;
9200   int new_group_nr = AmoebaNr[ax][ay];
9201   struct XY *xy = xy_topdown;
9202
9203   if (new_group_nr == 0)
9204     return;
9205
9206   for (i = 0; i < NUM_DIRECTIONS; i++)
9207   {
9208     x = ax + xy[i].x;
9209     y = ay + xy[i].y;
9210
9211     if (!IN_LEV_FIELD(x, y))
9212       continue;
9213
9214     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9215          Tile[x][y] == EL_BD_AMOEBA ||
9216          Tile[x][y] == EL_AMOEBA_DEAD) &&
9217         AmoebaNr[x][y] != new_group_nr)
9218     {
9219       int old_group_nr = AmoebaNr[x][y];
9220
9221       if (old_group_nr == 0)
9222         return;
9223
9224       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9225       AmoebaCnt[old_group_nr] = 0;
9226       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9227       AmoebaCnt2[old_group_nr] = 0;
9228
9229       SCAN_PLAYFIELD(xx, yy)
9230       {
9231         if (AmoebaNr[xx][yy] == old_group_nr)
9232           AmoebaNr[xx][yy] = new_group_nr;
9233       }
9234     }
9235   }
9236 }
9237
9238 void AmoebaToDiamond(int ax, int ay)
9239 {
9240   int i, x, y;
9241
9242   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9243   {
9244     int group_nr = AmoebaNr[ax][ay];
9245
9246 #ifdef DEBUG
9247     if (group_nr == 0)
9248     {
9249       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9250       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9251
9252       return;
9253     }
9254 #endif
9255
9256     SCAN_PLAYFIELD(x, y)
9257     {
9258       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9259       {
9260         AmoebaNr[x][y] = 0;
9261         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9262       }
9263     }
9264
9265     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9266                             SND_AMOEBA_TURNING_TO_GEM :
9267                             SND_AMOEBA_TURNING_TO_ROCK));
9268     Bang(ax, ay);
9269   }
9270   else
9271   {
9272     struct XY *xy = xy_topdown;
9273
9274     for (i = 0; i < NUM_DIRECTIONS; i++)
9275     {
9276       x = ax + xy[i].x;
9277       y = ay + xy[i].y;
9278
9279       if (!IN_LEV_FIELD(x, y))
9280         continue;
9281
9282       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9283       {
9284         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9285                               SND_AMOEBA_TURNING_TO_GEM :
9286                               SND_AMOEBA_TURNING_TO_ROCK));
9287         Bang(x, y);
9288       }
9289     }
9290   }
9291 }
9292
9293 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9294 {
9295   int x, y;
9296   int group_nr = AmoebaNr[ax][ay];
9297   boolean done = FALSE;
9298
9299 #ifdef DEBUG
9300   if (group_nr == 0)
9301   {
9302     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9303     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9304
9305     return;
9306   }
9307 #endif
9308
9309   SCAN_PLAYFIELD(x, y)
9310   {
9311     if (AmoebaNr[x][y] == group_nr &&
9312         (Tile[x][y] == EL_AMOEBA_DEAD ||
9313          Tile[x][y] == EL_BD_AMOEBA ||
9314          Tile[x][y] == EL_AMOEBA_GROWING))
9315     {
9316       AmoebaNr[x][y] = 0;
9317       Tile[x][y] = new_element;
9318       InitField(x, y, FALSE);
9319       TEST_DrawLevelField(x, y);
9320       done = TRUE;
9321     }
9322   }
9323
9324   if (done)
9325     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9326                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9327                             SND_BD_AMOEBA_TURNING_TO_GEM));
9328 }
9329
9330 static void AmoebaGrowing(int x, int y)
9331 {
9332   static DelayCounter sound_delay = { 0 };
9333
9334   if (!MovDelay[x][y])          // start new growing cycle
9335   {
9336     MovDelay[x][y] = 7;
9337
9338     if (DelayReached(&sound_delay))
9339     {
9340       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9341       sound_delay.value = 30;
9342     }
9343   }
9344
9345   if (MovDelay[x][y])           // wait some time before growing bigger
9346   {
9347     MovDelay[x][y]--;
9348     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9349     {
9350       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9351                                            6 - MovDelay[x][y]);
9352
9353       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9354     }
9355
9356     if (!MovDelay[x][y])
9357     {
9358       Tile[x][y] = Store[x][y];
9359       Store[x][y] = 0;
9360       TEST_DrawLevelField(x, y);
9361     }
9362   }
9363 }
9364
9365 static void AmoebaShrinking(int x, int y)
9366 {
9367   static DelayCounter sound_delay = { 0 };
9368
9369   if (!MovDelay[x][y])          // start new shrinking cycle
9370   {
9371     MovDelay[x][y] = 7;
9372
9373     if (DelayReached(&sound_delay))
9374       sound_delay.value = 30;
9375   }
9376
9377   if (MovDelay[x][y])           // wait some time before shrinking
9378   {
9379     MovDelay[x][y]--;
9380     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9381     {
9382       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9383                                            6 - MovDelay[x][y]);
9384
9385       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9386     }
9387
9388     if (!MovDelay[x][y])
9389     {
9390       Tile[x][y] = EL_EMPTY;
9391       TEST_DrawLevelField(x, y);
9392
9393       // don't let mole enter this field in this cycle;
9394       // (give priority to objects falling to this field from above)
9395       Stop[x][y] = TRUE;
9396     }
9397   }
9398 }
9399
9400 static void AmoebaReproduce(int ax, int ay)
9401 {
9402   int i;
9403   int element = Tile[ax][ay];
9404   int graphic = el2img(element);
9405   int newax = ax, neway = ay;
9406   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9407   struct XY *xy = xy_topdown;
9408
9409   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9410   {
9411     Tile[ax][ay] = EL_AMOEBA_DEAD;
9412     TEST_DrawLevelField(ax, ay);
9413     return;
9414   }
9415
9416   if (IS_ANIMATED(graphic))
9417     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9418
9419   if (!MovDelay[ax][ay])        // start making new amoeba field
9420     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9421
9422   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9423   {
9424     MovDelay[ax][ay]--;
9425     if (MovDelay[ax][ay])
9426       return;
9427   }
9428
9429   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9430   {
9431     int start = RND(4);
9432     int x = ax + xy[start].x;
9433     int y = ay + xy[start].y;
9434
9435     if (!IN_LEV_FIELD(x, y))
9436       return;
9437
9438     if (IS_FREE(x, y) ||
9439         CAN_GROW_INTO(Tile[x][y]) ||
9440         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9441         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9442     {
9443       newax = x;
9444       neway = y;
9445     }
9446
9447     if (newax == ax && neway == ay)
9448       return;
9449   }
9450   else                          // normal or "filled" (BD style) amoeba
9451   {
9452     int start = RND(4);
9453     boolean waiting_for_player = FALSE;
9454
9455     for (i = 0; i < NUM_DIRECTIONS; i++)
9456     {
9457       int j = (start + i) % 4;
9458       int x = ax + xy[j].x;
9459       int y = ay + xy[j].y;
9460
9461       if (!IN_LEV_FIELD(x, y))
9462         continue;
9463
9464       if (IS_FREE(x, y) ||
9465           CAN_GROW_INTO(Tile[x][y]) ||
9466           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9467           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9468       {
9469         newax = x;
9470         neway = y;
9471         break;
9472       }
9473       else if (IS_PLAYER(x, y))
9474         waiting_for_player = TRUE;
9475     }
9476
9477     if (newax == ax && neway == ay)             // amoeba cannot grow
9478     {
9479       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9480       {
9481         Tile[ax][ay] = EL_AMOEBA_DEAD;
9482         TEST_DrawLevelField(ax, ay);
9483         AmoebaCnt[AmoebaNr[ax][ay]]--;
9484
9485         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9486         {
9487           if (element == EL_AMOEBA_FULL)
9488             AmoebaToDiamond(ax, ay);
9489           else if (element == EL_BD_AMOEBA)
9490             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9491         }
9492       }
9493       return;
9494     }
9495     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9496     {
9497       // amoeba gets larger by growing in some direction
9498
9499       int new_group_nr = AmoebaNr[ax][ay];
9500
9501 #ifdef DEBUG
9502   if (new_group_nr == 0)
9503   {
9504     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9505           newax, neway);
9506     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9507
9508     return;
9509   }
9510 #endif
9511
9512       AmoebaNr[newax][neway] = new_group_nr;
9513       AmoebaCnt[new_group_nr]++;
9514       AmoebaCnt2[new_group_nr]++;
9515
9516       // if amoeba touches other amoeba(s) after growing, unify them
9517       AmoebaMerge(newax, neway);
9518
9519       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9520       {
9521         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9522         return;
9523       }
9524     }
9525   }
9526
9527   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9528       (neway == lev_fieldy - 1 && newax != ax))
9529   {
9530     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9531     Store[newax][neway] = element;
9532   }
9533   else if (neway == ay || element == EL_EMC_DRIPPER)
9534   {
9535     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9536
9537     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9538   }
9539   else
9540   {
9541     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9542     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9543     Store[ax][ay] = EL_AMOEBA_DROP;
9544     ContinueMoving(ax, ay);
9545     return;
9546   }
9547
9548   TEST_DrawLevelField(newax, neway);
9549 }
9550
9551 static void Life(int ax, int ay)
9552 {
9553   int x1, y1, x2, y2;
9554   int life_time = 40;
9555   int element = Tile[ax][ay];
9556   int graphic = el2img(element);
9557   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9558                          level.biomaze);
9559   boolean changed = FALSE;
9560
9561   if (IS_ANIMATED(graphic))
9562     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9563
9564   if (Stop[ax][ay])
9565     return;
9566
9567   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9568     MovDelay[ax][ay] = life_time;
9569
9570   if (MovDelay[ax][ay])         // wait some time before next cycle
9571   {
9572     MovDelay[ax][ay]--;
9573     if (MovDelay[ax][ay])
9574       return;
9575   }
9576
9577   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9578   {
9579     int xx = ax + x1, yy = ay + y1;
9580     int old_element = Tile[xx][yy];
9581     int num_neighbours = 0;
9582
9583     if (!IN_LEV_FIELD(xx, yy))
9584       continue;
9585
9586     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9587     {
9588       int x = xx + x2, y = yy + y2;
9589
9590       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9591         continue;
9592
9593       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9594       boolean is_neighbour = FALSE;
9595
9596       if (level.use_life_bugs)
9597         is_neighbour =
9598           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9599            (IS_FREE(x, y)                             &&  Stop[x][y]));
9600       else
9601         is_neighbour =
9602           (Last[x][y] == element || is_player_cell);
9603
9604       if (is_neighbour)
9605         num_neighbours++;
9606     }
9607
9608     boolean is_free = FALSE;
9609
9610     if (level.use_life_bugs)
9611       is_free = (IS_FREE(xx, yy));
9612     else
9613       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9614
9615     if (xx == ax && yy == ay)           // field in the middle
9616     {
9617       if (num_neighbours < life_parameter[0] ||
9618           num_neighbours > life_parameter[1])
9619       {
9620         Tile[xx][yy] = EL_EMPTY;
9621         if (Tile[xx][yy] != old_element)
9622           TEST_DrawLevelField(xx, yy);
9623         Stop[xx][yy] = TRUE;
9624         changed = TRUE;
9625       }
9626     }
9627     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9628     {                                   // free border field
9629       if (num_neighbours >= life_parameter[2] &&
9630           num_neighbours <= life_parameter[3])
9631       {
9632         Tile[xx][yy] = element;
9633         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9634         if (Tile[xx][yy] != old_element)
9635           TEST_DrawLevelField(xx, yy);
9636         Stop[xx][yy] = TRUE;
9637         changed = TRUE;
9638       }
9639     }
9640   }
9641
9642   if (changed)
9643     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9644                    SND_GAME_OF_LIFE_GROWING);
9645 }
9646
9647 static void InitRobotWheel(int x, int y)
9648 {
9649   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9650 }
9651
9652 static void RunRobotWheel(int x, int y)
9653 {
9654   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9655 }
9656
9657 static void StopRobotWheel(int x, int y)
9658 {
9659   if (game.robot_wheel_x == x &&
9660       game.robot_wheel_y == y)
9661   {
9662     game.robot_wheel_x = -1;
9663     game.robot_wheel_y = -1;
9664     game.robot_wheel_active = FALSE;
9665   }
9666 }
9667
9668 static void InitTimegateWheel(int x, int y)
9669 {
9670   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9671 }
9672
9673 static void RunTimegateWheel(int x, int y)
9674 {
9675   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9676 }
9677
9678 static void InitMagicBallDelay(int x, int y)
9679 {
9680   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9681 }
9682
9683 static void ActivateMagicBall(int bx, int by)
9684 {
9685   int x, y;
9686
9687   if (level.ball_random)
9688   {
9689     int pos_border = RND(8);    // select one of the eight border elements
9690     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9691     int xx = pos_content % 3;
9692     int yy = pos_content / 3;
9693
9694     x = bx - 1 + xx;
9695     y = by - 1 + yy;
9696
9697     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9698       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9699   }
9700   else
9701   {
9702     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9703     {
9704       int xx = x - bx + 1;
9705       int yy = y - by + 1;
9706
9707       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9708         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9709     }
9710   }
9711
9712   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9713 }
9714
9715 static void CheckExit(int x, int y)
9716 {
9717   if (game.gems_still_needed > 0 ||
9718       game.sokoban_fields_still_needed > 0 ||
9719       game.sokoban_objects_still_needed > 0 ||
9720       game.lights_still_needed > 0)
9721   {
9722     int element = Tile[x][y];
9723     int graphic = el2img(element);
9724
9725     if (IS_ANIMATED(graphic))
9726       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9727
9728     return;
9729   }
9730
9731   // do not re-open exit door closed after last player
9732   if (game.all_players_gone)
9733     return;
9734
9735   Tile[x][y] = EL_EXIT_OPENING;
9736
9737   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9738 }
9739
9740 static void CheckExitEM(int x, int y)
9741 {
9742   if (game.gems_still_needed > 0 ||
9743       game.sokoban_fields_still_needed > 0 ||
9744       game.sokoban_objects_still_needed > 0 ||
9745       game.lights_still_needed > 0)
9746   {
9747     int element = Tile[x][y];
9748     int graphic = el2img(element);
9749
9750     if (IS_ANIMATED(graphic))
9751       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9752
9753     return;
9754   }
9755
9756   // do not re-open exit door closed after last player
9757   if (game.all_players_gone)
9758     return;
9759
9760   Tile[x][y] = EL_EM_EXIT_OPENING;
9761
9762   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9763 }
9764
9765 static void CheckExitSteel(int x, int y)
9766 {
9767   if (game.gems_still_needed > 0 ||
9768       game.sokoban_fields_still_needed > 0 ||
9769       game.sokoban_objects_still_needed > 0 ||
9770       game.lights_still_needed > 0)
9771   {
9772     int element = Tile[x][y];
9773     int graphic = el2img(element);
9774
9775     if (IS_ANIMATED(graphic))
9776       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9777
9778     return;
9779   }
9780
9781   // do not re-open exit door closed after last player
9782   if (game.all_players_gone)
9783     return;
9784
9785   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9786
9787   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9788 }
9789
9790 static void CheckExitSteelEM(int x, int y)
9791 {
9792   if (game.gems_still_needed > 0 ||
9793       game.sokoban_fields_still_needed > 0 ||
9794       game.sokoban_objects_still_needed > 0 ||
9795       game.lights_still_needed > 0)
9796   {
9797     int element = Tile[x][y];
9798     int graphic = el2img(element);
9799
9800     if (IS_ANIMATED(graphic))
9801       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9802
9803     return;
9804   }
9805
9806   // do not re-open exit door closed after last player
9807   if (game.all_players_gone)
9808     return;
9809
9810   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9811
9812   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9813 }
9814
9815 static void CheckExitSP(int x, int y)
9816 {
9817   if (game.gems_still_needed > 0)
9818   {
9819     int element = Tile[x][y];
9820     int graphic = el2img(element);
9821
9822     if (IS_ANIMATED(graphic))
9823       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9824
9825     return;
9826   }
9827
9828   // do not re-open exit door closed after last player
9829   if (game.all_players_gone)
9830     return;
9831
9832   Tile[x][y] = EL_SP_EXIT_OPENING;
9833
9834   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9835 }
9836
9837 static void CloseAllOpenTimegates(void)
9838 {
9839   int x, y;
9840
9841   SCAN_PLAYFIELD(x, y)
9842   {
9843     int element = Tile[x][y];
9844
9845     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9846     {
9847       Tile[x][y] = EL_TIMEGATE_CLOSING;
9848
9849       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9850     }
9851   }
9852 }
9853
9854 static void DrawTwinkleOnField(int x, int y)
9855 {
9856   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9857     return;
9858
9859   if (Tile[x][y] == EL_BD_DIAMOND)
9860     return;
9861
9862   if (MovDelay[x][y] == 0)      // next animation frame
9863     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9864
9865   if (MovDelay[x][y] != 0)      // wait some time before next frame
9866   {
9867     MovDelay[x][y]--;
9868
9869     DrawLevelElementAnimation(x, y, Tile[x][y]);
9870
9871     if (MovDelay[x][y] != 0)
9872     {
9873       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9874                                            10 - MovDelay[x][y]);
9875
9876       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9877     }
9878   }
9879 }
9880
9881 static void WallGrowing(int x, int y)
9882 {
9883   int delay = 6;
9884
9885   if (!MovDelay[x][y])          // next animation frame
9886     MovDelay[x][y] = 3 * delay;
9887
9888   if (MovDelay[x][y])           // wait some time before next frame
9889   {
9890     MovDelay[x][y]--;
9891
9892     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9893     {
9894       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9895       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9896
9897       DrawLevelGraphic(x, y, graphic, frame);
9898     }
9899
9900     if (!MovDelay[x][y])
9901     {
9902       if (MovDir[x][y] == MV_LEFT)
9903       {
9904         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9905           TEST_DrawLevelField(x - 1, y);
9906       }
9907       else if (MovDir[x][y] == MV_RIGHT)
9908       {
9909         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9910           TEST_DrawLevelField(x + 1, y);
9911       }
9912       else if (MovDir[x][y] == MV_UP)
9913       {
9914         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9915           TEST_DrawLevelField(x, y - 1);
9916       }
9917       else
9918       {
9919         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9920           TEST_DrawLevelField(x, y + 1);
9921       }
9922
9923       Tile[x][y] = Store[x][y];
9924       Store[x][y] = 0;
9925       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9926       TEST_DrawLevelField(x, y);
9927     }
9928   }
9929 }
9930
9931 static void CheckWallGrowing(int ax, int ay)
9932 {
9933   int element = Tile[ax][ay];
9934   int graphic = el2img(element);
9935   boolean free_top    = FALSE;
9936   boolean free_bottom = FALSE;
9937   boolean free_left   = FALSE;
9938   boolean free_right  = FALSE;
9939   boolean stop_top    = FALSE;
9940   boolean stop_bottom = FALSE;
9941   boolean stop_left   = FALSE;
9942   boolean stop_right  = FALSE;
9943   boolean new_wall    = FALSE;
9944
9945   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9946                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9947                            element == EL_EXPANDABLE_STEELWALL_ANY);
9948
9949   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9950                              element == EL_EXPANDABLE_WALL_ANY ||
9951                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9952                              element == EL_EXPANDABLE_STEELWALL_ANY);
9953
9954   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9955                              element == EL_EXPANDABLE_WALL_ANY ||
9956                              element == EL_EXPANDABLE_WALL ||
9957                              element == EL_BD_EXPANDABLE_WALL ||
9958                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9959                              element == EL_EXPANDABLE_STEELWALL_ANY);
9960
9961   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9962                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9963
9964   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9965                              element == EL_EXPANDABLE_WALL ||
9966                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9967
9968   int wall_growing = (is_steelwall ?
9969                       EL_EXPANDABLE_STEELWALL_GROWING :
9970                       EL_EXPANDABLE_WALL_GROWING);
9971
9972   int gfx_wall_growing_up    = (is_steelwall ?
9973                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9974                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9975   int gfx_wall_growing_down  = (is_steelwall ?
9976                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9977                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9978   int gfx_wall_growing_left  = (is_steelwall ?
9979                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9980                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9981   int gfx_wall_growing_right = (is_steelwall ?
9982                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9983                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9984
9985   if (IS_ANIMATED(graphic))
9986     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9987
9988   if (!MovDelay[ax][ay])        // start building new wall
9989     MovDelay[ax][ay] = 6;
9990
9991   if (MovDelay[ax][ay])         // wait some time before building new wall
9992   {
9993     MovDelay[ax][ay]--;
9994     if (MovDelay[ax][ay])
9995       return;
9996   }
9997
9998   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9999     free_top = TRUE;
10000   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
10001     free_bottom = TRUE;
10002   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
10003     free_left = TRUE;
10004   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
10005     free_right = TRUE;
10006
10007   if (grow_vertical)
10008   {
10009     if (free_top)
10010     {
10011       Tile[ax][ay - 1] = wall_growing;
10012       Store[ax][ay - 1] = element;
10013       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
10014
10015       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
10016         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
10017
10018       new_wall = TRUE;
10019     }
10020
10021     if (free_bottom)
10022     {
10023       Tile[ax][ay + 1] = wall_growing;
10024       Store[ax][ay + 1] = element;
10025       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
10026
10027       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
10028         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
10029
10030       new_wall = TRUE;
10031     }
10032   }
10033
10034   if (grow_horizontal)
10035   {
10036     if (free_left)
10037     {
10038       Tile[ax - 1][ay] = wall_growing;
10039       Store[ax - 1][ay] = element;
10040       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
10041
10042       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
10043         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
10044
10045       new_wall = TRUE;
10046     }
10047
10048     if (free_right)
10049     {
10050       Tile[ax + 1][ay] = wall_growing;
10051       Store[ax + 1][ay] = element;
10052       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10053
10054       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10055         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10056
10057       new_wall = TRUE;
10058     }
10059   }
10060
10061   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10062     TEST_DrawLevelField(ax, ay);
10063
10064   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10065     stop_top = TRUE;
10066   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10067     stop_bottom = TRUE;
10068   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10069     stop_left = TRUE;
10070   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10071     stop_right = TRUE;
10072
10073   if (((stop_top && stop_bottom) || stop_horizontal) &&
10074       ((stop_left && stop_right) || stop_vertical))
10075     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10076
10077   if (new_wall)
10078     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10079 }
10080
10081 static void CheckForDragon(int x, int y)
10082 {
10083   int i, j;
10084   boolean dragon_found = FALSE;
10085   struct XY *xy = xy_topdown;
10086
10087   for (i = 0; i < NUM_DIRECTIONS; i++)
10088   {
10089     for (j = 0; j < 4; j++)
10090     {
10091       int xx = x + j * xy[i].x;
10092       int yy = y + j * xy[i].y;
10093
10094       if (IN_LEV_FIELD(xx, yy) &&
10095           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10096       {
10097         if (Tile[xx][yy] == EL_DRAGON)
10098           dragon_found = TRUE;
10099       }
10100       else
10101         break;
10102     }
10103   }
10104
10105   if (!dragon_found)
10106   {
10107     for (i = 0; i < NUM_DIRECTIONS; i++)
10108     {
10109       for (j = 0; j < 3; j++)
10110       {
10111         int xx = x + j * xy[i].x;
10112         int yy = y + j * xy[i].y;
10113
10114         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10115         {
10116           Tile[xx][yy] = EL_EMPTY;
10117           TEST_DrawLevelField(xx, yy);
10118         }
10119         else
10120           break;
10121       }
10122     }
10123   }
10124 }
10125
10126 static void InitBuggyBase(int x, int y)
10127 {
10128   int element = Tile[x][y];
10129   int activating_delay = FRAMES_PER_SECOND / 4;
10130
10131   ChangeDelay[x][y] =
10132     (element == EL_SP_BUGGY_BASE ?
10133      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10134      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10135      activating_delay :
10136      element == EL_SP_BUGGY_BASE_ACTIVE ?
10137      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10138 }
10139
10140 static void WarnBuggyBase(int x, int y)
10141 {
10142   int i;
10143   struct XY *xy = xy_topdown;
10144
10145   for (i = 0; i < NUM_DIRECTIONS; i++)
10146   {
10147     int xx = x + xy[i].x;
10148     int yy = y + xy[i].y;
10149
10150     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10151     {
10152       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10153
10154       break;
10155     }
10156   }
10157 }
10158
10159 static void InitTrap(int x, int y)
10160 {
10161   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10162 }
10163
10164 static void ActivateTrap(int x, int y)
10165 {
10166   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10167 }
10168
10169 static void ChangeActiveTrap(int x, int y)
10170 {
10171   int graphic = IMG_TRAP_ACTIVE;
10172
10173   // if new animation frame was drawn, correct crumbled sand border
10174   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10175     TEST_DrawLevelFieldCrumbled(x, y);
10176 }
10177
10178 static int getSpecialActionElement(int element, int number, int base_element)
10179 {
10180   return (element != EL_EMPTY ? element :
10181           number != -1 ? base_element + number - 1 :
10182           EL_EMPTY);
10183 }
10184
10185 static int getModifiedActionNumber(int value_old, int operator, int operand,
10186                                    int value_min, int value_max)
10187 {
10188   int value_new = (operator == CA_MODE_SET      ? operand :
10189                    operator == CA_MODE_ADD      ? value_old + operand :
10190                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10191                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10192                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10193                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10194                    value_old);
10195
10196   return (value_new < value_min ? value_min :
10197           value_new > value_max ? value_max :
10198           value_new);
10199 }
10200
10201 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10202 {
10203   struct ElementInfo *ei = &element_info[element];
10204   struct ElementChangeInfo *change = &ei->change_page[page];
10205   int target_element = change->target_element;
10206   int action_type = change->action_type;
10207   int action_mode = change->action_mode;
10208   int action_arg = change->action_arg;
10209   int action_element = change->action_element;
10210   int i;
10211
10212   if (!change->has_action)
10213     return;
10214
10215   // ---------- determine action paramater values -----------------------------
10216
10217   int level_time_value =
10218     (level.time > 0 ? TimeLeft :
10219      TimePlayed);
10220
10221   int action_arg_element_raw =
10222     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10223      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10224      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10225      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10226      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10227      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10228      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10229      EL_EMPTY);
10230   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10231
10232   int action_arg_direction =
10233     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10234      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10235      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10236      change->actual_trigger_side :
10237      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10238      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10239      MV_NONE);
10240
10241   int action_arg_number_min =
10242     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10243      CA_ARG_MIN);
10244
10245   int action_arg_number_max =
10246     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10247      action_type == CA_SET_LEVEL_GEMS ? 999 :
10248      action_type == CA_SET_LEVEL_TIME ? 9999 :
10249      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10250      action_type == CA_SET_CE_VALUE ? 9999 :
10251      action_type == CA_SET_CE_SCORE ? 9999 :
10252      CA_ARG_MAX);
10253
10254   int action_arg_number_reset =
10255     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10256      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10257      action_type == CA_SET_LEVEL_TIME ? level.time :
10258      action_type == CA_SET_LEVEL_SCORE ? 0 :
10259      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10260      action_type == CA_SET_CE_SCORE ? 0 :
10261      0);
10262
10263   int action_arg_number =
10264     (action_arg <= CA_ARG_MAX ? action_arg :
10265      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10266      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10267      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10268      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10269      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10270      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10271      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10272      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10273      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10274      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10275      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10276      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10277      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10278      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10279      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10280      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10281      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10282      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10283      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10284      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10285      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10286      -1);
10287
10288   int action_arg_number_old =
10289     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10290      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10291      action_type == CA_SET_LEVEL_SCORE ? game.score :
10292      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10293      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10294      0);
10295
10296   int action_arg_number_new =
10297     getModifiedActionNumber(action_arg_number_old,
10298                             action_mode, action_arg_number,
10299                             action_arg_number_min, action_arg_number_max);
10300
10301   int trigger_player_bits =
10302     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10303      change->actual_trigger_player_bits : change->trigger_player);
10304
10305   int action_arg_player_bits =
10306     (action_arg >= CA_ARG_PLAYER_1 &&
10307      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10308      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10309      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10310      PLAYER_BITS_ANY);
10311
10312   // ---------- execute action  -----------------------------------------------
10313
10314   switch (action_type)
10315   {
10316     case CA_NO_ACTION:
10317     {
10318       return;
10319     }
10320
10321     // ---------- level actions  ----------------------------------------------
10322
10323     case CA_RESTART_LEVEL:
10324     {
10325       game.restart_level = TRUE;
10326
10327       break;
10328     }
10329
10330     case CA_SHOW_ENVELOPE:
10331     {
10332       int element = getSpecialActionElement(action_arg_element,
10333                                             action_arg_number, EL_ENVELOPE_1);
10334
10335       if (IS_ENVELOPE(element))
10336         local_player->show_envelope = element;
10337
10338       break;
10339     }
10340
10341     case CA_SET_LEVEL_TIME:
10342     {
10343       if (level.time > 0)       // only modify limited time value
10344       {
10345         TimeLeft = action_arg_number_new;
10346
10347         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10348
10349         DisplayGameControlValues();
10350
10351         if (!TimeLeft && game.time_limit)
10352           for (i = 0; i < MAX_PLAYERS; i++)
10353             KillPlayer(&stored_player[i]);
10354       }
10355
10356       break;
10357     }
10358
10359     case CA_SET_LEVEL_SCORE:
10360     {
10361       game.score = action_arg_number_new;
10362
10363       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10364
10365       DisplayGameControlValues();
10366
10367       break;
10368     }
10369
10370     case CA_SET_LEVEL_GEMS:
10371     {
10372       game.gems_still_needed = action_arg_number_new;
10373
10374       game.snapshot.collected_item = TRUE;
10375
10376       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10377
10378       DisplayGameControlValues();
10379
10380       break;
10381     }
10382
10383     case CA_SET_LEVEL_WIND:
10384     {
10385       game.wind_direction = action_arg_direction;
10386
10387       break;
10388     }
10389
10390     case CA_SET_LEVEL_RANDOM_SEED:
10391     {
10392       // ensure that setting a new random seed while playing is predictable
10393       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10394
10395       break;
10396     }
10397
10398     // ---------- player actions  ---------------------------------------------
10399
10400     case CA_MOVE_PLAYER:
10401     case CA_MOVE_PLAYER_NEW:
10402     {
10403       // automatically move to the next field in specified direction
10404       for (i = 0; i < MAX_PLAYERS; i++)
10405         if (trigger_player_bits & (1 << i))
10406           if (action_type == CA_MOVE_PLAYER ||
10407               stored_player[i].MovPos == 0)
10408             stored_player[i].programmed_action = action_arg_direction;
10409
10410       break;
10411     }
10412
10413     case CA_EXIT_PLAYER:
10414     {
10415       for (i = 0; i < MAX_PLAYERS; i++)
10416         if (action_arg_player_bits & (1 << i))
10417           ExitPlayer(&stored_player[i]);
10418
10419       if (game.players_still_needed == 0)
10420         LevelSolved();
10421
10422       break;
10423     }
10424
10425     case CA_KILL_PLAYER:
10426     {
10427       for (i = 0; i < MAX_PLAYERS; i++)
10428         if (action_arg_player_bits & (1 << i))
10429           KillPlayer(&stored_player[i]);
10430
10431       break;
10432     }
10433
10434     case CA_SET_PLAYER_KEYS:
10435     {
10436       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10437       int element = getSpecialActionElement(action_arg_element,
10438                                             action_arg_number, EL_KEY_1);
10439
10440       if (IS_KEY(element))
10441       {
10442         for (i = 0; i < MAX_PLAYERS; i++)
10443         {
10444           if (trigger_player_bits & (1 << i))
10445           {
10446             stored_player[i].key[KEY_NR(element)] = key_state;
10447
10448             DrawGameDoorValues();
10449           }
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456     case CA_SET_PLAYER_SPEED:
10457     {
10458       for (i = 0; i < MAX_PLAYERS; i++)
10459       {
10460         if (trigger_player_bits & (1 << i))
10461         {
10462           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10463
10464           if (action_arg == CA_ARG_SPEED_FASTER &&
10465               stored_player[i].cannot_move)
10466           {
10467             action_arg_number = STEPSIZE_VERY_SLOW;
10468           }
10469           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10470                    action_arg == CA_ARG_SPEED_FASTER)
10471           {
10472             action_arg_number = 2;
10473             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10474                            CA_MODE_MULTIPLY);
10475           }
10476           else if (action_arg == CA_ARG_NUMBER_RESET)
10477           {
10478             action_arg_number = level.initial_player_stepsize[i];
10479           }
10480
10481           move_stepsize =
10482             getModifiedActionNumber(move_stepsize,
10483                                     action_mode,
10484                                     action_arg_number,
10485                                     action_arg_number_min,
10486                                     action_arg_number_max);
10487
10488           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10489         }
10490       }
10491
10492       break;
10493     }
10494
10495     case CA_SET_PLAYER_SHIELD:
10496     {
10497       for (i = 0; i < MAX_PLAYERS; i++)
10498       {
10499         if (trigger_player_bits & (1 << i))
10500         {
10501           if (action_arg == CA_ARG_SHIELD_OFF)
10502           {
10503             stored_player[i].shield_normal_time_left = 0;
10504             stored_player[i].shield_deadly_time_left = 0;
10505           }
10506           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10507           {
10508             stored_player[i].shield_normal_time_left = 999999;
10509           }
10510           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10511           {
10512             stored_player[i].shield_normal_time_left = 999999;
10513             stored_player[i].shield_deadly_time_left = 999999;
10514           }
10515         }
10516       }
10517
10518       break;
10519     }
10520
10521     case CA_SET_PLAYER_GRAVITY:
10522     {
10523       for (i = 0; i < MAX_PLAYERS; i++)
10524       {
10525         if (trigger_player_bits & (1 << i))
10526         {
10527           stored_player[i].gravity =
10528             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10529              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10530              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10531              stored_player[i].gravity);
10532         }
10533       }
10534
10535       break;
10536     }
10537
10538     case CA_SET_PLAYER_ARTWORK:
10539     {
10540       for (i = 0; i < MAX_PLAYERS; i++)
10541       {
10542         if (trigger_player_bits & (1 << i))
10543         {
10544           int artwork_element = action_arg_element;
10545
10546           if (action_arg == CA_ARG_ELEMENT_RESET)
10547             artwork_element =
10548               (level.use_artwork_element[i] ? level.artwork_element[i] :
10549                stored_player[i].element_nr);
10550
10551           if (stored_player[i].artwork_element != artwork_element)
10552             stored_player[i].Frame = 0;
10553
10554           stored_player[i].artwork_element = artwork_element;
10555
10556           SetPlayerWaiting(&stored_player[i], FALSE);
10557
10558           // set number of special actions for bored and sleeping animation
10559           stored_player[i].num_special_action_bored =
10560             get_num_special_action(artwork_element,
10561                                    ACTION_BORING_1, ACTION_BORING_LAST);
10562           stored_player[i].num_special_action_sleeping =
10563             get_num_special_action(artwork_element,
10564                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10565         }
10566       }
10567
10568       break;
10569     }
10570
10571     case CA_SET_PLAYER_INVENTORY:
10572     {
10573       for (i = 0; i < MAX_PLAYERS; i++)
10574       {
10575         struct PlayerInfo *player = &stored_player[i];
10576         int j, k;
10577
10578         if (trigger_player_bits & (1 << i))
10579         {
10580           int inventory_element = action_arg_element;
10581
10582           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10583               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10584               action_arg == CA_ARG_ELEMENT_ACTION)
10585           {
10586             int element = inventory_element;
10587             int collect_count = element_info[element].collect_count_initial;
10588
10589             if (!IS_CUSTOM_ELEMENT(element))
10590               collect_count = 1;
10591
10592             if (collect_count == 0)
10593               player->inventory_infinite_element = element;
10594             else
10595               for (k = 0; k < collect_count; k++)
10596                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10597                   player->inventory_element[player->inventory_size++] =
10598                     element;
10599           }
10600           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10601                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10602                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10603           {
10604             if (player->inventory_infinite_element != EL_UNDEFINED &&
10605                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10606                                      action_arg_element_raw))
10607               player->inventory_infinite_element = EL_UNDEFINED;
10608
10609             for (k = 0, j = 0; j < player->inventory_size; j++)
10610             {
10611               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10612                                         action_arg_element_raw))
10613                 player->inventory_element[k++] = player->inventory_element[j];
10614             }
10615
10616             player->inventory_size = k;
10617           }
10618           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10619           {
10620             if (player->inventory_size > 0)
10621             {
10622               for (j = 0; j < player->inventory_size - 1; j++)
10623                 player->inventory_element[j] = player->inventory_element[j + 1];
10624
10625               player->inventory_size--;
10626             }
10627           }
10628           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10629           {
10630             if (player->inventory_size > 0)
10631               player->inventory_size--;
10632           }
10633           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10634           {
10635             player->inventory_infinite_element = EL_UNDEFINED;
10636             player->inventory_size = 0;
10637           }
10638           else if (action_arg == CA_ARG_INVENTORY_RESET)
10639           {
10640             player->inventory_infinite_element = EL_UNDEFINED;
10641             player->inventory_size = 0;
10642
10643             if (level.use_initial_inventory[i])
10644             {
10645               for (j = 0; j < level.initial_inventory_size[i]; j++)
10646               {
10647                 int element = level.initial_inventory_content[i][j];
10648                 int collect_count = element_info[element].collect_count_initial;
10649
10650                 if (!IS_CUSTOM_ELEMENT(element))
10651                   collect_count = 1;
10652
10653                 if (collect_count == 0)
10654                   player->inventory_infinite_element = element;
10655                 else
10656                   for (k = 0; k < collect_count; k++)
10657                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10658                       player->inventory_element[player->inventory_size++] =
10659                         element;
10660               }
10661             }
10662           }
10663         }
10664       }
10665
10666       break;
10667     }
10668
10669     // ---------- CE actions  -------------------------------------------------
10670
10671     case CA_SET_CE_VALUE:
10672     {
10673       int last_ce_value = CustomValue[x][y];
10674
10675       CustomValue[x][y] = action_arg_number_new;
10676
10677       if (CustomValue[x][y] != last_ce_value)
10678       {
10679         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10680         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10681
10682         if (CustomValue[x][y] == 0)
10683         {
10684           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10685           ChangeCount[x][y] = 0;        // allow at least one more change
10686
10687           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10688           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10689         }
10690       }
10691
10692       break;
10693     }
10694
10695     case CA_SET_CE_SCORE:
10696     {
10697       int last_ce_score = ei->collect_score;
10698
10699       ei->collect_score = action_arg_number_new;
10700
10701       if (ei->collect_score != last_ce_score)
10702       {
10703         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10704         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10705
10706         if (ei->collect_score == 0)
10707         {
10708           int xx, yy;
10709
10710           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10711           ChangeCount[x][y] = 0;        // allow at least one more change
10712
10713           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10714           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10715
10716           /*
10717             This is a very special case that seems to be a mixture between
10718             CheckElementChange() and CheckTriggeredElementChange(): while
10719             the first one only affects single elements that are triggered
10720             directly, the second one affects multiple elements in the playfield
10721             that are triggered indirectly by another element. This is a third
10722             case: Changing the CE score always affects multiple identical CEs,
10723             so every affected CE must be checked, not only the single CE for
10724             which the CE score was changed in the first place (as every instance
10725             of that CE shares the same CE score, and therefore also can change)!
10726           */
10727           SCAN_PLAYFIELD(xx, yy)
10728           {
10729             if (Tile[xx][yy] == element)
10730               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10731                                  CE_SCORE_GETS_ZERO);
10732           }
10733         }
10734       }
10735
10736       break;
10737     }
10738
10739     case CA_SET_CE_ARTWORK:
10740     {
10741       int artwork_element = action_arg_element;
10742       boolean reset_frame = FALSE;
10743       int xx, yy;
10744
10745       if (action_arg == CA_ARG_ELEMENT_RESET)
10746         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10747                            element);
10748
10749       if (ei->gfx_element != artwork_element)
10750         reset_frame = TRUE;
10751
10752       ei->gfx_element = artwork_element;
10753
10754       SCAN_PLAYFIELD(xx, yy)
10755       {
10756         if (Tile[xx][yy] == element)
10757         {
10758           if (reset_frame)
10759           {
10760             ResetGfxAnimation(xx, yy);
10761             ResetRandomAnimationValue(xx, yy);
10762           }
10763
10764           TEST_DrawLevelField(xx, yy);
10765         }
10766       }
10767
10768       break;
10769     }
10770
10771     // ---------- engine actions  ---------------------------------------------
10772
10773     case CA_SET_ENGINE_SCAN_MODE:
10774     {
10775       InitPlayfieldScanMode(action_arg);
10776
10777       break;
10778     }
10779
10780     default:
10781       break;
10782   }
10783 }
10784
10785 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10786 {
10787   int old_element = Tile[x][y];
10788   int new_element = GetElementFromGroupElement(element);
10789   int previous_move_direction = MovDir[x][y];
10790   int last_ce_value = CustomValue[x][y];
10791   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10792   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10793   boolean add_player_onto_element = (new_element_is_player &&
10794                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10795                                      IS_WALKABLE(old_element));
10796
10797   if (!add_player_onto_element)
10798   {
10799     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10800       RemoveMovingField(x, y);
10801     else
10802       RemoveField(x, y);
10803
10804     Tile[x][y] = new_element;
10805
10806     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10807       MovDir[x][y] = previous_move_direction;
10808
10809     if (element_info[new_element].use_last_ce_value)
10810       CustomValue[x][y] = last_ce_value;
10811
10812     InitField_WithBug1(x, y, FALSE);
10813
10814     new_element = Tile[x][y];   // element may have changed
10815
10816     ResetGfxAnimation(x, y);
10817     ResetRandomAnimationValue(x, y);
10818
10819     TEST_DrawLevelField(x, y);
10820
10821     if (GFX_CRUMBLED(new_element))
10822       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10823
10824     if (old_element == EL_EXPLOSION)
10825     {
10826       Store[x][y] = Store2[x][y] = 0;
10827
10828       // check if new element replaces an exploding player, requiring cleanup
10829       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10830         StorePlayer[x][y] = 0;
10831     }
10832
10833     // check if element under the player changes from accessible to unaccessible
10834     // (needed for special case of dropping element which then changes)
10835     // (must be checked after creating new element for walkable group elements)
10836     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10837         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10838     {
10839       KillPlayer(PLAYERINFO(x, y));
10840
10841       return;
10842     }
10843   }
10844
10845   // "ChangeCount" not set yet to allow "entered by player" change one time
10846   if (new_element_is_player)
10847     RelocatePlayer(x, y, new_element);
10848
10849   if (is_change)
10850     ChangeCount[x][y]++;        // count number of changes in the same frame
10851
10852   TestIfBadThingTouchesPlayer(x, y);
10853   TestIfPlayerTouchesCustomElement(x, y);
10854   TestIfElementTouchesCustomElement(x, y);
10855 }
10856
10857 static void CreateField(int x, int y, int element)
10858 {
10859   CreateFieldExt(x, y, element, FALSE);
10860 }
10861
10862 static void CreateElementFromChange(int x, int y, int element)
10863 {
10864   element = GET_VALID_RUNTIME_ELEMENT(element);
10865
10866   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10867   {
10868     int old_element = Tile[x][y];
10869
10870     // prevent changed element from moving in same engine frame
10871     // unless both old and new element can either fall or move
10872     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10873         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10874       Stop[x][y] = TRUE;
10875   }
10876
10877   CreateFieldExt(x, y, element, TRUE);
10878 }
10879
10880 static boolean ChangeElement(int x, int y, int element, int page)
10881 {
10882   struct ElementInfo *ei = &element_info[element];
10883   struct ElementChangeInfo *change = &ei->change_page[page];
10884   int ce_value = CustomValue[x][y];
10885   int ce_score = ei->collect_score;
10886   int target_element;
10887   int old_element = Tile[x][y];
10888
10889   // always use default change event to prevent running into a loop
10890   if (ChangeEvent[x][y] == -1)
10891     ChangeEvent[x][y] = CE_DELAY;
10892
10893   if (ChangeEvent[x][y] == CE_DELAY)
10894   {
10895     // reset actual trigger element, trigger player and action element
10896     change->actual_trigger_element = EL_EMPTY;
10897     change->actual_trigger_player = EL_EMPTY;
10898     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10899     change->actual_trigger_side = CH_SIDE_NONE;
10900     change->actual_trigger_ce_value = 0;
10901     change->actual_trigger_ce_score = 0;
10902     change->actual_trigger_x = -1;
10903     change->actual_trigger_y = -1;
10904   }
10905
10906   // do not change elements more than a specified maximum number of changes
10907   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10908     return FALSE;
10909
10910   ChangeCount[x][y]++;          // count number of changes in the same frame
10911
10912   if (ei->has_anim_event)
10913     HandleGlobalAnimEventByElementChange(element, page, x, y,
10914                                          change->actual_trigger_x,
10915                                          change->actual_trigger_y);
10916
10917   if (change->explode)
10918   {
10919     Bang(x, y);
10920
10921     return TRUE;
10922   }
10923
10924   if (change->use_target_content)
10925   {
10926     boolean complete_replace = TRUE;
10927     boolean can_replace[3][3];
10928     int xx, yy;
10929
10930     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10931     {
10932       boolean is_empty;
10933       boolean is_walkable;
10934       boolean is_diggable;
10935       boolean is_collectible;
10936       boolean is_removable;
10937       boolean is_destructible;
10938       int ex = x + xx - 1;
10939       int ey = y + yy - 1;
10940       int content_element = change->target_content.e[xx][yy];
10941       int e;
10942
10943       can_replace[xx][yy] = TRUE;
10944
10945       if (ex == x && ey == y)   // do not check changing element itself
10946         continue;
10947
10948       if (content_element == EL_EMPTY_SPACE)
10949       {
10950         can_replace[xx][yy] = FALSE;    // do not replace border with space
10951
10952         continue;
10953       }
10954
10955       if (!IN_LEV_FIELD(ex, ey))
10956       {
10957         can_replace[xx][yy] = FALSE;
10958         complete_replace = FALSE;
10959
10960         continue;
10961       }
10962
10963       e = Tile[ex][ey];
10964
10965       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10966         e = MovingOrBlocked2Element(ex, ey);
10967
10968       is_empty = (IS_FREE(ex, ey) ||
10969                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10970
10971       is_walkable     = (is_empty || IS_WALKABLE(e));
10972       is_diggable     = (is_empty || IS_DIGGABLE(e));
10973       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10974       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10975       is_removable    = (is_diggable || is_collectible);
10976
10977       can_replace[xx][yy] =
10978         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10979           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10980           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10981           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10982           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10983           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10984          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10985
10986       if (!can_replace[xx][yy])
10987         complete_replace = FALSE;
10988     }
10989
10990     if (!change->only_if_complete || complete_replace)
10991     {
10992       boolean something_has_changed = FALSE;
10993
10994       if (change->only_if_complete && change->use_random_replace &&
10995           RND(100) < change->random_percentage)
10996         return FALSE;
10997
10998       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10999       {
11000         int ex = x + xx - 1;
11001         int ey = y + yy - 1;
11002         int content_element;
11003
11004         if (can_replace[xx][yy] && (!change->use_random_replace ||
11005                                     RND(100) < change->random_percentage))
11006         {
11007           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
11008             RemoveMovingField(ex, ey);
11009
11010           ChangeEvent[ex][ey] = ChangeEvent[x][y];
11011
11012           content_element = change->target_content.e[xx][yy];
11013           target_element = GET_TARGET_ELEMENT(element, content_element, change,
11014                                               ce_value, ce_score);
11015
11016           CreateElementFromChange(ex, ey, target_element);
11017
11018           something_has_changed = TRUE;
11019
11020           // for symmetry reasons, freeze newly created border elements
11021           if (ex != x || ey != y)
11022             Stop[ex][ey] = TRUE;        // no more moving in this frame
11023         }
11024       }
11025
11026       if (something_has_changed)
11027       {
11028         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11029         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11030       }
11031     }
11032   }
11033   else
11034   {
11035     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11036                                         ce_value, ce_score);
11037
11038     if (element == EL_DIAGONAL_GROWING ||
11039         element == EL_DIAGONAL_SHRINKING)
11040     {
11041       target_element = Store[x][y];
11042
11043       Store[x][y] = EL_EMPTY;
11044     }
11045
11046     // special case: element changes to player (and may be kept if walkable)
11047     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11048       CreateElementFromChange(x, y, EL_EMPTY);
11049
11050     CreateElementFromChange(x, y, target_element);
11051
11052     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11053     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11054   }
11055
11056   // this uses direct change before indirect change
11057   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11058
11059   return TRUE;
11060 }
11061
11062 static void HandleElementChange(int x, int y, int page)
11063 {
11064   int element = MovingOrBlocked2Element(x, y);
11065   struct ElementInfo *ei = &element_info[element];
11066   struct ElementChangeInfo *change = &ei->change_page[page];
11067   boolean handle_action_before_change = FALSE;
11068
11069 #ifdef DEBUG
11070   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11071       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11072   {
11073     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11074           x, y, element, element_info[element].token_name);
11075     Debug("game:playing:HandleElementChange", "This should never happen!");
11076   }
11077 #endif
11078
11079   // this can happen with classic bombs on walkable, changing elements
11080   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11081   {
11082     return;
11083   }
11084
11085   if (ChangeDelay[x][y] == 0)           // initialize element change
11086   {
11087     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11088
11089     if (change->can_change)
11090     {
11091       // !!! not clear why graphic animation should be reset at all here !!!
11092       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11093       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11094
11095       /*
11096         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11097
11098         When using an animation frame delay of 1 (this only happens with
11099         "sp_zonk.moving.left/right" in the classic graphics), the default
11100         (non-moving) animation shows wrong animation frames (while the
11101         moving animation, like "sp_zonk.moving.left/right", is correct,
11102         so this graphical bug never shows up with the classic graphics).
11103         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11104         be drawn instead of the correct frames 0,1,2,3. This is caused by
11105         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11106         an element change: First when the change delay ("ChangeDelay[][]")
11107         counter has reached zero after decrementing, then a second time in
11108         the next frame (after "GfxFrame[][]" was already incremented) when
11109         "ChangeDelay[][]" is reset to the initial delay value again.
11110
11111         This causes frame 0 to be drawn twice, while the last frame won't
11112         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11113
11114         As some animations may already be cleverly designed around this bug
11115         (at least the "Snake Bite" snake tail animation does this), it cannot
11116         simply be fixed here without breaking such existing animations.
11117         Unfortunately, it cannot easily be detected if a graphics set was
11118         designed "before" or "after" the bug was fixed. As a workaround,
11119         a new graphics set option "game.graphics_engine_version" was added
11120         to be able to specify the game's major release version for which the
11121         graphics set was designed, which can then be used to decide if the
11122         bugfix should be used (version 4 and above) or not (version 3 or
11123         below, or if no version was specified at all, as with old sets).
11124
11125         (The wrong/fixed animation frames can be tested with the test level set
11126         "test_gfxframe" and level "000", which contains a specially prepared
11127         custom element at level position (x/y) == (11/9) which uses the zonk
11128         animation mentioned above. Using "game.graphics_engine_version: 4"
11129         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11130         This can also be seen from the debug output for this test element.)
11131       */
11132
11133       // when a custom element is about to change (for example by change delay),
11134       // do not reset graphic animation when the custom element is moving
11135       if (game.graphics_engine_version < 4 &&
11136           !IS_MOVING(x, y))
11137       {
11138         ResetGfxAnimation(x, y);
11139         ResetRandomAnimationValue(x, y);
11140       }
11141
11142       if (change->pre_change_function)
11143         change->pre_change_function(x, y);
11144     }
11145   }
11146
11147   ChangeDelay[x][y]--;
11148
11149   if (ChangeDelay[x][y] != 0)           // continue element change
11150   {
11151     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11152
11153     // also needed if CE can not change, but has CE delay with CE action
11154     if (IS_ANIMATED(graphic))
11155       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11156
11157     if (change->can_change)
11158     {
11159       if (change->change_function)
11160         change->change_function(x, y);
11161     }
11162   }
11163   else                                  // finish element change
11164   {
11165     if (ChangePage[x][y] != -1)         // remember page from delayed change
11166     {
11167       page = ChangePage[x][y];
11168       ChangePage[x][y] = -1;
11169
11170       change = &ei->change_page[page];
11171     }
11172
11173     if (IS_MOVING(x, y))                // never change a running system ;-)
11174     {
11175       ChangeDelay[x][y] = 1;            // try change after next move step
11176       ChangePage[x][y] = page;          // remember page to use for change
11177
11178       return;
11179     }
11180
11181     // special case: set new level random seed before changing element
11182     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11183       handle_action_before_change = TRUE;
11184
11185     if (change->has_action && handle_action_before_change)
11186       ExecuteCustomElementAction(x, y, element, page);
11187
11188     if (change->can_change)
11189     {
11190       if (ChangeElement(x, y, element, page))
11191       {
11192         if (change->post_change_function)
11193           change->post_change_function(x, y);
11194       }
11195     }
11196
11197     if (change->has_action && !handle_action_before_change)
11198       ExecuteCustomElementAction(x, y, element, page);
11199   }
11200 }
11201
11202 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11203                                               int trigger_element,
11204                                               int trigger_event,
11205                                               int trigger_player,
11206                                               int trigger_side,
11207                                               int trigger_page)
11208 {
11209   boolean change_done_any = FALSE;
11210   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11211   int i;
11212
11213   if (!(trigger_events[trigger_element][trigger_event]))
11214     return FALSE;
11215
11216   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11217
11218   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11219   {
11220     int element = EL_CUSTOM_START + i;
11221     boolean change_done = FALSE;
11222     int p;
11223
11224     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11225         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11226       continue;
11227
11228     for (p = 0; p < element_info[element].num_change_pages; p++)
11229     {
11230       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11231
11232       if (change->can_change_or_has_action &&
11233           change->has_event[trigger_event] &&
11234           change->trigger_side & trigger_side &&
11235           change->trigger_player & trigger_player &&
11236           change->trigger_page & trigger_page_bits &&
11237           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11238       {
11239         change->actual_trigger_element = trigger_element;
11240         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11241         change->actual_trigger_player_bits = trigger_player;
11242         change->actual_trigger_side = trigger_side;
11243         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11244         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11245         change->actual_trigger_x = trigger_x;
11246         change->actual_trigger_y = trigger_y;
11247
11248         if ((change->can_change && !change_done) || change->has_action)
11249         {
11250           int x, y;
11251
11252           SCAN_PLAYFIELD(x, y)
11253           {
11254             if (Tile[x][y] == element)
11255             {
11256               if (change->can_change && !change_done)
11257               {
11258                 // if element already changed in this frame, not only prevent
11259                 // another element change (checked in ChangeElement()), but
11260                 // also prevent additional element actions for this element
11261
11262                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11263                     !level.use_action_after_change_bug)
11264                   continue;
11265
11266                 ChangeDelay[x][y] = 1;
11267                 ChangeEvent[x][y] = trigger_event;
11268
11269                 HandleElementChange(x, y, p);
11270               }
11271               else if (change->has_action)
11272               {
11273                 // if element already changed in this frame, not only prevent
11274                 // another element change (checked in ChangeElement()), but
11275                 // also prevent additional element actions for this element
11276
11277                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11278                     !level.use_action_after_change_bug)
11279                   continue;
11280
11281                 ExecuteCustomElementAction(x, y, element, p);
11282                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11283               }
11284             }
11285           }
11286
11287           if (change->can_change)
11288           {
11289             change_done = TRUE;
11290             change_done_any = TRUE;
11291           }
11292         }
11293       }
11294     }
11295   }
11296
11297   RECURSION_LOOP_DETECTION_END();
11298
11299   return change_done_any;
11300 }
11301
11302 static boolean CheckElementChangeExt(int x, int y,
11303                                      int element,
11304                                      int trigger_element,
11305                                      int trigger_event,
11306                                      int trigger_player,
11307                                      int trigger_side)
11308 {
11309   boolean change_done = FALSE;
11310   int p;
11311
11312   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11313       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11314     return FALSE;
11315
11316   if (Tile[x][y] == EL_BLOCKED)
11317   {
11318     Blocked2Moving(x, y, &x, &y);
11319     element = Tile[x][y];
11320   }
11321
11322   // check if element has already changed or is about to change after moving
11323   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11324        Tile[x][y] != element) ||
11325
11326       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11327        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11328         ChangePage[x][y] != -1)))
11329     return FALSE;
11330
11331   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11332
11333   for (p = 0; p < element_info[element].num_change_pages; p++)
11334   {
11335     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11336
11337     /* check trigger element for all events where the element that is checked
11338        for changing interacts with a directly adjacent element -- this is
11339        different to element changes that affect other elements to change on the
11340        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11341     boolean check_trigger_element =
11342       (trigger_event == CE_NEXT_TO_X ||
11343        trigger_event == CE_TOUCHING_X ||
11344        trigger_event == CE_HITTING_X ||
11345        trigger_event == CE_HIT_BY_X ||
11346        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11347
11348     if (change->can_change_or_has_action &&
11349         change->has_event[trigger_event] &&
11350         change->trigger_side & trigger_side &&
11351         change->trigger_player & trigger_player &&
11352         (!check_trigger_element ||
11353          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11354     {
11355       change->actual_trigger_element = trigger_element;
11356       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11357       change->actual_trigger_player_bits = trigger_player;
11358       change->actual_trigger_side = trigger_side;
11359       change->actual_trigger_ce_value = CustomValue[x][y];
11360       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11361       change->actual_trigger_x = x;
11362       change->actual_trigger_y = y;
11363
11364       // special case: trigger element not at (x,y) position for some events
11365       if (check_trigger_element)
11366       {
11367         static struct
11368         {
11369           int dx, dy;
11370         } move_xy[] =
11371           {
11372             {  0,  0 },
11373             { -1,  0 },
11374             { +1,  0 },
11375             {  0,  0 },
11376             {  0, -1 },
11377             {  0,  0 }, { 0, 0 }, { 0, 0 },
11378             {  0, +1 }
11379           };
11380
11381         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11382         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11383
11384         change->actual_trigger_ce_value = CustomValue[xx][yy];
11385         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11386         change->actual_trigger_x = xx;
11387         change->actual_trigger_y = yy;
11388       }
11389
11390       if (change->can_change && !change_done)
11391       {
11392         ChangeDelay[x][y] = 1;
11393         ChangeEvent[x][y] = trigger_event;
11394
11395         HandleElementChange(x, y, p);
11396
11397         change_done = TRUE;
11398       }
11399       else if (change->has_action)
11400       {
11401         ExecuteCustomElementAction(x, y, element, p);
11402         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11403       }
11404     }
11405   }
11406
11407   RECURSION_LOOP_DETECTION_END();
11408
11409   return change_done;
11410 }
11411
11412 static void PlayPlayerSound(struct PlayerInfo *player)
11413 {
11414   int jx = player->jx, jy = player->jy;
11415   int sound_element = player->artwork_element;
11416   int last_action = player->last_action_waiting;
11417   int action = player->action_waiting;
11418
11419   if (player->is_waiting)
11420   {
11421     if (action != last_action)
11422       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11423     else
11424       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11425   }
11426   else
11427   {
11428     if (action != last_action)
11429       StopSound(element_info[sound_element].sound[last_action]);
11430
11431     if (last_action == ACTION_SLEEPING)
11432       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11433   }
11434 }
11435
11436 static void PlayAllPlayersSound(void)
11437 {
11438   int i;
11439
11440   for (i = 0; i < MAX_PLAYERS; i++)
11441     if (stored_player[i].active)
11442       PlayPlayerSound(&stored_player[i]);
11443 }
11444
11445 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11446 {
11447   boolean last_waiting = player->is_waiting;
11448   int move_dir = player->MovDir;
11449
11450   player->dir_waiting = move_dir;
11451   player->last_action_waiting = player->action_waiting;
11452
11453   if (is_waiting)
11454   {
11455     if (!last_waiting)          // not waiting -> waiting
11456     {
11457       player->is_waiting = TRUE;
11458
11459       player->frame_counter_bored =
11460         FrameCounter +
11461         game.player_boring_delay_fixed +
11462         GetSimpleRandom(game.player_boring_delay_random);
11463       player->frame_counter_sleeping =
11464         FrameCounter +
11465         game.player_sleeping_delay_fixed +
11466         GetSimpleRandom(game.player_sleeping_delay_random);
11467
11468       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11469     }
11470
11471     if (game.player_sleeping_delay_fixed +
11472         game.player_sleeping_delay_random > 0 &&
11473         player->anim_delay_counter == 0 &&
11474         player->post_delay_counter == 0 &&
11475         FrameCounter >= player->frame_counter_sleeping)
11476       player->is_sleeping = TRUE;
11477     else if (game.player_boring_delay_fixed +
11478              game.player_boring_delay_random > 0 &&
11479              FrameCounter >= player->frame_counter_bored)
11480       player->is_bored = TRUE;
11481
11482     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11483                               player->is_bored ? ACTION_BORING :
11484                               ACTION_WAITING);
11485
11486     if (player->is_sleeping && player->use_murphy)
11487     {
11488       // special case for sleeping Murphy when leaning against non-free tile
11489
11490       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11491           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11492            !IS_MOVING(player->jx - 1, player->jy)))
11493         move_dir = MV_LEFT;
11494       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11495                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11496                 !IS_MOVING(player->jx + 1, player->jy)))
11497         move_dir = MV_RIGHT;
11498       else
11499         player->is_sleeping = FALSE;
11500
11501       player->dir_waiting = move_dir;
11502     }
11503
11504     if (player->is_sleeping)
11505     {
11506       if (player->num_special_action_sleeping > 0)
11507       {
11508         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11509         {
11510           int last_special_action = player->special_action_sleeping;
11511           int num_special_action = player->num_special_action_sleeping;
11512           int special_action =
11513             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11514              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11515              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11516              last_special_action + 1 : ACTION_SLEEPING);
11517           int special_graphic =
11518             el_act_dir2img(player->artwork_element, special_action, move_dir);
11519
11520           player->anim_delay_counter =
11521             graphic_info[special_graphic].anim_delay_fixed +
11522             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11523           player->post_delay_counter =
11524             graphic_info[special_graphic].post_delay_fixed +
11525             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11526
11527           player->special_action_sleeping = special_action;
11528         }
11529
11530         if (player->anim_delay_counter > 0)
11531         {
11532           player->action_waiting = player->special_action_sleeping;
11533           player->anim_delay_counter--;
11534         }
11535         else if (player->post_delay_counter > 0)
11536         {
11537           player->post_delay_counter--;
11538         }
11539       }
11540     }
11541     else if (player->is_bored)
11542     {
11543       if (player->num_special_action_bored > 0)
11544       {
11545         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11546         {
11547           int special_action =
11548             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11549           int special_graphic =
11550             el_act_dir2img(player->artwork_element, special_action, move_dir);
11551
11552           player->anim_delay_counter =
11553             graphic_info[special_graphic].anim_delay_fixed +
11554             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11555           player->post_delay_counter =
11556             graphic_info[special_graphic].post_delay_fixed +
11557             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11558
11559           player->special_action_bored = special_action;
11560         }
11561
11562         if (player->anim_delay_counter > 0)
11563         {
11564           player->action_waiting = player->special_action_bored;
11565           player->anim_delay_counter--;
11566         }
11567         else if (player->post_delay_counter > 0)
11568         {
11569           player->post_delay_counter--;
11570         }
11571       }
11572     }
11573   }
11574   else if (last_waiting)        // waiting -> not waiting
11575   {
11576     player->is_waiting = FALSE;
11577     player->is_bored = FALSE;
11578     player->is_sleeping = FALSE;
11579
11580     player->frame_counter_bored = -1;
11581     player->frame_counter_sleeping = -1;
11582
11583     player->anim_delay_counter = 0;
11584     player->post_delay_counter = 0;
11585
11586     player->dir_waiting = player->MovDir;
11587     player->action_waiting = ACTION_DEFAULT;
11588
11589     player->special_action_bored = ACTION_DEFAULT;
11590     player->special_action_sleeping = ACTION_DEFAULT;
11591   }
11592 }
11593
11594 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11595 {
11596   if ((!player->is_moving  && player->was_moving) ||
11597       (player->MovPos == 0 && player->was_moving) ||
11598       (player->is_snapping && !player->was_snapping) ||
11599       (player->is_dropping && !player->was_dropping))
11600   {
11601     if (!CheckSaveEngineSnapshotToList())
11602       return;
11603
11604     player->was_moving = FALSE;
11605     player->was_snapping = TRUE;
11606     player->was_dropping = TRUE;
11607   }
11608   else
11609   {
11610     if (player->is_moving)
11611       player->was_moving = TRUE;
11612
11613     if (!player->is_snapping)
11614       player->was_snapping = FALSE;
11615
11616     if (!player->is_dropping)
11617       player->was_dropping = FALSE;
11618   }
11619
11620   static struct MouseActionInfo mouse_action_last = { 0 };
11621   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11622   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11623
11624   if (new_released)
11625     CheckSaveEngineSnapshotToList();
11626
11627   mouse_action_last = mouse_action;
11628 }
11629
11630 static void CheckSingleStepMode(struct PlayerInfo *player)
11631 {
11632   if (tape.single_step && tape.recording && !tape.pausing)
11633   {
11634     // as it is called "single step mode", just return to pause mode when the
11635     // player stopped moving after one tile (or never starts moving at all)
11636     // (reverse logic needed here in case single step mode used in team mode)
11637     if (player->is_moving ||
11638         player->is_pushing ||
11639         player->is_dropping_pressed ||
11640         player->effective_mouse_action.button)
11641       game.enter_single_step_mode = FALSE;
11642   }
11643
11644   CheckSaveEngineSnapshot(player);
11645 }
11646
11647 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11648 {
11649   int left      = player_action & JOY_LEFT;
11650   int right     = player_action & JOY_RIGHT;
11651   int up        = player_action & JOY_UP;
11652   int down      = player_action & JOY_DOWN;
11653   int button1   = player_action & JOY_BUTTON_1;
11654   int button2   = player_action & JOY_BUTTON_2;
11655   int dx        = (left ? -1 : right ? 1 : 0);
11656   int dy        = (up   ? -1 : down  ? 1 : 0);
11657
11658   if (!player->active || tape.pausing)
11659     return 0;
11660
11661   if (player_action)
11662   {
11663     if (button1)
11664       SnapField(player, dx, dy);
11665     else
11666     {
11667       if (button2)
11668         DropElement(player);
11669
11670       MovePlayer(player, dx, dy);
11671     }
11672
11673     CheckSingleStepMode(player);
11674
11675     SetPlayerWaiting(player, FALSE);
11676
11677     return player_action;
11678   }
11679   else
11680   {
11681     // no actions for this player (no input at player's configured device)
11682
11683     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11684     SnapField(player, 0, 0);
11685     CheckGravityMovementWhenNotMoving(player);
11686
11687     if (player->MovPos == 0)
11688       SetPlayerWaiting(player, TRUE);
11689
11690     if (player->MovPos == 0)    // needed for tape.playing
11691       player->is_moving = FALSE;
11692
11693     player->is_dropping = FALSE;
11694     player->is_dropping_pressed = FALSE;
11695     player->drop_pressed_delay = 0;
11696
11697     CheckSingleStepMode(player);
11698
11699     return 0;
11700   }
11701 }
11702
11703 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11704                                          byte *tape_action)
11705 {
11706   if (!tape.use_mouse_actions)
11707     return;
11708
11709   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11710   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11711   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11712 }
11713
11714 static void SetTapeActionFromMouseAction(byte *tape_action,
11715                                          struct MouseActionInfo *mouse_action)
11716 {
11717   if (!tape.use_mouse_actions)
11718     return;
11719
11720   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11721   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11722   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11723 }
11724
11725 static void CheckLevelSolved(void)
11726 {
11727   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11728   {
11729     if (game_bd.level_solved &&
11730         !game_bd.game_over)                             // game won
11731     {
11732       LevelSolved();
11733
11734       game_bd.game_over = TRUE;
11735
11736       game.all_players_gone = TRUE;
11737     }
11738
11739     if (game_bd.game_over)                              // game lost
11740       game.all_players_gone = TRUE;
11741   }
11742   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11743   {
11744     if (game_em.level_solved &&
11745         !game_em.game_over)                             // game won
11746     {
11747       LevelSolved();
11748
11749       game_em.game_over = TRUE;
11750
11751       game.all_players_gone = TRUE;
11752     }
11753
11754     if (game_em.game_over)                              // game lost
11755       game.all_players_gone = TRUE;
11756   }
11757   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11758   {
11759     if (game_sp.level_solved &&
11760         !game_sp.game_over)                             // game won
11761     {
11762       LevelSolved();
11763
11764       game_sp.game_over = TRUE;
11765
11766       game.all_players_gone = TRUE;
11767     }
11768
11769     if (game_sp.game_over)                              // game lost
11770       game.all_players_gone = TRUE;
11771   }
11772   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11773   {
11774     if (game_mm.level_solved &&
11775         !game_mm.game_over)                             // game won
11776     {
11777       LevelSolved();
11778
11779       game_mm.game_over = TRUE;
11780
11781       game.all_players_gone = TRUE;
11782     }
11783
11784     if (game_mm.game_over)                              // game lost
11785       game.all_players_gone = TRUE;
11786   }
11787 }
11788
11789 static void PlayTimeoutSound(int seconds_left)
11790 {
11791   // will be played directly by BD engine (for classic bonus time sounds)
11792   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11793     return;
11794
11795   // try to use individual "running out of time" sound for each second left
11796   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11797
11798   // if special sound per second not defined, use default sound
11799   if (getSoundInfoEntryFilename(sound) == NULL)
11800     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11801
11802   // if out of time, but player still alive, play special "timeout" sound, if defined
11803   if (seconds_left == 0 && !checkGameFailed())
11804     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11805       sound = SND_GAME_TIMEOUT;
11806
11807   PlaySound(sound);
11808 }
11809
11810 static void CheckLevelTime_StepCounter(void)
11811 {
11812   int i;
11813
11814   TimePlayed++;
11815
11816   if (TimeLeft > 0)
11817   {
11818     TimeLeft--;
11819
11820     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11821       PlayTimeoutSound(TimeLeft);
11822
11823     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11824
11825     DisplayGameControlValues();
11826
11827     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11828       for (i = 0; i < MAX_PLAYERS; i++)
11829         KillPlayer(&stored_player[i]);
11830   }
11831   else if (game.no_level_time_limit && !game.all_players_gone)
11832   {
11833     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11834
11835     DisplayGameControlValues();
11836   }
11837 }
11838
11839 static void CheckLevelTime(void)
11840 {
11841   int frames_per_second = FRAMES_PER_SECOND;
11842   int i;
11843
11844   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11845   {
11846     // level time may be running slower in native BD engine
11847     frames_per_second = getFramesPerSecond_BD();
11848
11849     // if native engine time changed, force main engine time change
11850     if (getTimeLeft_BD() < TimeLeft)
11851       TimeFrames = frames_per_second;
11852
11853     // if last second running, wait for native engine time to exactly reach zero
11854     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11855       TimeFrames = frames_per_second - 1;
11856
11857     // needed to store final time after solving game (before counting down remaining time)
11858     SetTimeFrames_BD(TimePlayed * FRAMES_PER_SECOND + TimeFrames);
11859   }
11860
11861   if (TimeFrames >= frames_per_second)
11862   {
11863     TimeFrames = 0;
11864
11865     for (i = 0; i < MAX_PLAYERS; i++)
11866     {
11867       struct PlayerInfo *player = &stored_player[i];
11868
11869       if (SHIELD_ON(player))
11870       {
11871         player->shield_normal_time_left--;
11872
11873         if (player->shield_deadly_time_left > 0)
11874           player->shield_deadly_time_left--;
11875       }
11876     }
11877
11878     if (!game.LevelSolved && !level.use_step_counter)
11879     {
11880       TimePlayed++;
11881
11882       if (TimeLeft > 0)
11883       {
11884         TimeLeft--;
11885
11886         if (TimeLeft <= 10 && game.time_limit)
11887           PlayTimeoutSound(TimeLeft);
11888
11889         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11890            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11891
11892         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11893
11894         if (!TimeLeft && game.time_limit)
11895         {
11896           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11897           {
11898             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11899               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11900           }
11901           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11902           {
11903             game_em.lev->killed_out_of_time = TRUE;
11904           }
11905           else
11906           {
11907             for (i = 0; i < MAX_PLAYERS; i++)
11908               KillPlayer(&stored_player[i]);
11909           }
11910         }
11911       }
11912       else if (game.no_level_time_limit && !game.all_players_gone)
11913       {
11914         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11915       }
11916
11917       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11918     }
11919   }
11920
11921   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11922   {
11923     TapeTimeFrames = 0;
11924     TapeTime++;
11925
11926     if (tape.recording || tape.playing)
11927       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11928   }
11929
11930   if (tape.recording || tape.playing)
11931     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11932
11933   UpdateAndDisplayGameControlValues();
11934 }
11935
11936 void AdvanceFrameAndPlayerCounters(int player_nr)
11937 {
11938   int i;
11939
11940   // handle game and tape time differently for native BD game engine
11941
11942   // tape time is running in native BD engine even if player is not hatched yet
11943   if (!checkGameRunning())
11944     return;
11945
11946   // advance frame counters (global frame counter and tape time frame counter)
11947   FrameCounter++;
11948   TapeTimeFrames++;
11949
11950   // level time is running in native BD engine after player is being hatched
11951   if (!checkGamePlaying())
11952     return;
11953
11954   // advance time frame counter (used to control available time to solve level)
11955   TimeFrames++;
11956
11957   // advance player counters (counters for move delay, move animation etc.)
11958   for (i = 0; i < MAX_PLAYERS; i++)
11959   {
11960     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11961     int move_delay_value = stored_player[i].move_delay_value;
11962     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11963
11964     if (!advance_player_counters)       // not all players may be affected
11965       continue;
11966
11967     if (move_frames == 0)       // less than one move per game frame
11968     {
11969       int stepsize = TILEX / move_delay_value;
11970       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11971       int count = (stored_player[i].is_moving ?
11972                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11973
11974       if (count % delay == 0)
11975         move_frames = 1;
11976     }
11977
11978     stored_player[i].Frame += move_frames;
11979
11980     if (stored_player[i].MovPos != 0)
11981       stored_player[i].StepFrame += move_frames;
11982
11983     if (stored_player[i].move_delay > 0)
11984       stored_player[i].move_delay--;
11985
11986     // due to bugs in previous versions, counter must count up, not down
11987     if (stored_player[i].push_delay != -1)
11988       stored_player[i].push_delay++;
11989
11990     if (stored_player[i].drop_delay > 0)
11991       stored_player[i].drop_delay--;
11992
11993     if (stored_player[i].is_dropping_pressed)
11994       stored_player[i].drop_pressed_delay++;
11995   }
11996 }
11997
11998 void AdvanceFrameCounter(void)
11999 {
12000   FrameCounter++;
12001 }
12002
12003 void AdvanceGfxFrame(void)
12004 {
12005   int x, y;
12006
12007   SCAN_PLAYFIELD(x, y)
12008   {
12009     GfxFrame[x][y]++;
12010   }
12011 }
12012
12013 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
12014                               struct MouseActionInfo *mouse_action_last)
12015 {
12016   if (mouse_action->button)
12017   {
12018     int new_button = (mouse_action->button && mouse_action_last->button == 0);
12019     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
12020     int x = mouse_action->lx;
12021     int y = mouse_action->ly;
12022     int element = Tile[x][y];
12023
12024     if (new_button)
12025     {
12026       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12027       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12028                                          ch_button);
12029     }
12030
12031     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12032     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12033                                        ch_button);
12034
12035     if (level.use_step_counter)
12036     {
12037       boolean counted_click = FALSE;
12038
12039       // element clicked that can change when clicked/pressed
12040       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12041           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12042            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12043         counted_click = TRUE;
12044
12045       // element clicked that can trigger change when clicked/pressed
12046       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12047           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12048         counted_click = TRUE;
12049
12050       if (new_button && counted_click)
12051         CheckLevelTime_StepCounter();
12052     }
12053   }
12054 }
12055
12056 void StartGameActions(boolean init_network_game, boolean record_tape,
12057                       int random_seed)
12058 {
12059   unsigned int new_random_seed = InitRND(random_seed);
12060
12061   if (record_tape)
12062     TapeStartRecording(new_random_seed);
12063
12064   if (setup.auto_pause_on_start && !tape.pausing)
12065     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12066
12067   if (init_network_game)
12068   {
12069     SendToServer_LevelFile();
12070     SendToServer_StartPlaying();
12071
12072     return;
12073   }
12074
12075   InitGame();
12076 }
12077
12078 static void GameActionsExt(void)
12079 {
12080 #if 0
12081   static unsigned int game_frame_delay = 0;
12082 #endif
12083   unsigned int game_frame_delay_value;
12084   byte *recorded_player_action;
12085   byte summarized_player_action = 0;
12086   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12087   int i;
12088
12089   // detect endless loops, caused by custom element programming
12090   if (recursion_loop_detected && recursion_loop_depth == 0)
12091   {
12092     char *message = getStringCat3("Internal Error! Element ",
12093                                   EL_NAME(recursion_loop_element),
12094                                   " caused endless loop! Quit the game?");
12095
12096     Warn("element '%s' caused endless loop in game engine",
12097          EL_NAME(recursion_loop_element));
12098
12099     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12100
12101     recursion_loop_detected = FALSE;    // if game should be continued
12102
12103     free(message);
12104
12105     return;
12106   }
12107
12108   if (game.restart_level)
12109     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12110
12111   CheckLevelSolved();
12112
12113   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12114     GameWon();
12115
12116   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12117     TapeStop();
12118
12119   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12120     return;
12121
12122   game_frame_delay_value =
12123     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12124
12125   if (tape.playing && tape.warp_forward && !tape.pausing)
12126     game_frame_delay_value = 0;
12127
12128   SetVideoFrameDelay(game_frame_delay_value);
12129
12130   // (de)activate virtual buttons depending on current game status
12131   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12132   {
12133     if (game.all_players_gone)  // if no players there to be controlled anymore
12134       SetOverlayActive(FALSE);
12135     else if (!tape.playing)     // if game continues after tape stopped playing
12136       SetOverlayActive(TRUE);
12137   }
12138
12139 #if 0
12140 #if 0
12141   // ---------- main game synchronization point ----------
12142
12143   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12144
12145   Debug("game:playing:skip", "skip == %d", skip);
12146
12147 #else
12148   // ---------- main game synchronization point ----------
12149
12150   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12151 #endif
12152 #endif
12153
12154   if (network_playing && !network_player_action_received)
12155   {
12156     // try to get network player actions in time
12157
12158     // last chance to get network player actions without main loop delay
12159     HandleNetworking();
12160
12161     // game was quit by network peer
12162     if (game_status != GAME_MODE_PLAYING)
12163       return;
12164
12165     // check if network player actions still missing and game still running
12166     if (!network_player_action_received && !checkGameEnded())
12167       return;           // failed to get network player actions in time
12168
12169     // do not yet reset "network_player_action_received" (for tape.pausing)
12170   }
12171
12172   if (tape.pausing)
12173     return;
12174
12175   // at this point we know that we really continue executing the game
12176
12177   network_player_action_received = FALSE;
12178
12179   // when playing tape, read previously recorded player input from tape data
12180   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12181
12182   local_player->effective_mouse_action = local_player->mouse_action;
12183
12184   if (recorded_player_action != NULL)
12185     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12186                                  recorded_player_action);
12187
12188   // TapePlayAction() may return NULL when toggling to "pause before death"
12189   if (tape.pausing)
12190     return;
12191
12192   if (tape.set_centered_player)
12193   {
12194     game.centered_player_nr_next = tape.centered_player_nr_next;
12195     game.set_centered_player = TRUE;
12196   }
12197
12198   for (i = 0; i < MAX_PLAYERS; i++)
12199   {
12200     summarized_player_action |= stored_player[i].action;
12201
12202     if (!network_playing && (game.team_mode || tape.playing))
12203       stored_player[i].effective_action = stored_player[i].action;
12204   }
12205
12206   if (network_playing && !checkGameEnded())
12207     SendToServer_MovePlayer(summarized_player_action);
12208
12209   // summarize all actions at local players mapped input device position
12210   // (this allows using different input devices in single player mode)
12211   if (!network.enabled && !game.team_mode)
12212     stored_player[map_player_action[local_player->index_nr]].effective_action =
12213       summarized_player_action;
12214
12215   // summarize all actions at centered player in local team mode
12216   if (tape.recording &&
12217       setup.team_mode && !network.enabled &&
12218       setup.input_on_focus &&
12219       game.centered_player_nr != -1)
12220   {
12221     for (i = 0; i < MAX_PLAYERS; i++)
12222       stored_player[map_player_action[i]].effective_action =
12223         (i == game.centered_player_nr ? summarized_player_action : 0);
12224   }
12225
12226   if (recorded_player_action != NULL)
12227     for (i = 0; i < MAX_PLAYERS; i++)
12228       stored_player[i].effective_action = recorded_player_action[i];
12229
12230   for (i = 0; i < MAX_PLAYERS; i++)
12231   {
12232     tape_action[i] = stored_player[i].effective_action;
12233
12234     /* (this may happen in the RND game engine if a player was not present on
12235        the playfield on level start, but appeared later from a custom element */
12236     if (setup.team_mode &&
12237         tape.recording &&
12238         tape_action[i] &&
12239         !tape.player_participates[i])
12240       tape.player_participates[i] = TRUE;
12241   }
12242
12243   SetTapeActionFromMouseAction(tape_action,
12244                                &local_player->effective_mouse_action);
12245
12246   // only record actions from input devices, but not programmed actions
12247   if (tape.recording)
12248     TapeRecordAction(tape_action);
12249
12250   // remember if game was played (especially after tape stopped playing)
12251   if (!tape.playing && summarized_player_action && !checkGameFailed())
12252     game.GamePlayed = TRUE;
12253
12254 #if USE_NEW_PLAYER_ASSIGNMENTS
12255   // !!! also map player actions in single player mode !!!
12256   // if (game.team_mode)
12257   if (1)
12258   {
12259     byte mapped_action[MAX_PLAYERS];
12260
12261 #if DEBUG_PLAYER_ACTIONS
12262     for (i = 0; i < MAX_PLAYERS; i++)
12263       DebugContinued("", "%d, ", stored_player[i].effective_action);
12264 #endif
12265
12266     for (i = 0; i < MAX_PLAYERS; i++)
12267       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12268
12269     for (i = 0; i < MAX_PLAYERS; i++)
12270       stored_player[i].effective_action = mapped_action[i];
12271
12272 #if DEBUG_PLAYER_ACTIONS
12273     DebugContinued("", "=> ");
12274     for (i = 0; i < MAX_PLAYERS; i++)
12275       DebugContinued("", "%d, ", stored_player[i].effective_action);
12276     DebugContinued("game:playing:player", "\n");
12277 #endif
12278   }
12279 #if DEBUG_PLAYER_ACTIONS
12280   else
12281   {
12282     for (i = 0; i < MAX_PLAYERS; i++)
12283       DebugContinued("", "%d, ", stored_player[i].effective_action);
12284     DebugContinued("game:playing:player", "\n");
12285   }
12286 #endif
12287 #endif
12288
12289   for (i = 0; i < MAX_PLAYERS; i++)
12290   {
12291     // allow engine snapshot in case of changed movement attempt
12292     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12293         (stored_player[i].effective_action & KEY_MOTION))
12294       game.snapshot.changed_action = TRUE;
12295
12296     // allow engine snapshot in case of snapping/dropping attempt
12297     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12298         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12299       game.snapshot.changed_action = TRUE;
12300
12301     game.snapshot.last_action[i] = stored_player[i].effective_action;
12302   }
12303
12304   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12305   {
12306     GameActions_BD_Main();
12307   }
12308   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12309   {
12310     GameActions_EM_Main();
12311   }
12312   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12313   {
12314     GameActions_SP_Main();
12315   }
12316   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12317   {
12318     GameActions_MM_Main();
12319   }
12320   else
12321   {
12322     GameActions_RND_Main();
12323   }
12324
12325   BlitScreenToBitmap(backbuffer);
12326
12327   CheckLevelSolved();
12328   CheckLevelTime();
12329
12330   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12331
12332   if (global.show_frames_per_second)
12333   {
12334     static unsigned int fps_counter = 0;
12335     static int fps_frames = 0;
12336     unsigned int fps_delay_ms = Counter() - fps_counter;
12337
12338     fps_frames++;
12339
12340     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12341     {
12342       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12343
12344       fps_frames = 0;
12345       fps_counter = Counter();
12346
12347       // always draw FPS to screen after FPS value was updated
12348       redraw_mask |= REDRAW_FPS;
12349     }
12350
12351     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12352     if (GetDrawDeactivationMask() == REDRAW_NONE)
12353       redraw_mask |= REDRAW_FPS;
12354   }
12355 }
12356
12357 static void GameActions_CheckSaveEngineSnapshot(void)
12358 {
12359   if (!game.snapshot.save_snapshot)
12360     return;
12361
12362   // clear flag for saving snapshot _before_ saving snapshot
12363   game.snapshot.save_snapshot = FALSE;
12364
12365   SaveEngineSnapshotToList();
12366 }
12367
12368 void GameActions(void)
12369 {
12370   GameActionsExt();
12371
12372   GameActions_CheckSaveEngineSnapshot();
12373 }
12374
12375 void GameActions_BD_Main(void)
12376 {
12377   byte effective_action[MAX_PLAYERS];
12378   int i;
12379
12380   for (i = 0; i < MAX_PLAYERS; i++)
12381     effective_action[i] = stored_player[i].effective_action;
12382
12383   GameActions_BD(effective_action);
12384 }
12385
12386 void GameActions_EM_Main(void)
12387 {
12388   byte effective_action[MAX_PLAYERS];
12389   int i;
12390
12391   for (i = 0; i < MAX_PLAYERS; i++)
12392     effective_action[i] = stored_player[i].effective_action;
12393
12394   GameActions_EM(effective_action);
12395 }
12396
12397 void GameActions_SP_Main(void)
12398 {
12399   byte effective_action[MAX_PLAYERS];
12400   int i;
12401
12402   for (i = 0; i < MAX_PLAYERS; i++)
12403     effective_action[i] = stored_player[i].effective_action;
12404
12405   GameActions_SP(effective_action);
12406
12407   for (i = 0; i < MAX_PLAYERS; i++)
12408   {
12409     if (stored_player[i].force_dropping)
12410       stored_player[i].action |= KEY_BUTTON_DROP;
12411
12412     stored_player[i].force_dropping = FALSE;
12413   }
12414 }
12415
12416 void GameActions_MM_Main(void)
12417 {
12418   AdvanceGfxFrame();
12419
12420   GameActions_MM(local_player->effective_mouse_action);
12421 }
12422
12423 void GameActions_RND_Main(void)
12424 {
12425   GameActions_RND();
12426 }
12427
12428 void GameActions_RND(void)
12429 {
12430   static struct MouseActionInfo mouse_action_last = { 0 };
12431   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12432   int magic_wall_x = 0, magic_wall_y = 0;
12433   int i, x, y, element, graphic, last_gfx_frame;
12434
12435   InitPlayfieldScanModeVars();
12436
12437   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12438   {
12439     SCAN_PLAYFIELD(x, y)
12440     {
12441       ChangeCount[x][y] = 0;
12442       ChangeEvent[x][y] = -1;
12443     }
12444   }
12445
12446   if (game.set_centered_player)
12447   {
12448     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12449
12450     // switching to "all players" only possible if all players fit to screen
12451     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12452     {
12453       game.centered_player_nr_next = game.centered_player_nr;
12454       game.set_centered_player = FALSE;
12455     }
12456
12457     // do not switch focus to non-existing (or non-active) player
12458     if (game.centered_player_nr_next >= 0 &&
12459         !stored_player[game.centered_player_nr_next].active)
12460     {
12461       game.centered_player_nr_next = game.centered_player_nr;
12462       game.set_centered_player = FALSE;
12463     }
12464   }
12465
12466   if (game.set_centered_player &&
12467       ScreenMovPos == 0)        // screen currently aligned at tile position
12468   {
12469     int sx, sy;
12470
12471     if (game.centered_player_nr_next == -1)
12472     {
12473       setScreenCenteredToAllPlayers(&sx, &sy);
12474     }
12475     else
12476     {
12477       sx = stored_player[game.centered_player_nr_next].jx;
12478       sy = stored_player[game.centered_player_nr_next].jy;
12479     }
12480
12481     game.centered_player_nr = game.centered_player_nr_next;
12482     game.set_centered_player = FALSE;
12483
12484     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12485     DrawGameDoorValues();
12486   }
12487
12488   // check single step mode (set flag and clear again if any player is active)
12489   game.enter_single_step_mode =
12490     (tape.single_step && tape.recording && !tape.pausing);
12491
12492   for (i = 0; i < MAX_PLAYERS; i++)
12493   {
12494     int actual_player_action = stored_player[i].effective_action;
12495
12496 #if 1
12497     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12498        - rnd_equinox_tetrachloride 048
12499        - rnd_equinox_tetrachloride_ii 096
12500        - rnd_emanuel_schmieg 002
12501        - doctor_sloan_ww 001, 020
12502     */
12503     if (stored_player[i].MovPos == 0)
12504       CheckGravityMovement(&stored_player[i]);
12505 #endif
12506
12507     // overwrite programmed action with tape action
12508     if (stored_player[i].programmed_action)
12509       actual_player_action = stored_player[i].programmed_action;
12510
12511     PlayerActions(&stored_player[i], actual_player_action);
12512
12513     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12514   }
12515
12516   // single step pause mode may already have been toggled by "ScrollPlayer()"
12517   if (game.enter_single_step_mode && !tape.pausing)
12518     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12519
12520   ScrollScreen(NULL, SCROLL_GO_ON);
12521
12522   /* for backwards compatibility, the following code emulates a fixed bug that
12523      occured when pushing elements (causing elements that just made their last
12524      pushing step to already (if possible) make their first falling step in the
12525      same game frame, which is bad); this code is also needed to use the famous
12526      "spring push bug" which is used in older levels and might be wanted to be
12527      used also in newer levels, but in this case the buggy pushing code is only
12528      affecting the "spring" element and no other elements */
12529
12530   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12531   {
12532     for (i = 0; i < MAX_PLAYERS; i++)
12533     {
12534       struct PlayerInfo *player = &stored_player[i];
12535       int x = player->jx;
12536       int y = player->jy;
12537
12538       if (player->active && player->is_pushing && player->is_moving &&
12539           IS_MOVING(x, y) &&
12540           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12541            Tile[x][y] == EL_SPRING))
12542       {
12543         ContinueMoving(x, y);
12544
12545         // continue moving after pushing (this is actually a bug)
12546         if (!IS_MOVING(x, y))
12547           Stop[x][y] = FALSE;
12548       }
12549     }
12550   }
12551
12552   SCAN_PLAYFIELD(x, y)
12553   {
12554     Last[x][y] = Tile[x][y];
12555
12556     ChangeCount[x][y] = 0;
12557     ChangeEvent[x][y] = -1;
12558
12559     // this must be handled before main playfield loop
12560     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12561     {
12562       MovDelay[x][y]--;
12563       if (MovDelay[x][y] <= 0)
12564         RemoveField(x, y);
12565     }
12566
12567     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12568     {
12569       MovDelay[x][y]--;
12570       if (MovDelay[x][y] <= 0)
12571       {
12572         int element = Store[x][y];
12573         int move_direction = MovDir[x][y];
12574         int player_index_bit = Store2[x][y];
12575
12576         Store[x][y] = 0;
12577         Store2[x][y] = 0;
12578
12579         RemoveField(x, y);
12580         TEST_DrawLevelField(x, y);
12581
12582         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12583
12584         if (IS_ENVELOPE(element))
12585           local_player->show_envelope = element;
12586       }
12587     }
12588
12589 #if DEBUG
12590     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12591     {
12592       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12593             x, y);
12594       Debug("game:playing:GameActions_RND", "This should never happen!");
12595
12596       ChangePage[x][y] = -1;
12597     }
12598 #endif
12599
12600     Stop[x][y] = FALSE;
12601     if (WasJustMoving[x][y] > 0)
12602       WasJustMoving[x][y]--;
12603     if (WasJustFalling[x][y] > 0)
12604       WasJustFalling[x][y]--;
12605     if (CheckCollision[x][y] > 0)
12606       CheckCollision[x][y]--;
12607     if (CheckImpact[x][y] > 0)
12608       CheckImpact[x][y]--;
12609
12610     GfxFrame[x][y]++;
12611
12612     /* reset finished pushing action (not done in ContinueMoving() to allow
12613        continuous pushing animation for elements with zero push delay) */
12614     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12615     {
12616       ResetGfxAnimation(x, y);
12617       TEST_DrawLevelField(x, y);
12618     }
12619
12620 #if DEBUG
12621     if (IS_BLOCKED(x, y))
12622     {
12623       int oldx, oldy;
12624
12625       Blocked2Moving(x, y, &oldx, &oldy);
12626       if (!IS_MOVING(oldx, oldy))
12627       {
12628         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12629         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12630         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12631         Debug("game:playing:GameActions_RND", "This should never happen!");
12632       }
12633     }
12634 #endif
12635   }
12636
12637   HandleMouseAction(&mouse_action, &mouse_action_last);
12638
12639   SCAN_PLAYFIELD(x, y)
12640   {
12641     element = Tile[x][y];
12642     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12643     last_gfx_frame = GfxFrame[x][y];
12644
12645     if (element == EL_EMPTY)
12646       graphic = el2img(GfxElementEmpty[x][y]);
12647
12648     ResetGfxFrame(x, y);
12649
12650     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12651       DrawLevelGraphicAnimation(x, y, graphic);
12652
12653     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12654         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12655       ResetRandomAnimationValue(x, y);
12656
12657     SetRandomAnimationValue(x, y);
12658
12659     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12660
12661     if (IS_INACTIVE(element))
12662     {
12663       if (IS_ANIMATED(graphic))
12664         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12665
12666       continue;
12667     }
12668
12669     // this may take place after moving, so 'element' may have changed
12670     if (IS_CHANGING(x, y) &&
12671         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12672     {
12673       int page = element_info[element].event_page_nr[CE_DELAY];
12674
12675       HandleElementChange(x, y, page);
12676
12677       element = Tile[x][y];
12678       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12679     }
12680
12681     CheckNextToConditions(x, y);
12682
12683     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12684     {
12685       StartMoving(x, y);
12686
12687       element = Tile[x][y];
12688       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12689
12690       if (IS_ANIMATED(graphic) &&
12691           !IS_MOVING(x, y) &&
12692           !Stop[x][y])
12693         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12694
12695       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12696         TEST_DrawTwinkleOnField(x, y);
12697     }
12698     else if (element == EL_ACID)
12699     {
12700       if (!Stop[x][y])
12701         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12702     }
12703     else if ((element == EL_EXIT_OPEN ||
12704               element == EL_EM_EXIT_OPEN ||
12705               element == EL_SP_EXIT_OPEN ||
12706               element == EL_STEEL_EXIT_OPEN ||
12707               element == EL_EM_STEEL_EXIT_OPEN ||
12708               element == EL_SP_TERMINAL ||
12709               element == EL_SP_TERMINAL_ACTIVE ||
12710               element == EL_EXTRA_TIME ||
12711               element == EL_SHIELD_NORMAL ||
12712               element == EL_SHIELD_DEADLY) &&
12713              IS_ANIMATED(graphic))
12714       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12715     else if (IS_MOVING(x, y))
12716       ContinueMoving(x, y);
12717     else if (IS_ACTIVE_BOMB(element))
12718       CheckDynamite(x, y);
12719     else if (element == EL_AMOEBA_GROWING)
12720       AmoebaGrowing(x, y);
12721     else if (element == EL_AMOEBA_SHRINKING)
12722       AmoebaShrinking(x, y);
12723
12724 #if !USE_NEW_AMOEBA_CODE
12725     else if (IS_AMOEBALIVE(element))
12726       AmoebaReproduce(x, y);
12727 #endif
12728
12729     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12730       Life(x, y);
12731     else if (element == EL_EXIT_CLOSED)
12732       CheckExit(x, y);
12733     else if (element == EL_EM_EXIT_CLOSED)
12734       CheckExitEM(x, y);
12735     else if (element == EL_STEEL_EXIT_CLOSED)
12736       CheckExitSteel(x, y);
12737     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12738       CheckExitSteelEM(x, y);
12739     else if (element == EL_SP_EXIT_CLOSED)
12740       CheckExitSP(x, y);
12741     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12742              element == EL_EXPANDABLE_STEELWALL_GROWING)
12743       WallGrowing(x, y);
12744     else if (element == EL_EXPANDABLE_WALL ||
12745              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12746              element == EL_EXPANDABLE_WALL_VERTICAL ||
12747              element == EL_EXPANDABLE_WALL_ANY ||
12748              element == EL_BD_EXPANDABLE_WALL ||
12749              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12750              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12751              element == EL_EXPANDABLE_STEELWALL_ANY)
12752       CheckWallGrowing(x, y);
12753     else if (element == EL_FLAMES)
12754       CheckForDragon(x, y);
12755     else if (element == EL_EXPLOSION)
12756       ; // drawing of correct explosion animation is handled separately
12757     else if (element == EL_ELEMENT_SNAPPING ||
12758              element == EL_DIAGONAL_SHRINKING ||
12759              element == EL_DIAGONAL_GROWING)
12760     {
12761       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12762
12763       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12764     }
12765     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12766       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12767
12768     if (IS_BELT_ACTIVE(element))
12769       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12770
12771     if (game.magic_wall_active)
12772     {
12773       int jx = local_player->jx, jy = local_player->jy;
12774
12775       // play the element sound at the position nearest to the player
12776       if ((element == EL_MAGIC_WALL_FULL ||
12777            element == EL_MAGIC_WALL_ACTIVE ||
12778            element == EL_MAGIC_WALL_EMPTYING ||
12779            element == EL_BD_MAGIC_WALL_FULL ||
12780            element == EL_BD_MAGIC_WALL_ACTIVE ||
12781            element == EL_BD_MAGIC_WALL_EMPTYING ||
12782            element == EL_DC_MAGIC_WALL_FULL ||
12783            element == EL_DC_MAGIC_WALL_ACTIVE ||
12784            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12785           ABS(x - jx) + ABS(y - jy) <
12786           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12787       {
12788         magic_wall_x = x;
12789         magic_wall_y = y;
12790       }
12791     }
12792   }
12793
12794 #if USE_NEW_AMOEBA_CODE
12795   // new experimental amoeba growth stuff
12796   if (!(FrameCounter % 8))
12797   {
12798     static unsigned int random = 1684108901;
12799
12800     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12801     {
12802       x = RND(lev_fieldx);
12803       y = RND(lev_fieldy);
12804       element = Tile[x][y];
12805
12806       if (!IS_PLAYER(x, y) &&
12807           (element == EL_EMPTY ||
12808            CAN_GROW_INTO(element) ||
12809            element == EL_QUICKSAND_EMPTY ||
12810            element == EL_QUICKSAND_FAST_EMPTY ||
12811            element == EL_ACID_SPLASH_LEFT ||
12812            element == EL_ACID_SPLASH_RIGHT))
12813       {
12814         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12815             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12816             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12817             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12818           Tile[x][y] = EL_AMOEBA_DROP;
12819       }
12820
12821       random = random * 129 + 1;
12822     }
12823   }
12824 #endif
12825
12826   game.explosions_delayed = FALSE;
12827
12828   SCAN_PLAYFIELD(x, y)
12829   {
12830     element = Tile[x][y];
12831
12832     if (ExplodeField[x][y])
12833       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12834     else if (element == EL_EXPLOSION)
12835       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12836
12837     ExplodeField[x][y] = EX_TYPE_NONE;
12838   }
12839
12840   game.explosions_delayed = TRUE;
12841
12842   if (game.magic_wall_active)
12843   {
12844     if (!(game.magic_wall_time_left % 4))
12845     {
12846       int element = Tile[magic_wall_x][magic_wall_y];
12847
12848       if (element == EL_BD_MAGIC_WALL_FULL ||
12849           element == EL_BD_MAGIC_WALL_ACTIVE ||
12850           element == EL_BD_MAGIC_WALL_EMPTYING)
12851         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12852       else if (element == EL_DC_MAGIC_WALL_FULL ||
12853                element == EL_DC_MAGIC_WALL_ACTIVE ||
12854                element == EL_DC_MAGIC_WALL_EMPTYING)
12855         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12856       else
12857         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12858     }
12859
12860     if (game.magic_wall_time_left > 0)
12861     {
12862       game.magic_wall_time_left--;
12863
12864       if (!game.magic_wall_time_left)
12865       {
12866         SCAN_PLAYFIELD(x, y)
12867         {
12868           element = Tile[x][y];
12869
12870           if (element == EL_MAGIC_WALL_ACTIVE ||
12871               element == EL_MAGIC_WALL_FULL)
12872           {
12873             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12874             TEST_DrawLevelField(x, y);
12875           }
12876           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12877                    element == EL_BD_MAGIC_WALL_FULL)
12878           {
12879             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12880             TEST_DrawLevelField(x, y);
12881           }
12882           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12883                    element == EL_DC_MAGIC_WALL_FULL)
12884           {
12885             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12886             TEST_DrawLevelField(x, y);
12887           }
12888         }
12889
12890         game.magic_wall_active = FALSE;
12891       }
12892     }
12893   }
12894
12895   if (game.light_time_left > 0)
12896   {
12897     game.light_time_left--;
12898
12899     if (game.light_time_left == 0)
12900       RedrawAllLightSwitchesAndInvisibleElements();
12901   }
12902
12903   if (game.timegate_time_left > 0)
12904   {
12905     game.timegate_time_left--;
12906
12907     if (game.timegate_time_left == 0)
12908       CloseAllOpenTimegates();
12909   }
12910
12911   if (game.lenses_time_left > 0)
12912   {
12913     game.lenses_time_left--;
12914
12915     if (game.lenses_time_left == 0)
12916       RedrawAllInvisibleElementsForLenses();
12917   }
12918
12919   if (game.magnify_time_left > 0)
12920   {
12921     game.magnify_time_left--;
12922
12923     if (game.magnify_time_left == 0)
12924       RedrawAllInvisibleElementsForMagnifier();
12925   }
12926
12927   for (i = 0; i < MAX_PLAYERS; i++)
12928   {
12929     struct PlayerInfo *player = &stored_player[i];
12930
12931     if (SHIELD_ON(player))
12932     {
12933       if (player->shield_deadly_time_left)
12934         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12935       else if (player->shield_normal_time_left)
12936         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12937     }
12938   }
12939
12940 #if USE_DELAYED_GFX_REDRAW
12941   SCAN_PLAYFIELD(x, y)
12942   {
12943     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12944     {
12945       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12946          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12947
12948       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12949         DrawLevelField(x, y);
12950
12951       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12952         DrawLevelFieldCrumbled(x, y);
12953
12954       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12955         DrawLevelFieldCrumbledNeighbours(x, y);
12956
12957       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12958         DrawTwinkleOnField(x, y);
12959     }
12960
12961     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12962   }
12963 #endif
12964
12965   DrawAllPlayers();
12966   PlayAllPlayersSound();
12967
12968   for (i = 0; i < MAX_PLAYERS; i++)
12969   {
12970     struct PlayerInfo *player = &stored_player[i];
12971
12972     if (player->show_envelope != 0 && (!player->active ||
12973                                        player->MovPos == 0))
12974     {
12975       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12976
12977       player->show_envelope = 0;
12978     }
12979   }
12980
12981   // use random number generator in every frame to make it less predictable
12982   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12983     RND(1);
12984
12985   mouse_action_last = mouse_action;
12986 }
12987
12988 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12989 {
12990   int min_x = x, min_y = y, max_x = x, max_y = y;
12991   int scr_fieldx = getScreenFieldSizeX();
12992   int scr_fieldy = getScreenFieldSizeY();
12993   int i;
12994
12995   for (i = 0; i < MAX_PLAYERS; i++)
12996   {
12997     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12998
12999     if (!stored_player[i].active || &stored_player[i] == player)
13000       continue;
13001
13002     min_x = MIN(min_x, jx);
13003     min_y = MIN(min_y, jy);
13004     max_x = MAX(max_x, jx);
13005     max_y = MAX(max_y, jy);
13006   }
13007
13008   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
13009 }
13010
13011 static boolean AllPlayersInVisibleScreen(void)
13012 {
13013   int i;
13014
13015   for (i = 0; i < MAX_PLAYERS; i++)
13016   {
13017     int jx = stored_player[i].jx, jy = stored_player[i].jy;
13018
13019     if (!stored_player[i].active)
13020       continue;
13021
13022     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13023       return FALSE;
13024   }
13025
13026   return TRUE;
13027 }
13028
13029 void ScrollLevel(int dx, int dy)
13030 {
13031   int scroll_offset = 2 * TILEX_VAR;
13032   int x, y;
13033
13034   BlitBitmap(drawto_field, drawto_field,
13035              FX + TILEX_VAR * (dx == -1) - scroll_offset,
13036              FY + TILEY_VAR * (dy == -1) - scroll_offset,
13037              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
13038              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
13039              FX + TILEX_VAR * (dx == 1) - scroll_offset,
13040              FY + TILEY_VAR * (dy == 1) - scroll_offset);
13041
13042   if (dx != 0)
13043   {
13044     x = (dx == 1 ? BX1 : BX2);
13045     for (y = BY1; y <= BY2; y++)
13046       DrawScreenField(x, y);
13047   }
13048
13049   if (dy != 0)
13050   {
13051     y = (dy == 1 ? BY1 : BY2);
13052     for (x = BX1; x <= BX2; x++)
13053       DrawScreenField(x, y);
13054   }
13055
13056   redraw_mask |= REDRAW_FIELD;
13057 }
13058
13059 static boolean canFallDown(struct PlayerInfo *player)
13060 {
13061   int jx = player->jx, jy = player->jy;
13062
13063   return (IN_LEV_FIELD(jx, jy + 1) &&
13064           (IS_FREE(jx, jy + 1) ||
13065            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13066           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13067           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13068 }
13069
13070 static boolean canPassField(int x, int y, int move_dir)
13071 {
13072   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13073   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13074   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13075   int nextx = x + dx;
13076   int nexty = y + dy;
13077   int element = Tile[x][y];
13078
13079   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13080           !CAN_MOVE(element) &&
13081           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13082           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13083           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13084 }
13085
13086 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13087 {
13088   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13089   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13090   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13091   int newx = x + dx;
13092   int newy = y + dy;
13093
13094   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13095           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13096           (IS_DIGGABLE(Tile[newx][newy]) ||
13097            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13098            canPassField(newx, newy, move_dir)));
13099 }
13100
13101 static void CheckGravityMovement(struct PlayerInfo *player)
13102 {
13103   if (player->gravity && !player->programmed_action)
13104   {
13105     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13106     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13107     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13108     int jx = player->jx, jy = player->jy;
13109     boolean player_is_moving_to_valid_field =
13110       (!player_is_snapping &&
13111        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13112         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13113     boolean player_can_fall_down = canFallDown(player);
13114
13115     if (player_can_fall_down &&
13116         !player_is_moving_to_valid_field)
13117       player->programmed_action = MV_DOWN;
13118   }
13119 }
13120
13121 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13122 {
13123   return CheckGravityMovement(player);
13124
13125   if (player->gravity && !player->programmed_action)
13126   {
13127     int jx = player->jx, jy = player->jy;
13128     boolean field_under_player_is_free =
13129       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13130     boolean player_is_standing_on_valid_field =
13131       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13132        (IS_WALKABLE(Tile[jx][jy]) &&
13133         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13134
13135     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13136       player->programmed_action = MV_DOWN;
13137   }
13138 }
13139
13140 /*
13141   MovePlayerOneStep()
13142   -----------------------------------------------------------------------------
13143   dx, dy:               direction (non-diagonal) to try to move the player to
13144   real_dx, real_dy:     direction as read from input device (can be diagonal)
13145 */
13146
13147 boolean MovePlayerOneStep(struct PlayerInfo *player,
13148                           int dx, int dy, int real_dx, int real_dy)
13149 {
13150   int jx = player->jx, jy = player->jy;
13151   int new_jx = jx + dx, new_jy = jy + dy;
13152   int can_move;
13153   boolean player_can_move = !player->cannot_move;
13154
13155   if (!player->active || (!dx && !dy))
13156     return MP_NO_ACTION;
13157
13158   player->MovDir = (dx < 0 ? MV_LEFT :
13159                     dx > 0 ? MV_RIGHT :
13160                     dy < 0 ? MV_UP :
13161                     dy > 0 ? MV_DOWN :  MV_NONE);
13162
13163   if (!IN_LEV_FIELD(new_jx, new_jy))
13164     return MP_NO_ACTION;
13165
13166   if (!player_can_move)
13167   {
13168     if (player->MovPos == 0)
13169     {
13170       player->is_moving = FALSE;
13171       player->is_digging = FALSE;
13172       player->is_collecting = FALSE;
13173       player->is_snapping = FALSE;
13174       player->is_pushing = FALSE;
13175     }
13176   }
13177
13178   if (!network.enabled && game.centered_player_nr == -1 &&
13179       !AllPlayersInSight(player, new_jx, new_jy))
13180     return MP_NO_ACTION;
13181
13182   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13183   if (can_move != MP_MOVING)
13184     return can_move;
13185
13186   // check if DigField() has caused relocation of the player
13187   if (player->jx != jx || player->jy != jy)
13188     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13189
13190   StorePlayer[jx][jy] = 0;
13191   player->last_jx = jx;
13192   player->last_jy = jy;
13193   player->jx = new_jx;
13194   player->jy = new_jy;
13195   StorePlayer[new_jx][new_jy] = player->element_nr;
13196
13197   if (player->move_delay_value_next != -1)
13198   {
13199     player->move_delay_value = player->move_delay_value_next;
13200     player->move_delay_value_next = -1;
13201   }
13202
13203   player->MovPos =
13204     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13205
13206   player->step_counter++;
13207
13208   PlayerVisit[jx][jy] = FrameCounter;
13209
13210   player->is_moving = TRUE;
13211
13212 #if 1
13213   // should better be called in MovePlayer(), but this breaks some tapes
13214   ScrollPlayer(player, SCROLL_INIT);
13215 #endif
13216
13217   return MP_MOVING;
13218 }
13219
13220 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13221 {
13222   int jx = player->jx, jy = player->jy;
13223   int old_jx = jx, old_jy = jy;
13224   int moved = MP_NO_ACTION;
13225
13226   if (!player->active)
13227     return FALSE;
13228
13229   if (!dx && !dy)
13230   {
13231     if (player->MovPos == 0)
13232     {
13233       player->is_moving = FALSE;
13234       player->is_digging = FALSE;
13235       player->is_collecting = FALSE;
13236       player->is_snapping = FALSE;
13237       player->is_pushing = FALSE;
13238     }
13239
13240     return FALSE;
13241   }
13242
13243   if (player->move_delay > 0)
13244     return FALSE;
13245
13246   player->move_delay = -1;              // set to "uninitialized" value
13247
13248   // store if player is automatically moved to next field
13249   player->is_auto_moving = (player->programmed_action != MV_NONE);
13250
13251   // remove the last programmed player action
13252   player->programmed_action = 0;
13253
13254   if (player->MovPos)
13255   {
13256     // should only happen if pre-1.2 tape recordings are played
13257     // this is only for backward compatibility
13258
13259     int original_move_delay_value = player->move_delay_value;
13260
13261 #if DEBUG
13262     Debug("game:playing:MovePlayer",
13263           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13264           tape.counter);
13265 #endif
13266
13267     // scroll remaining steps with finest movement resolution
13268     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13269
13270     while (player->MovPos)
13271     {
13272       ScrollPlayer(player, SCROLL_GO_ON);
13273       ScrollScreen(NULL, SCROLL_GO_ON);
13274
13275       AdvanceFrameAndPlayerCounters(player->index_nr);
13276
13277       DrawAllPlayers();
13278       BackToFront_WithFrameDelay(0);
13279     }
13280
13281     player->move_delay_value = original_move_delay_value;
13282   }
13283
13284   player->is_active = FALSE;
13285
13286   if (player->last_move_dir & MV_HORIZONTAL)
13287   {
13288     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13289       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13290   }
13291   else
13292   {
13293     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13294       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13295   }
13296
13297   if (!moved && !player->is_active)
13298   {
13299     player->is_moving = FALSE;
13300     player->is_digging = FALSE;
13301     player->is_collecting = FALSE;
13302     player->is_snapping = FALSE;
13303     player->is_pushing = FALSE;
13304   }
13305
13306   jx = player->jx;
13307   jy = player->jy;
13308
13309   if (moved & MP_MOVING && !ScreenMovPos &&
13310       (player->index_nr == game.centered_player_nr ||
13311        game.centered_player_nr == -1))
13312   {
13313     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13314
13315     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13316     {
13317       // actual player has left the screen -- scroll in that direction
13318       if (jx != old_jx)         // player has moved horizontally
13319         scroll_x += (jx - old_jx);
13320       else                      // player has moved vertically
13321         scroll_y += (jy - old_jy);
13322     }
13323     else
13324     {
13325       int offset_raw = game.scroll_delay_value;
13326
13327       if (jx != old_jx)         // player has moved horizontally
13328       {
13329         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13330         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13331         int new_scroll_x = jx - MIDPOSX + offset_x;
13332
13333         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13334             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13335           scroll_x = new_scroll_x;
13336
13337         // don't scroll over playfield boundaries
13338         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13339
13340         // don't scroll more than one field at a time
13341         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13342
13343         // don't scroll against the player's moving direction
13344         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13345             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13346           scroll_x = old_scroll_x;
13347       }
13348       else                      // player has moved vertically
13349       {
13350         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13351         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13352         int new_scroll_y = jy - MIDPOSY + offset_y;
13353
13354         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13355             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13356           scroll_y = new_scroll_y;
13357
13358         // don't scroll over playfield boundaries
13359         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13360
13361         // don't scroll more than one field at a time
13362         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13363
13364         // don't scroll against the player's moving direction
13365         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13366             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13367           scroll_y = old_scroll_y;
13368       }
13369     }
13370
13371     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13372     {
13373       if (!network.enabled && game.centered_player_nr == -1 &&
13374           !AllPlayersInVisibleScreen())
13375       {
13376         scroll_x = old_scroll_x;
13377         scroll_y = old_scroll_y;
13378       }
13379       else
13380       {
13381         ScrollScreen(player, SCROLL_INIT);
13382         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13383       }
13384     }
13385   }
13386
13387   player->StepFrame = 0;
13388
13389   if (moved & MP_MOVING)
13390   {
13391     if (old_jx != jx && old_jy == jy)
13392       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13393     else if (old_jx == jx && old_jy != jy)
13394       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13395
13396     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13397
13398     player->last_move_dir = player->MovDir;
13399     player->is_moving = TRUE;
13400     player->is_snapping = FALSE;
13401     player->is_switching = FALSE;
13402     player->is_dropping = FALSE;
13403     player->is_dropping_pressed = FALSE;
13404     player->drop_pressed_delay = 0;
13405
13406 #if 0
13407     // should better be called here than above, but this breaks some tapes
13408     ScrollPlayer(player, SCROLL_INIT);
13409 #endif
13410   }
13411   else
13412   {
13413     CheckGravityMovementWhenNotMoving(player);
13414
13415     player->is_moving = FALSE;
13416
13417     /* at this point, the player is allowed to move, but cannot move right now
13418        (e.g. because of something blocking the way) -- ensure that the player
13419        is also allowed to move in the next frame (in old versions before 3.1.1,
13420        the player was forced to wait again for eight frames before next try) */
13421
13422     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13423       player->move_delay = 0;   // allow direct movement in the next frame
13424   }
13425
13426   if (player->move_delay == -1)         // not yet initialized by DigField()
13427     player->move_delay = player->move_delay_value;
13428
13429   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13430   {
13431     TestIfPlayerTouchesBadThing(jx, jy);
13432     TestIfPlayerTouchesCustomElement(jx, jy);
13433   }
13434
13435   if (!player->active)
13436     RemovePlayer(player);
13437
13438   return moved;
13439 }
13440
13441 void ScrollPlayer(struct PlayerInfo *player, int mode)
13442 {
13443   int jx = player->jx, jy = player->jy;
13444   int last_jx = player->last_jx, last_jy = player->last_jy;
13445   int move_stepsize = TILEX / player->move_delay_value;
13446
13447   if (!player->active)
13448     return;
13449
13450   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13451     return;
13452
13453   if (mode == SCROLL_INIT)
13454   {
13455     player->actual_frame_counter.count = FrameCounter;
13456     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13457
13458     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13459         Tile[last_jx][last_jy] == EL_EMPTY)
13460     {
13461       int last_field_block_delay = 0;   // start with no blocking at all
13462       int block_delay_adjustment = player->block_delay_adjustment;
13463
13464       // if player blocks last field, add delay for exactly one move
13465       if (player->block_last_field)
13466       {
13467         last_field_block_delay += player->move_delay_value;
13468
13469         // when blocking enabled, prevent moving up despite gravity
13470         if (player->gravity && player->MovDir == MV_UP)
13471           block_delay_adjustment = -1;
13472       }
13473
13474       // add block delay adjustment (also possible when not blocking)
13475       last_field_block_delay += block_delay_adjustment;
13476
13477       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13478       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13479     }
13480
13481     if (player->MovPos != 0)    // player has not yet reached destination
13482       return;
13483   }
13484   else if (!FrameReached(&player->actual_frame_counter))
13485     return;
13486
13487   if (player->MovPos != 0)
13488   {
13489     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13490     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13491
13492     // before DrawPlayer() to draw correct player graphic for this case
13493     if (player->MovPos == 0)
13494       CheckGravityMovement(player);
13495   }
13496
13497   if (player->MovPos == 0)      // player reached destination field
13498   {
13499     if (player->move_delay_reset_counter > 0)
13500     {
13501       player->move_delay_reset_counter--;
13502
13503       if (player->move_delay_reset_counter == 0)
13504       {
13505         // continue with normal speed after quickly moving through gate
13506         HALVE_PLAYER_SPEED(player);
13507
13508         // be able to make the next move without delay
13509         player->move_delay = 0;
13510       }
13511     }
13512
13513     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13514         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13515         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13516         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13517         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13518         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13519         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13520         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13521     {
13522       ExitPlayer(player);
13523
13524       if (game.players_still_needed == 0 &&
13525           (game.friends_still_needed == 0 ||
13526            IS_SP_ELEMENT(Tile[jx][jy])))
13527         LevelSolved();
13528     }
13529
13530     player->last_jx = jx;
13531     player->last_jy = jy;
13532
13533     // this breaks one level: "machine", level 000
13534     {
13535       int move_direction = player->MovDir;
13536       int enter_side = MV_DIR_OPPOSITE(move_direction);
13537       int leave_side = move_direction;
13538       int old_jx = last_jx;
13539       int old_jy = last_jy;
13540       int old_element = Tile[old_jx][old_jy];
13541       int new_element = Tile[jx][jy];
13542
13543       if (IS_CUSTOM_ELEMENT(old_element))
13544         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13545                                    CE_LEFT_BY_PLAYER,
13546                                    player->index_bit, leave_side);
13547
13548       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13549                                           CE_PLAYER_LEAVES_X,
13550                                           player->index_bit, leave_side);
13551
13552       // needed because pushed element has not yet reached its destination,
13553       // so it would trigger a change event at its previous field location
13554       if (!player->is_pushing)
13555       {
13556         if (IS_CUSTOM_ELEMENT(new_element))
13557           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13558                                      player->index_bit, enter_side);
13559
13560         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13561                                             CE_PLAYER_ENTERS_X,
13562                                             player->index_bit, enter_side);
13563       }
13564
13565       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13566                                         CE_MOVE_OF_X, move_direction);
13567     }
13568
13569     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13570     {
13571       TestIfPlayerTouchesBadThing(jx, jy);
13572       TestIfPlayerTouchesCustomElement(jx, jy);
13573
13574       // needed because pushed element has not yet reached its destination,
13575       // so it would trigger a change event at its previous field location
13576       if (!player->is_pushing)
13577         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13578
13579       if (level.finish_dig_collect &&
13580           (player->is_digging || player->is_collecting))
13581       {
13582         int last_element = player->last_removed_element;
13583         int move_direction = player->MovDir;
13584         int enter_side = MV_DIR_OPPOSITE(move_direction);
13585         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13586                             CE_PLAYER_COLLECTS_X);
13587
13588         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13589                                             player->index_bit, enter_side);
13590
13591         player->last_removed_element = EL_UNDEFINED;
13592       }
13593
13594       if (!player->active)
13595         RemovePlayer(player);
13596     }
13597
13598     if (level.use_step_counter)
13599       CheckLevelTime_StepCounter();
13600
13601     if (tape.single_step && tape.recording && !tape.pausing &&
13602         !player->programmed_action)
13603       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13604
13605     if (!player->programmed_action)
13606       CheckSaveEngineSnapshot(player);
13607   }
13608 }
13609
13610 void ScrollScreen(struct PlayerInfo *player, int mode)
13611 {
13612   static DelayCounter screen_frame_counter = { 0 };
13613
13614   if (mode == SCROLL_INIT)
13615   {
13616     // set scrolling step size according to actual player's moving speed
13617     ScrollStepSize = TILEX / player->move_delay_value;
13618
13619     screen_frame_counter.count = FrameCounter;
13620     screen_frame_counter.value = 1;
13621
13622     ScreenMovDir = player->MovDir;
13623     ScreenMovPos = player->MovPos;
13624     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13625     return;
13626   }
13627   else if (!FrameReached(&screen_frame_counter))
13628     return;
13629
13630   if (ScreenMovPos)
13631   {
13632     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13633     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13634     redraw_mask |= REDRAW_FIELD;
13635   }
13636   else
13637     ScreenMovDir = MV_NONE;
13638 }
13639
13640 void CheckNextToConditions(int x, int y)
13641 {
13642   int element = Tile[x][y];
13643
13644   if (IS_PLAYER(x, y))
13645     TestIfPlayerNextToCustomElement(x, y);
13646
13647   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13648       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13649     TestIfElementNextToCustomElement(x, y);
13650 }
13651
13652 void TestIfPlayerNextToCustomElement(int x, int y)
13653 {
13654   struct XY *xy = xy_topdown;
13655   static int trigger_sides[4][2] =
13656   {
13657     // center side       border side
13658     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13659     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13660     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13661     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13662   };
13663   int i;
13664
13665   if (!IS_PLAYER(x, y))
13666     return;
13667
13668   struct PlayerInfo *player = PLAYERINFO(x, y);
13669
13670   if (player->is_moving)
13671     return;
13672
13673   for (i = 0; i < NUM_DIRECTIONS; i++)
13674   {
13675     int xx = x + xy[i].x;
13676     int yy = y + xy[i].y;
13677     int border_side = trigger_sides[i][1];
13678     int border_element;
13679
13680     if (!IN_LEV_FIELD(xx, yy))
13681       continue;
13682
13683     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13684       continue;         // center and border element not connected
13685
13686     border_element = Tile[xx][yy];
13687
13688     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13689                                player->index_bit, border_side);
13690     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13691                                         CE_PLAYER_NEXT_TO_X,
13692                                         player->index_bit, border_side);
13693
13694     /* use player element that is initially defined in the level playfield,
13695        not the player element that corresponds to the runtime player number
13696        (example: a level that contains EL_PLAYER_3 as the only player would
13697        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13698
13699     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13700                              CE_NEXT_TO_X, border_side);
13701   }
13702 }
13703
13704 void TestIfPlayerTouchesCustomElement(int x, int y)
13705 {
13706   struct XY *xy = xy_topdown;
13707   static int trigger_sides[4][2] =
13708   {
13709     // center side       border side
13710     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13711     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13712     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13713     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13714   };
13715   static int touch_dir[4] =
13716   {
13717     MV_LEFT | MV_RIGHT,
13718     MV_UP   | MV_DOWN,
13719     MV_UP   | MV_DOWN,
13720     MV_LEFT | MV_RIGHT
13721   };
13722   int center_element = Tile[x][y];      // should always be non-moving!
13723   int i;
13724
13725   for (i = 0; i < NUM_DIRECTIONS; i++)
13726   {
13727     int xx = x + xy[i].x;
13728     int yy = y + xy[i].y;
13729     int center_side = trigger_sides[i][0];
13730     int border_side = trigger_sides[i][1];
13731     int border_element;
13732
13733     if (!IN_LEV_FIELD(xx, yy))
13734       continue;
13735
13736     if (IS_PLAYER(x, y))                // player found at center element
13737     {
13738       struct PlayerInfo *player = PLAYERINFO(x, y);
13739
13740       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13741         border_element = Tile[xx][yy];          // may be moving!
13742       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13743         border_element = Tile[xx][yy];
13744       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13745         border_element = MovingOrBlocked2Element(xx, yy);
13746       else
13747         continue;               // center and border element do not touch
13748
13749       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13750                                  player->index_bit, border_side);
13751       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13752                                           CE_PLAYER_TOUCHES_X,
13753                                           player->index_bit, border_side);
13754
13755       {
13756         /* use player element that is initially defined in the level playfield,
13757            not the player element that corresponds to the runtime player number
13758            (example: a level that contains EL_PLAYER_3 as the only player would
13759            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13760         int player_element = PLAYERINFO(x, y)->initial_element;
13761
13762         // as element "X" is the player here, check opposite (center) side
13763         CheckElementChangeBySide(xx, yy, border_element, player_element,
13764                                  CE_TOUCHING_X, center_side);
13765       }
13766     }
13767     else if (IS_PLAYER(xx, yy))         // player found at border element
13768     {
13769       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13770
13771       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13772       {
13773         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13774           continue;             // center and border element do not touch
13775       }
13776
13777       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13778                                  player->index_bit, center_side);
13779       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13780                                           CE_PLAYER_TOUCHES_X,
13781                                           player->index_bit, center_side);
13782
13783       {
13784         /* use player element that is initially defined in the level playfield,
13785            not the player element that corresponds to the runtime player number
13786            (example: a level that contains EL_PLAYER_3 as the only player would
13787            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13788         int player_element = PLAYERINFO(xx, yy)->initial_element;
13789
13790         // as element "X" is the player here, check opposite (border) side
13791         CheckElementChangeBySide(x, y, center_element, player_element,
13792                                  CE_TOUCHING_X, border_side);
13793       }
13794
13795       break;
13796     }
13797   }
13798 }
13799
13800 void TestIfElementNextToCustomElement(int x, int y)
13801 {
13802   struct XY *xy = xy_topdown;
13803   static int trigger_sides[4][2] =
13804   {
13805     // center side      border side
13806     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13807     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13808     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13809     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13810   };
13811   int center_element = Tile[x][y];      // should always be non-moving!
13812   int i;
13813
13814   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13815     return;
13816
13817   for (i = 0; i < NUM_DIRECTIONS; i++)
13818   {
13819     int xx = x + xy[i].x;
13820     int yy = y + xy[i].y;
13821     int border_side = trigger_sides[i][1];
13822     int border_element;
13823
13824     if (!IN_LEV_FIELD(xx, yy))
13825       continue;
13826
13827     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13828       continue;                 // center and border element not connected
13829
13830     border_element = Tile[xx][yy];
13831
13832     // check for change of center element (but change it only once)
13833     if (CheckElementChangeBySide(x, y, center_element, border_element,
13834                                  CE_NEXT_TO_X, border_side))
13835       break;
13836   }
13837 }
13838
13839 void TestIfElementTouchesCustomElement(int x, int y)
13840 {
13841   struct XY *xy = xy_topdown;
13842   static int trigger_sides[4][2] =
13843   {
13844     // center side      border side
13845     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13846     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13847     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13848     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13849   };
13850   static int touch_dir[4] =
13851   {
13852     MV_LEFT | MV_RIGHT,
13853     MV_UP   | MV_DOWN,
13854     MV_UP   | MV_DOWN,
13855     MV_LEFT | MV_RIGHT
13856   };
13857   boolean change_center_element = FALSE;
13858   int center_element = Tile[x][y];      // should always be non-moving!
13859   int border_element_old[NUM_DIRECTIONS];
13860   int i;
13861
13862   for (i = 0; i < NUM_DIRECTIONS; i++)
13863   {
13864     int xx = x + xy[i].x;
13865     int yy = y + xy[i].y;
13866     int border_element;
13867
13868     border_element_old[i] = -1;
13869
13870     if (!IN_LEV_FIELD(xx, yy))
13871       continue;
13872
13873     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13874       border_element = Tile[xx][yy];    // may be moving!
13875     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13876       border_element = Tile[xx][yy];
13877     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13878       border_element = MovingOrBlocked2Element(xx, yy);
13879     else
13880       continue;                 // center and border element do not touch
13881
13882     border_element_old[i] = border_element;
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 center_side = trigger_sides[i][0];
13890     int border_element = border_element_old[i];
13891
13892     if (border_element == -1)
13893       continue;
13894
13895     // check for change of border element
13896     CheckElementChangeBySide(xx, yy, border_element, center_element,
13897                              CE_TOUCHING_X, center_side);
13898
13899     // (center element cannot be player, so we don't have to check this here)
13900   }
13901
13902   for (i = 0; i < NUM_DIRECTIONS; i++)
13903   {
13904     int xx = x + xy[i].x;
13905     int yy = y + xy[i].y;
13906     int border_side = trigger_sides[i][1];
13907     int border_element = border_element_old[i];
13908
13909     if (border_element == -1)
13910       continue;
13911
13912     // check for change of center element (but change it only once)
13913     if (!change_center_element)
13914       change_center_element =
13915         CheckElementChangeBySide(x, y, center_element, border_element,
13916                                  CE_TOUCHING_X, border_side);
13917
13918     if (IS_PLAYER(xx, yy))
13919     {
13920       /* use player element that is initially defined in the level playfield,
13921          not the player element that corresponds to the runtime player number
13922          (example: a level that contains EL_PLAYER_3 as the only player would
13923          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13924       int player_element = PLAYERINFO(xx, yy)->initial_element;
13925
13926       // as element "X" is the player here, check opposite (border) side
13927       CheckElementChangeBySide(x, y, center_element, player_element,
13928                                CE_TOUCHING_X, border_side);
13929     }
13930   }
13931 }
13932
13933 void TestIfElementHitsCustomElement(int x, int y, int direction)
13934 {
13935   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13936   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13937   int hitx = x + dx, hity = y + dy;
13938   int hitting_element = Tile[x][y];
13939   int touched_element;
13940
13941   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13942     return;
13943
13944   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13945                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13946
13947   if (IN_LEV_FIELD(hitx, hity))
13948   {
13949     int opposite_direction = MV_DIR_OPPOSITE(direction);
13950     int hitting_side = direction;
13951     int touched_side = opposite_direction;
13952     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13953                           MovDir[hitx][hity] != direction ||
13954                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13955
13956     object_hit = TRUE;
13957
13958     if (object_hit)
13959     {
13960       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13961                                CE_HITTING_X, touched_side);
13962
13963       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13964                                CE_HIT_BY_X, hitting_side);
13965
13966       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13967                                CE_HIT_BY_SOMETHING, opposite_direction);
13968
13969       if (IS_PLAYER(hitx, hity))
13970       {
13971         /* use player element that is initially defined in the level playfield,
13972            not the player element that corresponds to the runtime player number
13973            (example: a level that contains EL_PLAYER_3 as the only player would
13974            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13975         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13976
13977         CheckElementChangeBySide(x, y, hitting_element, player_element,
13978                                  CE_HITTING_X, touched_side);
13979       }
13980     }
13981   }
13982
13983   // "hitting something" is also true when hitting the playfield border
13984   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13985                            CE_HITTING_SOMETHING, direction);
13986 }
13987
13988 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13989 {
13990   int i, kill_x = -1, kill_y = -1;
13991
13992   int bad_element = -1;
13993   struct XY *test_xy = xy_topdown;
13994   static int test_dir[4] =
13995   {
13996     MV_UP,
13997     MV_LEFT,
13998     MV_RIGHT,
13999     MV_DOWN
14000   };
14001
14002   for (i = 0; i < NUM_DIRECTIONS; i++)
14003   {
14004     int test_x, test_y, test_move_dir, test_element;
14005
14006     test_x = good_x + test_xy[i].x;
14007     test_y = good_y + test_xy[i].y;
14008
14009     if (!IN_LEV_FIELD(test_x, test_y))
14010       continue;
14011
14012     test_move_dir =
14013       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14014
14015     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
14016
14017     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14018        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14019     */
14020     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
14021         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
14022     {
14023       kill_x = test_x;
14024       kill_y = test_y;
14025       bad_element = test_element;
14026
14027       break;
14028     }
14029   }
14030
14031   if (kill_x != -1 || kill_y != -1)
14032   {
14033     if (IS_PLAYER(good_x, good_y))
14034     {
14035       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
14036
14037       if (player->shield_deadly_time_left > 0 &&
14038           !IS_INDESTRUCTIBLE(bad_element))
14039         Bang(kill_x, kill_y);
14040       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14041         KillPlayer(player);
14042     }
14043     else
14044       Bang(good_x, good_y);
14045   }
14046 }
14047
14048 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14049 {
14050   int i, kill_x = -1, kill_y = -1;
14051   int bad_element = Tile[bad_x][bad_y];
14052   struct XY *test_xy = xy_topdown;
14053   static int touch_dir[4] =
14054   {
14055     MV_LEFT | MV_RIGHT,
14056     MV_UP   | MV_DOWN,
14057     MV_UP   | MV_DOWN,
14058     MV_LEFT | MV_RIGHT
14059   };
14060   static int test_dir[4] =
14061   {
14062     MV_UP,
14063     MV_LEFT,
14064     MV_RIGHT,
14065     MV_DOWN
14066   };
14067
14068   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14069     return;
14070
14071   for (i = 0; i < NUM_DIRECTIONS; i++)
14072   {
14073     int test_x, test_y, test_move_dir, test_element;
14074
14075     test_x = bad_x + test_xy[i].x;
14076     test_y = bad_y + test_xy[i].y;
14077
14078     if (!IN_LEV_FIELD(test_x, test_y))
14079       continue;
14080
14081     test_move_dir =
14082       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14083
14084     test_element = Tile[test_x][test_y];
14085
14086     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14087        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14088     */
14089     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14090         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14091     {
14092       // good thing is player or penguin that does not move away
14093       if (IS_PLAYER(test_x, test_y))
14094       {
14095         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14096
14097         if (bad_element == EL_ROBOT && player->is_moving)
14098           continue;     // robot does not kill player if he is moving
14099
14100         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14101         {
14102           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14103             continue;           // center and border element do not touch
14104         }
14105
14106         kill_x = test_x;
14107         kill_y = test_y;
14108
14109         break;
14110       }
14111       else if (test_element == EL_PENGUIN)
14112       {
14113         kill_x = test_x;
14114         kill_y = test_y;
14115
14116         break;
14117       }
14118     }
14119   }
14120
14121   if (kill_x != -1 || kill_y != -1)
14122   {
14123     if (IS_PLAYER(kill_x, kill_y))
14124     {
14125       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14126
14127       if (player->shield_deadly_time_left > 0 &&
14128           !IS_INDESTRUCTIBLE(bad_element))
14129         Bang(bad_x, bad_y);
14130       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14131         KillPlayer(player);
14132     }
14133     else
14134       Bang(kill_x, kill_y);
14135   }
14136 }
14137
14138 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14139 {
14140   int bad_element = Tile[bad_x][bad_y];
14141   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14142   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14143   int test_x = bad_x + dx, test_y = bad_y + dy;
14144   int test_move_dir, test_element;
14145   int kill_x = -1, kill_y = -1;
14146
14147   if (!IN_LEV_FIELD(test_x, test_y))
14148     return;
14149
14150   test_move_dir =
14151     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14152
14153   test_element = Tile[test_x][test_y];
14154
14155   if (test_move_dir != bad_move_dir)
14156   {
14157     // good thing can be player or penguin that does not move away
14158     if (IS_PLAYER(test_x, test_y))
14159     {
14160       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14161
14162       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14163          player as being hit when he is moving towards the bad thing, because
14164          the "get hit by" condition would be lost after the player stops) */
14165       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14166         return;         // player moves away from bad thing
14167
14168       kill_x = test_x;
14169       kill_y = test_y;
14170     }
14171     else if (test_element == EL_PENGUIN)
14172     {
14173       kill_x = test_x;
14174       kill_y = test_y;
14175     }
14176   }
14177
14178   if (kill_x != -1 || kill_y != -1)
14179   {
14180     if (IS_PLAYER(kill_x, kill_y))
14181     {
14182       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14183
14184       if (player->shield_deadly_time_left > 0 &&
14185           !IS_INDESTRUCTIBLE(bad_element))
14186         Bang(bad_x, bad_y);
14187       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14188         KillPlayer(player);
14189     }
14190     else
14191       Bang(kill_x, kill_y);
14192   }
14193 }
14194
14195 void TestIfPlayerTouchesBadThing(int x, int y)
14196 {
14197   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14198 }
14199
14200 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14201 {
14202   TestIfGoodThingHitsBadThing(x, y, move_dir);
14203 }
14204
14205 void TestIfBadThingTouchesPlayer(int x, int y)
14206 {
14207   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14208 }
14209
14210 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14211 {
14212   TestIfBadThingHitsGoodThing(x, y, move_dir);
14213 }
14214
14215 void TestIfFriendTouchesBadThing(int x, int y)
14216 {
14217   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14218 }
14219
14220 void TestIfBadThingTouchesFriend(int x, int y)
14221 {
14222   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14223 }
14224
14225 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14226 {
14227   int i, kill_x = bad_x, kill_y = bad_y;
14228   struct XY *xy = xy_topdown;
14229
14230   for (i = 0; i < NUM_DIRECTIONS; i++)
14231   {
14232     int x, y, element;
14233
14234     x = bad_x + xy[i].x;
14235     y = bad_y + xy[i].y;
14236     if (!IN_LEV_FIELD(x, y))
14237       continue;
14238
14239     element = Tile[x][y];
14240     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14241         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14242     {
14243       kill_x = x;
14244       kill_y = y;
14245       break;
14246     }
14247   }
14248
14249   if (kill_x != bad_x || kill_y != bad_y)
14250     Bang(bad_x, bad_y);
14251 }
14252
14253 void KillPlayer(struct PlayerInfo *player)
14254 {
14255   int jx = player->jx, jy = player->jy;
14256
14257   if (!player->active)
14258     return;
14259
14260 #if 0
14261   Debug("game:playing:KillPlayer",
14262         "0: killed == %d, active == %d, reanimated == %d",
14263         player->killed, player->active, player->reanimated);
14264 #endif
14265
14266   /* the following code was introduced to prevent an infinite loop when calling
14267      -> Bang()
14268      -> CheckTriggeredElementChangeExt()
14269      -> ExecuteCustomElementAction()
14270      -> KillPlayer()
14271      -> (infinitely repeating the above sequence of function calls)
14272      which occurs when killing the player while having a CE with the setting
14273      "kill player X when explosion of <player X>"; the solution using a new
14274      field "player->killed" was chosen for backwards compatibility, although
14275      clever use of the fields "player->active" etc. would probably also work */
14276 #if 1
14277   if (player->killed)
14278     return;
14279 #endif
14280
14281   player->killed = TRUE;
14282
14283   // remove accessible field at the player's position
14284   RemoveField(jx, jy);
14285
14286   // deactivate shield (else Bang()/Explode() would not work right)
14287   player->shield_normal_time_left = 0;
14288   player->shield_deadly_time_left = 0;
14289
14290 #if 0
14291   Debug("game:playing:KillPlayer",
14292         "1: killed == %d, active == %d, reanimated == %d",
14293         player->killed, player->active, player->reanimated);
14294 #endif
14295
14296   Bang(jx, jy);
14297
14298 #if 0
14299   Debug("game:playing:KillPlayer",
14300         "2: killed == %d, active == %d, reanimated == %d",
14301         player->killed, player->active, player->reanimated);
14302 #endif
14303
14304   if (player->reanimated)       // killed player may have been reanimated
14305     player->killed = player->reanimated = FALSE;
14306   else
14307     BuryPlayer(player);
14308 }
14309
14310 static void KillPlayerUnlessEnemyProtected(int x, int y)
14311 {
14312   if (!PLAYER_ENEMY_PROTECTED(x, y))
14313     KillPlayer(PLAYERINFO(x, y));
14314 }
14315
14316 static void KillPlayerUnlessExplosionProtected(int x, int y)
14317 {
14318   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14319     KillPlayer(PLAYERINFO(x, y));
14320 }
14321
14322 void BuryPlayer(struct PlayerInfo *player)
14323 {
14324   int jx = player->jx, jy = player->jy;
14325
14326   if (!player->active)
14327     return;
14328
14329   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14330
14331   RemovePlayer(player);
14332
14333   player->buried = TRUE;
14334
14335   if (game.all_players_gone)
14336     game.GameOver = TRUE;
14337 }
14338
14339 void RemovePlayer(struct PlayerInfo *player)
14340 {
14341   int jx = player->jx, jy = player->jy;
14342   int i, found = FALSE;
14343
14344   player->present = FALSE;
14345   player->active = FALSE;
14346
14347   // required for some CE actions (even if the player is not active anymore)
14348   player->MovPos = 0;
14349
14350   if (!ExplodeField[jx][jy])
14351     StorePlayer[jx][jy] = 0;
14352
14353   if (player->is_moving)
14354     TEST_DrawLevelField(player->last_jx, player->last_jy);
14355
14356   for (i = 0; i < MAX_PLAYERS; i++)
14357     if (stored_player[i].active)
14358       found = TRUE;
14359
14360   if (!found)
14361   {
14362     game.all_players_gone = TRUE;
14363     game.GameOver = TRUE;
14364   }
14365
14366   game.exit_x = game.robot_wheel_x = jx;
14367   game.exit_y = game.robot_wheel_y = jy;
14368 }
14369
14370 void ExitPlayer(struct PlayerInfo *player)
14371 {
14372   DrawPlayer(player);   // needed here only to cleanup last field
14373   RemovePlayer(player);
14374
14375   if (game.players_still_needed > 0)
14376     game.players_still_needed--;
14377 }
14378
14379 static void SetFieldForSnapping(int x, int y, int element, int direction,
14380                                 int player_index_bit)
14381 {
14382   struct ElementInfo *ei = &element_info[element];
14383   int direction_bit = MV_DIR_TO_BIT(direction);
14384   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14385   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14386                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14387
14388   Tile[x][y] = EL_ELEMENT_SNAPPING;
14389   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14390   MovDir[x][y] = direction;
14391   Store[x][y] = element;
14392   Store2[x][y] = player_index_bit;
14393
14394   ResetGfxAnimation(x, y);
14395
14396   GfxElement[x][y] = element;
14397   GfxAction[x][y] = action;
14398   GfxDir[x][y] = direction;
14399   GfxFrame[x][y] = -1;
14400 }
14401
14402 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14403                                    int player_index_bit)
14404 {
14405   TestIfElementTouchesCustomElement(x, y);      // for empty space
14406
14407   if (level.finish_dig_collect)
14408   {
14409     int dig_side = MV_DIR_OPPOSITE(direction);
14410     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14411                         CE_PLAYER_COLLECTS_X);
14412
14413     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14414                                         player_index_bit, dig_side);
14415     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14416                                         player_index_bit, dig_side);
14417   }
14418 }
14419
14420 /*
14421   =============================================================================
14422   checkDiagonalPushing()
14423   -----------------------------------------------------------------------------
14424   check if diagonal input device direction results in pushing of object
14425   (by checking if the alternative direction is walkable, diggable, ...)
14426   =============================================================================
14427 */
14428
14429 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14430                                     int x, int y, int real_dx, int real_dy)
14431 {
14432   int jx, jy, dx, dy, xx, yy;
14433
14434   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14435     return TRUE;
14436
14437   // diagonal direction: check alternative direction
14438   jx = player->jx;
14439   jy = player->jy;
14440   dx = x - jx;
14441   dy = y - jy;
14442   xx = jx + (dx == 0 ? real_dx : 0);
14443   yy = jy + (dy == 0 ? real_dy : 0);
14444
14445   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14446 }
14447
14448 /*
14449   =============================================================================
14450   DigField()
14451   -----------------------------------------------------------------------------
14452   x, y:                 field next to player (non-diagonal) to try to dig to
14453   real_dx, real_dy:     direction as read from input device (can be diagonal)
14454   =============================================================================
14455 */
14456
14457 static int DigField(struct PlayerInfo *player,
14458                     int oldx, int oldy, int x, int y,
14459                     int real_dx, int real_dy, int mode)
14460 {
14461   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14462   boolean player_was_pushing = player->is_pushing;
14463   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14464   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14465   int jx = oldx, jy = oldy;
14466   int dx = x - jx, dy = y - jy;
14467   int nextx = x + dx, nexty = y + dy;
14468   int move_direction = (dx == -1 ? MV_LEFT  :
14469                         dx == +1 ? MV_RIGHT :
14470                         dy == -1 ? MV_UP    :
14471                         dy == +1 ? MV_DOWN  : MV_NONE);
14472   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14473   int dig_side = MV_DIR_OPPOSITE(move_direction);
14474   int old_element = Tile[jx][jy];
14475   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14476   int collect_count;
14477
14478   if (is_player)                // function can also be called by EL_PENGUIN
14479   {
14480     if (player->MovPos == 0)
14481     {
14482       player->is_digging = FALSE;
14483       player->is_collecting = FALSE;
14484     }
14485
14486     if (player->MovPos == 0)    // last pushing move finished
14487       player->is_pushing = FALSE;
14488
14489     if (mode == DF_NO_PUSH)     // player just stopped pushing
14490     {
14491       player->is_switching = FALSE;
14492       player->push_delay = -1;
14493
14494       return MP_NO_ACTION;
14495     }
14496   }
14497   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14498     old_element = Back[jx][jy];
14499
14500   // in case of element dropped at player position, check background
14501   else if (Back[jx][jy] != EL_EMPTY &&
14502            game.engine_version >= VERSION_IDENT(2,2,0,0))
14503     old_element = Back[jx][jy];
14504
14505   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14506     return MP_NO_ACTION;        // field has no opening in this direction
14507
14508   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14509     return MP_NO_ACTION;        // field has no opening in this direction
14510
14511   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14512   {
14513     SplashAcid(x, y);
14514
14515     Tile[jx][jy] = player->artwork_element;
14516     InitMovingField(jx, jy, MV_DOWN);
14517     Store[jx][jy] = EL_ACID;
14518     ContinueMoving(jx, jy);
14519     BuryPlayer(player);
14520
14521     return MP_DONT_RUN_INTO;
14522   }
14523
14524   if (player_can_move && DONT_RUN_INTO(element))
14525   {
14526     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14527
14528     return MP_DONT_RUN_INTO;
14529   }
14530
14531   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14532     return MP_NO_ACTION;
14533
14534   collect_count = element_info[element].collect_count_initial;
14535
14536   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14537     return MP_NO_ACTION;
14538
14539   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14540     player_can_move = player_can_move_or_snap;
14541
14542   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14543       game.engine_version >= VERSION_IDENT(2,2,0,0))
14544   {
14545     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14546                                player->index_bit, dig_side);
14547     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14548                                         player->index_bit, dig_side);
14549
14550     if (element == EL_DC_LANDMINE)
14551       Bang(x, y);
14552
14553     if (Tile[x][y] != element)          // field changed by snapping
14554       return MP_ACTION;
14555
14556     return MP_NO_ACTION;
14557   }
14558
14559   if (player->gravity && is_player && !player->is_auto_moving &&
14560       canFallDown(player) && move_direction != MV_DOWN &&
14561       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14562     return MP_NO_ACTION;        // player cannot walk here due to gravity
14563
14564   if (player_can_move &&
14565       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14566   {
14567     int sound_element = SND_ELEMENT(element);
14568     int sound_action = ACTION_WALKING;
14569
14570     if (IS_RND_GATE(element))
14571     {
14572       if (!player->key[RND_GATE_NR(element)])
14573         return MP_NO_ACTION;
14574     }
14575     else if (IS_RND_GATE_GRAY(element))
14576     {
14577       if (!player->key[RND_GATE_GRAY_NR(element)])
14578         return MP_NO_ACTION;
14579     }
14580     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14581     {
14582       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14583         return MP_NO_ACTION;
14584     }
14585     else if (element == EL_EXIT_OPEN ||
14586              element == EL_EM_EXIT_OPEN ||
14587              element == EL_EM_EXIT_OPENING ||
14588              element == EL_STEEL_EXIT_OPEN ||
14589              element == EL_EM_STEEL_EXIT_OPEN ||
14590              element == EL_EM_STEEL_EXIT_OPENING ||
14591              element == EL_SP_EXIT_OPEN ||
14592              element == EL_SP_EXIT_OPENING)
14593     {
14594       sound_action = ACTION_PASSING;    // player is passing exit
14595     }
14596     else if (element == EL_EMPTY)
14597     {
14598       sound_action = ACTION_MOVING;             // nothing to walk on
14599     }
14600
14601     // play sound from background or player, whatever is available
14602     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14603       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14604     else
14605       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14606   }
14607   else if (player_can_move &&
14608            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14609   {
14610     if (!ACCESS_FROM(element, opposite_direction))
14611       return MP_NO_ACTION;      // field not accessible from this direction
14612
14613     if (CAN_MOVE(element))      // only fixed elements can be passed!
14614       return MP_NO_ACTION;
14615
14616     if (IS_EM_GATE(element))
14617     {
14618       if (!player->key[EM_GATE_NR(element)])
14619         return MP_NO_ACTION;
14620     }
14621     else if (IS_EM_GATE_GRAY(element))
14622     {
14623       if (!player->key[EM_GATE_GRAY_NR(element)])
14624         return MP_NO_ACTION;
14625     }
14626     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14627     {
14628       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14629         return MP_NO_ACTION;
14630     }
14631     else if (IS_EMC_GATE(element))
14632     {
14633       if (!player->key[EMC_GATE_NR(element)])
14634         return MP_NO_ACTION;
14635     }
14636     else if (IS_EMC_GATE_GRAY(element))
14637     {
14638       if (!player->key[EMC_GATE_GRAY_NR(element)])
14639         return MP_NO_ACTION;
14640     }
14641     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14642     {
14643       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14644         return MP_NO_ACTION;
14645     }
14646     else if (element == EL_DC_GATE_WHITE ||
14647              element == EL_DC_GATE_WHITE_GRAY ||
14648              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14649     {
14650       if (player->num_white_keys == 0)
14651         return MP_NO_ACTION;
14652
14653       player->num_white_keys--;
14654     }
14655     else if (IS_SP_PORT(element))
14656     {
14657       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14658           element == EL_SP_GRAVITY_PORT_RIGHT ||
14659           element == EL_SP_GRAVITY_PORT_UP ||
14660           element == EL_SP_GRAVITY_PORT_DOWN)
14661         player->gravity = !player->gravity;
14662       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14663                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14664                element == EL_SP_GRAVITY_ON_PORT_UP ||
14665                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14666         player->gravity = TRUE;
14667       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14668                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14669                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14670                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14671         player->gravity = FALSE;
14672     }
14673
14674     // automatically move to the next field with double speed
14675     player->programmed_action = move_direction;
14676
14677     if (player->move_delay_reset_counter == 0)
14678     {
14679       player->move_delay_reset_counter = 2;     // two double speed steps
14680
14681       DOUBLE_PLAYER_SPEED(player);
14682     }
14683
14684     PlayLevelSoundAction(x, y, ACTION_PASSING);
14685   }
14686   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14687   {
14688     RemoveField(x, y);
14689
14690     if (mode != DF_SNAP)
14691     {
14692       GfxElement[x][y] = GFX_ELEMENT(element);
14693       player->is_digging = TRUE;
14694     }
14695
14696     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14697
14698     // use old behaviour for old levels (digging)
14699     if (!level.finish_dig_collect)
14700     {
14701       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14702                                           player->index_bit, dig_side);
14703
14704       // if digging triggered player relocation, finish digging tile
14705       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14706         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14707     }
14708
14709     if (mode == DF_SNAP)
14710     {
14711       if (level.block_snap_field)
14712         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14713       else
14714         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14715
14716       // use old behaviour for old levels (snapping)
14717       if (!level.finish_dig_collect)
14718         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14719                                             player->index_bit, dig_side);
14720     }
14721   }
14722   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14723   {
14724     RemoveField(x, y);
14725
14726     if (is_player && mode != DF_SNAP)
14727     {
14728       GfxElement[x][y] = element;
14729       player->is_collecting = TRUE;
14730     }
14731
14732     if (element == EL_SPEED_PILL)
14733     {
14734       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14735     }
14736     else if (element == EL_EXTRA_TIME && level.time > 0)
14737     {
14738       TimeLeft += level.extra_time;
14739
14740       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14741
14742       DisplayGameControlValues();
14743     }
14744     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14745     {
14746       int shield_time = (element == EL_SHIELD_DEADLY ?
14747                          level.shield_deadly_time :
14748                          level.shield_normal_time);
14749
14750       player->shield_normal_time_left += shield_time;
14751       if (element == EL_SHIELD_DEADLY)
14752         player->shield_deadly_time_left += shield_time;
14753     }
14754     else if (element == EL_DYNAMITE ||
14755              element == EL_EM_DYNAMITE ||
14756              element == EL_SP_DISK_RED)
14757     {
14758       if (player->inventory_size < MAX_INVENTORY_SIZE)
14759         player->inventory_element[player->inventory_size++] = element;
14760
14761       DrawGameDoorValues();
14762     }
14763     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14764     {
14765       player->dynabomb_count++;
14766       player->dynabombs_left++;
14767     }
14768     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14769     {
14770       player->dynabomb_size++;
14771     }
14772     else if (element == EL_DYNABOMB_INCREASE_POWER)
14773     {
14774       player->dynabomb_xl = TRUE;
14775     }
14776     else if (IS_KEY(element))
14777     {
14778       player->key[KEY_NR(element)] = TRUE;
14779
14780       DrawGameDoorValues();
14781     }
14782     else if (element == EL_DC_KEY_WHITE)
14783     {
14784       player->num_white_keys++;
14785
14786       // display white keys?
14787       // DrawGameDoorValues();
14788     }
14789     else if (IS_ENVELOPE(element))
14790     {
14791       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14792
14793       if (!wait_for_snapping)
14794         player->show_envelope = element;
14795     }
14796     else if (element == EL_EMC_LENSES)
14797     {
14798       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14799
14800       RedrawAllInvisibleElementsForLenses();
14801     }
14802     else if (element == EL_EMC_MAGNIFIER)
14803     {
14804       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14805
14806       RedrawAllInvisibleElementsForMagnifier();
14807     }
14808     else if (IS_DROPPABLE(element) ||
14809              IS_THROWABLE(element))     // can be collected and dropped
14810     {
14811       int i;
14812
14813       if (collect_count == 0)
14814         player->inventory_infinite_element = element;
14815       else
14816         for (i = 0; i < collect_count; i++)
14817           if (player->inventory_size < MAX_INVENTORY_SIZE)
14818             player->inventory_element[player->inventory_size++] = element;
14819
14820       DrawGameDoorValues();
14821     }
14822     else if (collect_count > 0)
14823     {
14824       game.gems_still_needed -= collect_count;
14825       if (game.gems_still_needed < 0)
14826         game.gems_still_needed = 0;
14827
14828       game.snapshot.collected_item = TRUE;
14829
14830       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14831
14832       DisplayGameControlValues();
14833     }
14834
14835     RaiseScoreElement(element);
14836     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14837
14838     // use old behaviour for old levels (collecting)
14839     if (!level.finish_dig_collect && is_player)
14840     {
14841       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14842                                           player->index_bit, dig_side);
14843
14844       // if collecting triggered player relocation, finish collecting tile
14845       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14846         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14847     }
14848
14849     if (mode == DF_SNAP)
14850     {
14851       if (level.block_snap_field)
14852         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14853       else
14854         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14855
14856       // use old behaviour for old levels (snapping)
14857       if (!level.finish_dig_collect)
14858         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14859                                             player->index_bit, dig_side);
14860     }
14861   }
14862   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14863   {
14864     if (mode == DF_SNAP && element != EL_BD_ROCK)
14865       return MP_NO_ACTION;
14866
14867     if (CAN_FALL(element) && dy)
14868       return MP_NO_ACTION;
14869
14870     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14871         !(element == EL_SPRING && level.use_spring_bug))
14872       return MP_NO_ACTION;
14873
14874     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14875         ((move_direction & MV_VERTICAL &&
14876           ((element_info[element].move_pattern & MV_LEFT &&
14877             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14878            (element_info[element].move_pattern & MV_RIGHT &&
14879             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14880          (move_direction & MV_HORIZONTAL &&
14881           ((element_info[element].move_pattern & MV_UP &&
14882             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14883            (element_info[element].move_pattern & MV_DOWN &&
14884             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14885       return MP_NO_ACTION;
14886
14887     // do not push elements already moving away faster than player
14888     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14889         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14890       return MP_NO_ACTION;
14891
14892     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14893     {
14894       if (player->push_delay_value == -1 || !player_was_pushing)
14895         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14896     }
14897     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14898     {
14899       if (player->push_delay_value == -1)
14900         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14901     }
14902     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14903     {
14904       if (!player->is_pushing)
14905         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14906     }
14907
14908     player->is_pushing = TRUE;
14909     player->is_active = TRUE;
14910
14911     if (!(IN_LEV_FIELD(nextx, nexty) &&
14912           (IS_FREE(nextx, nexty) ||
14913            (IS_SB_ELEMENT(element) &&
14914             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14915            (IS_CUSTOM_ELEMENT(element) &&
14916             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14917       return MP_NO_ACTION;
14918
14919     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14920       return MP_NO_ACTION;
14921
14922     if (player->push_delay == -1)       // new pushing; restart delay
14923       player->push_delay = 0;
14924
14925     if (player->push_delay < player->push_delay_value &&
14926         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14927         element != EL_SPRING && element != EL_BALLOON)
14928     {
14929       // make sure that there is no move delay before next try to push
14930       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14931         player->move_delay = 0;
14932
14933       return MP_NO_ACTION;
14934     }
14935
14936     if (IS_CUSTOM_ELEMENT(element) &&
14937         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14938     {
14939       if (!DigFieldByCE(nextx, nexty, element))
14940         return MP_NO_ACTION;
14941     }
14942
14943     if (IS_SB_ELEMENT(element))
14944     {
14945       boolean sokoban_task_solved = FALSE;
14946
14947       if (element == EL_SOKOBAN_FIELD_FULL)
14948       {
14949         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14950
14951         IncrementSokobanFieldsNeeded();
14952         IncrementSokobanObjectsNeeded();
14953       }
14954
14955       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14956       {
14957         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14958
14959         DecrementSokobanFieldsNeeded();
14960         DecrementSokobanObjectsNeeded();
14961
14962         // sokoban object was pushed from empty field to sokoban field
14963         if (Back[x][y] == EL_EMPTY)
14964           sokoban_task_solved = TRUE;
14965       }
14966
14967       Tile[x][y] = EL_SOKOBAN_OBJECT;
14968
14969       if (Back[x][y] == Back[nextx][nexty])
14970         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14971       else if (Back[x][y] != 0)
14972         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14973                                     ACTION_EMPTYING);
14974       else
14975         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14976                                     ACTION_FILLING);
14977
14978       if (sokoban_task_solved &&
14979           game.sokoban_fields_still_needed == 0 &&
14980           game.sokoban_objects_still_needed == 0 &&
14981           level.auto_exit_sokoban)
14982       {
14983         game.players_still_needed = 0;
14984
14985         LevelSolved();
14986
14987         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14988       }
14989     }
14990     else
14991       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14992
14993     InitMovingField(x, y, move_direction);
14994     GfxAction[x][y] = ACTION_PUSHING;
14995
14996     if (mode == DF_SNAP)
14997       ContinueMoving(x, y);
14998     else
14999       MovPos[x][y] = (dx != 0 ? dx : dy);
15000
15001     Pushed[x][y] = TRUE;
15002     Pushed[nextx][nexty] = TRUE;
15003
15004     if (game.engine_version < VERSION_IDENT(2,2,0,7))
15005       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
15006     else
15007       player->push_delay_value = -1;    // get new value later
15008
15009     // check for element change _after_ element has been pushed
15010     if (game.use_change_when_pushing_bug)
15011     {
15012       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
15013                                  player->index_bit, dig_side);
15014       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
15015                                           player->index_bit, dig_side);
15016     }
15017   }
15018   else if (IS_SWITCHABLE(element))
15019   {
15020     if (PLAYER_SWITCHING(player, x, y))
15021     {
15022       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15023                                           player->index_bit, dig_side);
15024
15025       return MP_ACTION;
15026     }
15027
15028     player->is_switching = TRUE;
15029     player->switch_x = x;
15030     player->switch_y = y;
15031
15032     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15033
15034     if (element == EL_ROBOT_WHEEL)
15035     {
15036       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
15037
15038       game.robot_wheel_x = x;
15039       game.robot_wheel_y = y;
15040       game.robot_wheel_active = TRUE;
15041
15042       TEST_DrawLevelField(x, y);
15043     }
15044     else if (element == EL_SP_TERMINAL)
15045     {
15046       int xx, yy;
15047
15048       SCAN_PLAYFIELD(xx, yy)
15049       {
15050         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15051         {
15052           Bang(xx, yy);
15053         }
15054         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15055         {
15056           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15057
15058           ResetGfxAnimation(xx, yy);
15059           TEST_DrawLevelField(xx, yy);
15060         }
15061       }
15062     }
15063     else if (IS_BELT_SWITCH(element))
15064     {
15065       ToggleBeltSwitch(x, y);
15066     }
15067     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15068              element == EL_SWITCHGATE_SWITCH_DOWN ||
15069              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15070              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15071     {
15072       ToggleSwitchgateSwitch();
15073     }
15074     else if (element == EL_LIGHT_SWITCH ||
15075              element == EL_LIGHT_SWITCH_ACTIVE)
15076     {
15077       ToggleLightSwitch(x, y);
15078     }
15079     else if (element == EL_TIMEGATE_SWITCH ||
15080              element == EL_DC_TIMEGATE_SWITCH)
15081     {
15082       ActivateTimegateSwitch(x, y);
15083     }
15084     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15085              element == EL_BALLOON_SWITCH_RIGHT ||
15086              element == EL_BALLOON_SWITCH_UP    ||
15087              element == EL_BALLOON_SWITCH_DOWN  ||
15088              element == EL_BALLOON_SWITCH_NONE  ||
15089              element == EL_BALLOON_SWITCH_ANY)
15090     {
15091       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15092                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15093                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15094                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15095                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15096                              move_direction);
15097     }
15098     else if (element == EL_LAMP)
15099     {
15100       Tile[x][y] = EL_LAMP_ACTIVE;
15101       game.lights_still_needed--;
15102
15103       ResetGfxAnimation(x, y);
15104       TEST_DrawLevelField(x, y);
15105     }
15106     else if (element == EL_TIME_ORB_FULL)
15107     {
15108       Tile[x][y] = EL_TIME_ORB_EMPTY;
15109
15110       if (level.time > 0 || level.use_time_orb_bug)
15111       {
15112         TimeLeft += level.time_orb_time;
15113         game.no_level_time_limit = FALSE;
15114
15115         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15116
15117         DisplayGameControlValues();
15118       }
15119
15120       ResetGfxAnimation(x, y);
15121       TEST_DrawLevelField(x, y);
15122     }
15123     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15124              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15125     {
15126       int xx, yy;
15127
15128       game.ball_active = !game.ball_active;
15129
15130       SCAN_PLAYFIELD(xx, yy)
15131       {
15132         int e = Tile[xx][yy];
15133
15134         if (game.ball_active)
15135         {
15136           if (e == EL_EMC_MAGIC_BALL)
15137             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15138           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15139             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15140         }
15141         else
15142         {
15143           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15144             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15145           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15146             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15147         }
15148       }
15149     }
15150
15151     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15152                                         player->index_bit, dig_side);
15153
15154     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15155                                         player->index_bit, dig_side);
15156
15157     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15158                                         player->index_bit, dig_side);
15159
15160     return MP_ACTION;
15161   }
15162   else
15163   {
15164     if (!PLAYER_SWITCHING(player, x, y))
15165     {
15166       player->is_switching = TRUE;
15167       player->switch_x = x;
15168       player->switch_y = y;
15169
15170       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15171                                  player->index_bit, dig_side);
15172       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15173                                           player->index_bit, dig_side);
15174
15175       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15176                                  player->index_bit, dig_side);
15177       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15178                                           player->index_bit, dig_side);
15179     }
15180
15181     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15182                                player->index_bit, dig_side);
15183     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15184                                         player->index_bit, dig_side);
15185
15186     return MP_NO_ACTION;
15187   }
15188
15189   player->push_delay = -1;
15190
15191   if (is_player)                // function can also be called by EL_PENGUIN
15192   {
15193     if (Tile[x][y] != element)          // really digged/collected something
15194     {
15195       player->is_collecting = !player->is_digging;
15196       player->is_active = TRUE;
15197
15198       player->last_removed_element = element;
15199     }
15200   }
15201
15202   return MP_MOVING;
15203 }
15204
15205 static boolean DigFieldByCE(int x, int y, int digging_element)
15206 {
15207   int element = Tile[x][y];
15208
15209   if (!IS_FREE(x, y))
15210   {
15211     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15212                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15213                   ACTION_BREAKING);
15214
15215     // no element can dig solid indestructible elements
15216     if (IS_INDESTRUCTIBLE(element) &&
15217         !IS_DIGGABLE(element) &&
15218         !IS_COLLECTIBLE(element))
15219       return FALSE;
15220
15221     if (AmoebaNr[x][y] &&
15222         (element == EL_AMOEBA_FULL ||
15223          element == EL_BD_AMOEBA ||
15224          element == EL_AMOEBA_GROWING))
15225     {
15226       AmoebaCnt[AmoebaNr[x][y]]--;
15227       AmoebaCnt2[AmoebaNr[x][y]]--;
15228     }
15229
15230     if (IS_MOVING(x, y))
15231       RemoveMovingField(x, y);
15232     else
15233     {
15234       RemoveField(x, y);
15235       TEST_DrawLevelField(x, y);
15236     }
15237
15238     // if digged element was about to explode, prevent the explosion
15239     ExplodeField[x][y] = EX_TYPE_NONE;
15240
15241     PlayLevelSoundAction(x, y, action);
15242   }
15243
15244   Store[x][y] = EL_EMPTY;
15245
15246   // this makes it possible to leave the removed element again
15247   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15248     Store[x][y] = element;
15249
15250   return TRUE;
15251 }
15252
15253 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15254 {
15255   int jx = player->jx, jy = player->jy;
15256   int x = jx + dx, y = jy + dy;
15257   int snap_direction = (dx == -1 ? MV_LEFT  :
15258                         dx == +1 ? MV_RIGHT :
15259                         dy == -1 ? MV_UP    :
15260                         dy == +1 ? MV_DOWN  : MV_NONE);
15261   boolean can_continue_snapping = (level.continuous_snapping &&
15262                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15263
15264   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15265     return FALSE;
15266
15267   if (!player->active || !IN_LEV_FIELD(x, y))
15268     return FALSE;
15269
15270   if (dx && dy)
15271     return FALSE;
15272
15273   if (!dx && !dy)
15274   {
15275     if (player->MovPos == 0)
15276       player->is_pushing = FALSE;
15277
15278     player->is_snapping = FALSE;
15279
15280     if (player->MovPos == 0)
15281     {
15282       player->is_moving = FALSE;
15283       player->is_digging = FALSE;
15284       player->is_collecting = FALSE;
15285     }
15286
15287     return FALSE;
15288   }
15289
15290   // prevent snapping with already pressed snap key when not allowed
15291   if (player->is_snapping && !can_continue_snapping)
15292     return FALSE;
15293
15294   player->MovDir = snap_direction;
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   player->is_dropping = FALSE;
15304   player->is_dropping_pressed = FALSE;
15305   player->drop_pressed_delay = 0;
15306
15307   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15308     return FALSE;
15309
15310   player->is_snapping = TRUE;
15311   player->is_active = TRUE;
15312
15313   if (player->MovPos == 0)
15314   {
15315     player->is_moving = FALSE;
15316     player->is_digging = FALSE;
15317     player->is_collecting = FALSE;
15318   }
15319
15320   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15321     TEST_DrawLevelField(player->last_jx, player->last_jy);
15322
15323   TEST_DrawLevelField(x, y);
15324
15325   return TRUE;
15326 }
15327
15328 static boolean DropElement(struct PlayerInfo *player)
15329 {
15330   int old_element, new_element;
15331   int dropx = player->jx, dropy = player->jy;
15332   int drop_direction = player->MovDir;
15333   int drop_side = drop_direction;
15334   int drop_element = get_next_dropped_element(player);
15335
15336   /* do not drop an element on top of another element; when holding drop key
15337      pressed without moving, dropped element must move away before the next
15338      element can be dropped (this is especially important if the next element
15339      is dynamite, which can be placed on background for historical reasons) */
15340   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15341     return MP_ACTION;
15342
15343   if (IS_THROWABLE(drop_element))
15344   {
15345     dropx += GET_DX_FROM_DIR(drop_direction);
15346     dropy += GET_DY_FROM_DIR(drop_direction);
15347
15348     if (!IN_LEV_FIELD(dropx, dropy))
15349       return FALSE;
15350   }
15351
15352   old_element = Tile[dropx][dropy];     // old element at dropping position
15353   new_element = drop_element;           // default: no change when dropping
15354
15355   // check if player is active, not moving and ready to drop
15356   if (!player->active || player->MovPos || player->drop_delay > 0)
15357     return FALSE;
15358
15359   // check if player has anything that can be dropped
15360   if (new_element == EL_UNDEFINED)
15361     return FALSE;
15362
15363   // only set if player has anything that can be dropped
15364   player->is_dropping_pressed = TRUE;
15365
15366   // check if drop key was pressed long enough for EM style dynamite
15367   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15368     return FALSE;
15369
15370   // check if anything can be dropped at the current position
15371   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15372     return FALSE;
15373
15374   // collected custom elements can only be dropped on empty fields
15375   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15376     return FALSE;
15377
15378   if (old_element != EL_EMPTY)
15379     Back[dropx][dropy] = old_element;   // store old element on this field
15380
15381   ResetGfxAnimation(dropx, dropy);
15382   ResetRandomAnimationValue(dropx, dropy);
15383
15384   if (player->inventory_size > 0 ||
15385       player->inventory_infinite_element != EL_UNDEFINED)
15386   {
15387     if (player->inventory_size > 0)
15388     {
15389       player->inventory_size--;
15390
15391       DrawGameDoorValues();
15392
15393       if (new_element == EL_DYNAMITE)
15394         new_element = EL_DYNAMITE_ACTIVE;
15395       else if (new_element == EL_EM_DYNAMITE)
15396         new_element = EL_EM_DYNAMITE_ACTIVE;
15397       else if (new_element == EL_SP_DISK_RED)
15398         new_element = EL_SP_DISK_RED_ACTIVE;
15399     }
15400
15401     Tile[dropx][dropy] = new_element;
15402
15403     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15404       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15405                           el2img(Tile[dropx][dropy]), 0);
15406
15407     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15408
15409     // needed if previous element just changed to "empty" in the last frame
15410     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15411
15412     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15413                                player->index_bit, drop_side);
15414     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15415                                         CE_PLAYER_DROPS_X,
15416                                         player->index_bit, drop_side);
15417
15418     TestIfElementTouchesCustomElement(dropx, dropy);
15419   }
15420   else          // player is dropping a dyna bomb
15421   {
15422     player->dynabombs_left--;
15423
15424     Tile[dropx][dropy] = new_element;
15425
15426     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15427       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15428                           el2img(Tile[dropx][dropy]), 0);
15429
15430     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15431   }
15432
15433   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15434     InitField_WithBug1(dropx, dropy, FALSE);
15435
15436   new_element = Tile[dropx][dropy];     // element might have changed
15437
15438   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15439       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15440   {
15441     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15442       MovDir[dropx][dropy] = drop_direction;
15443
15444     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15445
15446     // do not cause impact style collision by dropping elements that can fall
15447     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15448   }
15449
15450   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15451   player->is_dropping = TRUE;
15452
15453   player->drop_pressed_delay = 0;
15454   player->is_dropping_pressed = FALSE;
15455
15456   player->drop_x = dropx;
15457   player->drop_y = dropy;
15458
15459   return TRUE;
15460 }
15461
15462 // ----------------------------------------------------------------------------
15463 // game sound playing functions
15464 // ----------------------------------------------------------------------------
15465
15466 static int *loop_sound_frame = NULL;
15467 static int *loop_sound_volume = NULL;
15468
15469 void InitPlayLevelSound(void)
15470 {
15471   int num_sounds = getSoundListSize();
15472
15473   checked_free(loop_sound_frame);
15474   checked_free(loop_sound_volume);
15475
15476   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15477   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15478 }
15479
15480 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15481 {
15482   int sx = SCREENX(x), sy = SCREENY(y);
15483   int volume, stereo_position;
15484   int max_distance = 8;
15485   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15486
15487   if ((!setup.sound_simple && !is_loop_sound) ||
15488       (!setup.sound_loops && is_loop_sound))
15489     return;
15490
15491   if (!IN_LEV_FIELD(x, y) ||
15492       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15493       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15494     return;
15495
15496   volume = SOUND_MAX_VOLUME;
15497
15498   if (!IN_SCR_FIELD(sx, sy))
15499   {
15500     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15501     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15502
15503     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15504   }
15505
15506   stereo_position = (SOUND_MAX_LEFT +
15507                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15508                      (SCR_FIELDX + 2 * max_distance));
15509
15510   if (is_loop_sound)
15511   {
15512     /* This assures that quieter loop sounds do not overwrite louder ones,
15513        while restarting sound volume comparison with each new game frame. */
15514
15515     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15516       return;
15517
15518     loop_sound_volume[nr] = volume;
15519     loop_sound_frame[nr] = FrameCounter;
15520   }
15521
15522   PlaySoundExt(nr, volume, stereo_position, type);
15523 }
15524
15525 static void PlayLevelSound(int x, int y, int nr)
15526 {
15527   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15528 }
15529
15530 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15531 {
15532   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15533                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15534                  y < LEVELY(BY1) ? LEVELY(BY1) :
15535                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15536                  sound_action);
15537 }
15538
15539 static void PlayLevelSoundAction(int x, int y, int action)
15540 {
15541   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15542 }
15543
15544 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15545 {
15546   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15547
15548   if (sound_effect != SND_UNDEFINED)
15549     PlayLevelSound(x, y, sound_effect);
15550 }
15551
15552 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15553                                               int action)
15554 {
15555   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15556
15557   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15558     PlayLevelSound(x, y, sound_effect);
15559 }
15560
15561 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15562 {
15563   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15564
15565   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15566     PlayLevelSound(x, y, sound_effect);
15567 }
15568
15569 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15570 {
15571   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15572
15573   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15574     StopSound(sound_effect);
15575 }
15576
15577 static int getLevelMusicNr(void)
15578 {
15579   int level_pos = level_nr - leveldir_current->first_level;
15580
15581   if (levelset.music[level_nr] != MUS_UNDEFINED)
15582     return levelset.music[level_nr];            // from config file
15583   else
15584     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15585 }
15586
15587 static void FadeLevelSounds(void)
15588 {
15589   FadeSounds();
15590 }
15591
15592 static void FadeLevelMusic(void)
15593 {
15594   int music_nr = getLevelMusicNr();
15595   char *curr_music = getCurrentlyPlayingMusicFilename();
15596   char *next_music = getMusicInfoEntryFilename(music_nr);
15597
15598   if (!strEqual(curr_music, next_music))
15599     FadeMusic();
15600 }
15601
15602 void FadeLevelSoundsAndMusic(void)
15603 {
15604   FadeLevelSounds();
15605   FadeLevelMusic();
15606 }
15607
15608 static void PlayLevelMusic(void)
15609 {
15610   int music_nr = getLevelMusicNr();
15611   char *curr_music = getCurrentlyPlayingMusicFilename();
15612   char *next_music = getMusicInfoEntryFilename(music_nr);
15613
15614   if (!strEqual(curr_music, next_music))
15615     PlayMusicLoop(music_nr);
15616 }
15617
15618 static int getSoundAction_BD(int sample)
15619 {
15620   switch (sample)
15621   {
15622     case GD_S_STONE_PUSHING:
15623     case GD_S_MEGA_STONE_PUSHING:
15624     case GD_S_FLYING_STONE_PUSHING:
15625     case GD_S_WAITING_STONE_PUSHING:
15626     case GD_S_CHASING_STONE_PUSHING:
15627     case GD_S_NUT_PUSHING:
15628     case GD_S_NITRO_PACK_PUSHING:
15629     case GD_S_BLADDER_PUSHING:
15630     case GD_S_BOX_PUSHING:
15631       return ACTION_PUSHING;
15632
15633     case GD_S_STONE_FALLING:
15634     case GD_S_MEGA_STONE_FALLING:
15635     case GD_S_FLYING_STONE_FALLING:
15636     case GD_S_NUT_FALLING:
15637     case GD_S_DIRT_BALL_FALLING:
15638     case GD_S_DIRT_LOOSE_FALLING:
15639     case GD_S_NITRO_PACK_FALLING:
15640     case GD_S_FALLING_WALL_FALLING:
15641       return ACTION_FALLING;
15642
15643     case GD_S_STONE_IMPACT:
15644     case GD_S_MEGA_STONE_IMPACT:
15645     case GD_S_FLYING_STONE_IMPACT:
15646     case GD_S_NUT_IMPACT:
15647     case GD_S_DIRT_BALL_IMPACT:
15648     case GD_S_DIRT_LOOSE_IMPACT:
15649     case GD_S_NITRO_PACK_IMPACT:
15650     case GD_S_FALLING_WALL_IMPACT:
15651       return ACTION_IMPACT;
15652
15653     case GD_S_NUT_CRACKING:
15654       return ACTION_BREAKING;
15655
15656     case GD_S_EXPANDING_WALL:
15657     case GD_S_WALL_REAPPEARING:
15658     case GD_S_SLIME:
15659     case GD_S_LAVA:
15660     case GD_S_ACID_SPREADING:
15661       return ACTION_GROWING;
15662
15663     case GD_S_DIAMOND_COLLECTING:
15664     case GD_S_FLYING_DIAMOND_COLLECTING:
15665     case GD_S_SKELETON_COLLECTING:
15666     case GD_S_PNEUMATIC_COLLECTING:
15667     case GD_S_BOMB_COLLECTING:
15668     case GD_S_CLOCK_COLLECTING:
15669     case GD_S_SWEET_COLLECTING:
15670     case GD_S_KEY_COLLECTING:
15671     case GD_S_DIAMOND_KEY_COLLECTING:
15672       return ACTION_COLLECTING;
15673
15674     case GD_S_BOMB_PLACING:
15675     case GD_S_REPLICATOR:
15676       return ACTION_DROPPING;
15677
15678     case GD_S_BLADDER_MOVING:
15679       return ACTION_MOVING;
15680
15681     case GD_S_BLADDER_SPENDER:
15682     case GD_S_BLADDER_CONVERTING:
15683     case GD_S_GRAVITY_CHANGING:
15684       return ACTION_CHANGING;
15685
15686     case GD_S_BITER_EATING:
15687       return ACTION_EATING;
15688
15689     case GD_S_DOOR_OPENING:
15690     case GD_S_CRACKING:
15691       return ACTION_OPENING;
15692
15693     case GD_S_DIRT_WALKING:
15694       return ACTION_DIGGING;
15695
15696     case GD_S_EMPTY_WALKING:
15697       return ACTION_WALKING;
15698
15699     case GD_S_SWITCH_BITER:
15700     case GD_S_SWITCH_CREATURES:
15701     case GD_S_SWITCH_GRAVITY:
15702     case GD_S_SWITCH_EXPANDING:
15703     case GD_S_SWITCH_CONVEYOR:
15704     case GD_S_SWITCH_REPLICATOR:
15705     case GD_S_STIRRING:
15706       return ACTION_ACTIVATING;
15707
15708     case GD_S_TELEPORTER:
15709       return ACTION_PASSING;
15710
15711     case GD_S_EXPLODING:
15712     case GD_S_BOMB_EXPLODING:
15713     case GD_S_GHOST_EXPLODING:
15714     case GD_S_VOODOO_EXPLODING:
15715     case GD_S_NITRO_PACK_EXPLODING:
15716       return ACTION_EXPLODING;
15717
15718     case GD_S_COVERING:
15719     case GD_S_AMOEBA:
15720     case GD_S_MAGIC_WALL:
15721     case GD_S_PNEUMATIC_HAMMER:
15722     case GD_S_WATER:
15723       return ACTION_ACTIVE;
15724
15725     case GD_S_DIAMOND_FALLING_RANDOM:
15726     case GD_S_DIAMOND_FALLING_1:
15727     case GD_S_DIAMOND_FALLING_2:
15728     case GD_S_DIAMOND_FALLING_3:
15729     case GD_S_DIAMOND_FALLING_4:
15730     case GD_S_DIAMOND_FALLING_5:
15731     case GD_S_DIAMOND_FALLING_6:
15732     case GD_S_DIAMOND_FALLING_7:
15733     case GD_S_DIAMOND_FALLING_8:
15734     case GD_S_DIAMOND_IMPACT_RANDOM:
15735     case GD_S_DIAMOND_IMPACT_1:
15736     case GD_S_DIAMOND_IMPACT_2:
15737     case GD_S_DIAMOND_IMPACT_3:
15738     case GD_S_DIAMOND_IMPACT_4:
15739     case GD_S_DIAMOND_IMPACT_5:
15740     case GD_S_DIAMOND_IMPACT_6:
15741     case GD_S_DIAMOND_IMPACT_7:
15742     case GD_S_DIAMOND_IMPACT_8:
15743     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15744     case GD_S_FLYING_DIAMOND_FALLING_1:
15745     case GD_S_FLYING_DIAMOND_FALLING_2:
15746     case GD_S_FLYING_DIAMOND_FALLING_3:
15747     case GD_S_FLYING_DIAMOND_FALLING_4:
15748     case GD_S_FLYING_DIAMOND_FALLING_5:
15749     case GD_S_FLYING_DIAMOND_FALLING_6:
15750     case GD_S_FLYING_DIAMOND_FALLING_7:
15751     case GD_S_FLYING_DIAMOND_FALLING_8:
15752     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15753     case GD_S_FLYING_DIAMOND_IMPACT_1:
15754     case GD_S_FLYING_DIAMOND_IMPACT_2:
15755     case GD_S_FLYING_DIAMOND_IMPACT_3:
15756     case GD_S_FLYING_DIAMOND_IMPACT_4:
15757     case GD_S_FLYING_DIAMOND_IMPACT_5:
15758     case GD_S_FLYING_DIAMOND_IMPACT_6:
15759     case GD_S_FLYING_DIAMOND_IMPACT_7:
15760     case GD_S_FLYING_DIAMOND_IMPACT_8:
15761     case GD_S_TIMEOUT_0:
15762     case GD_S_TIMEOUT_1:
15763     case GD_S_TIMEOUT_2:
15764     case GD_S_TIMEOUT_3:
15765     case GD_S_TIMEOUT_4:
15766     case GD_S_TIMEOUT_5:
15767     case GD_S_TIMEOUT_6:
15768     case GD_S_TIMEOUT_7:
15769     case GD_S_TIMEOUT_8:
15770     case GD_S_TIMEOUT_9:
15771     case GD_S_TIMEOUT_10:
15772     case GD_S_BONUS_LIFE:
15773       // trigger special post-processing (and force sound to be non-looping)
15774       return ACTION_OTHER;
15775
15776     case GD_S_AMOEBA_MAGIC:
15777     case GD_S_FINISHED:
15778       // trigger special post-processing (and force sound to be looping)
15779       return ACTION_DEFAULT;
15780
15781     default:
15782       return ACTION_DEFAULT;
15783   }
15784 }
15785
15786 static int getSoundEffect_BD(int element_bd, int sample)
15787 {
15788   int sound_action = getSoundAction_BD(sample);
15789   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15790   int nr;
15791
15792   // standard sounds
15793   if (sound_action != ACTION_OTHER &&
15794       sound_action != ACTION_DEFAULT)
15795     return sound_effect;
15796
15797   // special post-processing for some sounds
15798   switch (sample)
15799   {
15800     case GD_S_DIAMOND_FALLING_RANDOM:
15801     case GD_S_DIAMOND_FALLING_1:
15802     case GD_S_DIAMOND_FALLING_2:
15803     case GD_S_DIAMOND_FALLING_3:
15804     case GD_S_DIAMOND_FALLING_4:
15805     case GD_S_DIAMOND_FALLING_5:
15806     case GD_S_DIAMOND_FALLING_6:
15807     case GD_S_DIAMOND_FALLING_7:
15808     case GD_S_DIAMOND_FALLING_8:
15809       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15810             sample - GD_S_DIAMOND_FALLING_1);
15811       sound_effect = SND_BDX_DIAMOND_FALLING_RANDOM_1 + nr;
15812
15813       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15814         sound_effect = SND_BDX_DIAMOND_FALLING;
15815       break;
15816
15817     case GD_S_DIAMOND_IMPACT_RANDOM:
15818     case GD_S_DIAMOND_IMPACT_1:
15819     case GD_S_DIAMOND_IMPACT_2:
15820     case GD_S_DIAMOND_IMPACT_3:
15821     case GD_S_DIAMOND_IMPACT_4:
15822     case GD_S_DIAMOND_IMPACT_5:
15823     case GD_S_DIAMOND_IMPACT_6:
15824     case GD_S_DIAMOND_IMPACT_7:
15825     case GD_S_DIAMOND_IMPACT_8:
15826       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15827             sample - GD_S_DIAMOND_IMPACT_1);
15828       sound_effect = SND_BDX_DIAMOND_IMPACT_RANDOM_1 + nr;
15829
15830       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15831         sound_effect = SND_BDX_DIAMOND_IMPACT;
15832       break;
15833
15834     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15835     case GD_S_FLYING_DIAMOND_FALLING_1:
15836     case GD_S_FLYING_DIAMOND_FALLING_2:
15837     case GD_S_FLYING_DIAMOND_FALLING_3:
15838     case GD_S_FLYING_DIAMOND_FALLING_4:
15839     case GD_S_FLYING_DIAMOND_FALLING_5:
15840     case GD_S_FLYING_DIAMOND_FALLING_6:
15841     case GD_S_FLYING_DIAMOND_FALLING_7:
15842     case GD_S_FLYING_DIAMOND_FALLING_8:
15843       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15844             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15845       sound_effect = SND_BDX_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15846
15847       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15848         sound_effect = SND_BDX_FLYING_DIAMOND_FALLING;
15849       break;
15850
15851     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15852     case GD_S_FLYING_DIAMOND_IMPACT_1:
15853     case GD_S_FLYING_DIAMOND_IMPACT_2:
15854     case GD_S_FLYING_DIAMOND_IMPACT_3:
15855     case GD_S_FLYING_DIAMOND_IMPACT_4:
15856     case GD_S_FLYING_DIAMOND_IMPACT_5:
15857     case GD_S_FLYING_DIAMOND_IMPACT_6:
15858     case GD_S_FLYING_DIAMOND_IMPACT_7:
15859     case GD_S_FLYING_DIAMOND_IMPACT_8:
15860       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15861             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15862       sound_effect = SND_BDX_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15863
15864       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15865         sound_effect = SND_BDX_FLYING_DIAMOND_IMPACT;
15866       break;
15867
15868     case GD_S_TIMEOUT_0:
15869     case GD_S_TIMEOUT_1:
15870     case GD_S_TIMEOUT_2:
15871     case GD_S_TIMEOUT_3:
15872     case GD_S_TIMEOUT_4:
15873     case GD_S_TIMEOUT_5:
15874     case GD_S_TIMEOUT_6:
15875     case GD_S_TIMEOUT_7:
15876     case GD_S_TIMEOUT_8:
15877     case GD_S_TIMEOUT_9:
15878     case GD_S_TIMEOUT_10:
15879       nr = sample - GD_S_TIMEOUT_0;
15880       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15881
15882       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15883         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15884       break;
15885
15886     case GD_S_BONUS_LIFE:
15887       sound_effect = SND_GAME_HEALTH_BONUS;
15888       break;
15889
15890     case GD_S_AMOEBA_MAGIC:
15891       sound_effect = SND_BDX_AMOEBA_1_OTHER;
15892       break;
15893
15894     case GD_S_FINISHED:
15895       sound_effect = SND_GAME_LEVELTIME_BONUS;
15896       break;
15897
15898     default:
15899       sound_effect = SND_UNDEFINED;
15900       break;
15901   }
15902
15903   return sound_effect;
15904 }
15905
15906 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15907 {
15908   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15909   int sound_effect = getSoundEffect_BD(element, sample);
15910   int sound_action = getSoundAction_BD(sample);
15911   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15912   int offset = 0;
15913   int x = xx - offset;
15914   int y = yy - offset;
15915
15916   // some sound actions are always looping in native BD game engine
15917   if (sound_action == ACTION_DEFAULT)
15918     is_loop_sound = TRUE;
15919
15920   // some sound actions are always non-looping in native BD game engine
15921   if (sound_action == ACTION_FALLING ||
15922       sound_action == ACTION_MOVING ||
15923       sound_action == ACTION_OTHER)
15924     is_loop_sound = FALSE;
15925
15926   if (sound_effect != SND_UNDEFINED)
15927     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15928 }
15929
15930 void StopSound_BD(int element_bd, int sample)
15931 {
15932   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15933   int sound_effect = getSoundEffect_BD(element, sample);
15934
15935   if (sound_effect != SND_UNDEFINED)
15936     StopSound(sound_effect);
15937 }
15938
15939 boolean isSoundPlaying_BD(int element_bd, int sample)
15940 {
15941   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15942   int sound_effect = getSoundEffect_BD(element, sample);
15943
15944   if (sound_effect != SND_UNDEFINED)
15945     return isSoundPlaying(sound_effect);
15946
15947   return FALSE;
15948 }
15949
15950 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15951 {
15952   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15953   int offset = 0;
15954   int x = xx - offset;
15955   int y = yy - offset;
15956
15957   switch (sample)
15958   {
15959     case SOUND_blank:
15960       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15961       break;
15962
15963     case SOUND_roll:
15964       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15965       break;
15966
15967     case SOUND_stone:
15968       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15969       break;
15970
15971     case SOUND_nut:
15972       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15973       break;
15974
15975     case SOUND_crack:
15976       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15977       break;
15978
15979     case SOUND_bug:
15980       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15981       break;
15982
15983     case SOUND_tank:
15984       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15985       break;
15986
15987     case SOUND_android_clone:
15988       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15989       break;
15990
15991     case SOUND_android_move:
15992       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15993       break;
15994
15995     case SOUND_spring:
15996       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15997       break;
15998
15999     case SOUND_slurp:
16000       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
16001       break;
16002
16003     case SOUND_eater:
16004       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
16005       break;
16006
16007     case SOUND_eater_eat:
16008       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16009       break;
16010
16011     case SOUND_alien:
16012       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
16013       break;
16014
16015     case SOUND_collect:
16016       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
16017       break;
16018
16019     case SOUND_diamond:
16020       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16021       break;
16022
16023     case SOUND_squash:
16024       // !!! CHECK THIS !!!
16025 #if 1
16026       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
16027 #else
16028       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
16029 #endif
16030       break;
16031
16032     case SOUND_wonderfall:
16033       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
16034       break;
16035
16036     case SOUND_drip:
16037       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16038       break;
16039
16040     case SOUND_push:
16041       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16042       break;
16043
16044     case SOUND_dirt:
16045       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16046       break;
16047
16048     case SOUND_acid:
16049       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16050       break;
16051
16052     case SOUND_ball:
16053       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16054       break;
16055
16056     case SOUND_slide:
16057       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16058       break;
16059
16060     case SOUND_wonder:
16061       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16062       break;
16063
16064     case SOUND_door:
16065       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16066       break;
16067
16068     case SOUND_exit_open:
16069       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16070       break;
16071
16072     case SOUND_exit_leave:
16073       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16074       break;
16075
16076     case SOUND_dynamite:
16077       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16078       break;
16079
16080     case SOUND_tick:
16081       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16082       break;
16083
16084     case SOUND_press:
16085       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16086       break;
16087
16088     case SOUND_wheel:
16089       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16090       break;
16091
16092     case SOUND_boom:
16093       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16094       break;
16095
16096     case SOUND_die:
16097       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16098       break;
16099
16100     case SOUND_time:
16101       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16102       break;
16103
16104     default:
16105       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16106       break;
16107   }
16108 }
16109
16110 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16111 {
16112   int element = map_element_SP_to_RND(element_sp);
16113   int action = map_action_SP_to_RND(action_sp);
16114   int offset = (setup.sp_show_border_elements ? 0 : 1);
16115   int x = xx - offset;
16116   int y = yy - offset;
16117
16118   PlayLevelSoundElementAction(x, y, element, action);
16119 }
16120
16121 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16122 {
16123   int element = map_element_MM_to_RND(element_mm);
16124   int action = map_action_MM_to_RND(action_mm);
16125   int offset = 0;
16126   int x = xx - offset;
16127   int y = yy - offset;
16128
16129   if (!IS_MM_ELEMENT(element))
16130     element = EL_MM_DEFAULT;
16131
16132   PlayLevelSoundElementAction(x, y, element, action);
16133 }
16134
16135 void PlaySound_MM(int sound_mm)
16136 {
16137   int sound = map_sound_MM_to_RND(sound_mm);
16138
16139   if (sound == SND_UNDEFINED)
16140     return;
16141
16142   PlaySound(sound);
16143 }
16144
16145 void PlaySoundLoop_MM(int sound_mm)
16146 {
16147   int sound = map_sound_MM_to_RND(sound_mm);
16148
16149   if (sound == SND_UNDEFINED)
16150     return;
16151
16152   PlaySoundLoop(sound);
16153 }
16154
16155 void StopSound_MM(int sound_mm)
16156 {
16157   int sound = map_sound_MM_to_RND(sound_mm);
16158
16159   if (sound == SND_UNDEFINED)
16160     return;
16161
16162   StopSound(sound);
16163 }
16164
16165 void RaiseScore(int value)
16166 {
16167   game.score += value;
16168
16169   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16170
16171   DisplayGameControlValues();
16172 }
16173
16174 void RaiseScoreElement(int element)
16175 {
16176   switch (element)
16177   {
16178     case EL_EMERALD:
16179     case EL_BD_DIAMOND:
16180     case EL_EMERALD_YELLOW:
16181     case EL_EMERALD_RED:
16182     case EL_EMERALD_PURPLE:
16183     case EL_SP_INFOTRON:
16184       RaiseScore(level.score[SC_EMERALD]);
16185       break;
16186     case EL_DIAMOND:
16187       RaiseScore(level.score[SC_DIAMOND]);
16188       break;
16189     case EL_CRYSTAL:
16190       RaiseScore(level.score[SC_CRYSTAL]);
16191       break;
16192     case EL_PEARL:
16193       RaiseScore(level.score[SC_PEARL]);
16194       break;
16195     case EL_BUG:
16196     case EL_BD_BUTTERFLY:
16197     case EL_SP_ELECTRON:
16198       RaiseScore(level.score[SC_BUG]);
16199       break;
16200     case EL_SPACESHIP:
16201     case EL_BD_FIREFLY:
16202     case EL_SP_SNIKSNAK:
16203       RaiseScore(level.score[SC_SPACESHIP]);
16204       break;
16205     case EL_YAMYAM:
16206     case EL_DARK_YAMYAM:
16207       RaiseScore(level.score[SC_YAMYAM]);
16208       break;
16209     case EL_ROBOT:
16210       RaiseScore(level.score[SC_ROBOT]);
16211       break;
16212     case EL_PACMAN:
16213       RaiseScore(level.score[SC_PACMAN]);
16214       break;
16215     case EL_NUT:
16216       RaiseScore(level.score[SC_NUT]);
16217       break;
16218     case EL_DYNAMITE:
16219     case EL_EM_DYNAMITE:
16220     case EL_SP_DISK_RED:
16221     case EL_DYNABOMB_INCREASE_NUMBER:
16222     case EL_DYNABOMB_INCREASE_SIZE:
16223     case EL_DYNABOMB_INCREASE_POWER:
16224       RaiseScore(level.score[SC_DYNAMITE]);
16225       break;
16226     case EL_SHIELD_NORMAL:
16227     case EL_SHIELD_DEADLY:
16228       RaiseScore(level.score[SC_SHIELD]);
16229       break;
16230     case EL_EXTRA_TIME:
16231       RaiseScore(level.extra_time_score);
16232       break;
16233     case EL_KEY_1:
16234     case EL_KEY_2:
16235     case EL_KEY_3:
16236     case EL_KEY_4:
16237     case EL_EM_KEY_1:
16238     case EL_EM_KEY_2:
16239     case EL_EM_KEY_3:
16240     case EL_EM_KEY_4:
16241     case EL_EMC_KEY_5:
16242     case EL_EMC_KEY_6:
16243     case EL_EMC_KEY_7:
16244     case EL_EMC_KEY_8:
16245     case EL_DC_KEY_WHITE:
16246       RaiseScore(level.score[SC_KEY]);
16247       break;
16248     default:
16249       RaiseScore(element_info[element].collect_score);
16250       break;
16251   }
16252 }
16253
16254 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16255 {
16256   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16257   {
16258     if (!quick_quit)
16259     {
16260       // prevent short reactivation of overlay buttons while closing door
16261       SetOverlayActive(FALSE);
16262       UnmapGameButtons();
16263
16264       // door may still be open due to skipped or envelope style request
16265       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16266     }
16267
16268     if (network.enabled)
16269     {
16270       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16271     }
16272     else
16273     {
16274       // when using BD game engine, cover screen before fading out
16275       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16276         game_bd.cover_screen = TRUE;
16277
16278       if (quick_quit)
16279         FadeSkipNextFadeIn();
16280
16281       SetGameStatus(GAME_MODE_MAIN);
16282
16283       DrawMainMenu();
16284     }
16285   }
16286   else          // continue playing the game
16287   {
16288     if (tape.playing && tape.deactivate_display)
16289       TapeDeactivateDisplayOff(TRUE);
16290
16291     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16292
16293     if (tape.playing && tape.deactivate_display)
16294       TapeDeactivateDisplayOn();
16295   }
16296 }
16297
16298 void RequestQuitGame(boolean escape_key_pressed)
16299 {
16300   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16301   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16302                         level_editor_test_game);
16303   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16304                           quick_quit || score_info_tape_play);
16305
16306   RequestQuitGameExt(skip_request, quick_quit,
16307                      "Do you really want to quit the game?");
16308 }
16309
16310 static char *getRestartGameMessage(void)
16311 {
16312   boolean play_again = hasStartedNetworkGame();
16313   static char message[MAX_OUTPUT_LINESIZE];
16314   char *game_over_text = "Game over!";
16315   char *play_again_text = " Play it again?";
16316
16317   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16318       game_mm.game_over_message != NULL)
16319     game_over_text = game_mm.game_over_message;
16320
16321   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16322            (play_again ? play_again_text : ""));
16323
16324   return message;
16325 }
16326
16327 static void RequestRestartGame(void)
16328 {
16329   char *message = getRestartGameMessage();
16330   boolean has_started_game = hasStartedNetworkGame();
16331   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16332   int door_state = DOOR_CLOSE_1;
16333
16334   boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
16335
16336   // if no restart wanted, continue with next level for BD style intermission levels
16337   if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
16338   {
16339     boolean success = AdvanceToNextLevel();
16340
16341     restart_wanted = (success && setup.auto_play_next_level);
16342   }
16343
16344   if (restart_wanted)
16345   {
16346     CloseDoor(door_state);
16347
16348     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16349   }
16350   else
16351   {
16352     // if game was invoked from level editor, also close tape recorder door
16353     if (level_editor_test_game)
16354       door_state = DOOR_CLOSE_ALL;
16355
16356     CloseDoor(door_state);
16357
16358     SetGameStatus(GAME_MODE_MAIN);
16359
16360     DrawMainMenu();
16361   }
16362 }
16363
16364 boolean CheckRestartGame(void)
16365 {
16366   static int game_over_delay = 0;
16367   int game_over_delay_value = 50;
16368   boolean game_over = checkGameFailed();
16369
16370   if (!game_over)
16371   {
16372     game_over_delay = game_over_delay_value;
16373
16374     return FALSE;
16375   }
16376
16377   if (game_over_delay > 0)
16378   {
16379     if (game_over_delay == game_over_delay_value / 2)
16380       PlaySound(SND_GAME_LOSING);
16381
16382     game_over_delay--;
16383
16384     return FALSE;
16385   }
16386
16387   // do not ask to play again if request dialog is already active
16388   if (checkRequestActive())
16389     return FALSE;
16390
16391   // do not ask to play again if request dialog already handled
16392   if (game.RestartGameRequested)
16393     return FALSE;
16394
16395   // do not ask to play again if game was never actually played
16396   if (!game.GamePlayed)
16397     return FALSE;
16398
16399   // do not ask to play again if this was disabled in setup menu
16400   if (!setup.ask_on_game_over)
16401     return FALSE;
16402
16403   game.RestartGameRequested = TRUE;
16404
16405   RequestRestartGame();
16406
16407   return TRUE;
16408 }
16409
16410 boolean checkGameRunning(void)
16411 {
16412   if (game_status != GAME_MODE_PLAYING)
16413     return FALSE;
16414
16415   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16416     return FALSE;
16417
16418   return TRUE;
16419 }
16420
16421 boolean checkGamePlaying(void)
16422 {
16423   if (game_status != GAME_MODE_PLAYING)
16424     return FALSE;
16425
16426   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16427     return FALSE;
16428
16429   return TRUE;
16430 }
16431
16432 boolean checkGameSolved(void)
16433 {
16434   // set for all game engines if level was solved
16435   return game.LevelSolved_GameEnd;
16436 }
16437
16438 boolean checkGameFailed(void)
16439 {
16440   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16441     return (game_bd.game_over && !game_bd.level_solved);
16442   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16443     return (game_em.game_over && !game_em.level_solved);
16444   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16445     return (game_sp.game_over && !game_sp.level_solved);
16446   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16447     return (game_mm.game_over && !game_mm.level_solved);
16448   else                          // GAME_ENGINE_TYPE_RND
16449     return (game.GameOver && !game.LevelSolved);
16450 }
16451
16452 boolean checkGameEnded(void)
16453 {
16454   return (checkGameSolved() || checkGameFailed());
16455 }
16456
16457 boolean checkRequestActive(void)
16458 {
16459   return (game.request_active || game.envelope_active || game.any_door_active);
16460 }
16461
16462
16463 // ----------------------------------------------------------------------------
16464 // random generator functions
16465 // ----------------------------------------------------------------------------
16466
16467 unsigned int InitEngineRandom_RND(int seed)
16468 {
16469   game.num_random_calls = 0;
16470
16471   return InitEngineRandom(seed);
16472 }
16473
16474 unsigned int RND(int max)
16475 {
16476   if (max > 0)
16477   {
16478     game.num_random_calls++;
16479
16480     return GetEngineRandom(max);
16481   }
16482
16483   return 0;
16484 }
16485
16486
16487 // ----------------------------------------------------------------------------
16488 // game engine snapshot handling functions
16489 // ----------------------------------------------------------------------------
16490
16491 struct EngineSnapshotInfo
16492 {
16493   // runtime values for custom element collect score
16494   int collect_score[NUM_CUSTOM_ELEMENTS];
16495
16496   // runtime values for group element choice position
16497   int choice_pos[NUM_GROUP_ELEMENTS];
16498
16499   // runtime values for belt position animations
16500   int belt_graphic[4][NUM_BELT_PARTS];
16501   int belt_anim_mode[4][NUM_BELT_PARTS];
16502 };
16503
16504 static struct EngineSnapshotInfo engine_snapshot_rnd;
16505 static char *snapshot_level_identifier = NULL;
16506 static int snapshot_level_nr = -1;
16507
16508 static void SaveEngineSnapshotValues_RND(void)
16509 {
16510   static int belt_base_active_element[4] =
16511   {
16512     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16513     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16514     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16515     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16516   };
16517   int i, j;
16518
16519   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16520   {
16521     int element = EL_CUSTOM_START + i;
16522
16523     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16524   }
16525
16526   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16527   {
16528     int element = EL_GROUP_START + i;
16529
16530     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16531   }
16532
16533   for (i = 0; i < 4; i++)
16534   {
16535     for (j = 0; j < NUM_BELT_PARTS; j++)
16536     {
16537       int element = belt_base_active_element[i] + j;
16538       int graphic = el2img(element);
16539       int anim_mode = graphic_info[graphic].anim_mode;
16540
16541       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16542       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16543     }
16544   }
16545 }
16546
16547 static void LoadEngineSnapshotValues_RND(void)
16548 {
16549   unsigned int num_random_calls = game.num_random_calls;
16550   int i, j;
16551
16552   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16553   {
16554     int element = EL_CUSTOM_START + i;
16555
16556     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16557   }
16558
16559   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16560   {
16561     int element = EL_GROUP_START + i;
16562
16563     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16564   }
16565
16566   for (i = 0; i < 4; i++)
16567   {
16568     for (j = 0; j < NUM_BELT_PARTS; j++)
16569     {
16570       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16571       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16572
16573       graphic_info[graphic].anim_mode = anim_mode;
16574     }
16575   }
16576
16577   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16578   {
16579     InitRND(tape.random_seed);
16580     for (i = 0; i < num_random_calls; i++)
16581       RND(1);
16582   }
16583
16584   if (game.num_random_calls != num_random_calls)
16585   {
16586     Error("number of random calls out of sync");
16587     Error("number of random calls should be %d", num_random_calls);
16588     Error("number of random calls is %d", game.num_random_calls);
16589
16590     Fail("this should not happen -- please debug");
16591   }
16592 }
16593
16594 void FreeEngineSnapshotSingle(void)
16595 {
16596   FreeSnapshotSingle();
16597
16598   setString(&snapshot_level_identifier, NULL);
16599   snapshot_level_nr = -1;
16600 }
16601
16602 void FreeEngineSnapshotList(void)
16603 {
16604   FreeSnapshotList();
16605 }
16606
16607 static ListNode *SaveEngineSnapshotBuffers(void)
16608 {
16609   ListNode *buffers = NULL;
16610
16611   // copy some special values to a structure better suited for the snapshot
16612
16613   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16614     SaveEngineSnapshotValues_RND();
16615   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16616     SaveEngineSnapshotValues_BD();
16617   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16618     SaveEngineSnapshotValues_EM();
16619   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16620     SaveEngineSnapshotValues_SP(&buffers);
16621   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16622     SaveEngineSnapshotValues_MM();
16623
16624   // save values stored in special snapshot structure
16625
16626   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16627     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16628   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16629     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_bd));
16630   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16631     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16632   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16633     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16634   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16635     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16636
16637   // save further RND engine values
16638
16639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16642
16643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16645   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16649
16650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16652   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16653
16654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16655
16656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16658
16659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16677
16678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16680
16681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16684
16685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16687
16688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16694
16695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16697
16698 #if 0
16699   ListNode *node = engine_snapshot_list_rnd;
16700   int num_bytes = 0;
16701
16702   while (node != NULL)
16703   {
16704     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16705
16706     node = node->next;
16707   }
16708
16709   Debug("game:playing:SaveEngineSnapshotBuffers",
16710         "size of engine snapshot: %d bytes", num_bytes);
16711 #endif
16712
16713   return buffers;
16714 }
16715
16716 void SaveEngineSnapshotSingle(void)
16717 {
16718   ListNode *buffers = SaveEngineSnapshotBuffers();
16719
16720   // finally save all snapshot buffers to single snapshot
16721   SaveSnapshotSingle(buffers);
16722
16723   // save level identification information
16724   setString(&snapshot_level_identifier, leveldir_current->identifier);
16725   snapshot_level_nr = level_nr;
16726 }
16727
16728 boolean CheckSaveEngineSnapshotToList(void)
16729 {
16730   boolean save_snapshot =
16731     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16732      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16733       game.snapshot.changed_action) ||
16734      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16735       game.snapshot.collected_item));
16736
16737   game.snapshot.changed_action = FALSE;
16738   game.snapshot.collected_item = FALSE;
16739   game.snapshot.save_snapshot = save_snapshot;
16740
16741   return save_snapshot;
16742 }
16743
16744 void SaveEngineSnapshotToList(void)
16745 {
16746   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16747       tape.quick_resume)
16748     return;
16749
16750   ListNode *buffers = SaveEngineSnapshotBuffers();
16751
16752   // finally save all snapshot buffers to snapshot list
16753   SaveSnapshotToList(buffers);
16754 }
16755
16756 void SaveEngineSnapshotToListInitial(void)
16757 {
16758   FreeEngineSnapshotList();
16759
16760   SaveEngineSnapshotToList();
16761 }
16762
16763 static void LoadEngineSnapshotValues(void)
16764 {
16765   // restore special values from snapshot structure
16766
16767   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16768     LoadEngineSnapshotValues_RND();
16769   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16770     LoadEngineSnapshotValues_BD();
16771   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16772     LoadEngineSnapshotValues_EM();
16773   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16774     LoadEngineSnapshotValues_SP();
16775   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16776     LoadEngineSnapshotValues_MM();
16777 }
16778
16779 void LoadEngineSnapshotSingle(void)
16780 {
16781   LoadSnapshotSingle();
16782
16783   LoadEngineSnapshotValues();
16784 }
16785
16786 static void LoadEngineSnapshot_Undo(int steps)
16787 {
16788   LoadSnapshotFromList_Older(steps);
16789
16790   LoadEngineSnapshotValues();
16791 }
16792
16793 static void LoadEngineSnapshot_Redo(int steps)
16794 {
16795   LoadSnapshotFromList_Newer(steps);
16796
16797   LoadEngineSnapshotValues();
16798 }
16799
16800 boolean CheckEngineSnapshotSingle(void)
16801 {
16802   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16803           snapshot_level_nr == level_nr);
16804 }
16805
16806 boolean CheckEngineSnapshotList(void)
16807 {
16808   return CheckSnapshotList();
16809 }
16810
16811
16812 // ---------- new game button stuff -------------------------------------------
16813
16814 static struct
16815 {
16816   int graphic;
16817   struct XY *pos;
16818   int gadget_id;
16819   boolean *setup_value;
16820   boolean allowed_on_tape;
16821   boolean is_touch_button;
16822   char *infotext;
16823 } gamebutton_info[NUM_GAME_BUTTONS] =
16824 {
16825   {
16826     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16827     GAME_CTRL_ID_STOP,                          NULL,
16828     TRUE, FALSE,                                "stop game"
16829   },
16830   {
16831     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16832     GAME_CTRL_ID_PAUSE,                         NULL,
16833     TRUE, FALSE,                                "pause game"
16834   },
16835   {
16836     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16837     GAME_CTRL_ID_PLAY,                          NULL,
16838     TRUE, FALSE,                                "play game"
16839   },
16840   {
16841     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16842     GAME_CTRL_ID_UNDO,                          NULL,
16843     TRUE, FALSE,                                "undo step"
16844   },
16845   {
16846     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16847     GAME_CTRL_ID_REDO,                          NULL,
16848     TRUE, FALSE,                                "redo step"
16849   },
16850   {
16851     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16852     GAME_CTRL_ID_SAVE,                          NULL,
16853     TRUE, FALSE,                                "save game"
16854   },
16855   {
16856     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16857     GAME_CTRL_ID_PAUSE2,                        NULL,
16858     TRUE, FALSE,                                "pause game"
16859   },
16860   {
16861     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16862     GAME_CTRL_ID_LOAD,                          NULL,
16863     TRUE, FALSE,                                "load game"
16864   },
16865   {
16866     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16867     GAME_CTRL_ID_RESTART,                       NULL,
16868     TRUE, FALSE,                                "restart game"
16869   },
16870   {
16871     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16872     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16873     FALSE, FALSE,                               "stop game"
16874   },
16875   {
16876     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16877     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16878     FALSE, FALSE,                               "pause game"
16879   },
16880   {
16881     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16882     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16883     FALSE, FALSE,                               "play game"
16884   },
16885   {
16886     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16887     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16888     FALSE, FALSE,                               "restart game"
16889   },
16890   {
16891     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16892     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16893     FALSE, TRUE,                                "stop game"
16894   },
16895   {
16896     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16897     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16898     FALSE, TRUE,                                "pause game"
16899   },
16900   {
16901     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16902     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16903     FALSE, TRUE,                                "restart game"
16904   },
16905   {
16906     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16907     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16908     TRUE, FALSE,                                "background music on/off"
16909   },
16910   {
16911     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16912     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16913     TRUE, FALSE,                                "sound loops on/off"
16914   },
16915   {
16916     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16917     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16918     TRUE, FALSE,                                "normal sounds on/off"
16919   },
16920   {
16921     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16922     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16923     FALSE, FALSE,                               "background music on/off"
16924   },
16925   {
16926     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16927     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16928     FALSE, FALSE,                               "sound loops on/off"
16929   },
16930   {
16931     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16932     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16933     FALSE, FALSE,                               "normal sounds on/off"
16934   }
16935 };
16936
16937 void CreateGameButtons(void)
16938 {
16939   int i;
16940
16941   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16942   {
16943     int graphic = gamebutton_info[i].graphic;
16944     struct GraphicInfo *gfx = &graphic_info[graphic];
16945     struct XY *pos = gamebutton_info[i].pos;
16946     struct GadgetInfo *gi;
16947     int button_type;
16948     boolean checked;
16949     unsigned int event_mask;
16950     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16951     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16952     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16953     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16954     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16955     int gd_x   = gfx->src_x;
16956     int gd_y   = gfx->src_y;
16957     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16958     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16959     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16960     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16961     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16962     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16963     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16964     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16965     int id = i;
16966
16967     // do not use touch buttons if overlay touch buttons are disabled
16968     if (is_touch_button && !setup.touch.overlay_buttons)
16969       continue;
16970
16971     if (gfx->bitmap == NULL)
16972     {
16973       game_gadget[id] = NULL;
16974
16975       continue;
16976     }
16977
16978     if (id == GAME_CTRL_ID_STOP ||
16979         id == GAME_CTRL_ID_PANEL_STOP ||
16980         id == GAME_CTRL_ID_TOUCH_STOP ||
16981         id == GAME_CTRL_ID_PLAY ||
16982         id == GAME_CTRL_ID_PANEL_PLAY ||
16983         id == GAME_CTRL_ID_SAVE ||
16984         id == GAME_CTRL_ID_LOAD ||
16985         id == GAME_CTRL_ID_RESTART ||
16986         id == GAME_CTRL_ID_PANEL_RESTART ||
16987         id == GAME_CTRL_ID_TOUCH_RESTART)
16988     {
16989       button_type = GD_TYPE_NORMAL_BUTTON;
16990       checked = FALSE;
16991       event_mask = GD_EVENT_RELEASED;
16992     }
16993     else if (id == GAME_CTRL_ID_UNDO ||
16994              id == GAME_CTRL_ID_REDO)
16995     {
16996       button_type = GD_TYPE_NORMAL_BUTTON;
16997       checked = FALSE;
16998       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16999     }
17000     else
17001     {
17002       button_type = GD_TYPE_CHECK_BUTTON;
17003       checked = (gamebutton_info[i].setup_value != NULL ?
17004                  *gamebutton_info[i].setup_value : FALSE);
17005       event_mask = GD_EVENT_PRESSED;
17006     }
17007
17008     gi = CreateGadget(GDI_CUSTOM_ID, id,
17009                       GDI_IMAGE_ID, graphic,
17010                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
17011                       GDI_X, base_x + x,
17012                       GDI_Y, base_y + y,
17013                       GDI_WIDTH, gfx->width,
17014                       GDI_HEIGHT, gfx->height,
17015                       GDI_TYPE, button_type,
17016                       GDI_STATE, GD_BUTTON_UNPRESSED,
17017                       GDI_CHECKED, checked,
17018                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
17019                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
17020                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
17021                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
17022                       GDI_DIRECT_DRAW, FALSE,
17023                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
17024                       GDI_EVENT_MASK, event_mask,
17025                       GDI_CALLBACK_ACTION, HandleGameButtons,
17026                       GDI_END);
17027
17028     if (gi == NULL)
17029       Fail("cannot create gadget");
17030
17031     game_gadget[id] = gi;
17032   }
17033 }
17034
17035 void FreeGameButtons(void)
17036 {
17037   int i;
17038
17039   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17040     FreeGadget(game_gadget[i]);
17041 }
17042
17043 static void UnmapGameButtonsAtSamePosition(int id)
17044 {
17045   int i;
17046
17047   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17048     if (i != id &&
17049         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
17050         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
17051       UnmapGadget(game_gadget[i]);
17052 }
17053
17054 static void UnmapGameButtonsAtSamePosition_All(void)
17055 {
17056   if (setup.show_load_save_buttons)
17057   {
17058     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17059     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17060     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17061   }
17062   else if (setup.show_undo_redo_buttons)
17063   {
17064     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17065     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17066     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17067   }
17068   else
17069   {
17070     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
17071     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17072     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17073
17074     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17075     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17076     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17077   }
17078 }
17079
17080 void MapLoadSaveButtons(void)
17081 {
17082   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17083   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17084
17085   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17086   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17087 }
17088
17089 void MapUndoRedoButtons(void)
17090 {
17091   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17092   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17093
17094   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17095   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17096 }
17097
17098 void ModifyPauseButtons(void)
17099 {
17100   static int ids[] =
17101   {
17102     GAME_CTRL_ID_PAUSE,
17103     GAME_CTRL_ID_PAUSE2,
17104     GAME_CTRL_ID_PANEL_PAUSE,
17105     GAME_CTRL_ID_TOUCH_PAUSE,
17106     -1
17107   };
17108   int i;
17109
17110   // do not redraw pause button on closed door (may happen when restarting game)
17111   if (!(GetDoorState() & DOOR_OPEN_1))
17112     return;
17113
17114   for (i = 0; ids[i] > -1; i++)
17115     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17116 }
17117
17118 static void MapGameButtonsExt(boolean on_tape)
17119 {
17120   int i;
17121
17122   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17123   {
17124     if ((i == GAME_CTRL_ID_UNDO ||
17125          i == GAME_CTRL_ID_REDO) &&
17126         game_status != GAME_MODE_PLAYING)
17127       continue;
17128
17129     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17130       MapGadget(game_gadget[i]);
17131   }
17132
17133   UnmapGameButtonsAtSamePosition_All();
17134
17135   RedrawGameButtons();
17136 }
17137
17138 static void UnmapGameButtonsExt(boolean on_tape)
17139 {
17140   int i;
17141
17142   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17143     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17144       UnmapGadget(game_gadget[i]);
17145 }
17146
17147 static void RedrawGameButtonsExt(boolean on_tape)
17148 {
17149   int i;
17150
17151   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17152     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17153       RedrawGadget(game_gadget[i]);
17154 }
17155
17156 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17157 {
17158   if (gi == NULL)
17159     return;
17160
17161   gi->checked = state;
17162 }
17163
17164 static void RedrawSoundButtonGadget(int id)
17165 {
17166   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17167              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17168              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17169              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17170              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17171              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17172              id);
17173
17174   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17175   RedrawGadget(game_gadget[id2]);
17176 }
17177
17178 void MapGameButtons(void)
17179 {
17180   MapGameButtonsExt(FALSE);
17181 }
17182
17183 void UnmapGameButtons(void)
17184 {
17185   UnmapGameButtonsExt(FALSE);
17186 }
17187
17188 void RedrawGameButtons(void)
17189 {
17190   RedrawGameButtonsExt(FALSE);
17191 }
17192
17193 void MapGameButtonsOnTape(void)
17194 {
17195   MapGameButtonsExt(TRUE);
17196 }
17197
17198 void UnmapGameButtonsOnTape(void)
17199 {
17200   UnmapGameButtonsExt(TRUE);
17201 }
17202
17203 void RedrawGameButtonsOnTape(void)
17204 {
17205   RedrawGameButtonsExt(TRUE);
17206 }
17207
17208 static void GameUndoRedoExt(void)
17209 {
17210   ClearPlayerAction();
17211
17212   tape.pausing = TRUE;
17213
17214   RedrawPlayfield();
17215   UpdateAndDisplayGameControlValues();
17216
17217   DrawCompleteVideoDisplay();
17218   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17219   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17220   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17221
17222   ModifyPauseButtons();
17223
17224   BackToFront();
17225 }
17226
17227 static void GameUndo(int steps)
17228 {
17229   if (!CheckEngineSnapshotList())
17230     return;
17231
17232   int tape_property_bits = tape.property_bits;
17233
17234   LoadEngineSnapshot_Undo(steps);
17235
17236   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17237
17238   GameUndoRedoExt();
17239 }
17240
17241 static void GameRedo(int steps)
17242 {
17243   if (!CheckEngineSnapshotList())
17244     return;
17245
17246   int tape_property_bits = tape.property_bits;
17247
17248   LoadEngineSnapshot_Redo(steps);
17249
17250   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17251
17252   GameUndoRedoExt();
17253 }
17254
17255 static void HandleGameButtonsExt(int id, int button)
17256 {
17257   static boolean game_undo_executed = FALSE;
17258   int steps = BUTTON_STEPSIZE(button);
17259   boolean handle_game_buttons =
17260     (game_status == GAME_MODE_PLAYING ||
17261      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17262
17263   if (!handle_game_buttons)
17264     return;
17265
17266   switch (id)
17267   {
17268     case GAME_CTRL_ID_STOP:
17269     case GAME_CTRL_ID_PANEL_STOP:
17270     case GAME_CTRL_ID_TOUCH_STOP:
17271       TapeStopGame();
17272
17273       break;
17274
17275     case GAME_CTRL_ID_PAUSE:
17276     case GAME_CTRL_ID_PAUSE2:
17277     case GAME_CTRL_ID_PANEL_PAUSE:
17278     case GAME_CTRL_ID_TOUCH_PAUSE:
17279       if (network.enabled && game_status == GAME_MODE_PLAYING)
17280       {
17281         if (tape.pausing)
17282           SendToServer_ContinuePlaying();
17283         else
17284           SendToServer_PausePlaying();
17285       }
17286       else
17287         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17288
17289       game_undo_executed = FALSE;
17290
17291       break;
17292
17293     case GAME_CTRL_ID_PLAY:
17294     case GAME_CTRL_ID_PANEL_PLAY:
17295       if (game_status == GAME_MODE_MAIN)
17296       {
17297         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17298       }
17299       else if (tape.pausing)
17300       {
17301         if (network.enabled)
17302           SendToServer_ContinuePlaying();
17303         else
17304           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17305       }
17306       break;
17307
17308     case GAME_CTRL_ID_UNDO:
17309       // Important: When using "save snapshot when collecting an item" mode,
17310       // load last (current) snapshot for first "undo" after pressing "pause"
17311       // (else the last-but-one snapshot would be loaded, because the snapshot
17312       // pointer already points to the last snapshot when pressing "pause",
17313       // which is fine for "every step/move" mode, but not for "every collect")
17314       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17315           !game_undo_executed)
17316         steps--;
17317
17318       game_undo_executed = TRUE;
17319
17320       GameUndo(steps);
17321       break;
17322
17323     case GAME_CTRL_ID_REDO:
17324       GameRedo(steps);
17325       break;
17326
17327     case GAME_CTRL_ID_SAVE:
17328       TapeQuickSave();
17329       break;
17330
17331     case GAME_CTRL_ID_LOAD:
17332       TapeQuickLoad();
17333       break;
17334
17335     case GAME_CTRL_ID_RESTART:
17336     case GAME_CTRL_ID_PANEL_RESTART:
17337     case GAME_CTRL_ID_TOUCH_RESTART:
17338       TapeRestartGame();
17339
17340       break;
17341
17342     case SOUND_CTRL_ID_MUSIC:
17343     case SOUND_CTRL_ID_PANEL_MUSIC:
17344       if (setup.sound_music)
17345       { 
17346         setup.sound_music = FALSE;
17347
17348         FadeMusic();
17349       }
17350       else if (audio.music_available)
17351       { 
17352         setup.sound = setup.sound_music = TRUE;
17353
17354         SetAudioMode(setup.sound);
17355
17356         if (game_status == GAME_MODE_PLAYING)
17357           PlayLevelMusic();
17358       }
17359
17360       RedrawSoundButtonGadget(id);
17361
17362       break;
17363
17364     case SOUND_CTRL_ID_LOOPS:
17365     case SOUND_CTRL_ID_PANEL_LOOPS:
17366       if (setup.sound_loops)
17367         setup.sound_loops = FALSE;
17368       else if (audio.loops_available)
17369       {
17370         setup.sound = setup.sound_loops = TRUE;
17371
17372         SetAudioMode(setup.sound);
17373       }
17374
17375       RedrawSoundButtonGadget(id);
17376
17377       break;
17378
17379     case SOUND_CTRL_ID_SIMPLE:
17380     case SOUND_CTRL_ID_PANEL_SIMPLE:
17381       if (setup.sound_simple)
17382         setup.sound_simple = FALSE;
17383       else if (audio.sound_available)
17384       {
17385         setup.sound = setup.sound_simple = TRUE;
17386
17387         SetAudioMode(setup.sound);
17388       }
17389
17390       RedrawSoundButtonGadget(id);
17391
17392       break;
17393
17394     default:
17395       break;
17396   }
17397 }
17398
17399 static void HandleGameButtons(struct GadgetInfo *gi)
17400 {
17401   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17402 }
17403
17404 void HandleSoundButtonKeys(Key key)
17405 {
17406   if (key == setup.shortcut.sound_simple)
17407     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17408   else if (key == setup.shortcut.sound_loops)
17409     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17410   else if (key == setup.shortcut.sound_music)
17411     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17412 }