fixed another bug with game restart request when opening/closing doors
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitFieldForEngine_RND(int x, int y)
1839 {
1840   int element = Tile[x][y];
1841
1842   // convert BD engine elements to corresponding R'n'D engine elements
1843   element = (element == EL_BD_EMPTY             ? EL_EMPTY :
1844              element == EL_BD_PLAYER            ? EL_PLAYER_1 :
1845              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1846              element == EL_BD_SAND              ? EL_SAND :
1847              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1848              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1849              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1850              element);
1851
1852   Tile[x][y] = element;
1853 }
1854
1855 static void InitFieldForEngine(int x, int y)
1856 {
1857   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1858     InitFieldForEngine_RND(x, y);
1859 }
1860
1861 static void InitField(int x, int y, boolean init_game)
1862 {
1863   int element = Tile[x][y];
1864
1865   switch (element)
1866   {
1867     case EL_SP_MURPHY:
1868     case EL_PLAYER_1:
1869     case EL_PLAYER_2:
1870     case EL_PLAYER_3:
1871     case EL_PLAYER_4:
1872       InitPlayerField(x, y, element, init_game);
1873       break;
1874
1875     case EL_SOKOBAN_FIELD_PLAYER:
1876       element = Tile[x][y] = EL_PLAYER_1;
1877       InitField(x, y, init_game);
1878
1879       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1880       InitField(x, y, init_game);
1881       break;
1882
1883     case EL_SOKOBAN_FIELD_EMPTY:
1884       IncrementSokobanFieldsNeeded();
1885       break;
1886
1887     case EL_SOKOBAN_OBJECT:
1888       IncrementSokobanObjectsNeeded();
1889       break;
1890
1891     case EL_STONEBLOCK:
1892       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1893         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1894       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1895         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1896       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1897         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1898       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1899         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1900       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1901         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1902       break;
1903
1904     case EL_BUG:
1905     case EL_BUG_RIGHT:
1906     case EL_BUG_UP:
1907     case EL_BUG_LEFT:
1908     case EL_BUG_DOWN:
1909     case EL_SPACESHIP:
1910     case EL_SPACESHIP_RIGHT:
1911     case EL_SPACESHIP_UP:
1912     case EL_SPACESHIP_LEFT:
1913     case EL_SPACESHIP_DOWN:
1914     case EL_BD_BUTTERFLY:
1915     case EL_BD_BUTTERFLY_RIGHT:
1916     case EL_BD_BUTTERFLY_UP:
1917     case EL_BD_BUTTERFLY_LEFT:
1918     case EL_BD_BUTTERFLY_DOWN:
1919     case EL_BD_FIREFLY:
1920     case EL_BD_FIREFLY_RIGHT:
1921     case EL_BD_FIREFLY_UP:
1922     case EL_BD_FIREFLY_LEFT:
1923     case EL_BD_FIREFLY_DOWN:
1924     case EL_PACMAN_RIGHT:
1925     case EL_PACMAN_UP:
1926     case EL_PACMAN_LEFT:
1927     case EL_PACMAN_DOWN:
1928     case EL_YAMYAM:
1929     case EL_YAMYAM_LEFT:
1930     case EL_YAMYAM_RIGHT:
1931     case EL_YAMYAM_UP:
1932     case EL_YAMYAM_DOWN:
1933     case EL_DARK_YAMYAM:
1934     case EL_ROBOT:
1935     case EL_PACMAN:
1936     case EL_SP_SNIKSNAK:
1937     case EL_SP_ELECTRON:
1938     case EL_MOLE:
1939     case EL_MOLE_LEFT:
1940     case EL_MOLE_RIGHT:
1941     case EL_MOLE_UP:
1942     case EL_MOLE_DOWN:
1943     case EL_SPRING_LEFT:
1944     case EL_SPRING_RIGHT:
1945       InitMovDir(x, y);
1946       break;
1947
1948     case EL_AMOEBA_FULL:
1949     case EL_BD_AMOEBA:
1950       InitAmoebaNr(x, y);
1951       break;
1952
1953     case EL_AMOEBA_DROP:
1954       if (y == lev_fieldy - 1)
1955       {
1956         Tile[x][y] = EL_AMOEBA_GROWING;
1957         Store[x][y] = EL_AMOEBA_WET;
1958       }
1959       break;
1960
1961     case EL_DYNAMITE_ACTIVE:
1962     case EL_SP_DISK_RED_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1966     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1967       MovDelay[x][y] = 96;
1968       break;
1969
1970     case EL_EM_DYNAMITE_ACTIVE:
1971       MovDelay[x][y] = 32;
1972       break;
1973
1974     case EL_LAMP:
1975       game.lights_still_needed++;
1976       break;
1977
1978     case EL_PENGUIN:
1979       game.friends_still_needed++;
1980       break;
1981
1982     case EL_PIG:
1983     case EL_DRAGON:
1984       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1985       break;
1986
1987     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1988     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1989     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1991     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1992     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1994     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1995     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1997     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1998     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1999       if (init_game)
2000       {
2001         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2003         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2004
2005         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2006         {
2007           game.belt_dir[belt_nr] = belt_dir;
2008           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2009         }
2010         else    // more than one switch -- set it like the first switch
2011         {
2012           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2013         }
2014       }
2015       break;
2016
2017     case EL_LIGHT_SWITCH_ACTIVE:
2018       if (init_game)
2019         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2020       break;
2021
2022     case EL_INVISIBLE_STEELWALL:
2023     case EL_INVISIBLE_WALL:
2024     case EL_INVISIBLE_SAND:
2025       if (game.light_time_left > 0 ||
2026           game.lenses_time_left > 0)
2027         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2028       break;
2029
2030     case EL_EMC_MAGIC_BALL:
2031       if (game.ball_active)
2032         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2033       break;
2034
2035     case EL_EMC_MAGIC_BALL_SWITCH:
2036       if (game.ball_active)
2037         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2038       break;
2039
2040     case EL_TRIGGER_PLAYER:
2041     case EL_TRIGGER_ELEMENT:
2042     case EL_TRIGGER_CE_VALUE:
2043     case EL_TRIGGER_CE_SCORE:
2044     case EL_SELF:
2045     case EL_ANY_ELEMENT:
2046     case EL_CURRENT_CE_VALUE:
2047     case EL_CURRENT_CE_SCORE:
2048     case EL_PREV_CE_1:
2049     case EL_PREV_CE_2:
2050     case EL_PREV_CE_3:
2051     case EL_PREV_CE_4:
2052     case EL_PREV_CE_5:
2053     case EL_PREV_CE_6:
2054     case EL_PREV_CE_7:
2055     case EL_PREV_CE_8:
2056     case EL_NEXT_CE_1:
2057     case EL_NEXT_CE_2:
2058     case EL_NEXT_CE_3:
2059     case EL_NEXT_CE_4:
2060     case EL_NEXT_CE_5:
2061     case EL_NEXT_CE_6:
2062     case EL_NEXT_CE_7:
2063     case EL_NEXT_CE_8:
2064       // reference elements should not be used on the playfield
2065       Tile[x][y] = EL_EMPTY;
2066       break;
2067
2068     default:
2069       if (IS_CUSTOM_ELEMENT(element))
2070       {
2071         if (CAN_MOVE(element))
2072           InitMovDir(x, y);
2073
2074         if (!element_info[element].use_last_ce_value || init_game)
2075           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2076       }
2077       else if (IS_GROUP_ELEMENT(element))
2078       {
2079         Tile[x][y] = GetElementFromGroupElement(element);
2080
2081         InitField(x, y, init_game);
2082       }
2083       else if (IS_EMPTY_ELEMENT(element))
2084       {
2085         GfxElementEmpty[x][y] = element;
2086         Tile[x][y] = EL_EMPTY;
2087
2088         if (element_info[element].use_gfx_element)
2089           game.use_masked_elements = TRUE;
2090       }
2091
2092       break;
2093   }
2094
2095   if (!init_game)
2096     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2097 }
2098
2099 static void InitField_WithBug1(int x, int y, boolean init_game)
2100 {
2101   InitField(x, y, init_game);
2102
2103   // not needed to call InitMovDir() -- already done by InitField()!
2104   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2105       CAN_MOVE(Tile[x][y]))
2106     InitMovDir(x, y);
2107 }
2108
2109 static void InitField_WithBug2(int x, int y, boolean init_game)
2110 {
2111   int old_element = Tile[x][y];
2112
2113   InitField(x, y, init_game);
2114
2115   // not needed to call InitMovDir() -- already done by InitField()!
2116   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2117       CAN_MOVE(old_element) &&
2118       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2119     InitMovDir(x, y);
2120
2121   /* this case is in fact a combination of not less than three bugs:
2122      first, it calls InitMovDir() for elements that can move, although this is
2123      already done by InitField(); then, it checks the element that was at this
2124      field _before_ the call to InitField() (which can change it); lastly, it
2125      was not called for "mole with direction" elements, which were treated as
2126      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2127   */
2128 }
2129
2130 static int get_key_element_from_nr(int key_nr)
2131 {
2132   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2133                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                           EL_EM_KEY_1 : EL_KEY_1);
2135
2136   return key_base_element + key_nr;
2137 }
2138
2139 static int get_next_dropped_element(struct PlayerInfo *player)
2140 {
2141   return (player->inventory_size > 0 ?
2142           player->inventory_element[player->inventory_size - 1] :
2143           player->inventory_infinite_element != EL_UNDEFINED ?
2144           player->inventory_infinite_element :
2145           player->dynabombs_left > 0 ?
2146           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2147           EL_UNDEFINED);
2148 }
2149
2150 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2151 {
2152   // pos >= 0: get element from bottom of the stack;
2153   // pos <  0: get element from top of the stack
2154
2155   if (pos < 0)
2156   {
2157     int min_inventory_size = -pos;
2158     int inventory_pos = player->inventory_size - min_inventory_size;
2159     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2160
2161     return (player->inventory_size >= min_inventory_size ?
2162             player->inventory_element[inventory_pos] :
2163             player->inventory_infinite_element != EL_UNDEFINED ?
2164             player->inventory_infinite_element :
2165             player->dynabombs_left >= min_dynabombs_left ?
2166             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2167             EL_UNDEFINED);
2168   }
2169   else
2170   {
2171     int min_dynabombs_left = pos + 1;
2172     int min_inventory_size = pos + 1 - player->dynabombs_left;
2173     int inventory_pos = pos - player->dynabombs_left;
2174
2175     return (player->inventory_infinite_element != EL_UNDEFINED ?
2176             player->inventory_infinite_element :
2177             player->dynabombs_left >= min_dynabombs_left ?
2178             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2179             player->inventory_size >= min_inventory_size ?
2180             player->inventory_element[inventory_pos] :
2181             EL_UNDEFINED);
2182   }
2183 }
2184
2185 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2186 {
2187   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2188   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2189   int compare_result;
2190
2191   if (gpo1->sort_priority != gpo2->sort_priority)
2192     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2193   else
2194     compare_result = gpo1->nr - gpo2->nr;
2195
2196   return compare_result;
2197 }
2198
2199 int getPlayerInventorySize(int player_nr)
2200 {
2201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202     return game_em.ply[player_nr]->dynamite;
2203   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2204     return game_sp.red_disk_count;
2205   else
2206     return stored_player[player_nr].inventory_size;
2207 }
2208
2209 static void InitGameControlValues(void)
2210 {
2211   int i;
2212
2213   for (i = 0; game_panel_controls[i].nr != -1; i++)
2214   {
2215     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2216     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2217     struct TextPosInfo *pos = gpc->pos;
2218     int nr = gpc->nr;
2219     int type = gpc->type;
2220
2221     if (nr != i)
2222     {
2223       Error("'game_panel_controls' structure corrupted at %d", i);
2224
2225       Fail("this should not happen -- please debug");
2226     }
2227
2228     // force update of game controls after initialization
2229     gpc->value = gpc->last_value = -1;
2230     gpc->frame = gpc->last_frame = -1;
2231     gpc->gfx_frame = -1;
2232
2233     // determine panel value width for later calculation of alignment
2234     if (type == TYPE_INTEGER || type == TYPE_STRING)
2235     {
2236       pos->width = pos->size * getFontWidth(pos->font);
2237       pos->height = getFontHeight(pos->font);
2238     }
2239     else if (type == TYPE_ELEMENT)
2240     {
2241       pos->width = pos->size;
2242       pos->height = pos->size;
2243     }
2244
2245     // fill structure for game panel draw order
2246     gpo->nr = gpc->nr;
2247     gpo->sort_priority = pos->sort_priority;
2248   }
2249
2250   // sort game panel controls according to sort_priority and control number
2251   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2252         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2253 }
2254
2255 static void UpdatePlayfieldElementCount(void)
2256 {
2257   boolean use_element_count = FALSE;
2258   int i, j, x, y;
2259
2260   // first check if it is needed at all to calculate playfield element count
2261   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2262     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2263       use_element_count = TRUE;
2264
2265   if (!use_element_count)
2266     return;
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269     element_info[i].element_count = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     element_info[Tile[x][y]].element_count++;
2274   }
2275
2276   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2277     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2278       if (IS_IN_GROUP(j, i))
2279         element_info[EL_GROUP_START + i].element_count +=
2280           element_info[j].element_count;
2281 }
2282
2283 static void UpdateGameControlValues(void)
2284 {
2285   int i, k;
2286   int time = (game.LevelSolved ?
2287               game.LevelSolved_CountingTime :
2288               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2289               game_bd.time_played :
2290               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2291               game_em.lev->time :
2292               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2293               game_sp.time_played :
2294               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2295               game_mm.energy_left :
2296               game.no_level_time_limit ? TimePlayed : TimeLeft);
2297   int score = (game.LevelSolved ?
2298                game.LevelSolved_CountingScore :
2299                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2300                game_bd.score :
2301                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2302                game_em.lev->score :
2303                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2304                game_sp.score :
2305                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2306                game_mm.score :
2307                game.score);
2308   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2309               game_bd.gems_still_needed :
2310               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2311               game_em.lev->gems_needed :
2312               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2313               game_sp.infotrons_still_needed :
2314               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2315               game_mm.kettles_still_needed :
2316               game.gems_still_needed);
2317   int gems_needed = level.gems_needed;
2318   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2319                         game_bd.game->cave->diamonds_collected :
2320                         gems_needed - gems);
2321   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2322                     game_bd.game->cave->diamond_value :
2323                     level.score[SC_EMERALD]);
2324   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2325                      game_bd.gems_still_needed > 0 :
2326                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2327                      game_em.lev->gems_needed > 0 :
2328                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2329                      game_sp.infotrons_still_needed > 0 :
2330                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2331                      game_mm.kettles_still_needed > 0 ||
2332                      game_mm.lights_still_needed > 0 :
2333                      game.gems_still_needed > 0 ||
2334                      game.sokoban_fields_still_needed > 0 ||
2335                      game.sokoban_objects_still_needed > 0 ||
2336                      game.lights_still_needed > 0);
2337   int health = (game.LevelSolved ?
2338                 game.LevelSolved_CountingHealth :
2339                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2340                 MM_HEALTH(game_mm.laser_overload_value) :
2341                 game.health);
2342   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2343
2344   UpdatePlayfieldElementCount();
2345
2346   // update game panel control values
2347
2348   // used instead of "level_nr" (for network games)
2349   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2350   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2351   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2352   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2353   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2354
2355   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2356   for (i = 0; i < MAX_NUM_KEYS; i++)
2357     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2359   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2360
2361   if (game.centered_player_nr == -1)
2362   {
2363     for (i = 0; i < MAX_PLAYERS; i++)
2364     {
2365       // only one player in Supaplex game engine
2366       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2367         break;
2368
2369       for (k = 0; k < MAX_NUM_KEYS; k++)
2370       {
2371         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2372         {
2373           if (game_em.ply[i]->keys & (1 << k))
2374             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2375               get_key_element_from_nr(k);
2376         }
2377         else if (stored_player[i].key[k])
2378           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2379             get_key_element_from_nr(k);
2380       }
2381
2382       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2383         getPlayerInventorySize(i);
2384
2385       if (stored_player[i].num_white_keys > 0)
2386         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2387           EL_DC_KEY_WHITE;
2388
2389       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2390         stored_player[i].num_white_keys;
2391     }
2392   }
2393   else
2394   {
2395     int player_nr = game.centered_player_nr;
2396
2397     for (k = 0; k < MAX_NUM_KEYS; k++)
2398     {
2399       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2400       {
2401         if (game_em.ply[player_nr]->keys & (1 << k))
2402           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2403             get_key_element_from_nr(k);
2404       }
2405       else if (stored_player[player_nr].key[k])
2406         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2407           get_key_element_from_nr(k);
2408     }
2409
2410     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2411       getPlayerInventorySize(player_nr);
2412
2413     if (stored_player[player_nr].num_white_keys > 0)
2414       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2415
2416     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2417       stored_player[player_nr].num_white_keys;
2418   }
2419
2420   // re-arrange keys on game panel, if needed or if defined by style settings
2421   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2422   {
2423     int nr = GAME_PANEL_KEY_1 + i;
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2425     struct TextPosInfo *pos = gpc->pos;
2426
2427     // skip check if key is not in the player's inventory
2428     if (gpc->value == EL_EMPTY)
2429       continue;
2430
2431     // check if keys should be arranged on panel from left to right
2432     if (pos->style == STYLE_LEFTMOST_POSITION)
2433     {
2434       // check previous key positions (left from current key)
2435       for (k = 0; k < i; k++)
2436       {
2437         int nr_new = GAME_PANEL_KEY_1 + k;
2438
2439         if (game_panel_controls[nr_new].value == EL_EMPTY)
2440         {
2441           game_panel_controls[nr_new].value = gpc->value;
2442           gpc->value = EL_EMPTY;
2443
2444           break;
2445         }
2446       }
2447     }
2448
2449     // check if "undefined" keys can be placed at some other position
2450     if (pos->x == -1 && pos->y == -1)
2451     {
2452       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2453
2454       // 1st try: display key at the same position as normal or EM keys
2455       if (game_panel_controls[nr_new].value == EL_EMPTY)
2456       {
2457         game_panel_controls[nr_new].value = gpc->value;
2458       }
2459       else
2460       {
2461         // 2nd try: display key at the next free position in the key panel
2462         for (k = 0; k < STD_NUM_KEYS; k++)
2463         {
2464           nr_new = GAME_PANEL_KEY_1 + k;
2465
2466           if (game_panel_controls[nr_new].value == EL_EMPTY)
2467           {
2468             game_panel_controls[nr_new].value = gpc->value;
2469
2470             break;
2471           }
2472         }
2473       }
2474     }
2475   }
2476
2477   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2478   {
2479     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2480       get_inventory_element_from_pos(local_player, i);
2481     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2482       get_inventory_element_from_pos(local_player, -i - 1);
2483   }
2484
2485   game_panel_controls[GAME_PANEL_SCORE].value = score;
2486   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2487
2488   game_panel_controls[GAME_PANEL_TIME].value = time;
2489
2490   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2491   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2492   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2493
2494   if (level.time == 0)
2495     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2496   else
2497     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2498
2499   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2500   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2501
2502   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2503
2504   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2505     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2506      EL_EMPTY);
2507   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2508     local_player->shield_normal_time_left;
2509   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2510     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2511      EL_EMPTY);
2512   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2513     local_player->shield_deadly_time_left;
2514
2515   game_panel_controls[GAME_PANEL_EXIT].value =
2516     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2517
2518   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2519     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2520   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2521     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2522      EL_EMC_MAGIC_BALL_SWITCH);
2523
2524   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2525     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2526   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2527     game.light_time_left;
2528
2529   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2530     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2531   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2532     game.timegate_time_left;
2533
2534   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2535     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2536
2537   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2538     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2539   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2540     game.lenses_time_left;
2541
2542   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2543     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2544   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2545     game.magnify_time_left;
2546
2547   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2548     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2549      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2550      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2551      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2552      EL_BALLOON_SWITCH_NONE);
2553
2554   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2555     local_player->dynabomb_count;
2556   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2557     local_player->dynabomb_size;
2558   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2559     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2560
2561   game_panel_controls[GAME_PANEL_PENGUINS].value =
2562     game.friends_still_needed;
2563
2564   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2565     game.sokoban_objects_still_needed;
2566   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2567     game.sokoban_fields_still_needed;
2568
2569   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2570     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2571
2572   for (i = 0; i < NUM_BELTS; i++)
2573   {
2574     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2575       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2576        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2577     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2578       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2579   }
2580
2581   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2582     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2583   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2584     game.magic_wall_time_left;
2585
2586   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2587     local_player->gravity;
2588
2589   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2590     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2591
2592   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2593     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2594       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2595        game.panel.element[i].id : EL_UNDEFINED);
2596
2597   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2598     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2599       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2600        element_info[game.panel.element_count[i].id].element_count : 0);
2601
2602   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2603     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2604       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2605        element_info[game.panel.ce_score[i].id].collect_score : 0);
2606
2607   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2608     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2609       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2610        element_info[game.panel.ce_score_element[i].id].collect_score :
2611        EL_UNDEFINED);
2612
2613   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2615   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2616
2617   // update game panel control frames
2618
2619   for (i = 0; game_panel_controls[i].nr != -1; i++)
2620   {
2621     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2622
2623     if (gpc->type == TYPE_ELEMENT)
2624     {
2625       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2626       {
2627         int last_anim_random_frame = gfx.anim_random_frame;
2628         int element = gpc->value;
2629         int graphic = el2panelimg(element);
2630         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2631                                sync_random_frame :
2632                                graphic_info[graphic].anim_global_anim_sync ?
2633                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2634
2635         if (gpc->value != gpc->last_value)
2636         {
2637           gpc->gfx_frame = 0;
2638           gpc->gfx_random = init_gfx_random;
2639         }
2640         else
2641         {
2642           gpc->gfx_frame++;
2643
2644           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2645               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2646             gpc->gfx_random = init_gfx_random;
2647         }
2648
2649         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2650           gfx.anim_random_frame = gpc->gfx_random;
2651
2652         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2653           gpc->gfx_frame = element_info[element].collect_score;
2654
2655         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2656
2657         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2658           gfx.anim_random_frame = last_anim_random_frame;
2659       }
2660     }
2661     else if (gpc->type == TYPE_GRAPHIC)
2662     {
2663       if (gpc->graphic != IMG_UNDEFINED)
2664       {
2665         int last_anim_random_frame = gfx.anim_random_frame;
2666         int graphic = gpc->graphic;
2667         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2668                                sync_random_frame :
2669                                graphic_info[graphic].anim_global_anim_sync ?
2670                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2671
2672         if (gpc->value != gpc->last_value)
2673         {
2674           gpc->gfx_frame = 0;
2675           gpc->gfx_random = init_gfx_random;
2676         }
2677         else
2678         {
2679           gpc->gfx_frame++;
2680
2681           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2682               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2683             gpc->gfx_random = init_gfx_random;
2684         }
2685
2686         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2687           gfx.anim_random_frame = gpc->gfx_random;
2688
2689         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2690
2691         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2692           gfx.anim_random_frame = last_anim_random_frame;
2693       }
2694     }
2695   }
2696 }
2697
2698 static void DisplayGameControlValues(void)
2699 {
2700   boolean redraw_panel = FALSE;
2701   int i;
2702
2703   for (i = 0; game_panel_controls[i].nr != -1; i++)
2704   {
2705     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2706
2707     if (PANEL_DEACTIVATED(gpc->pos))
2708       continue;
2709
2710     if (gpc->value == gpc->last_value &&
2711         gpc->frame == gpc->last_frame)
2712       continue;
2713
2714     redraw_panel = TRUE;
2715   }
2716
2717   if (!redraw_panel)
2718     return;
2719
2720   // copy default game door content to main double buffer
2721
2722   // !!! CHECK AGAIN !!!
2723   SetPanelBackground();
2724   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2725   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2726
2727   // redraw game control buttons
2728   RedrawGameButtons();
2729
2730   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2731
2732   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2733   {
2734     int nr = game_panel_order[i].nr;
2735     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2736     struct TextPosInfo *pos = gpc->pos;
2737     int type = gpc->type;
2738     int value = gpc->value;
2739     int frame = gpc->frame;
2740     int size = pos->size;
2741     int font = pos->font;
2742     boolean draw_masked = pos->draw_masked;
2743     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2744
2745     if (PANEL_DEACTIVATED(pos))
2746       continue;
2747
2748     if (pos->class == get_hash_from_string("extra_panel_items") &&
2749         !setup.prefer_extra_panel_items)
2750       continue;
2751
2752     gpc->last_value = value;
2753     gpc->last_frame = frame;
2754
2755     if (type == TYPE_INTEGER)
2756     {
2757       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2758           nr == GAME_PANEL_INVENTORY_COUNT ||
2759           nr == GAME_PANEL_SCORE ||
2760           nr == GAME_PANEL_HIGHSCORE ||
2761           nr == GAME_PANEL_TIME)
2762       {
2763         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2764
2765         if (use_dynamic_size)           // use dynamic number of digits
2766         {
2767           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2768                               nr == GAME_PANEL_INVENTORY_COUNT ||
2769                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2770           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2771                           nr == GAME_PANEL_INVENTORY_COUNT ||
2772                           nr == GAME_PANEL_TIME ? 1 : 2);
2773           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2774                        nr == GAME_PANEL_INVENTORY_COUNT ||
2775                        nr == GAME_PANEL_TIME ? 3 : 5);
2776           int size2 = size1 + size_add;
2777           int font1 = pos->font;
2778           int font2 = pos->font_alt;
2779
2780           size = (value < value_change ? size1 : size2);
2781           font = (value < value_change ? font1 : font2);
2782         }
2783       }
2784
2785       // correct text size if "digits" is zero or less
2786       if (size <= 0)
2787         size = strlen(int2str(value, size));
2788
2789       // dynamically correct text alignment
2790       pos->width = size * getFontWidth(font);
2791
2792       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2793                   int2str(value, size), font, mask_mode);
2794     }
2795     else if (type == TYPE_ELEMENT)
2796     {
2797       int element, graphic;
2798       Bitmap *src_bitmap;
2799       int src_x, src_y;
2800       int width, height;
2801       int dst_x = PANEL_XPOS(pos);
2802       int dst_y = PANEL_YPOS(pos);
2803
2804       if (value != EL_UNDEFINED && value != EL_EMPTY)
2805       {
2806         element = value;
2807         graphic = el2panelimg(value);
2808
2809 #if 0
2810         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2811               element, EL_NAME(element), size);
2812 #endif
2813
2814         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2815           size = TILESIZE;
2816
2817         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2818                               &src_x, &src_y);
2819
2820         width  = graphic_info[graphic].width  * size / TILESIZE;
2821         height = graphic_info[graphic].height * size / TILESIZE;
2822
2823         if (draw_masked)
2824           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2825                            dst_x, dst_y);
2826         else
2827           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2828                      dst_x, dst_y);
2829       }
2830     }
2831     else if (type == TYPE_GRAPHIC)
2832     {
2833       int graphic        = gpc->graphic;
2834       int graphic_active = gpc->graphic_active;
2835       Bitmap *src_bitmap;
2836       int src_x, src_y;
2837       int width, height;
2838       int dst_x = PANEL_XPOS(pos);
2839       int dst_y = PANEL_YPOS(pos);
2840       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2841                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2842
2843       if (graphic != IMG_UNDEFINED && !skip)
2844       {
2845         if (pos->style == STYLE_REVERSE)
2846           value = 100 - value;
2847
2848         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2849
2850         if (pos->direction & MV_HORIZONTAL)
2851         {
2852           width  = graphic_info[graphic_active].width * value / 100;
2853           height = graphic_info[graphic_active].height;
2854
2855           if (pos->direction == MV_LEFT)
2856           {
2857             src_x += graphic_info[graphic_active].width - width;
2858             dst_x += graphic_info[graphic_active].width - width;
2859           }
2860         }
2861         else
2862         {
2863           width  = graphic_info[graphic_active].width;
2864           height = graphic_info[graphic_active].height * value / 100;
2865
2866           if (pos->direction == MV_UP)
2867           {
2868             src_y += graphic_info[graphic_active].height - height;
2869             dst_y += graphic_info[graphic_active].height - height;
2870           }
2871         }
2872
2873         if (draw_masked)
2874           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2875                            dst_x, dst_y);
2876         else
2877           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2878                      dst_x, dst_y);
2879
2880         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2881
2882         if (pos->direction & MV_HORIZONTAL)
2883         {
2884           if (pos->direction == MV_RIGHT)
2885           {
2886             src_x += width;
2887             dst_x += width;
2888           }
2889           else
2890           {
2891             dst_x = PANEL_XPOS(pos);
2892           }
2893
2894           width = graphic_info[graphic].width - width;
2895         }
2896         else
2897         {
2898           if (pos->direction == MV_DOWN)
2899           {
2900             src_y += height;
2901             dst_y += height;
2902           }
2903           else
2904           {
2905             dst_y = PANEL_YPOS(pos);
2906           }
2907
2908           height = graphic_info[graphic].height - height;
2909         }
2910
2911         if (draw_masked)
2912           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2913                            dst_x, dst_y);
2914         else
2915           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2916                      dst_x, dst_y);
2917       }
2918     }
2919     else if (type == TYPE_STRING)
2920     {
2921       boolean active = (value != 0);
2922       char *state_normal = "off";
2923       char *state_active = "on";
2924       char *state = (active ? state_active : state_normal);
2925       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2926                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2927                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2928                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2929
2930       if (nr == GAME_PANEL_GRAVITY_STATE)
2931       {
2932         int font1 = pos->font;          // (used for normal state)
2933         int font2 = pos->font_alt;      // (used for active state)
2934
2935         font = (active ? font2 : font1);
2936       }
2937
2938       if (s != NULL)
2939       {
2940         char *s_cut;
2941
2942         if (size <= 0)
2943         {
2944           // don't truncate output if "chars" is zero or less
2945           size = strlen(s);
2946
2947           // dynamically correct text alignment
2948           pos->width = size * getFontWidth(font);
2949         }
2950
2951         s_cut = getStringCopyN(s, size);
2952
2953         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2954                     s_cut, font, mask_mode);
2955
2956         free(s_cut);
2957       }
2958     }
2959
2960     redraw_mask |= REDRAW_DOOR_1;
2961   }
2962
2963   SetGameStatus(GAME_MODE_PLAYING);
2964 }
2965
2966 void UpdateAndDisplayGameControlValues(void)
2967 {
2968   if (tape.deactivate_display)
2969     return;
2970
2971   UpdateGameControlValues();
2972   DisplayGameControlValues();
2973 }
2974
2975 void UpdateGameDoorValues(void)
2976 {
2977   UpdateGameControlValues();
2978 }
2979
2980 void DrawGameDoorValues(void)
2981 {
2982   DisplayGameControlValues();
2983 }
2984
2985
2986 // ============================================================================
2987 // InitGameEngine()
2988 // ----------------------------------------------------------------------------
2989 // initialize game engine due to level / tape version number
2990 // ============================================================================
2991
2992 static void InitGameEngine(void)
2993 {
2994   int i, j, k, l, x, y;
2995
2996   // set game engine from tape file when re-playing, else from level file
2997   game.engine_version = (tape.playing ? tape.engine_version :
2998                          level.game_version);
2999
3000   // set single or multi-player game mode (needed for re-playing tapes)
3001   game.team_mode = setup.team_mode;
3002
3003   if (tape.playing)
3004   {
3005     int num_players = 0;
3006
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (tape.player_participates[i])
3009         num_players++;
3010
3011     // multi-player tapes contain input data for more than one player
3012     game.team_mode = (num_players > 1);
3013   }
3014
3015 #if 0
3016   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3017         level.game_version);
3018   Debug("game:init:level", "          tape.file_version   == %06d",
3019         tape.file_version);
3020   Debug("game:init:level", "          tape.game_version   == %06d",
3021         tape.game_version);
3022   Debug("game:init:level", "          tape.engine_version == %06d",
3023         tape.engine_version);
3024   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3025         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3026 #endif
3027
3028   // --------------------------------------------------------------------------
3029   // set flags for bugs and changes according to active game engine version
3030   // --------------------------------------------------------------------------
3031
3032   /*
3033     Summary of bugfix:
3034     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3035
3036     Bug was introduced in version:
3037     2.0.1
3038
3039     Bug was fixed in version:
3040     4.2.0.0
3041
3042     Description:
3043     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3044     but the property "can fall" was missing, which caused some levels to be
3045     unsolvable. This was fixed in version 4.2.0.0.
3046
3047     Affected levels/tapes:
3048     An example for a tape that was fixed by this bugfix is tape 029 from the
3049     level set "rnd_sam_bateman".
3050     The wrong behaviour will still be used for all levels or tapes that were
3051     created/recorded with it. An example for this is tape 023 from the level
3052     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3053   */
3054
3055   boolean use_amoeba_dropping_cannot_fall_bug =
3056     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3057       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3058      (tape.playing &&
3059       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3060       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3061
3062   /*
3063     Summary of bugfix/change:
3064     Fixed move speed of elements entering or leaving magic wall.
3065
3066     Fixed/changed in version:
3067     2.0.1
3068
3069     Description:
3070     Before 2.0.1, move speed of elements entering or leaving magic wall was
3071     twice as fast as it is now.
3072     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3073
3074     Affected levels/tapes:
3075     The first condition is generally needed for all levels/tapes before version
3076     2.0.1, which might use the old behaviour before it was changed; known tapes
3077     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3078     The second condition is an exception from the above case and is needed for
3079     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3080     above, but before it was known that this change would break tapes like the
3081     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3082     although the engine version while recording maybe was before 2.0.1. There
3083     are a lot of tapes that are affected by this exception, like tape 006 from
3084     the level set "rnd_conor_mancone".
3085   */
3086
3087   boolean use_old_move_stepsize_for_magic_wall =
3088     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3089      !(tape.playing &&
3090        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3091        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3092
3093   /*
3094     Summary of bugfix/change:
3095     Fixed handling for custom elements that change when pushed by the player.
3096
3097     Fixed/changed in version:
3098     3.1.0
3099
3100     Description:
3101     Before 3.1.0, custom elements that "change when pushing" changed directly
3102     after the player started pushing them (until then handled in "DigField()").
3103     Since 3.1.0, these custom elements are not changed until the "pushing"
3104     move of the element is finished (now handled in "ContinueMoving()").
3105
3106     Affected levels/tapes:
3107     The first condition is generally needed for all levels/tapes before version
3108     3.1.0, which might use the old behaviour before it was changed; known tapes
3109     that are affected are some tapes from the level set "Walpurgis Gardens" by
3110     Jamie Cullen.
3111     The second condition is an exception from the above case and is needed for
3112     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3113     above (including some development versions of 3.1.0), but before it was
3114     known that this change would break tapes like the above and was fixed in
3115     3.1.1, so that the changed behaviour was active although the engine version
3116     while recording maybe was before 3.1.0. There is at least one tape that is
3117     affected by this exception, which is the tape for the one-level set "Bug
3118     Machine" by Juergen Bonhagen.
3119   */
3120
3121   game.use_change_when_pushing_bug =
3122     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3123      !(tape.playing &&
3124        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3125        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3126
3127   /*
3128     Summary of bugfix/change:
3129     Fixed handling for blocking the field the player leaves when moving.
3130
3131     Fixed/changed in version:
3132     3.1.1
3133
3134     Description:
3135     Before 3.1.1, when "block last field when moving" was enabled, the field
3136     the player is leaving when moving was blocked for the time of the move,
3137     and was directly unblocked afterwards. This resulted in the last field
3138     being blocked for exactly one less than the number of frames of one player
3139     move. Additionally, even when blocking was disabled, the last field was
3140     blocked for exactly one frame.
3141     Since 3.1.1, due to changes in player movement handling, the last field
3142     is not blocked at all when blocking is disabled. When blocking is enabled,
3143     the last field is blocked for exactly the number of frames of one player
3144     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3145     last field is blocked for exactly one more than the number of frames of
3146     one player move.
3147
3148     Affected levels/tapes:
3149     (!!! yet to be determined -- probably many !!!)
3150   */
3151
3152   game.use_block_last_field_bug =
3153     (game.engine_version < VERSION_IDENT(3,1,1,0));
3154
3155   /* various special flags and settings for native Emerald Mine game engine */
3156
3157   game_em.use_single_button =
3158     (game.engine_version > VERSION_IDENT(4,0,0,2));
3159
3160   game_em.use_push_delay =
3161     (game.engine_version > VERSION_IDENT(4,3,7,1));
3162
3163   game_em.use_snap_key_bug =
3164     (game.engine_version < VERSION_IDENT(4,0,1,0));
3165
3166   game_em.use_random_bug =
3167     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3168
3169   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3170
3171   game_em.use_old_explosions            = use_old_em_engine;
3172   game_em.use_old_android               = use_old_em_engine;
3173   game_em.use_old_push_elements         = use_old_em_engine;
3174   game_em.use_old_push_into_acid        = use_old_em_engine;
3175
3176   game_em.use_wrap_around               = !use_old_em_engine;
3177
3178   // --------------------------------------------------------------------------
3179
3180   // set maximal allowed number of custom element changes per game frame
3181   game.max_num_changes_per_frame = 1;
3182
3183   // default scan direction: scan playfield from top/left to bottom/right
3184   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3185
3186   // dynamically adjust element properties according to game engine version
3187   InitElementPropertiesEngine(game.engine_version);
3188
3189   // ---------- initialize special element properties -------------------------
3190
3191   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3192   if (use_amoeba_dropping_cannot_fall_bug)
3193     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3194
3195   // ---------- initialize player's initial move delay ------------------------
3196
3197   // dynamically adjust player properties according to level information
3198   for (i = 0; i < MAX_PLAYERS; i++)
3199     game.initial_move_delay_value[i] =
3200       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3201
3202   // dynamically adjust player properties according to game engine version
3203   for (i = 0; i < MAX_PLAYERS; i++)
3204     game.initial_move_delay[i] =
3205       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3206        game.initial_move_delay_value[i] : 0);
3207
3208   // ---------- initialize player's initial push delay ------------------------
3209
3210   // dynamically adjust player properties according to game engine version
3211   game.initial_push_delay_value =
3212     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3213
3214   // ---------- initialize changing elements ----------------------------------
3215
3216   // initialize changing elements information
3217   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[i];
3220
3221     // this pointer might have been changed in the level editor
3222     ei->change = &ei->change_page[0];
3223
3224     if (!IS_CUSTOM_ELEMENT(i))
3225     {
3226       ei->change->target_element = EL_EMPTY_SPACE;
3227       ei->change->delay_fixed = 0;
3228       ei->change->delay_random = 0;
3229       ei->change->delay_frames = 1;
3230     }
3231
3232     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3233     {
3234       ei->has_change_event[j] = FALSE;
3235
3236       ei->event_page_nr[j] = 0;
3237       ei->event_page[j] = &ei->change_page[0];
3238     }
3239   }
3240
3241   // add changing elements from pre-defined list
3242   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3243   {
3244     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3245     struct ElementInfo *ei = &element_info[ch_delay->element];
3246
3247     ei->change->target_element       = ch_delay->target_element;
3248     ei->change->delay_fixed          = ch_delay->change_delay;
3249
3250     ei->change->pre_change_function  = ch_delay->pre_change_function;
3251     ei->change->change_function      = ch_delay->change_function;
3252     ei->change->post_change_function = ch_delay->post_change_function;
3253
3254     ei->change->can_change = TRUE;
3255     ei->change->can_change_or_has_action = TRUE;
3256
3257     ei->has_change_event[CE_DELAY] = TRUE;
3258
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3260     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3261   }
3262
3263   // ---------- initialize if element can trigger global animations -----------
3264
3265   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3266   {
3267     struct ElementInfo *ei = &element_info[i];
3268
3269     ei->has_anim_event = FALSE;
3270   }
3271
3272   InitGlobalAnimEventsForCustomElements();
3273
3274   // ---------- initialize internal run-time variables ------------------------
3275
3276   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       ei->change_page[j].can_change_or_has_action =
3283         (ei->change_page[j].can_change |
3284          ei->change_page[j].has_action);
3285     }
3286   }
3287
3288   // add change events from custom element configuration
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3292
3293     for (j = 0; j < ei->num_change_pages; j++)
3294     {
3295       if (!ei->change_page[j].can_change_or_has_action)
3296         continue;
3297
3298       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3299       {
3300         // only add event page for the first page found with this event
3301         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3302         {
3303           ei->has_change_event[k] = TRUE;
3304
3305           ei->event_page_nr[k] = j;
3306           ei->event_page[k] = &ei->change_page[j];
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize reference elements in change conditions ------------
3313
3314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3315   {
3316     int element = EL_CUSTOM_START + i;
3317     struct ElementInfo *ei = &element_info[element];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       int trigger_element = ei->change_page[j].initial_trigger_element;
3322
3323       if (trigger_element >= EL_PREV_CE_8 &&
3324           trigger_element <= EL_NEXT_CE_8)
3325         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3326
3327       ei->change_page[j].trigger_element = trigger_element;
3328     }
3329   }
3330
3331   // ---------- initialize run-time trigger player and element ----------------
3332
3333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334   {
3335     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336
3337     for (j = 0; j < ei->num_change_pages; j++)
3338     {
3339       struct ElementChangeInfo *change = &ei->change_page[j];
3340
3341       change->actual_trigger_element = EL_EMPTY;
3342       change->actual_trigger_player = EL_EMPTY;
3343       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3344       change->actual_trigger_side = CH_SIDE_NONE;
3345       change->actual_trigger_ce_value = 0;
3346       change->actual_trigger_ce_score = 0;
3347       change->actual_trigger_x = -1;
3348       change->actual_trigger_y = -1;
3349     }
3350   }
3351
3352   // ---------- initialize trigger events -------------------------------------
3353
3354   // initialize trigger events information
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357       trigger_events[i][j] = FALSE;
3358
3359   // add trigger events from element change event properties
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361   {
3362     struct ElementInfo *ei = &element_info[i];
3363
3364     for (j = 0; j < ei->num_change_pages; j++)
3365     {
3366       struct ElementChangeInfo *change = &ei->change_page[j];
3367
3368       if (!change->can_change_or_has_action)
3369         continue;
3370
3371       if (change->has_event[CE_BY_OTHER_ACTION])
3372       {
3373         int trigger_element = change->trigger_element;
3374
3375         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3376         {
3377           if (change->has_event[k])
3378           {
3379             if (IS_GROUP_ELEMENT(trigger_element))
3380             {
3381               struct ElementGroupInfo *group =
3382                 element_info[trigger_element].group;
3383
3384               for (l = 0; l < group->num_elements_resolved; l++)
3385                 trigger_events[group->element_resolved[l]][k] = TRUE;
3386             }
3387             else if (trigger_element == EL_ANY_ELEMENT)
3388               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3389                 trigger_events[l][k] = TRUE;
3390             else
3391               trigger_events[trigger_element][k] = TRUE;
3392           }
3393         }
3394       }
3395     }
3396   }
3397
3398   // ---------- initialize push delay -----------------------------------------
3399
3400   // initialize push delay values to default
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402   {
3403     if (!IS_CUSTOM_ELEMENT(i))
3404     {
3405       // set default push delay values (corrected since version 3.0.7-1)
3406       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3407       {
3408         element_info[i].push_delay_fixed = 2;
3409         element_info[i].push_delay_random = 8;
3410       }
3411       else
3412       {
3413         element_info[i].push_delay_fixed = 8;
3414         element_info[i].push_delay_random = 8;
3415       }
3416     }
3417   }
3418
3419   // set push delay value for certain elements from pre-defined list
3420   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3421   {
3422     int e = push_delay_list[i].element;
3423
3424     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3425     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3426   }
3427
3428   // set push delay value for Supaplex elements for newer engine versions
3429   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3430   {
3431     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432     {
3433       if (IS_SP_ELEMENT(i))
3434       {
3435         // set SP push delay to just enough to push under a falling zonk
3436         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3437
3438         element_info[i].push_delay_fixed  = delay;
3439         element_info[i].push_delay_random = 0;
3440       }
3441     }
3442   }
3443
3444   // ---------- initialize move stepsize --------------------------------------
3445
3446   // initialize move stepsize values to default
3447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3448     if (!IS_CUSTOM_ELEMENT(i))
3449       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3450
3451   // set move stepsize value for certain elements from pre-defined list
3452   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3453   {
3454     int e = move_stepsize_list[i].element;
3455
3456     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3457
3458     // set move stepsize value for certain elements for older engine versions
3459     if (use_old_move_stepsize_for_magic_wall)
3460     {
3461       if (e == EL_MAGIC_WALL_FILLING ||
3462           e == EL_MAGIC_WALL_EMPTYING ||
3463           e == EL_BD_MAGIC_WALL_FILLING ||
3464           e == EL_BD_MAGIC_WALL_EMPTYING)
3465         element_info[e].move_stepsize *= 2;
3466     }
3467   }
3468
3469   // ---------- initialize collect score --------------------------------------
3470
3471   // initialize collect score values for custom elements from initial value
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   // ---------- initialize collect count --------------------------------------
3477
3478   // initialize collect count values for non-custom elements
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   // add collect count values for all elements from pre-defined list
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   // ---------- initialize access direction -----------------------------------
3489
3490   // initialize access direction values to default (access from every side)
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   // set access direction value for certain elements from pre-defined list
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   // ---------- initialize explosion content ----------------------------------
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   // ---------- initialize recursion detection --------------------------------
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   // ---------- initialize graphics engine ------------------------------------
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3542      !setup.forced_scroll_delay           ? 0 :
3543      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3544   if (game.forced_scroll_delay_value == -1)
3545     game.scroll_delay_value =
3546       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3547
3548   // ---------- initialize game engine snapshots ------------------------------
3549   for (i = 0; i < MAX_PLAYERS; i++)
3550     game.snapshot.last_action[i] = 0;
3551   game.snapshot.changed_action = FALSE;
3552   game.snapshot.collected_item = FALSE;
3553   game.snapshot.mode =
3554     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3555      SNAPSHOT_MODE_EVERY_STEP :
3556      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3557      SNAPSHOT_MODE_EVERY_MOVE :
3558      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3559      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3560   game.snapshot.save_snapshot = FALSE;
3561
3562   // ---------- initialize level time for Supaplex engine ---------------------
3563   // Supaplex levels with time limit currently unsupported -- should be added
3564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3565     level.time = 0;
3566
3567   // ---------- initialize flags for handling game actions --------------------
3568
3569   // set flags for game actions to default values
3570   game.use_key_actions = TRUE;
3571   game.use_mouse_actions = FALSE;
3572
3573   // when using Mirror Magic game engine, handle mouse events only
3574   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3575   {
3576     game.use_key_actions = FALSE;
3577     game.use_mouse_actions = TRUE;
3578   }
3579
3580   // check for custom elements with mouse click events
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3582   {
3583     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3584     {
3585       int element = EL_CUSTOM_START + i;
3586
3587       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3590           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3591         game.use_mouse_actions = TRUE;
3592     }
3593   }
3594 }
3595
3596 static int get_num_special_action(int element, int action_first,
3597                                   int action_last)
3598 {
3599   int num_special_action = 0;
3600   int i, j;
3601
3602   for (i = action_first; i <= action_last; i++)
3603   {
3604     boolean found = FALSE;
3605
3606     for (j = 0; j < NUM_DIRECTIONS; j++)
3607       if (el_act_dir2img(element, i, j) !=
3608           el_act_dir2img(element, ACTION_DEFAULT, j))
3609         found = TRUE;
3610
3611     if (found)
3612       num_special_action++;
3613     else
3614       break;
3615   }
3616
3617   return num_special_action;
3618 }
3619
3620
3621 // ============================================================================
3622 // InitGame()
3623 // ----------------------------------------------------------------------------
3624 // initialize and start new game
3625 // ============================================================================
3626
3627 #if DEBUG_INIT_PLAYER
3628 static void DebugPrintPlayerStatus(char *message)
3629 {
3630   int i;
3631
3632   if (!options.debug)
3633     return;
3634
3635   Debug("game:init:player", "%s:", message);
3636
3637   for (i = 0; i < MAX_PLAYERS; i++)
3638   {
3639     struct PlayerInfo *player = &stored_player[i];
3640
3641     Debug("game:init:player",
3642           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3643           i + 1,
3644           player->present,
3645           player->connected,
3646           player->connected_locally,
3647           player->connected_network,
3648           player->active,
3649           (local_player == player ? " (local player)" : ""));
3650   }
3651 }
3652 #endif
3653
3654 void InitGame(void)
3655 {
3656   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3658   int fade_mask = REDRAW_FIELD;
3659   boolean restarting = (game_status == GAME_MODE_PLAYING);
3660   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3661   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3662   int initial_move_dir = MV_DOWN;
3663   int i, j, x, y;
3664
3665   // required here to update video display before fading (FIX THIS)
3666   DrawMaskedBorder(REDRAW_DOOR_2);
3667
3668   if (!game.restart_level)
3669     CloseDoor(DOOR_CLOSE_1);
3670
3671   if (restarting)
3672   {
3673     // force fading out global animations displayed during game play
3674     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3675   }
3676   else
3677   {
3678     SetGameStatus(GAME_MODE_PLAYING);
3679   }
3680
3681   if (level_editor_test_game)
3682     FadeSkipNextFadeOut();
3683   else
3684     FadeSetEnterScreen();
3685
3686   if (CheckFadeAll())
3687     fade_mask = REDRAW_ALL;
3688
3689   FadeLevelSoundsAndMusic();
3690
3691   ExpireSoundLoops(TRUE);
3692
3693   FadeOut(fade_mask);
3694
3695   if (restarting)
3696   {
3697     // force restarting global animations displayed during game play
3698     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3699
3700     // this is required for "transforming" fade modes like cross-fading
3701     // (else global animations will be stopped, but not restarted here)
3702     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3703
3704     SetGameStatus(GAME_MODE_PLAYING);
3705   }
3706
3707   if (level_editor_test_game)
3708     FadeSkipNextFadeIn();
3709
3710   // needed if different viewport properties defined for playing
3711   ChangeViewportPropertiesIfNeeded();
3712
3713   ClearField();
3714
3715   DrawCompleteVideoDisplay();
3716
3717   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3718
3719   InitGameEngine();
3720   InitGameControlValues();
3721
3722   if (tape.recording)
3723   {
3724     // initialize tape actions from game when recording tape
3725     tape.use_key_actions   = game.use_key_actions;
3726     tape.use_mouse_actions = game.use_mouse_actions;
3727
3728     // initialize visible playfield size when recording tape (for team mode)
3729     tape.scr_fieldx = SCR_FIELDX;
3730     tape.scr_fieldy = SCR_FIELDY;
3731   }
3732
3733   // don't play tapes over network
3734   network_playing = (network.enabled && !tape.playing);
3735
3736   for (i = 0; i < MAX_PLAYERS; i++)
3737   {
3738     struct PlayerInfo *player = &stored_player[i];
3739
3740     player->index_nr = i;
3741     player->index_bit = (1 << i);
3742     player->element_nr = EL_PLAYER_1 + i;
3743
3744     player->present = FALSE;
3745     player->active = FALSE;
3746     player->mapped = FALSE;
3747
3748     player->killed = FALSE;
3749     player->reanimated = FALSE;
3750     player->buried = FALSE;
3751
3752     player->action = 0;
3753     player->effective_action = 0;
3754     player->programmed_action = 0;
3755     player->snap_action = 0;
3756
3757     player->mouse_action.lx = 0;
3758     player->mouse_action.ly = 0;
3759     player->mouse_action.button = 0;
3760     player->mouse_action.button_hint = 0;
3761
3762     player->effective_mouse_action.lx = 0;
3763     player->effective_mouse_action.ly = 0;
3764     player->effective_mouse_action.button = 0;
3765     player->effective_mouse_action.button_hint = 0;
3766
3767     for (j = 0; j < MAX_NUM_KEYS; j++)
3768       player->key[j] = FALSE;
3769
3770     player->num_white_keys = 0;
3771
3772     player->dynabomb_count = 0;
3773     player->dynabomb_size = 1;
3774     player->dynabombs_left = 0;
3775     player->dynabomb_xl = FALSE;
3776
3777     player->MovDir = initial_move_dir;
3778     player->MovPos = 0;
3779     player->GfxPos = 0;
3780     player->GfxDir = initial_move_dir;
3781     player->GfxAction = ACTION_DEFAULT;
3782     player->Frame = 0;
3783     player->StepFrame = 0;
3784
3785     player->initial_element = player->element_nr;
3786     player->artwork_element =
3787       (level.use_artwork_element[i] ? level.artwork_element[i] :
3788        player->element_nr);
3789     player->use_murphy = FALSE;
3790
3791     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3792     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3793
3794     player->gravity = level.initial_player_gravity[i];
3795
3796     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3797
3798     player->actual_frame_counter.count = 0;
3799     player->actual_frame_counter.value = 1;
3800
3801     player->step_counter = 0;
3802
3803     player->last_move_dir = initial_move_dir;
3804
3805     player->is_active = FALSE;
3806
3807     player->is_waiting = FALSE;
3808     player->is_moving = FALSE;
3809     player->is_auto_moving = FALSE;
3810     player->is_digging = FALSE;
3811     player->is_snapping = FALSE;
3812     player->is_collecting = FALSE;
3813     player->is_pushing = FALSE;
3814     player->is_switching = FALSE;
3815     player->is_dropping = FALSE;
3816     player->is_dropping_pressed = FALSE;
3817
3818     player->is_bored = FALSE;
3819     player->is_sleeping = FALSE;
3820
3821     player->was_waiting = TRUE;
3822     player->was_moving = FALSE;
3823     player->was_snapping = FALSE;
3824     player->was_dropping = FALSE;
3825
3826     player->force_dropping = FALSE;
3827
3828     player->frame_counter_bored = -1;
3829     player->frame_counter_sleeping = -1;
3830
3831     player->anim_delay_counter = 0;
3832     player->post_delay_counter = 0;
3833
3834     player->dir_waiting = initial_move_dir;
3835     player->action_waiting = ACTION_DEFAULT;
3836     player->last_action_waiting = ACTION_DEFAULT;
3837     player->special_action_bored = ACTION_DEFAULT;
3838     player->special_action_sleeping = ACTION_DEFAULT;
3839
3840     player->switch_x = -1;
3841     player->switch_y = -1;
3842
3843     player->drop_x = -1;
3844     player->drop_y = -1;
3845
3846     player->show_envelope = 0;
3847
3848     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3849
3850     player->push_delay       = -1;      // initialized when pushing starts
3851     player->push_delay_value = game.initial_push_delay_value;
3852
3853     player->drop_delay = 0;
3854     player->drop_pressed_delay = 0;
3855
3856     player->last_jx = -1;
3857     player->last_jy = -1;
3858     player->jx = -1;
3859     player->jy = -1;
3860
3861     player->shield_normal_time_left = 0;
3862     player->shield_deadly_time_left = 0;
3863
3864     player->last_removed_element = EL_UNDEFINED;
3865
3866     player->inventory_infinite_element = EL_UNDEFINED;
3867     player->inventory_size = 0;
3868
3869     if (level.use_initial_inventory[i])
3870     {
3871       for (j = 0; j < level.initial_inventory_size[i]; j++)
3872       {
3873         int element = level.initial_inventory_content[i][j];
3874         int collect_count = element_info[element].collect_count_initial;
3875         int k;
3876
3877         if (!IS_CUSTOM_ELEMENT(element))
3878           collect_count = 1;
3879
3880         if (collect_count == 0)
3881           player->inventory_infinite_element = element;
3882         else
3883           for (k = 0; k < collect_count; k++)
3884             if (player->inventory_size < MAX_INVENTORY_SIZE)
3885               player->inventory_element[player->inventory_size++] = element;
3886       }
3887     }
3888
3889     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3890     SnapField(player, 0, 0);
3891
3892     map_player_action[i] = i;
3893   }
3894
3895   network_player_action_received = FALSE;
3896
3897   // initial null action
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905
3906   TapeTimeFrames = 0;
3907   TapeTime = 0;
3908
3909   ScreenMovDir = MV_NONE;
3910   ScreenMovPos = 0;
3911   ScreenGfxPos = 0;
3912
3913   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3914
3915   game.robot_wheel_x = -1;
3916   game.robot_wheel_y = -1;
3917
3918   game.exit_x = -1;
3919   game.exit_y = -1;
3920
3921   game.all_players_gone = FALSE;
3922
3923   game.LevelSolved = FALSE;
3924   game.GameOver = FALSE;
3925
3926   game.GamePlayed = !tape.playing;
3927
3928   game.LevelSolved_GameWon = FALSE;
3929   game.LevelSolved_GameEnd = FALSE;
3930   game.LevelSolved_SaveTape = FALSE;
3931   game.LevelSolved_SaveScore = FALSE;
3932
3933   game.LevelSolved_CountingTime = 0;
3934   game.LevelSolved_CountingScore = 0;
3935   game.LevelSolved_CountingHealth = 0;
3936
3937   game.RestartGameRequested = FALSE;
3938
3939   game.panel.active = TRUE;
3940
3941   game.no_level_time_limit = (level.time == 0);
3942   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3943
3944   game.yamyam_content_nr = 0;
3945   game.robot_wheel_active = FALSE;
3946   game.magic_wall_active = FALSE;
3947   game.magic_wall_time_left = 0;
3948   game.light_time_left = 0;
3949   game.timegate_time_left = 0;
3950   game.switchgate_pos = 0;
3951   game.wind_direction = level.wind_direction_initial;
3952
3953   game.time_final = 0;
3954   game.score_time_final = 0;
3955
3956   game.score = 0;
3957   game.score_final = 0;
3958
3959   game.health = MAX_HEALTH;
3960   game.health_final = MAX_HEALTH;
3961
3962   game.gems_still_needed = level.gems_needed;
3963   game.sokoban_fields_still_needed = 0;
3964   game.sokoban_objects_still_needed = 0;
3965   game.lights_still_needed = 0;
3966   game.players_still_needed = 0;
3967   game.friends_still_needed = 0;
3968
3969   game.lenses_time_left = 0;
3970   game.magnify_time_left = 0;
3971
3972   game.ball_active = level.ball_active_initial;
3973   game.ball_content_nr = 0;
3974
3975   game.explosions_delayed = TRUE;
3976
3977   // special case: set custom artwork setting to initial value
3978   game.use_masked_elements = game.use_masked_elements_initial;
3979
3980   for (i = 0; i < NUM_BELTS; i++)
3981   {
3982     game.belt_dir[i] = MV_NONE;
3983     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3984   }
3985
3986   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3987     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3988
3989 #if DEBUG_INIT_PLAYER
3990   DebugPrintPlayerStatus("Player status at level initialization");
3991 #endif
3992
3993   SCAN_PLAYFIELD(x, y)
3994   {
3995     Tile[x][y] = Last[x][y] = level.field[x][y];
3996     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3997     ChangeDelay[x][y] = 0;
3998     ChangePage[x][y] = -1;
3999     CustomValue[x][y] = 0;              // initialized in InitField()
4000     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4001     AmoebaNr[x][y] = 0;
4002     WasJustMoving[x][y] = 0;
4003     WasJustFalling[x][y] = 0;
4004     CheckCollision[x][y] = 0;
4005     CheckImpact[x][y] = 0;
4006     Stop[x][y] = FALSE;
4007     Pushed[x][y] = FALSE;
4008
4009     ChangeCount[x][y] = 0;
4010     ChangeEvent[x][y] = -1;
4011
4012     ExplodePhase[x][y] = 0;
4013     ExplodeDelay[x][y] = 0;
4014     ExplodeField[x][y] = EX_TYPE_NONE;
4015
4016     RunnerVisit[x][y] = 0;
4017     PlayerVisit[x][y] = 0;
4018
4019     GfxFrame[x][y] = 0;
4020     GfxRandom[x][y] = INIT_GFX_RANDOM();
4021     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4022     GfxElement[x][y] = EL_UNDEFINED;
4023     GfxElementEmpty[x][y] = EL_EMPTY;
4024     GfxAction[x][y] = ACTION_DEFAULT;
4025     GfxDir[x][y] = MV_NONE;
4026     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4027   }
4028
4029   SCAN_PLAYFIELD(x, y)
4030   {
4031     InitFieldForEngine(x, y);
4032
4033     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4034       emulate_bd = FALSE;
4035     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4036       emulate_sp = FALSE;
4037
4038     InitField(x, y, TRUE);
4039
4040     ResetGfxAnimation(x, y);
4041   }
4042
4043   InitBeltMovement();
4044
4045   // required if level does not contain any "empty space" element
4046   if (element_info[EL_EMPTY].use_gfx_element)
4047     game.use_masked_elements = TRUE;
4048
4049   for (i = 0; i < MAX_PLAYERS; i++)
4050   {
4051     struct PlayerInfo *player = &stored_player[i];
4052
4053     // set number of special actions for bored and sleeping animation
4054     player->num_special_action_bored =
4055       get_num_special_action(player->artwork_element,
4056                              ACTION_BORING_1, ACTION_BORING_LAST);
4057     player->num_special_action_sleeping =
4058       get_num_special_action(player->artwork_element,
4059                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4060   }
4061
4062   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4063                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4064
4065   // initialize type of slippery elements
4066   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4067   {
4068     if (!IS_CUSTOM_ELEMENT(i))
4069     {
4070       // default: elements slip down either to the left or right randomly
4071       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4072
4073       // SP style elements prefer to slip down on the left side
4074       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4075         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4076
4077       // BD style elements prefer to slip down on the left side
4078       if (game.emulation == EMU_BOULDERDASH)
4079         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4080     }
4081   }
4082
4083   // initialize explosion and ignition delay
4084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4085   {
4086     if (!IS_CUSTOM_ELEMENT(i))
4087     {
4088       int num_phase = 8;
4089       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4090                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4091                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4092       int last_phase = (num_phase + 1) * delay;
4093       int half_phase = (num_phase / 2) * delay;
4094
4095       element_info[i].explosion_delay = last_phase - 1;
4096       element_info[i].ignition_delay = half_phase;
4097
4098       if (i == EL_BLACK_ORB)
4099         element_info[i].ignition_delay = 1;
4100     }
4101   }
4102
4103   // correct non-moving belts to start moving left
4104   for (i = 0; i < NUM_BELTS; i++)
4105     if (game.belt_dir[i] == MV_NONE)
4106       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4107
4108 #if USE_NEW_PLAYER_ASSIGNMENTS
4109   // use preferred player also in local single-player mode
4110   if (!network.enabled && !game.team_mode)
4111   {
4112     int new_index_nr = setup.network_player_nr;
4113
4114     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4115     {
4116       for (i = 0; i < MAX_PLAYERS; i++)
4117         stored_player[i].connected_locally = FALSE;
4118
4119       stored_player[new_index_nr].connected_locally = TRUE;
4120     }
4121   }
4122
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124   {
4125     stored_player[i].connected = FALSE;
4126
4127     // in network game mode, the local player might not be the first player
4128     if (stored_player[i].connected_locally)
4129       local_player = &stored_player[i];
4130   }
4131
4132   if (!network.enabled)
4133     local_player->connected = TRUE;
4134
4135   if (tape.playing)
4136   {
4137     for (i = 0; i < MAX_PLAYERS; i++)
4138       stored_player[i].connected = tape.player_participates[i];
4139   }
4140   else if (network.enabled)
4141   {
4142     // add team mode players connected over the network (needed for correct
4143     // assignment of player figures from level to locally playing players)
4144
4145     for (i = 0; i < MAX_PLAYERS; i++)
4146       if (stored_player[i].connected_network)
4147         stored_player[i].connected = TRUE;
4148   }
4149   else if (game.team_mode)
4150   {
4151     // try to guess locally connected team mode players (needed for correct
4152     // assignment of player figures from level to locally playing players)
4153
4154     for (i = 0; i < MAX_PLAYERS; i++)
4155       if (setup.input[i].use_joystick ||
4156           setup.input[i].key.left != KSYM_UNDEFINED)
4157         stored_player[i].connected = TRUE;
4158   }
4159
4160 #if DEBUG_INIT_PLAYER
4161   DebugPrintPlayerStatus("Player status after level initialization");
4162 #endif
4163
4164 #if DEBUG_INIT_PLAYER
4165   Debug("game:init:player", "Reassigning players ...");
4166 #endif
4167
4168   // check if any connected player was not found in playfield
4169   for (i = 0; i < MAX_PLAYERS; i++)
4170   {
4171     struct PlayerInfo *player = &stored_player[i];
4172
4173     if (player->connected && !player->present)
4174     {
4175       struct PlayerInfo *field_player = NULL;
4176
4177 #if DEBUG_INIT_PLAYER
4178       Debug("game:init:player",
4179             "- looking for field player for player %d ...", i + 1);
4180 #endif
4181
4182       // assign first free player found that is present in the playfield
4183
4184       // first try: look for unmapped playfield player that is not connected
4185       for (j = 0; j < MAX_PLAYERS; j++)
4186         if (field_player == NULL &&
4187             stored_player[j].present &&
4188             !stored_player[j].mapped &&
4189             !stored_player[j].connected)
4190           field_player = &stored_player[j];
4191
4192       // second try: look for *any* unmapped playfield player
4193       for (j = 0; j < MAX_PLAYERS; j++)
4194         if (field_player == NULL &&
4195             stored_player[j].present &&
4196             !stored_player[j].mapped)
4197           field_player = &stored_player[j];
4198
4199       if (field_player != NULL)
4200       {
4201         int jx = field_player->jx, jy = field_player->jy;
4202
4203 #if DEBUG_INIT_PLAYER
4204         Debug("game:init:player", "- found player %d",
4205               field_player->index_nr + 1);
4206 #endif
4207
4208         player->present = FALSE;
4209         player->active = FALSE;
4210
4211         field_player->present = TRUE;
4212         field_player->active = TRUE;
4213
4214         /*
4215         player->initial_element = field_player->initial_element;
4216         player->artwork_element = field_player->artwork_element;
4217
4218         player->block_last_field       = field_player->block_last_field;
4219         player->block_delay_adjustment = field_player->block_delay_adjustment;
4220         */
4221
4222         StorePlayer[jx][jy] = field_player->element_nr;
4223
4224         field_player->jx = field_player->last_jx = jx;
4225         field_player->jy = field_player->last_jy = jy;
4226
4227         if (local_player == player)
4228           local_player = field_player;
4229
4230         map_player_action[field_player->index_nr] = i;
4231
4232         field_player->mapped = TRUE;
4233
4234 #if DEBUG_INIT_PLAYER
4235         Debug("game:init:player", "- map_player_action[%d] == %d",
4236               field_player->index_nr + 1, i + 1);
4237 #endif
4238       }
4239     }
4240
4241     if (player->connected && player->present)
4242       player->mapped = TRUE;
4243   }
4244
4245 #if DEBUG_INIT_PLAYER
4246   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4247 #endif
4248
4249 #else
4250
4251   // check if any connected player was not found in playfield
4252   for (i = 0; i < MAX_PLAYERS; i++)
4253   {
4254     struct PlayerInfo *player = &stored_player[i];
4255
4256     if (player->connected && !player->present)
4257     {
4258       for (j = 0; j < MAX_PLAYERS; j++)
4259       {
4260         struct PlayerInfo *field_player = &stored_player[j];
4261         int jx = field_player->jx, jy = field_player->jy;
4262
4263         // assign first free player found that is present in the playfield
4264         if (field_player->present && !field_player->connected)
4265         {
4266           player->present = TRUE;
4267           player->active = TRUE;
4268
4269           field_player->present = FALSE;
4270           field_player->active = FALSE;
4271
4272           player->initial_element = field_player->initial_element;
4273           player->artwork_element = field_player->artwork_element;
4274
4275           player->block_last_field       = field_player->block_last_field;
4276           player->block_delay_adjustment = field_player->block_delay_adjustment;
4277
4278           StorePlayer[jx][jy] = player->element_nr;
4279
4280           player->jx = player->last_jx = jx;
4281           player->jy = player->last_jy = jy;
4282
4283           break;
4284         }
4285       }
4286     }
4287   }
4288 #endif
4289
4290 #if 0
4291   Debug("game:init:player", "local_player->present == %d",
4292         local_player->present);
4293 #endif
4294
4295   // set focus to local player for network games, else to all players
4296   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4297   game.centered_player_nr_next = game.centered_player_nr;
4298   game.set_centered_player = FALSE;
4299   game.set_centered_player_wrap = FALSE;
4300
4301   if (network_playing && tape.recording)
4302   {
4303     // store client dependent player focus when recording network games
4304     tape.centered_player_nr_next = game.centered_player_nr_next;
4305     tape.set_centered_player = TRUE;
4306   }
4307
4308   if (tape.playing)
4309   {
4310     // when playing a tape, eliminate all players who do not participate
4311
4312 #if USE_NEW_PLAYER_ASSIGNMENTS
4313
4314     if (!game.team_mode)
4315     {
4316       for (i = 0; i < MAX_PLAYERS; i++)
4317       {
4318         if (stored_player[i].active &&
4319             !tape.player_participates[map_player_action[i]])
4320         {
4321           struct PlayerInfo *player = &stored_player[i];
4322           int jx = player->jx, jy = player->jy;
4323
4324 #if DEBUG_INIT_PLAYER
4325           Debug("game:init:player", "Removing player %d at (%d, %d)",
4326                 i + 1, jx, jy);
4327 #endif
4328
4329           player->active = FALSE;
4330           StorePlayer[jx][jy] = 0;
4331           Tile[jx][jy] = EL_EMPTY;
4332         }
4333       }
4334     }
4335
4336 #else
4337
4338     for (i = 0; i < MAX_PLAYERS; i++)
4339     {
4340       if (stored_player[i].active &&
4341           !tape.player_participates[i])
4342       {
4343         struct PlayerInfo *player = &stored_player[i];
4344         int jx = player->jx, jy = player->jy;
4345
4346         player->active = FALSE;
4347         StorePlayer[jx][jy] = 0;
4348         Tile[jx][jy] = EL_EMPTY;
4349       }
4350     }
4351 #endif
4352   }
4353   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4354   {
4355     // when in single player mode, eliminate all but the local player
4356
4357     for (i = 0; i < MAX_PLAYERS; i++)
4358     {
4359       struct PlayerInfo *player = &stored_player[i];
4360
4361       if (player->active && player != local_player)
4362       {
4363         int jx = player->jx, jy = player->jy;
4364
4365         player->active = FALSE;
4366         player->present = FALSE;
4367
4368         StorePlayer[jx][jy] = 0;
4369         Tile[jx][jy] = EL_EMPTY;
4370       }
4371     }
4372   }
4373
4374   for (i = 0; i < MAX_PLAYERS; i++)
4375     if (stored_player[i].active)
4376       game.players_still_needed++;
4377
4378   if (level.solved_by_one_player)
4379     game.players_still_needed = 1;
4380
4381   // when recording the game, store which players take part in the game
4382   if (tape.recording)
4383   {
4384 #if USE_NEW_PLAYER_ASSIGNMENTS
4385     for (i = 0; i < MAX_PLAYERS; i++)
4386       if (stored_player[i].connected)
4387         tape.player_participates[i] = TRUE;
4388 #else
4389     for (i = 0; i < MAX_PLAYERS; i++)
4390       if (stored_player[i].active)
4391         tape.player_participates[i] = TRUE;
4392 #endif
4393   }
4394
4395 #if DEBUG_INIT_PLAYER
4396   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4397 #endif
4398
4399   if (BorderElement == EL_EMPTY)
4400   {
4401     SBX_Left = 0;
4402     SBX_Right = lev_fieldx - SCR_FIELDX;
4403     SBY_Upper = 0;
4404     SBY_Lower = lev_fieldy - SCR_FIELDY;
4405   }
4406   else
4407   {
4408     SBX_Left = -1;
4409     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4410     SBY_Upper = -1;
4411     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4412   }
4413
4414   if (full_lev_fieldx <= SCR_FIELDX)
4415     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4416   if (full_lev_fieldy <= SCR_FIELDY)
4417     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4418
4419   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4420     SBX_Left--;
4421   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4422     SBY_Upper--;
4423
4424   // if local player not found, look for custom element that might create
4425   // the player (make some assumptions about the right custom element)
4426   if (!local_player->present)
4427   {
4428     int start_x = 0, start_y = 0;
4429     int found_rating = 0;
4430     int found_element = EL_UNDEFINED;
4431     int player_nr = local_player->index_nr;
4432
4433     SCAN_PLAYFIELD(x, y)
4434     {
4435       int element = Tile[x][y];
4436       int content;
4437       int xx, yy;
4438       boolean is_player;
4439
4440       if (level.use_start_element[player_nr] &&
4441           level.start_element[player_nr] == element &&
4442           found_rating < 4)
4443       {
4444         start_x = x;
4445         start_y = y;
4446
4447         found_rating = 4;
4448         found_element = element;
4449       }
4450
4451       if (!IS_CUSTOM_ELEMENT(element))
4452         continue;
4453
4454       if (CAN_CHANGE(element))
4455       {
4456         for (i = 0; i < element_info[element].num_change_pages; i++)
4457         {
4458           // check for player created from custom element as single target
4459           content = element_info[element].change_page[i].target_element;
4460           is_player = IS_PLAYER_ELEMENT(content);
4461
4462           if (is_player && (found_rating < 3 ||
4463                             (found_rating == 3 && element < found_element)))
4464           {
4465             start_x = x;
4466             start_y = y;
4467
4468             found_rating = 3;
4469             found_element = element;
4470           }
4471         }
4472       }
4473
4474       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4475       {
4476         // check for player created from custom element as explosion content
4477         content = element_info[element].content.e[xx][yy];
4478         is_player = IS_PLAYER_ELEMENT(content);
4479
4480         if (is_player && (found_rating < 2 ||
4481                           (found_rating == 2 && element < found_element)))
4482         {
4483           start_x = x + xx - 1;
4484           start_y = y + yy - 1;
4485
4486           found_rating = 2;
4487           found_element = element;
4488         }
4489
4490         if (!CAN_CHANGE(element))
4491           continue;
4492
4493         for (i = 0; i < element_info[element].num_change_pages; i++)
4494         {
4495           // check for player created from custom element as extended target
4496           content =
4497             element_info[element].change_page[i].target_content.e[xx][yy];
4498
4499           is_player = IS_PLAYER_ELEMENT(content);
4500
4501           if (is_player && (found_rating < 1 ||
4502                             (found_rating == 1 && element < found_element)))
4503           {
4504             start_x = x + xx - 1;
4505             start_y = y + yy - 1;
4506
4507             found_rating = 1;
4508             found_element = element;
4509           }
4510         }
4511       }
4512     }
4513
4514     scroll_x = SCROLL_POSITION_X(start_x);
4515     scroll_y = SCROLL_POSITION_Y(start_y);
4516   }
4517   else
4518   {
4519     scroll_x = SCROLL_POSITION_X(local_player->jx);
4520     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4521   }
4522
4523   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4524     scroll_x = game.forced_scroll_x;
4525   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4526     scroll_y = game.forced_scroll_y;
4527
4528   // !!! FIX THIS (START) !!!
4529   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4530   {
4531     InitGameEngine_BD();
4532   }
4533   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4534   {
4535     InitGameEngine_EM();
4536   }
4537   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4538   {
4539     InitGameEngine_SP();
4540   }
4541   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542   {
4543     InitGameEngine_MM();
4544   }
4545   else
4546   {
4547     DrawLevel(REDRAW_FIELD);
4548     DrawAllPlayers();
4549
4550     // after drawing the level, correct some elements
4551     if (game.timegate_time_left == 0)
4552       CloseAllOpenTimegates();
4553   }
4554
4555   // blit playfield from scroll buffer to normal back buffer for fading in
4556   BlitScreenToBitmap(backbuffer);
4557   // !!! FIX THIS (END) !!!
4558
4559   DrawMaskedBorder(fade_mask);
4560
4561   FadeIn(fade_mask);
4562
4563 #if 1
4564   // full screen redraw is required at this point in the following cases:
4565   // - special editor door undrawn when game was started from level editor
4566   // - drawing area (playfield) was changed and has to be removed completely
4567   redraw_mask = REDRAW_ALL;
4568   BackToFront();
4569 #endif
4570
4571   if (!game.restart_level)
4572   {
4573     // copy default game door content to main double buffer
4574
4575     // !!! CHECK AGAIN !!!
4576     SetPanelBackground();
4577     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4578     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4579   }
4580
4581   SetPanelBackground();
4582   SetDrawBackgroundMask(REDRAW_DOOR_1);
4583
4584   UpdateAndDisplayGameControlValues();
4585
4586   if (!game.restart_level)
4587   {
4588     UnmapGameButtons();
4589     UnmapTapeButtons();
4590
4591     FreeGameButtons();
4592     CreateGameButtons();
4593
4594     MapGameButtons();
4595     MapTapeButtons();
4596
4597     // copy actual game door content to door double buffer for OpenDoor()
4598     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4599
4600     OpenDoor(DOOR_OPEN_ALL);
4601
4602     KeyboardAutoRepeatOffUnlessAutoplay();
4603
4604 #if DEBUG_INIT_PLAYER
4605     DebugPrintPlayerStatus("Player status (final)");
4606 #endif
4607   }
4608
4609   UnmapAllGadgets();
4610
4611   MapGameButtons();
4612   MapTapeButtons();
4613
4614   if (!game.restart_level && !tape.playing)
4615   {
4616     LevelStats_incPlayed(level_nr);
4617
4618     SaveLevelSetup_SeriesInfo();
4619   }
4620
4621   game.restart_level = FALSE;
4622   game.request_active = FALSE;
4623   game.envelope_active = FALSE;
4624   game.any_door_active = FALSE;
4625
4626   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4627     InitGameActions_MM();
4628
4629   SaveEngineSnapshotToListInitial();
4630
4631   if (!game.restart_level)
4632   {
4633     PlaySound(SND_GAME_STARTING);
4634
4635     if (setup.sound_music)
4636       PlayLevelMusic();
4637   }
4638
4639   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4640 }
4641
4642 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4643                         int actual_player_x, int actual_player_y)
4644 {
4645   // this is used for non-R'n'D game engines to update certain engine values
4646
4647   // needed to determine if sounds are played within the visible screen area
4648   scroll_x = actual_scroll_x;
4649   scroll_y = actual_scroll_y;
4650
4651   // needed to get player position for "follow finger" playing input method
4652   local_player->jx = actual_player_x;
4653   local_player->jy = actual_player_y;
4654 }
4655
4656 void InitMovDir(int x, int y)
4657 {
4658   int i, element = Tile[x][y];
4659   static int xy[4][2] =
4660   {
4661     {  0, +1 },
4662     { +1,  0 },
4663     {  0, -1 },
4664     { -1,  0 }
4665   };
4666   static int direction[3][4] =
4667   {
4668     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4669     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4670     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4671   };
4672
4673   switch (element)
4674   {
4675     case EL_BUG_RIGHT:
4676     case EL_BUG_UP:
4677     case EL_BUG_LEFT:
4678     case EL_BUG_DOWN:
4679       Tile[x][y] = EL_BUG;
4680       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4681       break;
4682
4683     case EL_SPACESHIP_RIGHT:
4684     case EL_SPACESHIP_UP:
4685     case EL_SPACESHIP_LEFT:
4686     case EL_SPACESHIP_DOWN:
4687       Tile[x][y] = EL_SPACESHIP;
4688       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4689       break;
4690
4691     case EL_BD_BUTTERFLY_RIGHT:
4692     case EL_BD_BUTTERFLY_UP:
4693     case EL_BD_BUTTERFLY_LEFT:
4694     case EL_BD_BUTTERFLY_DOWN:
4695       Tile[x][y] = EL_BD_BUTTERFLY;
4696       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4697       break;
4698
4699     case EL_BD_FIREFLY_RIGHT:
4700     case EL_BD_FIREFLY_UP:
4701     case EL_BD_FIREFLY_LEFT:
4702     case EL_BD_FIREFLY_DOWN:
4703       Tile[x][y] = EL_BD_FIREFLY;
4704       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4705       break;
4706
4707     case EL_PACMAN_RIGHT:
4708     case EL_PACMAN_UP:
4709     case EL_PACMAN_LEFT:
4710     case EL_PACMAN_DOWN:
4711       Tile[x][y] = EL_PACMAN;
4712       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4713       break;
4714
4715     case EL_YAMYAM_LEFT:
4716     case EL_YAMYAM_RIGHT:
4717     case EL_YAMYAM_UP:
4718     case EL_YAMYAM_DOWN:
4719       Tile[x][y] = EL_YAMYAM;
4720       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4721       break;
4722
4723     case EL_SP_SNIKSNAK:
4724       MovDir[x][y] = MV_UP;
4725       break;
4726
4727     case EL_SP_ELECTRON:
4728       MovDir[x][y] = MV_LEFT;
4729       break;
4730
4731     case EL_MOLE_LEFT:
4732     case EL_MOLE_RIGHT:
4733     case EL_MOLE_UP:
4734     case EL_MOLE_DOWN:
4735       Tile[x][y] = EL_MOLE;
4736       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4737       break;
4738
4739     case EL_SPRING_LEFT:
4740     case EL_SPRING_RIGHT:
4741       Tile[x][y] = EL_SPRING;
4742       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4743       break;
4744
4745     default:
4746       if (IS_CUSTOM_ELEMENT(element))
4747       {
4748         struct ElementInfo *ei = &element_info[element];
4749         int move_direction_initial = ei->move_direction_initial;
4750         int move_pattern = ei->move_pattern;
4751
4752         if (move_direction_initial == MV_START_PREVIOUS)
4753         {
4754           if (MovDir[x][y] != MV_NONE)
4755             return;
4756
4757           move_direction_initial = MV_START_AUTOMATIC;
4758         }
4759
4760         if (move_direction_initial == MV_START_RANDOM)
4761           MovDir[x][y] = 1 << RND(4);
4762         else if (move_direction_initial & MV_ANY_DIRECTION)
4763           MovDir[x][y] = move_direction_initial;
4764         else if (move_pattern == MV_ALL_DIRECTIONS ||
4765                  move_pattern == MV_TURNING_LEFT ||
4766                  move_pattern == MV_TURNING_RIGHT ||
4767                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4768                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4769                  move_pattern == MV_TURNING_RANDOM)
4770           MovDir[x][y] = 1 << RND(4);
4771         else if (move_pattern == MV_HORIZONTAL)
4772           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4773         else if (move_pattern == MV_VERTICAL)
4774           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4775         else if (move_pattern & MV_ANY_DIRECTION)
4776           MovDir[x][y] = element_info[element].move_pattern;
4777         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4778                  move_pattern == MV_ALONG_RIGHT_SIDE)
4779         {
4780           // use random direction as default start direction
4781           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4782             MovDir[x][y] = 1 << RND(4);
4783
4784           for (i = 0; i < NUM_DIRECTIONS; i++)
4785           {
4786             int x1 = x + xy[i][0];
4787             int y1 = y + xy[i][1];
4788
4789             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4790             {
4791               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4792                 MovDir[x][y] = direction[0][i];
4793               else
4794                 MovDir[x][y] = direction[1][i];
4795
4796               break;
4797             }
4798           }
4799         }                
4800       }
4801       else
4802       {
4803         MovDir[x][y] = 1 << RND(4);
4804
4805         if (element != EL_BUG &&
4806             element != EL_SPACESHIP &&
4807             element != EL_BD_BUTTERFLY &&
4808             element != EL_BD_FIREFLY)
4809           break;
4810
4811         for (i = 0; i < NUM_DIRECTIONS; i++)
4812         {
4813           int x1 = x + xy[i][0];
4814           int y1 = y + xy[i][1];
4815
4816           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4817           {
4818             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4819             {
4820               MovDir[x][y] = direction[0][i];
4821               break;
4822             }
4823             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4824                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4825             {
4826               MovDir[x][y] = direction[1][i];
4827               break;
4828             }
4829           }
4830         }
4831       }
4832       break;
4833   }
4834
4835   GfxDir[x][y] = MovDir[x][y];
4836 }
4837
4838 void InitAmoebaNr(int x, int y)
4839 {
4840   int i;
4841   int group_nr = AmoebaNeighbourNr(x, y);
4842
4843   if (group_nr == 0)
4844   {
4845     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4846     {
4847       if (AmoebaCnt[i] == 0)
4848       {
4849         group_nr = i;
4850         break;
4851       }
4852     }
4853   }
4854
4855   AmoebaNr[x][y] = group_nr;
4856   AmoebaCnt[group_nr]++;
4857   AmoebaCnt2[group_nr]++;
4858 }
4859
4860 static void LevelSolved_SetFinalGameValues(void)
4861 {
4862   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4863                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4864   game.score_time_final = (level.use_step_counter ? TimePlayed :
4865                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4866
4867   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4869                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4870                       game.score);
4871
4872   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4873                        MM_HEALTH(game_mm.laser_overload_value) :
4874                        game.health);
4875
4876   game.LevelSolved_CountingTime = game.time_final;
4877   game.LevelSolved_CountingScore = game.score_final;
4878   game.LevelSolved_CountingHealth = game.health_final;
4879 }
4880
4881 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4882 {
4883   game.LevelSolved_CountingTime = time;
4884   game.LevelSolved_CountingScore = score;
4885   game.LevelSolved_CountingHealth = health;
4886
4887   game_panel_controls[GAME_PANEL_TIME].value = time;
4888   game_panel_controls[GAME_PANEL_SCORE].value = score;
4889   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4890
4891   DisplayGameControlValues();
4892 }
4893
4894 static void LevelSolved(void)
4895 {
4896   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4897       game.players_still_needed > 0)
4898     return;
4899
4900   game.LevelSolved = TRUE;
4901   game.GameOver = TRUE;
4902
4903   tape.solved = TRUE;
4904
4905   // needed here to display correct panel values while player walks into exit
4906   LevelSolved_SetFinalGameValues();
4907 }
4908
4909 static boolean AdvanceToNextLevel(void)
4910 {
4911   if (setup.increment_levels &&
4912       level_nr < leveldir_current->last_level &&
4913       !network_playing)
4914   {
4915     level_nr++;         // advance to next level
4916     TapeErase();        // start with empty tape
4917
4918     if (setup.auto_play_next_level)
4919     {
4920       scores.continue_playing = TRUE;
4921       scores.next_level_nr = level_nr;
4922
4923       LoadLevel(level_nr);
4924
4925       SaveLevelSetup_SeriesInfo();
4926     }
4927
4928     return TRUE;
4929   }
4930
4931   return FALSE;
4932 }
4933
4934 void GameWon(void)
4935 {
4936   static int time_count_steps;
4937   static int time, time_final;
4938   static float score, score_final; // needed for time score < 10 for 10 seconds
4939   static int health, health_final;
4940   static int game_over_delay_1 = 0;
4941   static int game_over_delay_2 = 0;
4942   static int game_over_delay_3 = 0;
4943   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4944   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4945
4946   if (!game.LevelSolved_GameWon)
4947   {
4948     int i;
4949
4950     // do not start end game actions before the player stops moving (to exit)
4951     if (local_player->active && local_player->MovPos)
4952       return;
4953
4954     // calculate final game values after player finished walking into exit
4955     LevelSolved_SetFinalGameValues();
4956
4957     game.LevelSolved_GameWon = TRUE;
4958     game.LevelSolved_SaveTape = tape.recording;
4959     game.LevelSolved_SaveScore = !tape.playing;
4960
4961     if (!tape.playing)
4962     {
4963       LevelStats_incSolved(level_nr);
4964
4965       SaveLevelSetup_SeriesInfo();
4966     }
4967
4968     if (tape.auto_play)         // tape might already be stopped here
4969       tape.auto_play_level_solved = TRUE;
4970
4971     TapeStop();
4972
4973     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4974     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4975     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4976
4977     time = time_final = game.time_final;
4978     score = score_final = game.score_final;
4979     health = health_final = game.health_final;
4980
4981     // update game panel values before (delayed) counting of score (if any)
4982     LevelSolved_DisplayFinalGameValues(time, score, health);
4983
4984     // if level has time score defined, calculate new final game values
4985     if (time_score > 0)
4986     {
4987       int time_final_max = 999;
4988       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4989       int time_frames = 0;
4990       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4991       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4992
4993       if (TimeLeft > 0)
4994       {
4995         time_final = 0;
4996         time_frames = time_frames_left;
4997       }
4998       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4999       {
5000         time_final = time_final_max;
5001         time_frames = time_frames_final_max - time_frames_played;
5002       }
5003
5004       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5005
5006       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5007
5008       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5009       {
5010         // keep previous values (final values already processed here)
5011         time_final = time;
5012         score_final = score;
5013       }
5014       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5015       {
5016         health_final = 0;
5017         score_final += health * time_score;
5018       }
5019
5020       game.score_final = score_final;
5021       game.health_final = health_final;
5022     }
5023
5024     // if not counting score after game, immediately update game panel values
5025     if (level_editor_test_game || !setup.count_score_after_game ||
5026         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5027     {
5028       time = time_final;
5029       score = score_final;
5030
5031       LevelSolved_DisplayFinalGameValues(time, score, health);
5032     }
5033
5034     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5035     {
5036       // check if last player has left the level
5037       if (game.exit_x >= 0 &&
5038           game.exit_y >= 0)
5039       {
5040         int x = game.exit_x;
5041         int y = game.exit_y;
5042         int element = Tile[x][y];
5043
5044         // close exit door after last player
5045         if ((game.all_players_gone &&
5046              (element == EL_EXIT_OPEN ||
5047               element == EL_SP_EXIT_OPEN ||
5048               element == EL_STEEL_EXIT_OPEN)) ||
5049             element == EL_EM_EXIT_OPEN ||
5050             element == EL_EM_STEEL_EXIT_OPEN)
5051         {
5052
5053           Tile[x][y] =
5054             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5055              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5056              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5057              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5058              EL_EM_STEEL_EXIT_CLOSING);
5059
5060           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5061         }
5062
5063         // player disappears
5064         DrawLevelField(x, y);
5065       }
5066
5067       for (i = 0; i < MAX_PLAYERS; i++)
5068       {
5069         struct PlayerInfo *player = &stored_player[i];
5070
5071         if (player->present)
5072         {
5073           RemovePlayer(player);
5074
5075           // player disappears
5076           DrawLevelField(player->jx, player->jy);
5077         }
5078       }
5079     }
5080
5081     PlaySound(SND_GAME_WINNING);
5082   }
5083
5084   if (setup.count_score_after_game)
5085   {
5086     if (time != time_final)
5087     {
5088       if (game_over_delay_1 > 0)
5089       {
5090         game_over_delay_1--;
5091
5092         return;
5093       }
5094
5095       int time_to_go = ABS(time_final - time);
5096       int time_count_dir = (time < time_final ? +1 : -1);
5097
5098       if (time_to_go < time_count_steps)
5099         time_count_steps = 1;
5100
5101       time  += time_count_steps * time_count_dir;
5102       score += time_count_steps * time_score;
5103
5104       // set final score to correct rounding differences after counting score
5105       if (time == time_final)
5106         score = score_final;
5107
5108       LevelSolved_DisplayFinalGameValues(time, score, health);
5109
5110       if (time == time_final)
5111         StopSound(SND_GAME_LEVELTIME_BONUS);
5112       else if (setup.sound_loops)
5113         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5114       else
5115         PlaySound(SND_GAME_LEVELTIME_BONUS);
5116
5117       return;
5118     }
5119
5120     if (health != health_final)
5121     {
5122       if (game_over_delay_2 > 0)
5123       {
5124         game_over_delay_2--;
5125
5126         return;
5127       }
5128
5129       int health_count_dir = (health < health_final ? +1 : -1);
5130
5131       health += health_count_dir;
5132       score  += time_score;
5133
5134       LevelSolved_DisplayFinalGameValues(time, score, health);
5135
5136       if (health == health_final)
5137         StopSound(SND_GAME_LEVELTIME_BONUS);
5138       else if (setup.sound_loops)
5139         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5140       else
5141         PlaySound(SND_GAME_LEVELTIME_BONUS);
5142
5143       return;
5144     }
5145   }
5146
5147   game.panel.active = FALSE;
5148
5149   if (game_over_delay_3 > 0)
5150   {
5151     game_over_delay_3--;
5152
5153     return;
5154   }
5155
5156   GameEnd();
5157 }
5158
5159 void GameEnd(void)
5160 {
5161   // used instead of "level_nr" (needed for network games)
5162   int last_level_nr = levelset.level_nr;
5163   boolean tape_saved = FALSE;
5164
5165   // Important note: This function is not only called after "GameWon()", but also after
5166   // "game over" (if automatically asking for restarting the game is disabled in setup)
5167
5168   // do not handle game end if request dialog is already active
5169   if (checkRequestActive())
5170     return;
5171
5172   if (game.LevelSolved)
5173     game.LevelSolved_GameEnd = TRUE;
5174
5175   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5176   {
5177     // make sure that request dialog to save tape does not open door again
5178     if (!global.use_envelope_request)
5179       CloseDoor(DOOR_CLOSE_1);
5180
5181     // ask to save tape
5182     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5183
5184     // set unique basename for score tape (also saved in high score table)
5185     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5186   }
5187
5188   // if no tape is to be saved, close both doors simultaneously
5189   CloseDoor(DOOR_CLOSE_ALL);
5190
5191   if (level_editor_test_game || score_info_tape_play)
5192   {
5193     SetGameStatus(GAME_MODE_MAIN);
5194
5195     DrawMainMenu();
5196
5197     return;
5198   }
5199
5200   if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
5201   {
5202     SetGameStatus(GAME_MODE_MAIN);
5203
5204     DrawMainMenu();
5205
5206     return;
5207   }
5208
5209   if (level_nr == leveldir_current->handicap_level)
5210   {
5211     leveldir_current->handicap_level++;
5212
5213     SaveLevelSetup_SeriesInfo();
5214   }
5215
5216   // save score and score tape before potentially erasing tape below
5217   if (game.LevelSolved_SaveScore)
5218     NewHighScore(last_level_nr, tape_saved);
5219
5220   // increment and load next level (if possible and not configured otherwise)
5221   AdvanceToNextLevel();
5222
5223   if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
5224   {
5225     SetGameStatus(GAME_MODE_SCORES);
5226
5227     DrawHallOfFame(last_level_nr);
5228   }
5229   else if (scores.continue_playing)
5230   {
5231     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5232   }
5233   else
5234   {
5235     SetGameStatus(GAME_MODE_MAIN);
5236
5237     DrawMainMenu();
5238   }
5239 }
5240
5241 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5242                          boolean one_score_entry_per_name)
5243 {
5244   int i;
5245
5246   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5247     return -1;
5248
5249   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5250   {
5251     struct ScoreEntry *entry = &list->entry[i];
5252     boolean score_is_better = (new_entry->score >  entry->score);
5253     boolean score_is_equal  = (new_entry->score == entry->score);
5254     boolean time_is_better  = (new_entry->time  <  entry->time);
5255     boolean time_is_equal   = (new_entry->time  == entry->time);
5256     boolean better_by_score = (score_is_better ||
5257                                (score_is_equal && time_is_better));
5258     boolean better_by_time  = (time_is_better ||
5259                                (time_is_equal && score_is_better));
5260     boolean is_better = (level.rate_time_over_score ? better_by_time :
5261                          better_by_score);
5262     boolean entry_is_empty = (entry->score == 0 &&
5263                               entry->time == 0);
5264
5265     // prevent adding server score entries if also existing in local score file
5266     // (special case: historic score entries have an empty tape basename entry)
5267     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5268         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5269     {
5270       // add fields from server score entry not stored in local score entry
5271       // (currently, this means setting platform, version and country fields;
5272       // in rare cases, this may also correct an invalid score value, as
5273       // historic scores might have been truncated to 16-bit values locally)
5274       *entry = *new_entry;
5275
5276       return -1;
5277     }
5278
5279     if (is_better || entry_is_empty)
5280     {
5281       // player has made it to the hall of fame
5282
5283       if (i < MAX_SCORE_ENTRIES - 1)
5284       {
5285         int m = MAX_SCORE_ENTRIES - 1;
5286         int l;
5287
5288         if (one_score_entry_per_name)
5289         {
5290           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5291             if (strEqual(list->entry[l].name, new_entry->name))
5292               m = l;
5293
5294           if (m == i)   // player's new highscore overwrites his old one
5295             goto put_into_list;
5296         }
5297
5298         for (l = m; l > i; l--)
5299           list->entry[l] = list->entry[l - 1];
5300       }
5301
5302       put_into_list:
5303
5304       *entry = *new_entry;
5305
5306       return i;
5307     }
5308     else if (one_score_entry_per_name &&
5309              strEqual(entry->name, new_entry->name))
5310     {
5311       // player already in high score list with better score or time
5312
5313       return -1;
5314     }
5315   }
5316
5317   // special case: new score is beyond the last high score list position
5318   return MAX_SCORE_ENTRIES;
5319 }
5320
5321 void NewHighScore(int level_nr, boolean tape_saved)
5322 {
5323   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5324   boolean one_per_name = FALSE;
5325
5326   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5327   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5328
5329   new_entry.score = game.score_final;
5330   new_entry.time = game.score_time_final;
5331
5332   LoadScore(level_nr);
5333
5334   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5335
5336   if (scores.last_added >= MAX_SCORE_ENTRIES)
5337   {
5338     scores.last_added = MAX_SCORE_ENTRIES - 1;
5339     scores.force_last_added = TRUE;
5340
5341     scores.entry[scores.last_added] = new_entry;
5342
5343     // store last added local score entry (before merging server scores)
5344     scores.last_added_local = scores.last_added;
5345
5346     return;
5347   }
5348
5349   if (scores.last_added < 0)
5350     return;
5351
5352   SaveScore(level_nr);
5353
5354   // store last added local score entry (before merging server scores)
5355   scores.last_added_local = scores.last_added;
5356
5357   if (!game.LevelSolved_SaveTape)
5358     return;
5359
5360   SaveScoreTape(level_nr);
5361
5362   if (setup.ask_for_using_api_server)
5363   {
5364     setup.use_api_server =
5365       Request("Upload your score and tape to the high score server?", REQ_ASK);
5366
5367     if (!setup.use_api_server)
5368       Request("Not using high score server! Use setup menu to enable again!",
5369               REQ_CONFIRM);
5370
5371     runtime.use_api_server = setup.use_api_server;
5372
5373     // after asking for using API server once, do not ask again
5374     setup.ask_for_using_api_server = FALSE;
5375
5376     SaveSetup_ServerSetup();
5377   }
5378
5379   SaveServerScore(level_nr, tape_saved);
5380 }
5381
5382 void MergeServerScore(void)
5383 {
5384   struct ScoreEntry last_added_entry;
5385   boolean one_per_name = FALSE;
5386   int i;
5387
5388   if (scores.last_added >= 0)
5389     last_added_entry = scores.entry[scores.last_added];
5390
5391   for (i = 0; i < server_scores.num_entries; i++)
5392   {
5393     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5394
5395     if (pos >= 0 && pos <= scores.last_added)
5396       scores.last_added++;
5397   }
5398
5399   if (scores.last_added >= MAX_SCORE_ENTRIES)
5400   {
5401     scores.last_added = MAX_SCORE_ENTRIES - 1;
5402     scores.force_last_added = TRUE;
5403
5404     scores.entry[scores.last_added] = last_added_entry;
5405   }
5406 }
5407
5408 static int getElementMoveStepsizeExt(int x, int y, int direction)
5409 {
5410   int element = Tile[x][y];
5411   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5412   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5413   int horiz_move = (dx != 0);
5414   int sign = (horiz_move ? dx : dy);
5415   int step = sign * element_info[element].move_stepsize;
5416
5417   // special values for move stepsize for spring and things on conveyor belt
5418   if (horiz_move)
5419   {
5420     if (CAN_FALL(element) &&
5421         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5422       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5423     else if (element == EL_SPRING)
5424       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5425   }
5426
5427   return step;
5428 }
5429
5430 static int getElementMoveStepsize(int x, int y)
5431 {
5432   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5433 }
5434
5435 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5436 {
5437   if (player->GfxAction != action || player->GfxDir != dir)
5438   {
5439     player->GfxAction = action;
5440     player->GfxDir = dir;
5441     player->Frame = 0;
5442     player->StepFrame = 0;
5443   }
5444 }
5445
5446 static void ResetGfxFrame(int x, int y)
5447 {
5448   // profiling showed that "autotest" spends 10~20% of its time in this function
5449   if (DrawingDeactivatedField())
5450     return;
5451
5452   int element = Tile[x][y];
5453   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5454
5455   if (graphic_info[graphic].anim_global_sync)
5456     GfxFrame[x][y] = FrameCounter;
5457   else if (graphic_info[graphic].anim_global_anim_sync)
5458     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5459   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5460     GfxFrame[x][y] = CustomValue[x][y];
5461   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5462     GfxFrame[x][y] = element_info[element].collect_score;
5463   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5464     GfxFrame[x][y] = ChangeDelay[x][y];
5465 }
5466
5467 static void ResetGfxAnimation(int x, int y)
5468 {
5469   GfxAction[x][y] = ACTION_DEFAULT;
5470   GfxDir[x][y] = MovDir[x][y];
5471   GfxFrame[x][y] = 0;
5472
5473   ResetGfxFrame(x, y);
5474 }
5475
5476 static void ResetRandomAnimationValue(int x, int y)
5477 {
5478   GfxRandom[x][y] = INIT_GFX_RANDOM();
5479 }
5480
5481 static void InitMovingField(int x, int y, int direction)
5482 {
5483   int element = Tile[x][y];
5484   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5485   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5486   int newx = x + dx;
5487   int newy = y + dy;
5488   boolean is_moving_before, is_moving_after;
5489
5490   // check if element was/is moving or being moved before/after mode change
5491   is_moving_before = (WasJustMoving[x][y] != 0);
5492   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5493
5494   // reset animation only for moving elements which change direction of moving
5495   // or which just started or stopped moving
5496   // (else CEs with property "can move" / "not moving" are reset each frame)
5497   if (is_moving_before != is_moving_after ||
5498       direction != MovDir[x][y])
5499     ResetGfxAnimation(x, y);
5500
5501   MovDir[x][y] = direction;
5502   GfxDir[x][y] = direction;
5503
5504   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5505                      direction == MV_DOWN && CAN_FALL(element) ?
5506                      ACTION_FALLING : ACTION_MOVING);
5507
5508   // this is needed for CEs with property "can move" / "not moving"
5509
5510   if (is_moving_after)
5511   {
5512     if (Tile[newx][newy] == EL_EMPTY)
5513       Tile[newx][newy] = EL_BLOCKED;
5514
5515     MovDir[newx][newy] = MovDir[x][y];
5516
5517     CustomValue[newx][newy] = CustomValue[x][y];
5518
5519     GfxFrame[newx][newy] = GfxFrame[x][y];
5520     GfxRandom[newx][newy] = GfxRandom[x][y];
5521     GfxAction[newx][newy] = GfxAction[x][y];
5522     GfxDir[newx][newy] = GfxDir[x][y];
5523   }
5524 }
5525
5526 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5527 {
5528   int direction = MovDir[x][y];
5529   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5530   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5531
5532   *goes_to_x = newx;
5533   *goes_to_y = newy;
5534 }
5535
5536 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5537 {
5538   int direction = MovDir[x][y];
5539   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5540   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5541
5542   *comes_from_x = oldx;
5543   *comes_from_y = oldy;
5544 }
5545
5546 static int MovingOrBlocked2Element(int x, int y)
5547 {
5548   int element = Tile[x][y];
5549
5550   if (element == EL_BLOCKED)
5551   {
5552     int oldx, oldy;
5553
5554     Blocked2Moving(x, y, &oldx, &oldy);
5555
5556     return Tile[oldx][oldy];
5557   }
5558
5559   return element;
5560 }
5561
5562 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5563 {
5564   // like MovingOrBlocked2Element(), but if element is moving
5565   // and (x, y) is the field the moving element is just leaving,
5566   // return EL_BLOCKED instead of the element value
5567   int element = Tile[x][y];
5568
5569   if (IS_MOVING(x, y))
5570   {
5571     if (element == EL_BLOCKED)
5572     {
5573       int oldx, oldy;
5574
5575       Blocked2Moving(x, y, &oldx, &oldy);
5576       return Tile[oldx][oldy];
5577     }
5578     else
5579       return EL_BLOCKED;
5580   }
5581   else
5582     return element;
5583 }
5584
5585 static void RemoveField(int x, int y)
5586 {
5587   Tile[x][y] = EL_EMPTY;
5588
5589   MovPos[x][y] = 0;
5590   MovDir[x][y] = 0;
5591   MovDelay[x][y] = 0;
5592
5593   CustomValue[x][y] = 0;
5594
5595   AmoebaNr[x][y] = 0;
5596   ChangeDelay[x][y] = 0;
5597   ChangePage[x][y] = -1;
5598   Pushed[x][y] = FALSE;
5599
5600   GfxElement[x][y] = EL_UNDEFINED;
5601   GfxAction[x][y] = ACTION_DEFAULT;
5602   GfxDir[x][y] = MV_NONE;
5603 }
5604
5605 static void RemoveMovingField(int x, int y)
5606 {
5607   int oldx = x, oldy = y, newx = x, newy = y;
5608   int element = Tile[x][y];
5609   int next_element = EL_UNDEFINED;
5610
5611   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5612     return;
5613
5614   if (IS_MOVING(x, y))
5615   {
5616     Moving2Blocked(x, y, &newx, &newy);
5617
5618     if (Tile[newx][newy] != EL_BLOCKED)
5619     {
5620       // element is moving, but target field is not free (blocked), but
5621       // already occupied by something different (example: acid pool);
5622       // in this case, only remove the moving field, but not the target
5623
5624       RemoveField(oldx, oldy);
5625
5626       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5627
5628       TEST_DrawLevelField(oldx, oldy);
5629
5630       return;
5631     }
5632   }
5633   else if (element == EL_BLOCKED)
5634   {
5635     Blocked2Moving(x, y, &oldx, &oldy);
5636     if (!IS_MOVING(oldx, oldy))
5637       return;
5638   }
5639
5640   if (element == EL_BLOCKED &&
5641       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5642        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5643        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5644        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5645        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5646        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5647     next_element = get_next_element(Tile[oldx][oldy]);
5648
5649   RemoveField(oldx, oldy);
5650   RemoveField(newx, newy);
5651
5652   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5653
5654   if (next_element != EL_UNDEFINED)
5655     Tile[oldx][oldy] = next_element;
5656
5657   TEST_DrawLevelField(oldx, oldy);
5658   TEST_DrawLevelField(newx, newy);
5659 }
5660
5661 void DrawDynamite(int x, int y)
5662 {
5663   int sx = SCREENX(x), sy = SCREENY(y);
5664   int graphic = el2img(Tile[x][y]);
5665   int frame;
5666
5667   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5668     return;
5669
5670   if (IS_WALKABLE_INSIDE(Back[x][y]))
5671     return;
5672
5673   if (Back[x][y])
5674     DrawLevelElement(x, y, Back[x][y]);
5675   else if (Store[x][y])
5676     DrawLevelElement(x, y, Store[x][y]);
5677   else if (game.use_masked_elements)
5678     DrawLevelElement(x, y, EL_EMPTY);
5679
5680   frame = getGraphicAnimationFrameXY(graphic, x, y);
5681
5682   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5683     DrawGraphicThruMask(sx, sy, graphic, frame);
5684   else
5685     DrawGraphic(sx, sy, graphic, frame);
5686 }
5687
5688 static void CheckDynamite(int x, int y)
5689 {
5690   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5691   {
5692     MovDelay[x][y]--;
5693
5694     if (MovDelay[x][y] != 0)
5695     {
5696       DrawDynamite(x, y);
5697       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5698
5699       return;
5700     }
5701   }
5702
5703   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5704
5705   Bang(x, y);
5706 }
5707
5708 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5709 {
5710   boolean num_checked_players = 0;
5711   int i;
5712
5713   for (i = 0; i < MAX_PLAYERS; i++)
5714   {
5715     if (stored_player[i].active)
5716     {
5717       int sx = stored_player[i].jx;
5718       int sy = stored_player[i].jy;
5719
5720       if (num_checked_players == 0)
5721       {
5722         *sx1 = *sx2 = sx;
5723         *sy1 = *sy2 = sy;
5724       }
5725       else
5726       {
5727         *sx1 = MIN(*sx1, sx);
5728         *sy1 = MIN(*sy1, sy);
5729         *sx2 = MAX(*sx2, sx);
5730         *sy2 = MAX(*sy2, sy);
5731       }
5732
5733       num_checked_players++;
5734     }
5735   }
5736 }
5737
5738 static boolean checkIfAllPlayersFitToScreen_RND(void)
5739 {
5740   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5741
5742   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5743
5744   return (sx2 - sx1 < SCR_FIELDX &&
5745           sy2 - sy1 < SCR_FIELDY);
5746 }
5747
5748 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5749 {
5750   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5751
5752   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5753
5754   *sx = (sx1 + sx2) / 2;
5755   *sy = (sy1 + sy2) / 2;
5756 }
5757
5758 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5759                                boolean center_screen, boolean quick_relocation)
5760 {
5761   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5762   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5763   boolean no_delay = (tape.warp_forward);
5764   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5765   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5766   int new_scroll_x, new_scroll_y;
5767
5768   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5769   {
5770     // case 1: quick relocation inside visible screen (without scrolling)
5771
5772     RedrawPlayfield();
5773
5774     return;
5775   }
5776
5777   if (!level.shifted_relocation || center_screen)
5778   {
5779     // relocation _with_ centering of screen
5780
5781     new_scroll_x = SCROLL_POSITION_X(x);
5782     new_scroll_y = SCROLL_POSITION_Y(y);
5783   }
5784   else
5785   {
5786     // relocation _without_ centering of screen
5787
5788     // apply distance between old and new player position to scroll position
5789     int shifted_scroll_x = scroll_x + (x - old_x);
5790     int shifted_scroll_y = scroll_y + (y - old_y);
5791
5792     // make sure that shifted scroll position does not scroll beyond screen
5793     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5794     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5795
5796     // special case for teleporting from one end of the playfield to the other
5797     // (this kludge prevents the destination area to be shifted by half a tile
5798     // against the source destination for even screen width or screen height;
5799     // probably most useful when used with high "game.forced_scroll_delay_value"
5800     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5801     if (quick_relocation)
5802     {
5803       if (EVEN(SCR_FIELDX))
5804       {
5805         // relocate (teleport) between left and right border (half or full)
5806         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5807           new_scroll_x = SBX_Right;
5808         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5809           new_scroll_x = SBX_Right - 1;
5810         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5811           new_scroll_x = SBX_Left;
5812         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5813           new_scroll_x = SBX_Left + 1;
5814       }
5815
5816       if (EVEN(SCR_FIELDY))
5817       {
5818         // relocate (teleport) between top and bottom border (half or full)
5819         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5820           new_scroll_y = SBY_Lower;
5821         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5822           new_scroll_y = SBY_Lower - 1;
5823         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5824           new_scroll_y = SBY_Upper;
5825         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5826           new_scroll_y = SBY_Upper + 1;
5827       }
5828     }
5829   }
5830
5831   if (quick_relocation)
5832   {
5833     // case 2: quick relocation (redraw without visible scrolling)
5834
5835     scroll_x = new_scroll_x;
5836     scroll_y = new_scroll_y;
5837
5838     RedrawPlayfield();
5839
5840     return;
5841   }
5842
5843   // case 3: visible relocation (with scrolling to new position)
5844
5845   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5846
5847   SetVideoFrameDelay(wait_delay_value);
5848
5849   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5850   {
5851     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5852     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5853
5854     if (dx == 0 && dy == 0)             // no scrolling needed at all
5855       break;
5856
5857     scroll_x -= dx;
5858     scroll_y -= dy;
5859
5860     // set values for horizontal/vertical screen scrolling (half tile size)
5861     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5862     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5863     int pos_x = dx * TILEX / 2;
5864     int pos_y = dy * TILEY / 2;
5865     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5866     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5867
5868     ScrollLevel(dx, dy);
5869     DrawAllPlayers();
5870
5871     // scroll in two steps of half tile size to make things smoother
5872     BlitScreenToBitmapExt_RND(window, fx, fy);
5873
5874     // scroll second step to align at full tile size
5875     BlitScreenToBitmap(window);
5876   }
5877
5878   DrawAllPlayers();
5879   BackToFront();
5880
5881   SetVideoFrameDelay(frame_delay_value_old);
5882 }
5883
5884 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5885 {
5886   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5887   int player_nr = GET_PLAYER_NR(el_player);
5888   struct PlayerInfo *player = &stored_player[player_nr];
5889   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5890   boolean no_delay = (tape.warp_forward);
5891   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5892   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5893   int old_jx = player->jx;
5894   int old_jy = player->jy;
5895   int old_element = Tile[old_jx][old_jy];
5896   int element = Tile[jx][jy];
5897   boolean player_relocated = (old_jx != jx || old_jy != jy);
5898
5899   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5900   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5901   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5902   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5903   int leave_side_horiz = move_dir_horiz;
5904   int leave_side_vert  = move_dir_vert;
5905   int enter_side = enter_side_horiz | enter_side_vert;
5906   int leave_side = leave_side_horiz | leave_side_vert;
5907
5908   if (player->buried)           // do not reanimate dead player
5909     return;
5910
5911   if (!player_relocated)        // no need to relocate the player
5912     return;
5913
5914   if (IS_PLAYER(jx, jy))        // player already placed at new position
5915   {
5916     RemoveField(jx, jy);        // temporarily remove newly placed player
5917     DrawLevelField(jx, jy);
5918   }
5919
5920   if (player->present)
5921   {
5922     while (player->MovPos)
5923     {
5924       ScrollPlayer(player, SCROLL_GO_ON);
5925       ScrollScreen(NULL, SCROLL_GO_ON);
5926
5927       AdvanceFrameAndPlayerCounters(player->index_nr);
5928
5929       DrawPlayer(player);
5930
5931       BackToFront_WithFrameDelay(wait_delay_value);
5932     }
5933
5934     DrawPlayer(player);         // needed here only to cleanup last field
5935     DrawLevelField(player->jx, player->jy);     // remove player graphic
5936
5937     player->is_moving = FALSE;
5938   }
5939
5940   if (IS_CUSTOM_ELEMENT(old_element))
5941     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5942                                CE_LEFT_BY_PLAYER,
5943                                player->index_bit, leave_side);
5944
5945   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5946                                       CE_PLAYER_LEAVES_X,
5947                                       player->index_bit, leave_side);
5948
5949   Tile[jx][jy] = el_player;
5950   InitPlayerField(jx, jy, el_player, TRUE);
5951
5952   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5953      possible that the relocation target field did not contain a player element,
5954      but a walkable element, to which the new player was relocated -- in this
5955      case, restore that (already initialized!) element on the player field */
5956   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5957   {
5958     Tile[jx][jy] = element;     // restore previously existing element
5959   }
5960
5961   // only visually relocate centered player
5962   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5963                      FALSE, level.instant_relocation);
5964
5965   TestIfPlayerTouchesBadThing(jx, jy);
5966   TestIfPlayerTouchesCustomElement(jx, jy);
5967
5968   if (IS_CUSTOM_ELEMENT(element))
5969     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5970                                player->index_bit, enter_side);
5971
5972   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5973                                       player->index_bit, enter_side);
5974
5975   if (player->is_switching)
5976   {
5977     /* ensure that relocation while still switching an element does not cause
5978        a new element to be treated as also switched directly after relocation
5979        (this is important for teleporter switches that teleport the player to
5980        a place where another teleporter switch is in the same direction, which
5981        would then incorrectly be treated as immediately switched before the
5982        direction key that caused the switch was released) */
5983
5984     player->switch_x += jx - old_jx;
5985     player->switch_y += jy - old_jy;
5986   }
5987 }
5988
5989 static void Explode(int ex, int ey, int phase, int mode)
5990 {
5991   int x, y;
5992   int last_phase;
5993   int border_element;
5994
5995   if (game.explosions_delayed)
5996   {
5997     ExplodeField[ex][ey] = mode;
5998     return;
5999   }
6000
6001   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
6002   {
6003     int center_element = Tile[ex][ey];
6004     int ce_value = CustomValue[ex][ey];
6005     int ce_score = element_info[center_element].collect_score;
6006     int artwork_element, explosion_element;     // set these values later
6007
6008     // remove things displayed in background while burning dynamite
6009     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
6010       Back[ex][ey] = 0;
6011
6012     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6013     {
6014       // put moving element to center field (and let it explode there)
6015       center_element = MovingOrBlocked2Element(ex, ey);
6016       RemoveMovingField(ex, ey);
6017       Tile[ex][ey] = center_element;
6018     }
6019
6020     // now "center_element" is finally determined -- set related values now
6021     artwork_element = center_element;           // for custom player artwork
6022     explosion_element = center_element;         // for custom player artwork
6023
6024     if (IS_PLAYER(ex, ey))
6025     {
6026       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6027
6028       artwork_element = stored_player[player_nr].artwork_element;
6029
6030       if (level.use_explosion_element[player_nr])
6031       {
6032         explosion_element = level.explosion_element[player_nr];
6033         artwork_element = explosion_element;
6034       }
6035     }
6036
6037     if (mode == EX_TYPE_NORMAL ||
6038         mode == EX_TYPE_CENTER ||
6039         mode == EX_TYPE_CROSS)
6040       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6041
6042     last_phase = element_info[explosion_element].explosion_delay + 1;
6043
6044     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6045     {
6046       int xx = x - ex + 1;
6047       int yy = y - ey + 1;
6048       int element;
6049
6050       if (!IN_LEV_FIELD(x, y) ||
6051           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6052           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6053         continue;
6054
6055       element = Tile[x][y];
6056
6057       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6058       {
6059         element = MovingOrBlocked2Element(x, y);
6060
6061         if (!IS_EXPLOSION_PROOF(element))
6062           RemoveMovingField(x, y);
6063       }
6064
6065       // indestructible elements can only explode in center (but not flames)
6066       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6067                                            mode == EX_TYPE_BORDER)) ||
6068           element == EL_FLAMES)
6069         continue;
6070
6071       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6072          behaviour, for example when touching a yamyam that explodes to rocks
6073          with active deadly shield, a rock is created under the player !!! */
6074       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6075 #if 0
6076       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6077           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6078            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6079 #else
6080       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6081 #endif
6082       {
6083         if (IS_ACTIVE_BOMB(element))
6084         {
6085           // re-activate things under the bomb like gate or penguin
6086           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6087           Back[x][y] = 0;
6088         }
6089
6090         continue;
6091       }
6092
6093       // save walkable background elements while explosion on same tile
6094       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6095           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6096         Back[x][y] = element;
6097
6098       // ignite explodable elements reached by other explosion
6099       if (element == EL_EXPLOSION)
6100         element = Store2[x][y];
6101
6102       if (AmoebaNr[x][y] &&
6103           (element == EL_AMOEBA_FULL ||
6104            element == EL_BD_AMOEBA ||
6105            element == EL_AMOEBA_GROWING))
6106       {
6107         AmoebaCnt[AmoebaNr[x][y]]--;
6108         AmoebaCnt2[AmoebaNr[x][y]]--;
6109       }
6110
6111       RemoveField(x, y);
6112
6113       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6114       {
6115         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6116
6117         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6118
6119         if (PLAYERINFO(ex, ey)->use_murphy)
6120           Store[x][y] = EL_EMPTY;
6121       }
6122
6123       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6124       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6125       else if (IS_PLAYER_ELEMENT(center_element))
6126         Store[x][y] = EL_EMPTY;
6127       else if (center_element == EL_YAMYAM)
6128         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6129       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6130         Store[x][y] = element_info[center_element].content.e[xx][yy];
6131 #if 1
6132       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6133       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6134       // otherwise) -- FIX THIS !!!
6135       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6136         Store[x][y] = element_info[element].content.e[1][1];
6137 #else
6138       else if (!CAN_EXPLODE(element))
6139         Store[x][y] = element_info[element].content.e[1][1];
6140 #endif
6141       else
6142         Store[x][y] = EL_EMPTY;
6143
6144       if (IS_CUSTOM_ELEMENT(center_element))
6145         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6146                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6147                        Store[x][y] >= EL_PREV_CE_8 &&
6148                        Store[x][y] <= EL_NEXT_CE_8 ?
6149                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6150                        Store[x][y]);
6151
6152       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6153           center_element == EL_AMOEBA_TO_DIAMOND)
6154         Store2[x][y] = element;
6155
6156       Tile[x][y] = EL_EXPLOSION;
6157       GfxElement[x][y] = artwork_element;
6158
6159       ExplodePhase[x][y] = 1;
6160       ExplodeDelay[x][y] = last_phase;
6161
6162       Stop[x][y] = TRUE;
6163     }
6164
6165     if (center_element == EL_YAMYAM)
6166       game.yamyam_content_nr =
6167         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6168
6169     return;
6170   }
6171
6172   if (Stop[ex][ey])
6173     return;
6174
6175   x = ex;
6176   y = ey;
6177
6178   if (phase == 1)
6179     GfxFrame[x][y] = 0;         // restart explosion animation
6180
6181   last_phase = ExplodeDelay[x][y];
6182
6183   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6184
6185   // this can happen if the player leaves an explosion just in time
6186   if (GfxElement[x][y] == EL_UNDEFINED)
6187     GfxElement[x][y] = EL_EMPTY;
6188
6189   border_element = Store2[x][y];
6190   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6191     border_element = StorePlayer[x][y];
6192
6193   if (phase == element_info[border_element].ignition_delay ||
6194       phase == last_phase)
6195   {
6196     boolean border_explosion = FALSE;
6197
6198     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6199         !PLAYER_EXPLOSION_PROTECTED(x, y))
6200     {
6201       KillPlayerUnlessExplosionProtected(x, y);
6202       border_explosion = TRUE;
6203     }
6204     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6205     {
6206       Tile[x][y] = Store2[x][y];
6207       Store2[x][y] = 0;
6208       Bang(x, y);
6209       border_explosion = TRUE;
6210     }
6211     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6212     {
6213       AmoebaToDiamond(x, y);
6214       Store2[x][y] = 0;
6215       border_explosion = TRUE;
6216     }
6217
6218     // if an element just explodes due to another explosion (chain-reaction),
6219     // do not immediately end the new explosion when it was the last frame of
6220     // the explosion (as it would be done in the following "if"-statement!)
6221     if (border_explosion && phase == last_phase)
6222       return;
6223   }
6224
6225   // this can happen if the player was just killed by an explosion
6226   if (GfxElement[x][y] == EL_UNDEFINED)
6227     GfxElement[x][y] = EL_EMPTY;
6228
6229   if (phase == last_phase)
6230   {
6231     int element;
6232
6233     element = Tile[x][y] = Store[x][y];
6234     Store[x][y] = Store2[x][y] = 0;
6235     GfxElement[x][y] = EL_UNDEFINED;
6236
6237     // player can escape from explosions and might therefore be still alive
6238     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6239         element <= EL_PLAYER_IS_EXPLODING_4)
6240     {
6241       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6242       int explosion_element = EL_PLAYER_1 + player_nr;
6243       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6244       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6245
6246       if (level.use_explosion_element[player_nr])
6247         explosion_element = level.explosion_element[player_nr];
6248
6249       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6250                     element_info[explosion_element].content.e[xx][yy]);
6251     }
6252
6253     // restore probably existing indestructible background element
6254     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6255       element = Tile[x][y] = Back[x][y];
6256     Back[x][y] = 0;
6257
6258     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6259     GfxDir[x][y] = MV_NONE;
6260     ChangeDelay[x][y] = 0;
6261     ChangePage[x][y] = -1;
6262
6263     CustomValue[x][y] = 0;
6264
6265     InitField_WithBug2(x, y, FALSE);
6266
6267     TEST_DrawLevelField(x, y);
6268
6269     TestIfElementTouchesCustomElement(x, y);
6270
6271     if (GFX_CRUMBLED(element))
6272       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6273
6274     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6275       StorePlayer[x][y] = 0;
6276
6277     if (IS_PLAYER_ELEMENT(element))
6278       RelocatePlayer(x, y, element);
6279   }
6280   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6281   {
6282     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6283     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6284
6285     if (phase == 1)
6286       TEST_DrawLevelFieldCrumbled(x, y);
6287
6288     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6289     {
6290       DrawLevelElement(x, y, Back[x][y]);
6291       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6292     }
6293     else if (IS_WALKABLE_UNDER(Back[x][y]))
6294     {
6295       DrawLevelGraphic(x, y, graphic, frame);
6296       DrawLevelElementThruMask(x, y, Back[x][y]);
6297     }
6298     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6299       DrawLevelGraphic(x, y, graphic, frame);
6300   }
6301 }
6302
6303 static void DynaExplode(int ex, int ey)
6304 {
6305   int i, j;
6306   int dynabomb_element = Tile[ex][ey];
6307   int dynabomb_size = 1;
6308   boolean dynabomb_xl = FALSE;
6309   struct PlayerInfo *player;
6310   struct XY *xy = xy_topdown;
6311
6312   if (IS_ACTIVE_BOMB(dynabomb_element))
6313   {
6314     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6315     dynabomb_size = player->dynabomb_size;
6316     dynabomb_xl = player->dynabomb_xl;
6317     player->dynabombs_left++;
6318   }
6319
6320   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6321
6322   for (i = 0; i < NUM_DIRECTIONS; i++)
6323   {
6324     for (j = 1; j <= dynabomb_size; j++)
6325     {
6326       int x = ex + j * xy[i].x;
6327       int y = ey + j * xy[i].y;
6328       int element;
6329
6330       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6331         break;
6332
6333       element = Tile[x][y];
6334
6335       // do not restart explosions of fields with active bombs
6336       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6337         continue;
6338
6339       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6340
6341       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6342           !IS_DIGGABLE(element) && !dynabomb_xl)
6343         break;
6344     }
6345   }
6346 }
6347
6348 void Bang(int x, int y)
6349 {
6350   int element = MovingOrBlocked2Element(x, y);
6351   int explosion_type = EX_TYPE_NORMAL;
6352
6353   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6354   {
6355     struct PlayerInfo *player = PLAYERINFO(x, y);
6356
6357     element = Tile[x][y] = player->initial_element;
6358
6359     if (level.use_explosion_element[player->index_nr])
6360     {
6361       int explosion_element = level.explosion_element[player->index_nr];
6362
6363       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6364         explosion_type = EX_TYPE_CROSS;
6365       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6366         explosion_type = EX_TYPE_CENTER;
6367     }
6368   }
6369
6370   switch (element)
6371   {
6372     case EL_BUG:
6373     case EL_SPACESHIP:
6374     case EL_BD_BUTTERFLY:
6375     case EL_BD_FIREFLY:
6376     case EL_YAMYAM:
6377     case EL_DARK_YAMYAM:
6378     case EL_ROBOT:
6379     case EL_PACMAN:
6380     case EL_MOLE:
6381       RaiseScoreElement(element);
6382       break;
6383
6384     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6385     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6386     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6387     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6388     case EL_DYNABOMB_INCREASE_NUMBER:
6389     case EL_DYNABOMB_INCREASE_SIZE:
6390     case EL_DYNABOMB_INCREASE_POWER:
6391       explosion_type = EX_TYPE_DYNA;
6392       break;
6393
6394     case EL_DC_LANDMINE:
6395       explosion_type = EX_TYPE_CENTER;
6396       break;
6397
6398     case EL_PENGUIN:
6399     case EL_LAMP:
6400     case EL_LAMP_ACTIVE:
6401     case EL_AMOEBA_TO_DIAMOND:
6402       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6403         explosion_type = EX_TYPE_CENTER;
6404       break;
6405
6406     default:
6407       if (element_info[element].explosion_type == EXPLODES_CROSS)
6408         explosion_type = EX_TYPE_CROSS;
6409       else if (element_info[element].explosion_type == EXPLODES_1X1)
6410         explosion_type = EX_TYPE_CENTER;
6411       break;
6412   }
6413
6414   if (explosion_type == EX_TYPE_DYNA)
6415     DynaExplode(x, y);
6416   else
6417     Explode(x, y, EX_PHASE_START, explosion_type);
6418
6419   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6420 }
6421
6422 static void SplashAcid(int x, int y)
6423 {
6424   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6425       (!IN_LEV_FIELD(x - 1, y - 2) ||
6426        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6427     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6428
6429   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6430       (!IN_LEV_FIELD(x + 1, y - 2) ||
6431        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6432     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6433
6434   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6435 }
6436
6437 static void InitBeltMovement(void)
6438 {
6439   static int belt_base_element[4] =
6440   {
6441     EL_CONVEYOR_BELT_1_LEFT,
6442     EL_CONVEYOR_BELT_2_LEFT,
6443     EL_CONVEYOR_BELT_3_LEFT,
6444     EL_CONVEYOR_BELT_4_LEFT
6445   };
6446   static int belt_base_active_element[4] =
6447   {
6448     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6449     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6450     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6451     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6452   };
6453
6454   int x, y, i, j;
6455
6456   // set frame order for belt animation graphic according to belt direction
6457   for (i = 0; i < NUM_BELTS; i++)
6458   {
6459     int belt_nr = i;
6460
6461     for (j = 0; j < NUM_BELT_PARTS; j++)
6462     {
6463       int element = belt_base_active_element[belt_nr] + j;
6464       int graphic_1 = el2img(element);
6465       int graphic_2 = el2panelimg(element);
6466
6467       if (game.belt_dir[i] == MV_LEFT)
6468       {
6469         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6470         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6471       }
6472       else
6473       {
6474         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6475         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6476       }
6477     }
6478   }
6479
6480   SCAN_PLAYFIELD(x, y)
6481   {
6482     int element = Tile[x][y];
6483
6484     for (i = 0; i < NUM_BELTS; i++)
6485     {
6486       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6487       {
6488         int e_belt_nr = getBeltNrFromBeltElement(element);
6489         int belt_nr = i;
6490
6491         if (e_belt_nr == belt_nr)
6492         {
6493           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6494
6495           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6496         }
6497       }
6498     }
6499   }
6500 }
6501
6502 static void ToggleBeltSwitch(int x, int y)
6503 {
6504   static int belt_base_element[4] =
6505   {
6506     EL_CONVEYOR_BELT_1_LEFT,
6507     EL_CONVEYOR_BELT_2_LEFT,
6508     EL_CONVEYOR_BELT_3_LEFT,
6509     EL_CONVEYOR_BELT_4_LEFT
6510   };
6511   static int belt_base_active_element[4] =
6512   {
6513     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6514     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6515     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6516     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6517   };
6518   static int belt_base_switch_element[4] =
6519   {
6520     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6521     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6522     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6523     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6524   };
6525   static int belt_move_dir[4] =
6526   {
6527     MV_LEFT,
6528     MV_NONE,
6529     MV_RIGHT,
6530     MV_NONE,
6531   };
6532
6533   int element = Tile[x][y];
6534   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6535   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6536   int belt_dir = belt_move_dir[belt_dir_nr];
6537   int xx, yy, i;
6538
6539   if (!IS_BELT_SWITCH(element))
6540     return;
6541
6542   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6543   game.belt_dir[belt_nr] = belt_dir;
6544
6545   if (belt_dir_nr == 3)
6546     belt_dir_nr = 1;
6547
6548   // set frame order for belt animation graphic according to belt direction
6549   for (i = 0; i < NUM_BELT_PARTS; i++)
6550   {
6551     int element = belt_base_active_element[belt_nr] + i;
6552     int graphic_1 = el2img(element);
6553     int graphic_2 = el2panelimg(element);
6554
6555     if (belt_dir == MV_LEFT)
6556     {
6557       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6558       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6559     }
6560     else
6561     {
6562       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6563       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6564     }
6565   }
6566
6567   SCAN_PLAYFIELD(xx, yy)
6568   {
6569     int element = Tile[xx][yy];
6570
6571     if (IS_BELT_SWITCH(element))
6572     {
6573       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6574
6575       if (e_belt_nr == belt_nr)
6576       {
6577         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6578         TEST_DrawLevelField(xx, yy);
6579       }
6580     }
6581     else if (IS_BELT(element) && belt_dir != MV_NONE)
6582     {
6583       int e_belt_nr = getBeltNrFromBeltElement(element);
6584
6585       if (e_belt_nr == belt_nr)
6586       {
6587         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6588
6589         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6590         TEST_DrawLevelField(xx, yy);
6591       }
6592     }
6593     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6594     {
6595       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6596
6597       if (e_belt_nr == belt_nr)
6598       {
6599         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6600
6601         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6602         TEST_DrawLevelField(xx, yy);
6603       }
6604     }
6605   }
6606 }
6607
6608 static void ToggleSwitchgateSwitch(void)
6609 {
6610   int xx, yy;
6611
6612   game.switchgate_pos = !game.switchgate_pos;
6613
6614   SCAN_PLAYFIELD(xx, yy)
6615   {
6616     int element = Tile[xx][yy];
6617
6618     if (element == EL_SWITCHGATE_SWITCH_UP)
6619     {
6620       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6621       TEST_DrawLevelField(xx, yy);
6622     }
6623     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6624     {
6625       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6626       TEST_DrawLevelField(xx, yy);
6627     }
6628     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6629     {
6630       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6634     {
6635       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6636       TEST_DrawLevelField(xx, yy);
6637     }
6638     else if (element == EL_SWITCHGATE_OPEN ||
6639              element == EL_SWITCHGATE_OPENING)
6640     {
6641       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6642
6643       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6644     }
6645     else if (element == EL_SWITCHGATE_CLOSED ||
6646              element == EL_SWITCHGATE_CLOSING)
6647     {
6648       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6649
6650       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6651     }
6652   }
6653 }
6654
6655 static int getInvisibleActiveFromInvisibleElement(int element)
6656 {
6657   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6658           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6659           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6660           element);
6661 }
6662
6663 static int getInvisibleFromInvisibleActiveElement(int element)
6664 {
6665   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6666           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6667           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6668           element);
6669 }
6670
6671 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6672 {
6673   int x, y;
6674
6675   SCAN_PLAYFIELD(x, y)
6676   {
6677     int element = Tile[x][y];
6678
6679     if (element == EL_LIGHT_SWITCH &&
6680         game.light_time_left > 0)
6681     {
6682       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6686              game.light_time_left == 0)
6687     {
6688       Tile[x][y] = EL_LIGHT_SWITCH;
6689       TEST_DrawLevelField(x, y);
6690     }
6691     else if (element == EL_EMC_DRIPPER &&
6692              game.light_time_left > 0)
6693     {
6694       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6695       TEST_DrawLevelField(x, y);
6696     }
6697     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6698              game.light_time_left == 0)
6699     {
6700       Tile[x][y] = EL_EMC_DRIPPER;
6701       TEST_DrawLevelField(x, y);
6702     }
6703     else if (element == EL_INVISIBLE_STEELWALL ||
6704              element == EL_INVISIBLE_WALL ||
6705              element == EL_INVISIBLE_SAND)
6706     {
6707       if (game.light_time_left > 0)
6708         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6709
6710       TEST_DrawLevelField(x, y);
6711
6712       // uncrumble neighbour fields, if needed
6713       if (element == EL_INVISIBLE_SAND)
6714         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6715     }
6716     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6717              element == EL_INVISIBLE_WALL_ACTIVE ||
6718              element == EL_INVISIBLE_SAND_ACTIVE)
6719     {
6720       if (game.light_time_left == 0)
6721         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6722
6723       TEST_DrawLevelField(x, y);
6724
6725       // re-crumble neighbour fields, if needed
6726       if (element == EL_INVISIBLE_SAND)
6727         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6728     }
6729   }
6730 }
6731
6732 static void RedrawAllInvisibleElementsForLenses(void)
6733 {
6734   int x, y;
6735
6736   SCAN_PLAYFIELD(x, y)
6737   {
6738     int element = Tile[x][y];
6739
6740     if (element == EL_EMC_DRIPPER &&
6741         game.lenses_time_left > 0)
6742     {
6743       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6744       TEST_DrawLevelField(x, y);
6745     }
6746     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6747              game.lenses_time_left == 0)
6748     {
6749       Tile[x][y] = EL_EMC_DRIPPER;
6750       TEST_DrawLevelField(x, y);
6751     }
6752     else if (element == EL_INVISIBLE_STEELWALL ||
6753              element == EL_INVISIBLE_WALL ||
6754              element == EL_INVISIBLE_SAND)
6755     {
6756       if (game.lenses_time_left > 0)
6757         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6758
6759       TEST_DrawLevelField(x, y);
6760
6761       // uncrumble neighbour fields, if needed
6762       if (element == EL_INVISIBLE_SAND)
6763         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6764     }
6765     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6766              element == EL_INVISIBLE_WALL_ACTIVE ||
6767              element == EL_INVISIBLE_SAND_ACTIVE)
6768     {
6769       if (game.lenses_time_left == 0)
6770         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6771
6772       TEST_DrawLevelField(x, y);
6773
6774       // re-crumble neighbour fields, if needed
6775       if (element == EL_INVISIBLE_SAND)
6776         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6777     }
6778   }
6779 }
6780
6781 static void RedrawAllInvisibleElementsForMagnifier(void)
6782 {
6783   int x, y;
6784
6785   SCAN_PLAYFIELD(x, y)
6786   {
6787     int element = Tile[x][y];
6788
6789     if (element == EL_EMC_FAKE_GRASS &&
6790         game.magnify_time_left > 0)
6791     {
6792       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6793       TEST_DrawLevelField(x, y);
6794     }
6795     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6796              game.magnify_time_left == 0)
6797     {
6798       Tile[x][y] = EL_EMC_FAKE_GRASS;
6799       TEST_DrawLevelField(x, y);
6800     }
6801     else if (IS_GATE_GRAY(element) &&
6802              game.magnify_time_left > 0)
6803     {
6804       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6805                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6806                     IS_EM_GATE_GRAY(element) ?
6807                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6808                     IS_EMC_GATE_GRAY(element) ?
6809                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6810                     IS_DC_GATE_GRAY(element) ?
6811                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6812                     element);
6813       TEST_DrawLevelField(x, y);
6814     }
6815     else if (IS_GATE_GRAY_ACTIVE(element) &&
6816              game.magnify_time_left == 0)
6817     {
6818       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6819                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6820                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6821                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6822                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6823                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6824                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6825                     EL_DC_GATE_WHITE_GRAY :
6826                     element);
6827       TEST_DrawLevelField(x, y);
6828     }
6829   }
6830 }
6831
6832 static void ToggleLightSwitch(int x, int y)
6833 {
6834   int element = Tile[x][y];
6835
6836   game.light_time_left =
6837     (element == EL_LIGHT_SWITCH ?
6838      level.time_light * FRAMES_PER_SECOND : 0);
6839
6840   RedrawAllLightSwitchesAndInvisibleElements();
6841 }
6842
6843 static void ActivateTimegateSwitch(int x, int y)
6844 {
6845   int xx, yy;
6846
6847   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6848
6849   SCAN_PLAYFIELD(xx, yy)
6850   {
6851     int element = Tile[xx][yy];
6852
6853     if (element == EL_TIMEGATE_CLOSED ||
6854         element == EL_TIMEGATE_CLOSING)
6855     {
6856       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6857       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6858     }
6859
6860     /*
6861     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6862     {
6863       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6864       TEST_DrawLevelField(xx, yy);
6865     }
6866     */
6867
6868   }
6869
6870   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6871                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6872 }
6873
6874 static void Impact(int x, int y)
6875 {
6876   boolean last_line = (y == lev_fieldy - 1);
6877   boolean object_hit = FALSE;
6878   boolean impact = (last_line || object_hit);
6879   int element = Tile[x][y];
6880   int smashed = EL_STEELWALL;
6881
6882   if (!last_line)       // check if element below was hit
6883   {
6884     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6885       return;
6886
6887     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6888                                          MovDir[x][y + 1] != MV_DOWN ||
6889                                          MovPos[x][y + 1] <= TILEY / 2));
6890
6891     // do not smash moving elements that left the smashed field in time
6892     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6893         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6894       object_hit = FALSE;
6895
6896 #if USE_QUICKSAND_IMPACT_BUGFIX
6897     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6898     {
6899       RemoveMovingField(x, y + 1);
6900       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6901       Tile[x][y + 2] = EL_ROCK;
6902       TEST_DrawLevelField(x, y + 2);
6903
6904       object_hit = TRUE;
6905     }
6906
6907     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6908     {
6909       RemoveMovingField(x, y + 1);
6910       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6911       Tile[x][y + 2] = EL_ROCK;
6912       TEST_DrawLevelField(x, y + 2);
6913
6914       object_hit = TRUE;
6915     }
6916 #endif
6917
6918     if (object_hit)
6919       smashed = MovingOrBlocked2Element(x, y + 1);
6920
6921     impact = (last_line || object_hit);
6922   }
6923
6924   if (!last_line && smashed == EL_ACID) // element falls into acid
6925   {
6926     SplashAcid(x, y + 1);
6927     return;
6928   }
6929
6930   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6931   // only reset graphic animation if graphic really changes after impact
6932   if (impact &&
6933       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6934   {
6935     ResetGfxAnimation(x, y);
6936     TEST_DrawLevelField(x, y);
6937   }
6938
6939   if (impact && CAN_EXPLODE_IMPACT(element))
6940   {
6941     Bang(x, y);
6942     return;
6943   }
6944   else if (impact && element == EL_PEARL &&
6945            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6946   {
6947     ResetGfxAnimation(x, y);
6948
6949     Tile[x][y] = EL_PEARL_BREAKING;
6950     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6951     return;
6952   }
6953   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6954   {
6955     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6956
6957     return;
6958   }
6959
6960   if (impact && element == EL_AMOEBA_DROP)
6961   {
6962     if (object_hit && IS_PLAYER(x, y + 1))
6963       KillPlayerUnlessEnemyProtected(x, y + 1);
6964     else if (object_hit && smashed == EL_PENGUIN)
6965       Bang(x, y + 1);
6966     else
6967     {
6968       Tile[x][y] = EL_AMOEBA_GROWING;
6969       Store[x][y] = EL_AMOEBA_WET;
6970
6971       ResetRandomAnimationValue(x, y);
6972     }
6973     return;
6974   }
6975
6976   if (object_hit)               // check which object was hit
6977   {
6978     if ((CAN_PASS_MAGIC_WALL(element) && 
6979          (smashed == EL_MAGIC_WALL ||
6980           smashed == EL_BD_MAGIC_WALL)) ||
6981         (CAN_PASS_DC_MAGIC_WALL(element) &&
6982          smashed == EL_DC_MAGIC_WALL))
6983     {
6984       int xx, yy;
6985       int activated_magic_wall =
6986         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6987          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6988          EL_DC_MAGIC_WALL_ACTIVE);
6989
6990       // activate magic wall / mill
6991       SCAN_PLAYFIELD(xx, yy)
6992       {
6993         if (Tile[xx][yy] == smashed)
6994           Tile[xx][yy] = activated_magic_wall;
6995       }
6996
6997       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6998       game.magic_wall_active = TRUE;
6999
7000       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
7001                             SND_MAGIC_WALL_ACTIVATING :
7002                             smashed == EL_BD_MAGIC_WALL ?
7003                             SND_BD_MAGIC_WALL_ACTIVATING :
7004                             SND_DC_MAGIC_WALL_ACTIVATING));
7005     }
7006
7007     if (IS_PLAYER(x, y + 1))
7008     {
7009       if (CAN_SMASH_PLAYER(element))
7010       {
7011         KillPlayerUnlessEnemyProtected(x, y + 1);
7012         return;
7013       }
7014     }
7015     else if (smashed == EL_PENGUIN)
7016     {
7017       if (CAN_SMASH_PLAYER(element))
7018       {
7019         Bang(x, y + 1);
7020         return;
7021       }
7022     }
7023     else if (element == EL_BD_DIAMOND)
7024     {
7025       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7026       {
7027         Bang(x, y + 1);
7028         return;
7029       }
7030     }
7031     else if (((element == EL_SP_INFOTRON ||
7032                element == EL_SP_ZONK) &&
7033               (smashed == EL_SP_SNIKSNAK ||
7034                smashed == EL_SP_ELECTRON ||
7035                smashed == EL_SP_DISK_ORANGE)) ||
7036              (element == EL_SP_INFOTRON &&
7037               smashed == EL_SP_DISK_YELLOW))
7038     {
7039       Bang(x, y + 1);
7040       return;
7041     }
7042     else if (CAN_SMASH_EVERYTHING(element))
7043     {
7044       if (IS_CLASSIC_ENEMY(smashed) ||
7045           CAN_EXPLODE_SMASHED(smashed))
7046       {
7047         Bang(x, y + 1);
7048         return;
7049       }
7050       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7051       {
7052         if (smashed == EL_LAMP ||
7053             smashed == EL_LAMP_ACTIVE)
7054         {
7055           Bang(x, y + 1);
7056           return;
7057         }
7058         else if (smashed == EL_NUT)
7059         {
7060           Tile[x][y + 1] = EL_NUT_BREAKING;
7061           PlayLevelSound(x, y, SND_NUT_BREAKING);
7062           RaiseScoreElement(EL_NUT);
7063           return;
7064         }
7065         else if (smashed == EL_PEARL)
7066         {
7067           ResetGfxAnimation(x, y);
7068
7069           Tile[x][y + 1] = EL_PEARL_BREAKING;
7070           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7071           return;
7072         }
7073         else if (smashed == EL_DIAMOND)
7074         {
7075           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7076           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7077           return;
7078         }
7079         else if (IS_BELT_SWITCH(smashed))
7080         {
7081           ToggleBeltSwitch(x, y + 1);
7082         }
7083         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7084                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7085                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7086                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7087         {
7088           ToggleSwitchgateSwitch();
7089         }
7090         else if (smashed == EL_LIGHT_SWITCH ||
7091                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7092         {
7093           ToggleLightSwitch(x, y + 1);
7094         }
7095         else
7096         {
7097           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7098
7099           CheckElementChangeBySide(x, y + 1, smashed, element,
7100                                    CE_SWITCHED, CH_SIDE_TOP);
7101           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7102                                             CH_SIDE_TOP);
7103         }
7104       }
7105       else
7106       {
7107         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7108       }
7109     }
7110   }
7111
7112   // play sound of magic wall / mill
7113   if (!last_line &&
7114       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7115        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7116        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7117   {
7118     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7119       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7120     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7121       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7122     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7123       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7124
7125     return;
7126   }
7127
7128   // play sound of object that hits the ground
7129   if (last_line || object_hit)
7130     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7131 }
7132
7133 static void TurnRoundExt(int x, int y)
7134 {
7135   static struct
7136   {
7137     int dx, dy;
7138   } move_xy[] =
7139   {
7140     {  0,  0 },
7141     { -1,  0 },
7142     { +1,  0 },
7143     {  0,  0 },
7144     {  0, -1 },
7145     {  0,  0 }, { 0, 0 }, { 0, 0 },
7146     {  0, +1 }
7147   };
7148   static struct
7149   {
7150     int left, right, back;
7151   } turn[] =
7152   {
7153     { 0,        0,              0        },
7154     { MV_DOWN,  MV_UP,          MV_RIGHT },
7155     { MV_UP,    MV_DOWN,        MV_LEFT  },
7156     { 0,        0,              0        },
7157     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7158     { 0,        0,              0        },
7159     { 0,        0,              0        },
7160     { 0,        0,              0        },
7161     { MV_RIGHT, MV_LEFT,        MV_UP    }
7162   };
7163
7164   int element = Tile[x][y];
7165   int move_pattern = element_info[element].move_pattern;
7166
7167   int old_move_dir = MovDir[x][y];
7168   int left_dir  = turn[old_move_dir].left;
7169   int right_dir = turn[old_move_dir].right;
7170   int back_dir  = turn[old_move_dir].back;
7171
7172   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7173   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7174   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7175   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7176
7177   int left_x  = x + left_dx,  left_y  = y + left_dy;
7178   int right_x = x + right_dx, right_y = y + right_dy;
7179   int move_x  = x + move_dx,  move_y  = y + move_dy;
7180
7181   int xx, yy;
7182
7183   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7184   {
7185     TestIfBadThingTouchesOtherBadThing(x, y);
7186
7187     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7188       MovDir[x][y] = right_dir;
7189     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7190       MovDir[x][y] = left_dir;
7191
7192     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7193       MovDelay[x][y] = 9;
7194     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7195       MovDelay[x][y] = 1;
7196   }
7197   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7198   {
7199     TestIfBadThingTouchesOtherBadThing(x, y);
7200
7201     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7202       MovDir[x][y] = left_dir;
7203     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7204       MovDir[x][y] = right_dir;
7205
7206     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7207       MovDelay[x][y] = 9;
7208     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7209       MovDelay[x][y] = 1;
7210   }
7211   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7212   {
7213     TestIfBadThingTouchesOtherBadThing(x, y);
7214
7215     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7216       MovDir[x][y] = left_dir;
7217     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7218       MovDir[x][y] = right_dir;
7219
7220     if (MovDir[x][y] != old_move_dir)
7221       MovDelay[x][y] = 9;
7222   }
7223   else if (element == EL_YAMYAM)
7224   {
7225     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7226     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7227
7228     if (can_turn_left && can_turn_right)
7229       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7230     else if (can_turn_left)
7231       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7232     else if (can_turn_right)
7233       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7234     else
7235       MovDir[x][y] = back_dir;
7236
7237     MovDelay[x][y] = 16 + 16 * RND(3);
7238   }
7239   else if (element == EL_DARK_YAMYAM)
7240   {
7241     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7242                                                          left_x, left_y);
7243     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7244                                                          right_x, right_y);
7245
7246     if (can_turn_left && can_turn_right)
7247       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7248     else if (can_turn_left)
7249       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7250     else if (can_turn_right)
7251       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7252     else
7253       MovDir[x][y] = back_dir;
7254
7255     MovDelay[x][y] = 16 + 16 * RND(3);
7256   }
7257   else if (element == EL_PACMAN)
7258   {
7259     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7260     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7261
7262     if (can_turn_left && can_turn_right)
7263       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7264     else if (can_turn_left)
7265       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7266     else if (can_turn_right)
7267       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7268     else
7269       MovDir[x][y] = back_dir;
7270
7271     MovDelay[x][y] = 6 + RND(40);
7272   }
7273   else if (element == EL_PIG)
7274   {
7275     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7276     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7277     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7278     boolean should_turn_left, should_turn_right, should_move_on;
7279     int rnd_value = 24;
7280     int rnd = RND(rnd_value);
7281
7282     should_turn_left = (can_turn_left &&
7283                         (!can_move_on ||
7284                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7285                                                    y + back_dy + left_dy)));
7286     should_turn_right = (can_turn_right &&
7287                          (!can_move_on ||
7288                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7289                                                     y + back_dy + right_dy)));
7290     should_move_on = (can_move_on &&
7291                       (!can_turn_left ||
7292                        !can_turn_right ||
7293                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7294                                                  y + move_dy + left_dy) ||
7295                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7296                                                  y + move_dy + right_dy)));
7297
7298     if (should_turn_left || should_turn_right || should_move_on)
7299     {
7300       if (should_turn_left && should_turn_right && should_move_on)
7301         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7302                         rnd < 2 * rnd_value / 3 ? right_dir :
7303                         old_move_dir);
7304       else if (should_turn_left && should_turn_right)
7305         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7306       else if (should_turn_left && should_move_on)
7307         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7308       else if (should_turn_right && should_move_on)
7309         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7310       else if (should_turn_left)
7311         MovDir[x][y] = left_dir;
7312       else if (should_turn_right)
7313         MovDir[x][y] = right_dir;
7314       else if (should_move_on)
7315         MovDir[x][y] = old_move_dir;
7316     }
7317     else if (can_move_on && rnd > rnd_value / 8)
7318       MovDir[x][y] = old_move_dir;
7319     else if (can_turn_left && can_turn_right)
7320       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7321     else if (can_turn_left && rnd > rnd_value / 8)
7322       MovDir[x][y] = left_dir;
7323     else if (can_turn_right && rnd > rnd_value/8)
7324       MovDir[x][y] = right_dir;
7325     else
7326       MovDir[x][y] = back_dir;
7327
7328     xx = x + move_xy[MovDir[x][y]].dx;
7329     yy = y + move_xy[MovDir[x][y]].dy;
7330
7331     if (!IN_LEV_FIELD(xx, yy) ||
7332         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7333       MovDir[x][y] = old_move_dir;
7334
7335     MovDelay[x][y] = 0;
7336   }
7337   else if (element == EL_DRAGON)
7338   {
7339     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7340     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7341     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7342     int rnd_value = 24;
7343     int rnd = RND(rnd_value);
7344
7345     if (can_move_on && rnd > rnd_value / 8)
7346       MovDir[x][y] = old_move_dir;
7347     else if (can_turn_left && can_turn_right)
7348       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7349     else if (can_turn_left && rnd > rnd_value / 8)
7350       MovDir[x][y] = left_dir;
7351     else if (can_turn_right && rnd > rnd_value / 8)
7352       MovDir[x][y] = right_dir;
7353     else
7354       MovDir[x][y] = back_dir;
7355
7356     xx = x + move_xy[MovDir[x][y]].dx;
7357     yy = y + move_xy[MovDir[x][y]].dy;
7358
7359     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7360       MovDir[x][y] = old_move_dir;
7361
7362     MovDelay[x][y] = 0;
7363   }
7364   else if (element == EL_MOLE)
7365   {
7366     boolean can_move_on =
7367       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7368                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7369                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7370     if (!can_move_on)
7371     {
7372       boolean can_turn_left =
7373         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7374                               IS_AMOEBOID(Tile[left_x][left_y])));
7375
7376       boolean can_turn_right =
7377         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7378                               IS_AMOEBOID(Tile[right_x][right_y])));
7379
7380       if (can_turn_left && can_turn_right)
7381         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7382       else if (can_turn_left)
7383         MovDir[x][y] = left_dir;
7384       else
7385         MovDir[x][y] = right_dir;
7386     }
7387
7388     if (MovDir[x][y] != old_move_dir)
7389       MovDelay[x][y] = 9;
7390   }
7391   else if (element == EL_BALLOON)
7392   {
7393     MovDir[x][y] = game.wind_direction;
7394     MovDelay[x][y] = 0;
7395   }
7396   else if (element == EL_SPRING)
7397   {
7398     if (MovDir[x][y] & MV_HORIZONTAL)
7399     {
7400       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7401           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7402       {
7403         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7404         ResetGfxAnimation(move_x, move_y);
7405         TEST_DrawLevelField(move_x, move_y);
7406
7407         MovDir[x][y] = back_dir;
7408       }
7409       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7410                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7411         MovDir[x][y] = MV_NONE;
7412     }
7413
7414     MovDelay[x][y] = 0;
7415   }
7416   else if (element == EL_ROBOT ||
7417            element == EL_SATELLITE ||
7418            element == EL_PENGUIN ||
7419            element == EL_EMC_ANDROID)
7420   {
7421     int attr_x = -1, attr_y = -1;
7422
7423     if (game.all_players_gone)
7424     {
7425       attr_x = game.exit_x;
7426       attr_y = game.exit_y;
7427     }
7428     else
7429     {
7430       int i;
7431
7432       for (i = 0; i < MAX_PLAYERS; i++)
7433       {
7434         struct PlayerInfo *player = &stored_player[i];
7435         int jx = player->jx, jy = player->jy;
7436
7437         if (!player->active)
7438           continue;
7439
7440         if (attr_x == -1 ||
7441             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7442         {
7443           attr_x = jx;
7444           attr_y = jy;
7445         }
7446       }
7447     }
7448
7449     if (element == EL_ROBOT &&
7450         game.robot_wheel_x >= 0 &&
7451         game.robot_wheel_y >= 0 &&
7452         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7453          game.engine_version < VERSION_IDENT(3,1,0,0)))
7454     {
7455       attr_x = game.robot_wheel_x;
7456       attr_y = game.robot_wheel_y;
7457     }
7458
7459     if (element == EL_PENGUIN)
7460     {
7461       int i;
7462       struct XY *xy = xy_topdown;
7463
7464       for (i = 0; i < NUM_DIRECTIONS; i++)
7465       {
7466         int ex = x + xy[i].x;
7467         int ey = y + xy[i].y;
7468
7469         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7470                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7471                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7472                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7473         {
7474           attr_x = ex;
7475           attr_y = ey;
7476           break;
7477         }
7478       }
7479     }
7480
7481     MovDir[x][y] = MV_NONE;
7482     if (attr_x < x)
7483       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7484     else if (attr_x > x)
7485       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7486     if (attr_y < y)
7487       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7488     else if (attr_y > y)
7489       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7490
7491     if (element == EL_ROBOT)
7492     {
7493       int newx, newy;
7494
7495       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7496         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7497       Moving2Blocked(x, y, &newx, &newy);
7498
7499       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7500         MovDelay[x][y] = 8 + 8 * !RND(3);
7501       else
7502         MovDelay[x][y] = 16;
7503     }
7504     else if (element == EL_PENGUIN)
7505     {
7506       int newx, newy;
7507
7508       MovDelay[x][y] = 1;
7509
7510       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7511       {
7512         boolean first_horiz = RND(2);
7513         int new_move_dir = MovDir[x][y];
7514
7515         MovDir[x][y] =
7516           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7517         Moving2Blocked(x, y, &newx, &newy);
7518
7519         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7520           return;
7521
7522         MovDir[x][y] =
7523           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7524         Moving2Blocked(x, y, &newx, &newy);
7525
7526         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7527           return;
7528
7529         MovDir[x][y] = old_move_dir;
7530         return;
7531       }
7532     }
7533     else if (element == EL_SATELLITE)
7534     {
7535       int newx, newy;
7536
7537       MovDelay[x][y] = 1;
7538
7539       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7540       {
7541         boolean first_horiz = RND(2);
7542         int new_move_dir = MovDir[x][y];
7543
7544         MovDir[x][y] =
7545           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7546         Moving2Blocked(x, y, &newx, &newy);
7547
7548         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7549           return;
7550
7551         MovDir[x][y] =
7552           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7553         Moving2Blocked(x, y, &newx, &newy);
7554
7555         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7556           return;
7557
7558         MovDir[x][y] = old_move_dir;
7559         return;
7560       }
7561     }
7562     else if (element == EL_EMC_ANDROID)
7563     {
7564       static int check_pos[16] =
7565       {
7566         -1,             //  0 => (invalid)
7567         7,              //  1 => MV_LEFT
7568         3,              //  2 => MV_RIGHT
7569         -1,             //  3 => (invalid)
7570         1,              //  4 =>            MV_UP
7571         0,              //  5 => MV_LEFT  | MV_UP
7572         2,              //  6 => MV_RIGHT | MV_UP
7573         -1,             //  7 => (invalid)
7574         5,              //  8 =>            MV_DOWN
7575         6,              //  9 => MV_LEFT  | MV_DOWN
7576         4,              // 10 => MV_RIGHT | MV_DOWN
7577         -1,             // 11 => (invalid)
7578         -1,             // 12 => (invalid)
7579         -1,             // 13 => (invalid)
7580         -1,             // 14 => (invalid)
7581         -1,             // 15 => (invalid)
7582       };
7583       static struct
7584       {
7585         int dx, dy;
7586         int dir;
7587       } check_xy[8] =
7588       {
7589         { -1, -1,       MV_LEFT  | MV_UP   },
7590         {  0, -1,                  MV_UP   },
7591         { +1, -1,       MV_RIGHT | MV_UP   },
7592         { +1,  0,       MV_RIGHT           },
7593         { +1, +1,       MV_RIGHT | MV_DOWN },
7594         {  0, +1,                  MV_DOWN },
7595         { -1, +1,       MV_LEFT  | MV_DOWN },
7596         { -1,  0,       MV_LEFT            },
7597       };
7598       int start_pos, check_order;
7599       boolean can_clone = FALSE;
7600       int i;
7601
7602       // check if there is any free field around current position
7603       for (i = 0; i < 8; i++)
7604       {
7605         int newx = x + check_xy[i].dx;
7606         int newy = y + check_xy[i].dy;
7607
7608         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7609         {
7610           can_clone = TRUE;
7611
7612           break;
7613         }
7614       }
7615
7616       if (can_clone)            // randomly find an element to clone
7617       {
7618         can_clone = FALSE;
7619
7620         start_pos = check_pos[RND(8)];
7621         check_order = (RND(2) ? -1 : +1);
7622
7623         for (i = 0; i < 8; i++)
7624         {
7625           int pos_raw = start_pos + i * check_order;
7626           int pos = (pos_raw + 8) % 8;
7627           int newx = x + check_xy[pos].dx;
7628           int newy = y + check_xy[pos].dy;
7629
7630           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7631           {
7632             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7633             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7634
7635             Store[x][y] = Tile[newx][newy];
7636
7637             can_clone = TRUE;
7638
7639             break;
7640           }
7641         }
7642       }
7643
7644       if (can_clone)            // randomly find a direction to move
7645       {
7646         can_clone = FALSE;
7647
7648         start_pos = check_pos[RND(8)];
7649         check_order = (RND(2) ? -1 : +1);
7650
7651         for (i = 0; i < 8; i++)
7652         {
7653           int pos_raw = start_pos + i * check_order;
7654           int pos = (pos_raw + 8) % 8;
7655           int newx = x + check_xy[pos].dx;
7656           int newy = y + check_xy[pos].dy;
7657           int new_move_dir = check_xy[pos].dir;
7658
7659           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7660           {
7661             MovDir[x][y] = new_move_dir;
7662             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7663
7664             can_clone = TRUE;
7665
7666             break;
7667           }
7668         }
7669       }
7670
7671       if (can_clone)            // cloning and moving successful
7672         return;
7673
7674       // cannot clone -- try to move towards player
7675
7676       start_pos = check_pos[MovDir[x][y] & 0x0f];
7677       check_order = (RND(2) ? -1 : +1);
7678
7679       for (i = 0; i < 3; i++)
7680       {
7681         // first check start_pos, then previous/next or (next/previous) pos
7682         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7683         int pos = (pos_raw + 8) % 8;
7684         int newx = x + check_xy[pos].dx;
7685         int newy = y + check_xy[pos].dy;
7686         int new_move_dir = check_xy[pos].dir;
7687
7688         if (IS_PLAYER(newx, newy))
7689           break;
7690
7691         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7692         {
7693           MovDir[x][y] = new_move_dir;
7694           MovDelay[x][y] = level.android_move_time * 8 + 1;
7695
7696           break;
7697         }
7698       }
7699     }
7700   }
7701   else if (move_pattern == MV_TURNING_LEFT ||
7702            move_pattern == MV_TURNING_RIGHT ||
7703            move_pattern == MV_TURNING_LEFT_RIGHT ||
7704            move_pattern == MV_TURNING_RIGHT_LEFT ||
7705            move_pattern == MV_TURNING_RANDOM ||
7706            move_pattern == MV_ALL_DIRECTIONS)
7707   {
7708     boolean can_turn_left =
7709       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7710     boolean can_turn_right =
7711       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7712
7713     if (element_info[element].move_stepsize == 0)       // "not moving"
7714       return;
7715
7716     if (move_pattern == MV_TURNING_LEFT)
7717       MovDir[x][y] = left_dir;
7718     else if (move_pattern == MV_TURNING_RIGHT)
7719       MovDir[x][y] = right_dir;
7720     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7721       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7722     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7723       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7724     else if (move_pattern == MV_TURNING_RANDOM)
7725       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7726                       can_turn_right && !can_turn_left ? right_dir :
7727                       RND(2) ? left_dir : right_dir);
7728     else if (can_turn_left && can_turn_right)
7729       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7730     else if (can_turn_left)
7731       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7732     else if (can_turn_right)
7733       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7734     else
7735       MovDir[x][y] = back_dir;
7736
7737     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738   }
7739   else if (move_pattern == MV_HORIZONTAL ||
7740            move_pattern == MV_VERTICAL)
7741   {
7742     if (move_pattern & old_move_dir)
7743       MovDir[x][y] = back_dir;
7744     else if (move_pattern == MV_HORIZONTAL)
7745       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7746     else if (move_pattern == MV_VERTICAL)
7747       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7748
7749     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751   else if (move_pattern & MV_ANY_DIRECTION)
7752   {
7753     MovDir[x][y] = move_pattern;
7754     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7755   }
7756   else if (move_pattern & MV_WIND_DIRECTION)
7757   {
7758     MovDir[x][y] = game.wind_direction;
7759     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7760   }
7761   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7762   {
7763     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7764       MovDir[x][y] = left_dir;
7765     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7766       MovDir[x][y] = right_dir;
7767
7768     if (MovDir[x][y] != old_move_dir)
7769       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7770   }
7771   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7772   {
7773     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7774       MovDir[x][y] = right_dir;
7775     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7776       MovDir[x][y] = left_dir;
7777
7778     if (MovDir[x][y] != old_move_dir)
7779       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7780   }
7781   else if (move_pattern == MV_TOWARDS_PLAYER ||
7782            move_pattern == MV_AWAY_FROM_PLAYER)
7783   {
7784     int attr_x = -1, attr_y = -1;
7785     int newx, newy;
7786     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7787
7788     if (game.all_players_gone)
7789     {
7790       attr_x = game.exit_x;
7791       attr_y = game.exit_y;
7792     }
7793     else
7794     {
7795       int i;
7796
7797       for (i = 0; i < MAX_PLAYERS; i++)
7798       {
7799         struct PlayerInfo *player = &stored_player[i];
7800         int jx = player->jx, jy = player->jy;
7801
7802         if (!player->active)
7803           continue;
7804
7805         if (attr_x == -1 ||
7806             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7807         {
7808           attr_x = jx;
7809           attr_y = jy;
7810         }
7811       }
7812     }
7813
7814     MovDir[x][y] = MV_NONE;
7815     if (attr_x < x)
7816       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7817     else if (attr_x > x)
7818       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7819     if (attr_y < y)
7820       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7821     else if (attr_y > y)
7822       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7823
7824     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7825
7826     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7827     {
7828       boolean first_horiz = RND(2);
7829       int new_move_dir = MovDir[x][y];
7830
7831       if (element_info[element].move_stepsize == 0)     // "not moving"
7832       {
7833         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7834         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7835
7836         return;
7837       }
7838
7839       MovDir[x][y] =
7840         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7841       Moving2Blocked(x, y, &newx, &newy);
7842
7843       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7844         return;
7845
7846       MovDir[x][y] =
7847         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7848       Moving2Blocked(x, y, &newx, &newy);
7849
7850       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7851         return;
7852
7853       MovDir[x][y] = old_move_dir;
7854     }
7855   }
7856   else if (move_pattern == MV_WHEN_PUSHED ||
7857            move_pattern == MV_WHEN_DROPPED)
7858   {
7859     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7860       MovDir[x][y] = MV_NONE;
7861
7862     MovDelay[x][y] = 0;
7863   }
7864   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7865   {
7866     struct XY *test_xy = xy_topdown;
7867     static int test_dir[4] =
7868     {
7869       MV_UP,
7870       MV_LEFT,
7871       MV_RIGHT,
7872       MV_DOWN
7873     };
7874     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7875     int move_preference = -1000000;     // start with very low preference
7876     int new_move_dir = MV_NONE;
7877     int start_test = RND(4);
7878     int i;
7879
7880     for (i = 0; i < NUM_DIRECTIONS; i++)
7881     {
7882       int j = (start_test + i) % 4;
7883       int move_dir = test_dir[j];
7884       int move_dir_preference;
7885
7886       xx = x + test_xy[j].x;
7887       yy = y + test_xy[j].y;
7888
7889       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7890           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7891       {
7892         new_move_dir = move_dir;
7893
7894         break;
7895       }
7896
7897       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7898         continue;
7899
7900       move_dir_preference = -1 * RunnerVisit[xx][yy];
7901       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7902         move_dir_preference = PlayerVisit[xx][yy];
7903
7904       if (move_dir_preference > move_preference)
7905       {
7906         // prefer field that has not been visited for the longest time
7907         move_preference = move_dir_preference;
7908         new_move_dir = move_dir;
7909       }
7910       else if (move_dir_preference == move_preference &&
7911                move_dir == old_move_dir)
7912       {
7913         // prefer last direction when all directions are preferred equally
7914         move_preference = move_dir_preference;
7915         new_move_dir = move_dir;
7916       }
7917     }
7918
7919     MovDir[x][y] = new_move_dir;
7920     if (old_move_dir != new_move_dir)
7921       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7922   }
7923 }
7924
7925 static void TurnRound(int x, int y)
7926 {
7927   int direction = MovDir[x][y];
7928
7929   TurnRoundExt(x, y);
7930
7931   GfxDir[x][y] = MovDir[x][y];
7932
7933   if (direction != MovDir[x][y])
7934     GfxFrame[x][y] = 0;
7935
7936   if (MovDelay[x][y])
7937     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7938
7939   ResetGfxFrame(x, y);
7940 }
7941
7942 static boolean JustBeingPushed(int x, int y)
7943 {
7944   int i;
7945
7946   for (i = 0; i < MAX_PLAYERS; i++)
7947   {
7948     struct PlayerInfo *player = &stored_player[i];
7949
7950     if (player->active && player->is_pushing && player->MovPos)
7951     {
7952       int next_jx = player->jx + (player->jx - player->last_jx);
7953       int next_jy = player->jy + (player->jy - player->last_jy);
7954
7955       if (x == next_jx && y == next_jy)
7956         return TRUE;
7957     }
7958   }
7959
7960   return FALSE;
7961 }
7962
7963 static void StartMoving(int x, int y)
7964 {
7965   boolean started_moving = FALSE;       // some elements can fall _and_ move
7966   int element = Tile[x][y];
7967
7968   if (Stop[x][y])
7969     return;
7970
7971   if (MovDelay[x][y] == 0)
7972     GfxAction[x][y] = ACTION_DEFAULT;
7973
7974   if (CAN_FALL(element) && y < lev_fieldy - 1)
7975   {
7976     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7977         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7978       if (JustBeingPushed(x, y))
7979         return;
7980
7981     if (element == EL_QUICKSAND_FULL)
7982     {
7983       if (IS_FREE(x, y + 1))
7984       {
7985         InitMovingField(x, y, MV_DOWN);
7986         started_moving = TRUE;
7987
7988         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7989 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7990         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7991           Store[x][y] = EL_ROCK;
7992 #else
7993         Store[x][y] = EL_ROCK;
7994 #endif
7995
7996         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7997       }
7998       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7999       {
8000         if (!MovDelay[x][y])
8001         {
8002           MovDelay[x][y] = TILEY + 1;
8003
8004           ResetGfxAnimation(x, y);
8005           ResetGfxAnimation(x, y + 1);
8006         }
8007
8008         if (MovDelay[x][y])
8009         {
8010           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8011           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8012
8013           MovDelay[x][y]--;
8014           if (MovDelay[x][y])
8015             return;
8016         }
8017
8018         Tile[x][y] = EL_QUICKSAND_EMPTY;
8019         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8020         Store[x][y + 1] = Store[x][y];
8021         Store[x][y] = 0;
8022
8023         PlayLevelSoundAction(x, y, ACTION_FILLING);
8024       }
8025       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8026       {
8027         if (!MovDelay[x][y])
8028         {
8029           MovDelay[x][y] = TILEY + 1;
8030
8031           ResetGfxAnimation(x, y);
8032           ResetGfxAnimation(x, y + 1);
8033         }
8034
8035         if (MovDelay[x][y])
8036         {
8037           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8038           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8039
8040           MovDelay[x][y]--;
8041           if (MovDelay[x][y])
8042             return;
8043         }
8044
8045         Tile[x][y] = EL_QUICKSAND_EMPTY;
8046         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8047         Store[x][y + 1] = Store[x][y];
8048         Store[x][y] = 0;
8049
8050         PlayLevelSoundAction(x, y, ACTION_FILLING);
8051       }
8052     }
8053     else if (element == EL_QUICKSAND_FAST_FULL)
8054     {
8055       if (IS_FREE(x, y + 1))
8056       {
8057         InitMovingField(x, y, MV_DOWN);
8058         started_moving = TRUE;
8059
8060         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8061 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8062         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8063           Store[x][y] = EL_ROCK;
8064 #else
8065         Store[x][y] = EL_ROCK;
8066 #endif
8067
8068         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8069       }
8070       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8071       {
8072         if (!MovDelay[x][y])
8073         {
8074           MovDelay[x][y] = TILEY + 1;
8075
8076           ResetGfxAnimation(x, y);
8077           ResetGfxAnimation(x, y + 1);
8078         }
8079
8080         if (MovDelay[x][y])
8081         {
8082           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8083           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8084
8085           MovDelay[x][y]--;
8086           if (MovDelay[x][y])
8087             return;
8088         }
8089
8090         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8091         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8092         Store[x][y + 1] = Store[x][y];
8093         Store[x][y] = 0;
8094
8095         PlayLevelSoundAction(x, y, ACTION_FILLING);
8096       }
8097       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8098       {
8099         if (!MovDelay[x][y])
8100         {
8101           MovDelay[x][y] = TILEY + 1;
8102
8103           ResetGfxAnimation(x, y);
8104           ResetGfxAnimation(x, y + 1);
8105         }
8106
8107         if (MovDelay[x][y])
8108         {
8109           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8110           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8111
8112           MovDelay[x][y]--;
8113           if (MovDelay[x][y])
8114             return;
8115         }
8116
8117         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8118         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8119         Store[x][y + 1] = Store[x][y];
8120         Store[x][y] = 0;
8121
8122         PlayLevelSoundAction(x, y, ACTION_FILLING);
8123       }
8124     }
8125     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8126              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8127     {
8128       InitMovingField(x, y, MV_DOWN);
8129       started_moving = TRUE;
8130
8131       Tile[x][y] = EL_QUICKSAND_FILLING;
8132       Store[x][y] = element;
8133
8134       PlayLevelSoundAction(x, y, ACTION_FILLING);
8135     }
8136     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8137              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8138     {
8139       InitMovingField(x, y, MV_DOWN);
8140       started_moving = TRUE;
8141
8142       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8143       Store[x][y] = element;
8144
8145       PlayLevelSoundAction(x, y, ACTION_FILLING);
8146     }
8147     else if (element == EL_MAGIC_WALL_FULL)
8148     {
8149       if (IS_FREE(x, y + 1))
8150       {
8151         InitMovingField(x, y, MV_DOWN);
8152         started_moving = TRUE;
8153
8154         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8155         Store[x][y] = EL_CHANGED(Store[x][y]);
8156       }
8157       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8158       {
8159         if (!MovDelay[x][y])
8160           MovDelay[x][y] = TILEY / 4 + 1;
8161
8162         if (MovDelay[x][y])
8163         {
8164           MovDelay[x][y]--;
8165           if (MovDelay[x][y])
8166             return;
8167         }
8168
8169         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8170         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8171         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8172         Store[x][y] = 0;
8173       }
8174     }
8175     else if (element == EL_BD_MAGIC_WALL_FULL)
8176     {
8177       if (IS_FREE(x, y + 1))
8178       {
8179         InitMovingField(x, y, MV_DOWN);
8180         started_moving = TRUE;
8181
8182         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8183         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8184       }
8185       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8186       {
8187         if (!MovDelay[x][y])
8188           MovDelay[x][y] = TILEY / 4 + 1;
8189
8190         if (MovDelay[x][y])
8191         {
8192           MovDelay[x][y]--;
8193           if (MovDelay[x][y])
8194             return;
8195         }
8196
8197         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8198         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8199         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8200         Store[x][y] = 0;
8201       }
8202     }
8203     else if (element == EL_DC_MAGIC_WALL_FULL)
8204     {
8205       if (IS_FREE(x, y + 1))
8206       {
8207         InitMovingField(x, y, MV_DOWN);
8208         started_moving = TRUE;
8209
8210         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8211         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8212       }
8213       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8214       {
8215         if (!MovDelay[x][y])
8216           MovDelay[x][y] = TILEY / 4 + 1;
8217
8218         if (MovDelay[x][y])
8219         {
8220           MovDelay[x][y]--;
8221           if (MovDelay[x][y])
8222             return;
8223         }
8224
8225         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8226         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8227         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8228         Store[x][y] = 0;
8229       }
8230     }
8231     else if ((CAN_PASS_MAGIC_WALL(element) &&
8232               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8233                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8234              (CAN_PASS_DC_MAGIC_WALL(element) &&
8235               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8236
8237     {
8238       InitMovingField(x, y, MV_DOWN);
8239       started_moving = TRUE;
8240
8241       Tile[x][y] =
8242         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8243          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8244          EL_DC_MAGIC_WALL_FILLING);
8245       Store[x][y] = element;
8246     }
8247     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8248     {
8249       SplashAcid(x, y + 1);
8250
8251       InitMovingField(x, y, MV_DOWN);
8252       started_moving = TRUE;
8253
8254       Store[x][y] = EL_ACID;
8255     }
8256     else if (
8257              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8258               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8259              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8260               CAN_FALL(element) && WasJustFalling[x][y] &&
8261               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8262
8263              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8264               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8265               (Tile[x][y + 1] == EL_BLOCKED)))
8266     {
8267       /* this is needed for a special case not covered by calling "Impact()"
8268          from "ContinueMoving()": if an element moves to a tile directly below
8269          another element which was just falling on that tile (which was empty
8270          in the previous frame), the falling element above would just stop
8271          instead of smashing the element below (in previous version, the above
8272          element was just checked for "moving" instead of "falling", resulting
8273          in incorrect smashes caused by horizontal movement of the above
8274          element; also, the case of the player being the element to smash was
8275          simply not covered here... :-/ ) */
8276
8277       CheckCollision[x][y] = 0;
8278       CheckImpact[x][y] = 0;
8279
8280       Impact(x, y);
8281     }
8282     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8283     {
8284       if (MovDir[x][y] == MV_NONE)
8285       {
8286         InitMovingField(x, y, MV_DOWN);
8287         started_moving = TRUE;
8288       }
8289     }
8290     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8291     {
8292       if (WasJustFalling[x][y]) // prevent animation from being restarted
8293         MovDir[x][y] = MV_DOWN;
8294
8295       InitMovingField(x, y, MV_DOWN);
8296       started_moving = TRUE;
8297     }
8298     else if (element == EL_AMOEBA_DROP)
8299     {
8300       Tile[x][y] = EL_AMOEBA_GROWING;
8301       Store[x][y] = EL_AMOEBA_WET;
8302     }
8303     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8304               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8305              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8306              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8307     {
8308       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8309                                 (IS_FREE(x - 1, y + 1) ||
8310                                  Tile[x - 1][y + 1] == EL_ACID));
8311       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8312                                 (IS_FREE(x + 1, y + 1) ||
8313                                  Tile[x + 1][y + 1] == EL_ACID));
8314       boolean can_fall_any  = (can_fall_left || can_fall_right);
8315       boolean can_fall_both = (can_fall_left && can_fall_right);
8316       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8317
8318       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8319       {
8320         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8321           can_fall_right = FALSE;
8322         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8323           can_fall_left = FALSE;
8324         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8325           can_fall_right = FALSE;
8326         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8327           can_fall_left = FALSE;
8328
8329         can_fall_any  = (can_fall_left || can_fall_right);
8330         can_fall_both = FALSE;
8331       }
8332
8333       if (can_fall_both)
8334       {
8335         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8336           can_fall_right = FALSE;       // slip down on left side
8337         else
8338           can_fall_left = !(can_fall_right = RND(2));
8339
8340         can_fall_both = FALSE;
8341       }
8342
8343       if (can_fall_any)
8344       {
8345         // if not determined otherwise, prefer left side for slipping down
8346         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8347         started_moving = TRUE;
8348       }
8349     }
8350     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8351     {
8352       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8353       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8354       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8355       int belt_dir = game.belt_dir[belt_nr];
8356
8357       if ((belt_dir == MV_LEFT  && left_is_free) ||
8358           (belt_dir == MV_RIGHT && right_is_free))
8359       {
8360         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8361
8362         InitMovingField(x, y, belt_dir);
8363         started_moving = TRUE;
8364
8365         Pushed[x][y] = TRUE;
8366         Pushed[nextx][y] = TRUE;
8367
8368         GfxAction[x][y] = ACTION_DEFAULT;
8369       }
8370       else
8371       {
8372         MovDir[x][y] = 0;       // if element was moving, stop it
8373       }
8374     }
8375   }
8376
8377   // not "else if" because of elements that can fall and move (EL_SPRING)
8378   if (CAN_MOVE(element) && !started_moving)
8379   {
8380     int move_pattern = element_info[element].move_pattern;
8381     int newx, newy;
8382
8383     Moving2Blocked(x, y, &newx, &newy);
8384
8385     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8386       return;
8387
8388     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8389         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8390     {
8391       WasJustMoving[x][y] = 0;
8392       CheckCollision[x][y] = 0;
8393
8394       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8395
8396       if (Tile[x][y] != element)        // element has changed
8397         return;
8398     }
8399
8400     if (!MovDelay[x][y])        // start new movement phase
8401     {
8402       // all objects that can change their move direction after each step
8403       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8404
8405       if (element != EL_YAMYAM &&
8406           element != EL_DARK_YAMYAM &&
8407           element != EL_PACMAN &&
8408           !(move_pattern & MV_ANY_DIRECTION) &&
8409           move_pattern != MV_TURNING_LEFT &&
8410           move_pattern != MV_TURNING_RIGHT &&
8411           move_pattern != MV_TURNING_LEFT_RIGHT &&
8412           move_pattern != MV_TURNING_RIGHT_LEFT &&
8413           move_pattern != MV_TURNING_RANDOM)
8414       {
8415         TurnRound(x, y);
8416
8417         if (MovDelay[x][y] && (element == EL_BUG ||
8418                                element == EL_SPACESHIP ||
8419                                element == EL_SP_SNIKSNAK ||
8420                                element == EL_SP_ELECTRON ||
8421                                element == EL_MOLE))
8422           TEST_DrawLevelField(x, y);
8423       }
8424     }
8425
8426     if (MovDelay[x][y])         // wait some time before next movement
8427     {
8428       MovDelay[x][y]--;
8429
8430       if (element == EL_ROBOT ||
8431           element == EL_YAMYAM ||
8432           element == EL_DARK_YAMYAM)
8433       {
8434         DrawLevelElementAnimationIfNeeded(x, y, element);
8435         PlayLevelSoundAction(x, y, ACTION_WAITING);
8436       }
8437       else if (element == EL_SP_ELECTRON)
8438         DrawLevelElementAnimationIfNeeded(x, y, element);
8439       else if (element == EL_DRAGON)
8440       {
8441         int i;
8442         int dir = MovDir[x][y];
8443         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8444         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8445         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8446                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8447                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8448                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8449         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8450
8451         GfxAction[x][y] = ACTION_ATTACKING;
8452
8453         if (IS_PLAYER(x, y))
8454           DrawPlayerField(x, y);
8455         else
8456           TEST_DrawLevelField(x, y);
8457
8458         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8459
8460         for (i = 1; i <= 3; i++)
8461         {
8462           int xx = x + i * dx;
8463           int yy = y + i * dy;
8464           int sx = SCREENX(xx);
8465           int sy = SCREENY(yy);
8466           int flame_graphic = graphic + (i - 1);
8467
8468           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8469             break;
8470
8471           if (MovDelay[x][y])
8472           {
8473             int flamed = MovingOrBlocked2Element(xx, yy);
8474
8475             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8476               Bang(xx, yy);
8477             else
8478               RemoveMovingField(xx, yy);
8479
8480             ChangeDelay[xx][yy] = 0;
8481
8482             Tile[xx][yy] = EL_FLAMES;
8483
8484             if (IN_SCR_FIELD(sx, sy))
8485             {
8486               TEST_DrawLevelFieldCrumbled(xx, yy);
8487               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8488             }
8489           }
8490           else
8491           {
8492             if (Tile[xx][yy] == EL_FLAMES)
8493               Tile[xx][yy] = EL_EMPTY;
8494             TEST_DrawLevelField(xx, yy);
8495           }
8496         }
8497       }
8498
8499       if (MovDelay[x][y])       // element still has to wait some time
8500       {
8501         PlayLevelSoundAction(x, y, ACTION_WAITING);
8502
8503         return;
8504       }
8505     }
8506
8507     // now make next step
8508
8509     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8510
8511     if (DONT_COLLIDE_WITH(element) &&
8512         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8513         !PLAYER_ENEMY_PROTECTED(newx, newy))
8514     {
8515       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8516
8517       return;
8518     }
8519
8520     else if (CAN_MOVE_INTO_ACID(element) &&
8521              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8522              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8523              (MovDir[x][y] == MV_DOWN ||
8524               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8525     {
8526       SplashAcid(newx, newy);
8527       Store[x][y] = EL_ACID;
8528     }
8529     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8530     {
8531       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8532           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8533           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8534           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8535       {
8536         RemoveField(x, y);
8537         TEST_DrawLevelField(x, y);
8538
8539         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8540         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8541           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8542
8543         game.friends_still_needed--;
8544         if (!game.friends_still_needed &&
8545             !game.GameOver &&
8546             game.all_players_gone)
8547           LevelSolved();
8548
8549         return;
8550       }
8551       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8552       {
8553         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8554           TEST_DrawLevelField(newx, newy);
8555         else
8556           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8557       }
8558       else if (!IS_FREE(newx, newy))
8559       {
8560         GfxAction[x][y] = ACTION_WAITING;
8561
8562         if (IS_PLAYER(x, y))
8563           DrawPlayerField(x, y);
8564         else
8565           TEST_DrawLevelField(x, y);
8566
8567         return;
8568       }
8569     }
8570     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8571     {
8572       if (IS_FOOD_PIG(Tile[newx][newy]))
8573       {
8574         if (IS_MOVING(newx, newy))
8575           RemoveMovingField(newx, newy);
8576         else
8577         {
8578           Tile[newx][newy] = EL_EMPTY;
8579           TEST_DrawLevelField(newx, newy);
8580         }
8581
8582         PlayLevelSound(x, y, SND_PIG_DIGGING);
8583       }
8584       else if (!IS_FREE(newx, newy))
8585       {
8586         if (IS_PLAYER(x, y))
8587           DrawPlayerField(x, y);
8588         else
8589           TEST_DrawLevelField(x, y);
8590
8591         return;
8592       }
8593     }
8594     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8595     {
8596       if (Store[x][y] != EL_EMPTY)
8597       {
8598         boolean can_clone = FALSE;
8599         int xx, yy;
8600
8601         // check if element to clone is still there
8602         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8603         {
8604           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8605           {
8606             can_clone = TRUE;
8607
8608             break;
8609           }
8610         }
8611
8612         // cannot clone or target field not free anymore -- do not clone
8613         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8614           Store[x][y] = EL_EMPTY;
8615       }
8616
8617       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8618       {
8619         if (IS_MV_DIAGONAL(MovDir[x][y]))
8620         {
8621           int diagonal_move_dir = MovDir[x][y];
8622           int stored = Store[x][y];
8623           int change_delay = 8;
8624           int graphic;
8625
8626           // android is moving diagonally
8627
8628           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8629
8630           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8631           GfxElement[x][y] = EL_EMC_ANDROID;
8632           GfxAction[x][y] = ACTION_SHRINKING;
8633           GfxDir[x][y] = diagonal_move_dir;
8634           ChangeDelay[x][y] = change_delay;
8635
8636           if (Store[x][y] == EL_EMPTY)
8637             Store[x][y] = GfxElementEmpty[x][y];
8638
8639           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8640                                    GfxDir[x][y]);
8641
8642           DrawLevelGraphicAnimation(x, y, graphic);
8643           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8644
8645           if (Tile[newx][newy] == EL_ACID)
8646           {
8647             SplashAcid(newx, newy);
8648
8649             return;
8650           }
8651
8652           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8653
8654           Store[newx][newy] = EL_EMC_ANDROID;
8655           GfxElement[newx][newy] = EL_EMC_ANDROID;
8656           GfxAction[newx][newy] = ACTION_GROWING;
8657           GfxDir[newx][newy] = diagonal_move_dir;
8658           ChangeDelay[newx][newy] = change_delay;
8659
8660           graphic = el_act_dir2img(GfxElement[newx][newy],
8661                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8662
8663           DrawLevelGraphicAnimation(newx, newy, graphic);
8664           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8665
8666           return;
8667         }
8668         else
8669         {
8670           Tile[newx][newy] = EL_EMPTY;
8671           TEST_DrawLevelField(newx, newy);
8672
8673           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8674         }
8675       }
8676       else if (!IS_FREE(newx, newy))
8677       {
8678         return;
8679       }
8680     }
8681     else if (IS_CUSTOM_ELEMENT(element) &&
8682              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8683     {
8684       if (!DigFieldByCE(newx, newy, element))
8685         return;
8686
8687       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8688       {
8689         RunnerVisit[x][y] = FrameCounter;
8690         PlayerVisit[x][y] /= 8;         // expire player visit path
8691       }
8692     }
8693     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8694     {
8695       if (!IS_FREE(newx, newy))
8696       {
8697         if (IS_PLAYER(x, y))
8698           DrawPlayerField(x, y);
8699         else
8700           TEST_DrawLevelField(x, y);
8701
8702         return;
8703       }
8704       else
8705       {
8706         boolean wanna_flame = !RND(10);
8707         int dx = newx - x, dy = newy - y;
8708         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8709         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8710         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8711                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8712         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8713                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8714
8715         if ((wanna_flame ||
8716              IS_CLASSIC_ENEMY(element1) ||
8717              IS_CLASSIC_ENEMY(element2)) &&
8718             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8719             element1 != EL_FLAMES && element2 != EL_FLAMES)
8720         {
8721           ResetGfxAnimation(x, y);
8722           GfxAction[x][y] = ACTION_ATTACKING;
8723
8724           if (IS_PLAYER(x, y))
8725             DrawPlayerField(x, y);
8726           else
8727             TEST_DrawLevelField(x, y);
8728
8729           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8730
8731           MovDelay[x][y] = 50;
8732
8733           Tile[newx][newy] = EL_FLAMES;
8734           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8735             Tile[newx1][newy1] = EL_FLAMES;
8736           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8737             Tile[newx2][newy2] = EL_FLAMES;
8738
8739           return;
8740         }
8741       }
8742     }
8743     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8744              Tile[newx][newy] == EL_DIAMOND)
8745     {
8746       if (IS_MOVING(newx, newy))
8747         RemoveMovingField(newx, newy);
8748       else
8749       {
8750         Tile[newx][newy] = EL_EMPTY;
8751         TEST_DrawLevelField(newx, newy);
8752       }
8753
8754       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8755     }
8756     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8757              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8758     {
8759       if (AmoebaNr[newx][newy])
8760       {
8761         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8762         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8763             Tile[newx][newy] == EL_BD_AMOEBA)
8764           AmoebaCnt[AmoebaNr[newx][newy]]--;
8765       }
8766
8767       if (IS_MOVING(newx, newy))
8768       {
8769         RemoveMovingField(newx, newy);
8770       }
8771       else
8772       {
8773         Tile[newx][newy] = EL_EMPTY;
8774         TEST_DrawLevelField(newx, newy);
8775       }
8776
8777       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8778     }
8779     else if ((element == EL_PACMAN || element == EL_MOLE)
8780              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8781     {
8782       if (AmoebaNr[newx][newy])
8783       {
8784         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8785         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8786             Tile[newx][newy] == EL_BD_AMOEBA)
8787           AmoebaCnt[AmoebaNr[newx][newy]]--;
8788       }
8789
8790       if (element == EL_MOLE)
8791       {
8792         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8793         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8794
8795         ResetGfxAnimation(x, y);
8796         GfxAction[x][y] = ACTION_DIGGING;
8797         TEST_DrawLevelField(x, y);
8798
8799         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8800
8801         return;                         // wait for shrinking amoeba
8802       }
8803       else      // element == EL_PACMAN
8804       {
8805         Tile[newx][newy] = EL_EMPTY;
8806         TEST_DrawLevelField(newx, newy);
8807         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8808       }
8809     }
8810     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8811              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8812               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8813     {
8814       // wait for shrinking amoeba to completely disappear
8815       return;
8816     }
8817     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8818     {
8819       // object was running against a wall
8820
8821       TurnRound(x, y);
8822
8823       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8824         DrawLevelElementAnimation(x, y, element);
8825
8826       if (DONT_TOUCH(element))
8827         TestIfBadThingTouchesPlayer(x, y);
8828
8829       return;
8830     }
8831
8832     InitMovingField(x, y, MovDir[x][y]);
8833
8834     PlayLevelSoundAction(x, y, ACTION_MOVING);
8835   }
8836
8837   if (MovDir[x][y])
8838     ContinueMoving(x, y);
8839 }
8840
8841 void ContinueMoving(int x, int y)
8842 {
8843   int element = Tile[x][y];
8844   struct ElementInfo *ei = &element_info[element];
8845   int direction = MovDir[x][y];
8846   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8847   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8848   int newx = x + dx, newy = y + dy;
8849   int stored = Store[x][y];
8850   int stored_new = Store[newx][newy];
8851   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8852   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8853   boolean last_line = (newy == lev_fieldy - 1);
8854   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8855
8856   if (pushed_by_player)         // special case: moving object pushed by player
8857   {
8858     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8859   }
8860   else if (use_step_delay)      // special case: moving object has step delay
8861   {
8862     if (!MovDelay[x][y])
8863       MovPos[x][y] += getElementMoveStepsize(x, y);
8864
8865     if (MovDelay[x][y])
8866       MovDelay[x][y]--;
8867     else
8868       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8869
8870     if (MovDelay[x][y])
8871     {
8872       TEST_DrawLevelField(x, y);
8873
8874       return;   // element is still waiting
8875     }
8876   }
8877   else                          // normal case: generically moving object
8878   {
8879     MovPos[x][y] += getElementMoveStepsize(x, y);
8880   }
8881
8882   if (ABS(MovPos[x][y]) < TILEX)
8883   {
8884     TEST_DrawLevelField(x, y);
8885
8886     return;     // element is still moving
8887   }
8888
8889   // element reached destination field
8890
8891   Tile[x][y] = EL_EMPTY;
8892   Tile[newx][newy] = element;
8893   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8894
8895   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8896   {
8897     element = Tile[newx][newy] = EL_ACID;
8898   }
8899   else if (element == EL_MOLE)
8900   {
8901     Tile[x][y] = EL_SAND;
8902
8903     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8904   }
8905   else if (element == EL_QUICKSAND_FILLING)
8906   {
8907     element = Tile[newx][newy] = get_next_element(element);
8908     Store[newx][newy] = Store[x][y];
8909   }
8910   else if (element == EL_QUICKSAND_EMPTYING)
8911   {
8912     Tile[x][y] = get_next_element(element);
8913     element = Tile[newx][newy] = Store[x][y];
8914   }
8915   else if (element == EL_QUICKSAND_FAST_FILLING)
8916   {
8917     element = Tile[newx][newy] = get_next_element(element);
8918     Store[newx][newy] = Store[x][y];
8919   }
8920   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8921   {
8922     Tile[x][y] = get_next_element(element);
8923     element = Tile[newx][newy] = Store[x][y];
8924   }
8925   else if (element == EL_MAGIC_WALL_FILLING)
8926   {
8927     element = Tile[newx][newy] = get_next_element(element);
8928     if (!game.magic_wall_active)
8929       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8930     Store[newx][newy] = Store[x][y];
8931   }
8932   else if (element == EL_MAGIC_WALL_EMPTYING)
8933   {
8934     Tile[x][y] = get_next_element(element);
8935     if (!game.magic_wall_active)
8936       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8937     element = Tile[newx][newy] = Store[x][y];
8938
8939     InitField(newx, newy, FALSE);
8940   }
8941   else if (element == EL_BD_MAGIC_WALL_FILLING)
8942   {
8943     element = Tile[newx][newy] = get_next_element(element);
8944     if (!game.magic_wall_active)
8945       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8946     Store[newx][newy] = Store[x][y];
8947   }
8948   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8949   {
8950     Tile[x][y] = get_next_element(element);
8951     if (!game.magic_wall_active)
8952       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8953     element = Tile[newx][newy] = Store[x][y];
8954
8955     InitField(newx, newy, FALSE);
8956   }
8957   else if (element == EL_DC_MAGIC_WALL_FILLING)
8958   {
8959     element = Tile[newx][newy] = get_next_element(element);
8960     if (!game.magic_wall_active)
8961       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8962     Store[newx][newy] = Store[x][y];
8963   }
8964   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8965   {
8966     Tile[x][y] = get_next_element(element);
8967     if (!game.magic_wall_active)
8968       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8969     element = Tile[newx][newy] = Store[x][y];
8970
8971     InitField(newx, newy, FALSE);
8972   }
8973   else if (element == EL_AMOEBA_DROPPING)
8974   {
8975     Tile[x][y] = get_next_element(element);
8976     element = Tile[newx][newy] = Store[x][y];
8977   }
8978   else if (element == EL_SOKOBAN_OBJECT)
8979   {
8980     if (Back[x][y])
8981       Tile[x][y] = Back[x][y];
8982
8983     if (Back[newx][newy])
8984       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8985
8986     Back[x][y] = Back[newx][newy] = 0;
8987   }
8988
8989   Store[x][y] = EL_EMPTY;
8990   MovPos[x][y] = 0;
8991   MovDir[x][y] = 0;
8992   MovDelay[x][y] = 0;
8993
8994   MovDelay[newx][newy] = 0;
8995
8996   if (CAN_CHANGE_OR_HAS_ACTION(element))
8997   {
8998     // copy element change control values to new field
8999     ChangeDelay[newx][newy] = ChangeDelay[x][y];
9000     ChangePage[newx][newy]  = ChangePage[x][y];
9001     ChangeCount[newx][newy] = ChangeCount[x][y];
9002     ChangeEvent[newx][newy] = ChangeEvent[x][y];
9003   }
9004
9005   CustomValue[newx][newy] = CustomValue[x][y];
9006
9007   ChangeDelay[x][y] = 0;
9008   ChangePage[x][y] = -1;
9009   ChangeCount[x][y] = 0;
9010   ChangeEvent[x][y] = -1;
9011
9012   CustomValue[x][y] = 0;
9013
9014   // copy animation control values to new field
9015   GfxFrame[newx][newy]  = GfxFrame[x][y];
9016   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9017   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9018   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9019
9020   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9021
9022   // some elements can leave other elements behind after moving
9023   if (ei->move_leave_element != EL_EMPTY &&
9024       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9025       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9026   {
9027     int move_leave_element = ei->move_leave_element;
9028
9029     // this makes it possible to leave the removed element again
9030     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9031       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9032
9033     Tile[x][y] = move_leave_element;
9034
9035     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9036       MovDir[x][y] = direction;
9037
9038     InitField(x, y, FALSE);
9039
9040     if (GFX_CRUMBLED(Tile[x][y]))
9041       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9042
9043     if (IS_PLAYER_ELEMENT(move_leave_element))
9044       RelocatePlayer(x, y, move_leave_element);
9045   }
9046
9047   // do this after checking for left-behind element
9048   ResetGfxAnimation(x, y);      // reset animation values for old field
9049
9050   if (!CAN_MOVE(element) ||
9051       (CAN_FALL(element) && direction == MV_DOWN &&
9052        (element == EL_SPRING ||
9053         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9054         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9055     GfxDir[x][y] = MovDir[newx][newy] = 0;
9056
9057   TEST_DrawLevelField(x, y);
9058   TEST_DrawLevelField(newx, newy);
9059
9060   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9061
9062   // prevent pushed element from moving on in pushed direction
9063   if (pushed_by_player && CAN_MOVE(element) &&
9064       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9065       !(element_info[element].move_pattern & direction))
9066     TurnRound(newx, newy);
9067
9068   // prevent elements on conveyor belt from moving on in last direction
9069   if (pushed_by_conveyor && CAN_FALL(element) &&
9070       direction & MV_HORIZONTAL)
9071     MovDir[newx][newy] = 0;
9072
9073   if (!pushed_by_player)
9074   {
9075     int nextx = newx + dx, nexty = newy + dy;
9076     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9077
9078     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9079
9080     if (CAN_FALL(element) && direction == MV_DOWN)
9081       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9082
9083     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9084       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9085
9086     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9087       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9088   }
9089
9090   if (DONT_TOUCH(element))      // object may be nasty to player or others
9091   {
9092     TestIfBadThingTouchesPlayer(newx, newy);
9093     TestIfBadThingTouchesFriend(newx, newy);
9094
9095     if (!IS_CUSTOM_ELEMENT(element))
9096       TestIfBadThingTouchesOtherBadThing(newx, newy);
9097   }
9098   else if (element == EL_PENGUIN)
9099     TestIfFriendTouchesBadThing(newx, newy);
9100
9101   if (DONT_GET_HIT_BY(element))
9102   {
9103     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9104   }
9105
9106   // give the player one last chance (one more frame) to move away
9107   if (CAN_FALL(element) && direction == MV_DOWN &&
9108       (last_line || (!IS_FREE(x, newy + 1) &&
9109                      (!IS_PLAYER(x, newy + 1) ||
9110                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9111     Impact(x, newy);
9112
9113   if (pushed_by_player && !game.use_change_when_pushing_bug)
9114   {
9115     int push_side = MV_DIR_OPPOSITE(direction);
9116     struct PlayerInfo *player = PLAYERINFO(x, y);
9117
9118     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9119                                player->index_bit, push_side);
9120     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9121                                         player->index_bit, push_side);
9122   }
9123
9124   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9125     MovDelay[newx][newy] = 1;
9126
9127   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9128
9129   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9130   TestIfElementHitsCustomElement(newx, newy, direction);
9131   TestIfPlayerTouchesCustomElement(newx, newy);
9132   TestIfElementTouchesCustomElement(newx, newy);
9133
9134   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9135       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9136     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9137                              MV_DIR_OPPOSITE(direction));
9138 }
9139
9140 int AmoebaNeighbourNr(int ax, int ay)
9141 {
9142   int i;
9143   int element = Tile[ax][ay];
9144   int group_nr = 0;
9145   struct XY *xy = xy_topdown;
9146
9147   for (i = 0; i < NUM_DIRECTIONS; i++)
9148   {
9149     int x = ax + xy[i].x;
9150     int y = ay + xy[i].y;
9151
9152     if (!IN_LEV_FIELD(x, y))
9153       continue;
9154
9155     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9156       group_nr = AmoebaNr[x][y];
9157   }
9158
9159   return group_nr;
9160 }
9161
9162 static void AmoebaMerge(int ax, int ay)
9163 {
9164   int i, x, y, xx, yy;
9165   int new_group_nr = AmoebaNr[ax][ay];
9166   struct XY *xy = xy_topdown;
9167
9168   if (new_group_nr == 0)
9169     return;
9170
9171   for (i = 0; i < NUM_DIRECTIONS; i++)
9172   {
9173     x = ax + xy[i].x;
9174     y = ay + xy[i].y;
9175
9176     if (!IN_LEV_FIELD(x, y))
9177       continue;
9178
9179     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9180          Tile[x][y] == EL_BD_AMOEBA ||
9181          Tile[x][y] == EL_AMOEBA_DEAD) &&
9182         AmoebaNr[x][y] != new_group_nr)
9183     {
9184       int old_group_nr = AmoebaNr[x][y];
9185
9186       if (old_group_nr == 0)
9187         return;
9188
9189       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9190       AmoebaCnt[old_group_nr] = 0;
9191       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9192       AmoebaCnt2[old_group_nr] = 0;
9193
9194       SCAN_PLAYFIELD(xx, yy)
9195       {
9196         if (AmoebaNr[xx][yy] == old_group_nr)
9197           AmoebaNr[xx][yy] = new_group_nr;
9198       }
9199     }
9200   }
9201 }
9202
9203 void AmoebaToDiamond(int ax, int ay)
9204 {
9205   int i, x, y;
9206
9207   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9208   {
9209     int group_nr = AmoebaNr[ax][ay];
9210
9211 #ifdef DEBUG
9212     if (group_nr == 0)
9213     {
9214       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9215       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9216
9217       return;
9218     }
9219 #endif
9220
9221     SCAN_PLAYFIELD(x, y)
9222     {
9223       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9224       {
9225         AmoebaNr[x][y] = 0;
9226         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9227       }
9228     }
9229
9230     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9231                             SND_AMOEBA_TURNING_TO_GEM :
9232                             SND_AMOEBA_TURNING_TO_ROCK));
9233     Bang(ax, ay);
9234   }
9235   else
9236   {
9237     struct XY *xy = xy_topdown;
9238
9239     for (i = 0; i < NUM_DIRECTIONS; i++)
9240     {
9241       x = ax + xy[i].x;
9242       y = ay + xy[i].y;
9243
9244       if (!IN_LEV_FIELD(x, y))
9245         continue;
9246
9247       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9248       {
9249         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9250                               SND_AMOEBA_TURNING_TO_GEM :
9251                               SND_AMOEBA_TURNING_TO_ROCK));
9252         Bang(x, y);
9253       }
9254     }
9255   }
9256 }
9257
9258 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9259 {
9260   int x, y;
9261   int group_nr = AmoebaNr[ax][ay];
9262   boolean done = FALSE;
9263
9264 #ifdef DEBUG
9265   if (group_nr == 0)
9266   {
9267     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9268     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9269
9270     return;
9271   }
9272 #endif
9273
9274   SCAN_PLAYFIELD(x, y)
9275   {
9276     if (AmoebaNr[x][y] == group_nr &&
9277         (Tile[x][y] == EL_AMOEBA_DEAD ||
9278          Tile[x][y] == EL_BD_AMOEBA ||
9279          Tile[x][y] == EL_AMOEBA_GROWING))
9280     {
9281       AmoebaNr[x][y] = 0;
9282       Tile[x][y] = new_element;
9283       InitField(x, y, FALSE);
9284       TEST_DrawLevelField(x, y);
9285       done = TRUE;
9286     }
9287   }
9288
9289   if (done)
9290     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9291                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9292                             SND_BD_AMOEBA_TURNING_TO_GEM));
9293 }
9294
9295 static void AmoebaGrowing(int x, int y)
9296 {
9297   static DelayCounter sound_delay = { 0 };
9298
9299   if (!MovDelay[x][y])          // start new growing cycle
9300   {
9301     MovDelay[x][y] = 7;
9302
9303     if (DelayReached(&sound_delay))
9304     {
9305       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9306       sound_delay.value = 30;
9307     }
9308   }
9309
9310   if (MovDelay[x][y])           // wait some time before growing bigger
9311   {
9312     MovDelay[x][y]--;
9313     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9314     {
9315       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9316                                            6 - MovDelay[x][y]);
9317
9318       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9319     }
9320
9321     if (!MovDelay[x][y])
9322     {
9323       Tile[x][y] = Store[x][y];
9324       Store[x][y] = 0;
9325       TEST_DrawLevelField(x, y);
9326     }
9327   }
9328 }
9329
9330 static void AmoebaShrinking(int x, int y)
9331 {
9332   static DelayCounter sound_delay = { 0 };
9333
9334   if (!MovDelay[x][y])          // start new shrinking cycle
9335   {
9336     MovDelay[x][y] = 7;
9337
9338     if (DelayReached(&sound_delay))
9339       sound_delay.value = 30;
9340   }
9341
9342   if (MovDelay[x][y])           // wait some time before shrinking
9343   {
9344     MovDelay[x][y]--;
9345     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9346     {
9347       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9348                                            6 - MovDelay[x][y]);
9349
9350       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9351     }
9352
9353     if (!MovDelay[x][y])
9354     {
9355       Tile[x][y] = EL_EMPTY;
9356       TEST_DrawLevelField(x, y);
9357
9358       // don't let mole enter this field in this cycle;
9359       // (give priority to objects falling to this field from above)
9360       Stop[x][y] = TRUE;
9361     }
9362   }
9363 }
9364
9365 static void AmoebaReproduce(int ax, int ay)
9366 {
9367   int i;
9368   int element = Tile[ax][ay];
9369   int graphic = el2img(element);
9370   int newax = ax, neway = ay;
9371   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9372   struct XY *xy = xy_topdown;
9373
9374   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9375   {
9376     Tile[ax][ay] = EL_AMOEBA_DEAD;
9377     TEST_DrawLevelField(ax, ay);
9378     return;
9379   }
9380
9381   if (IS_ANIMATED(graphic))
9382     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9383
9384   if (!MovDelay[ax][ay])        // start making new amoeba field
9385     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9386
9387   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9388   {
9389     MovDelay[ax][ay]--;
9390     if (MovDelay[ax][ay])
9391       return;
9392   }
9393
9394   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9395   {
9396     int start = RND(4);
9397     int x = ax + xy[start].x;
9398     int y = ay + xy[start].y;
9399
9400     if (!IN_LEV_FIELD(x, y))
9401       return;
9402
9403     if (IS_FREE(x, y) ||
9404         CAN_GROW_INTO(Tile[x][y]) ||
9405         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9406         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9407     {
9408       newax = x;
9409       neway = y;
9410     }
9411
9412     if (newax == ax && neway == ay)
9413       return;
9414   }
9415   else                          // normal or "filled" (BD style) amoeba
9416   {
9417     int start = RND(4);
9418     boolean waiting_for_player = FALSE;
9419
9420     for (i = 0; i < NUM_DIRECTIONS; i++)
9421     {
9422       int j = (start + i) % 4;
9423       int x = ax + xy[j].x;
9424       int y = ay + xy[j].y;
9425
9426       if (!IN_LEV_FIELD(x, y))
9427         continue;
9428
9429       if (IS_FREE(x, y) ||
9430           CAN_GROW_INTO(Tile[x][y]) ||
9431           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9432           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9433       {
9434         newax = x;
9435         neway = y;
9436         break;
9437       }
9438       else if (IS_PLAYER(x, y))
9439         waiting_for_player = TRUE;
9440     }
9441
9442     if (newax == ax && neway == ay)             // amoeba cannot grow
9443     {
9444       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9445       {
9446         Tile[ax][ay] = EL_AMOEBA_DEAD;
9447         TEST_DrawLevelField(ax, ay);
9448         AmoebaCnt[AmoebaNr[ax][ay]]--;
9449
9450         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9451         {
9452           if (element == EL_AMOEBA_FULL)
9453             AmoebaToDiamond(ax, ay);
9454           else if (element == EL_BD_AMOEBA)
9455             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9456         }
9457       }
9458       return;
9459     }
9460     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9461     {
9462       // amoeba gets larger by growing in some direction
9463
9464       int new_group_nr = AmoebaNr[ax][ay];
9465
9466 #ifdef DEBUG
9467   if (new_group_nr == 0)
9468   {
9469     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9470           newax, neway);
9471     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9472
9473     return;
9474   }
9475 #endif
9476
9477       AmoebaNr[newax][neway] = new_group_nr;
9478       AmoebaCnt[new_group_nr]++;
9479       AmoebaCnt2[new_group_nr]++;
9480
9481       // if amoeba touches other amoeba(s) after growing, unify them
9482       AmoebaMerge(newax, neway);
9483
9484       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9485       {
9486         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9487         return;
9488       }
9489     }
9490   }
9491
9492   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9493       (neway == lev_fieldy - 1 && newax != ax))
9494   {
9495     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9496     Store[newax][neway] = element;
9497   }
9498   else if (neway == ay || element == EL_EMC_DRIPPER)
9499   {
9500     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9501
9502     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9503   }
9504   else
9505   {
9506     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9507     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9508     Store[ax][ay] = EL_AMOEBA_DROP;
9509     ContinueMoving(ax, ay);
9510     return;
9511   }
9512
9513   TEST_DrawLevelField(newax, neway);
9514 }
9515
9516 static void Life(int ax, int ay)
9517 {
9518   int x1, y1, x2, y2;
9519   int life_time = 40;
9520   int element = Tile[ax][ay];
9521   int graphic = el2img(element);
9522   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9523                          level.biomaze);
9524   boolean changed = FALSE;
9525
9526   if (IS_ANIMATED(graphic))
9527     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9528
9529   if (Stop[ax][ay])
9530     return;
9531
9532   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9533     MovDelay[ax][ay] = life_time;
9534
9535   if (MovDelay[ax][ay])         // wait some time before next cycle
9536   {
9537     MovDelay[ax][ay]--;
9538     if (MovDelay[ax][ay])
9539       return;
9540   }
9541
9542   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9543   {
9544     int xx = ax + x1, yy = ay + y1;
9545     int old_element = Tile[xx][yy];
9546     int num_neighbours = 0;
9547
9548     if (!IN_LEV_FIELD(xx, yy))
9549       continue;
9550
9551     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9552     {
9553       int x = xx + x2, y = yy + y2;
9554
9555       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9556         continue;
9557
9558       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9559       boolean is_neighbour = FALSE;
9560
9561       if (level.use_life_bugs)
9562         is_neighbour =
9563           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9564            (IS_FREE(x, y)                             &&  Stop[x][y]));
9565       else
9566         is_neighbour =
9567           (Last[x][y] == element || is_player_cell);
9568
9569       if (is_neighbour)
9570         num_neighbours++;
9571     }
9572
9573     boolean is_free = FALSE;
9574
9575     if (level.use_life_bugs)
9576       is_free = (IS_FREE(xx, yy));
9577     else
9578       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9579
9580     if (xx == ax && yy == ay)           // field in the middle
9581     {
9582       if (num_neighbours < life_parameter[0] ||
9583           num_neighbours > life_parameter[1])
9584       {
9585         Tile[xx][yy] = EL_EMPTY;
9586         if (Tile[xx][yy] != old_element)
9587           TEST_DrawLevelField(xx, yy);
9588         Stop[xx][yy] = TRUE;
9589         changed = TRUE;
9590       }
9591     }
9592     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9593     {                                   // free border field
9594       if (num_neighbours >= life_parameter[2] &&
9595           num_neighbours <= life_parameter[3])
9596       {
9597         Tile[xx][yy] = element;
9598         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9599         if (Tile[xx][yy] != old_element)
9600           TEST_DrawLevelField(xx, yy);
9601         Stop[xx][yy] = TRUE;
9602         changed = TRUE;
9603       }
9604     }
9605   }
9606
9607   if (changed)
9608     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9609                    SND_GAME_OF_LIFE_GROWING);
9610 }
9611
9612 static void InitRobotWheel(int x, int y)
9613 {
9614   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9615 }
9616
9617 static void RunRobotWheel(int x, int y)
9618 {
9619   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9620 }
9621
9622 static void StopRobotWheel(int x, int y)
9623 {
9624   if (game.robot_wheel_x == x &&
9625       game.robot_wheel_y == y)
9626   {
9627     game.robot_wheel_x = -1;
9628     game.robot_wheel_y = -1;
9629     game.robot_wheel_active = FALSE;
9630   }
9631 }
9632
9633 static void InitTimegateWheel(int x, int y)
9634 {
9635   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9636 }
9637
9638 static void RunTimegateWheel(int x, int y)
9639 {
9640   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9641 }
9642
9643 static void InitMagicBallDelay(int x, int y)
9644 {
9645   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9646 }
9647
9648 static void ActivateMagicBall(int bx, int by)
9649 {
9650   int x, y;
9651
9652   if (level.ball_random)
9653   {
9654     int pos_border = RND(8);    // select one of the eight border elements
9655     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9656     int xx = pos_content % 3;
9657     int yy = pos_content / 3;
9658
9659     x = bx - 1 + xx;
9660     y = by - 1 + yy;
9661
9662     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9663       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9664   }
9665   else
9666   {
9667     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9668     {
9669       int xx = x - bx + 1;
9670       int yy = y - by + 1;
9671
9672       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9673         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9674     }
9675   }
9676
9677   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9678 }
9679
9680 static void CheckExit(int x, int y)
9681 {
9682   if (game.gems_still_needed > 0 ||
9683       game.sokoban_fields_still_needed > 0 ||
9684       game.sokoban_objects_still_needed > 0 ||
9685       game.lights_still_needed > 0)
9686   {
9687     int element = Tile[x][y];
9688     int graphic = el2img(element);
9689
9690     if (IS_ANIMATED(graphic))
9691       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9692
9693     return;
9694   }
9695
9696   // do not re-open exit door closed after last player
9697   if (game.all_players_gone)
9698     return;
9699
9700   Tile[x][y] = EL_EXIT_OPENING;
9701
9702   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9703 }
9704
9705 static void CheckExitEM(int x, int y)
9706 {
9707   if (game.gems_still_needed > 0 ||
9708       game.sokoban_fields_still_needed > 0 ||
9709       game.sokoban_objects_still_needed > 0 ||
9710       game.lights_still_needed > 0)
9711   {
9712     int element = Tile[x][y];
9713     int graphic = el2img(element);
9714
9715     if (IS_ANIMATED(graphic))
9716       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9717
9718     return;
9719   }
9720
9721   // do not re-open exit door closed after last player
9722   if (game.all_players_gone)
9723     return;
9724
9725   Tile[x][y] = EL_EM_EXIT_OPENING;
9726
9727   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9728 }
9729
9730 static void CheckExitSteel(int x, int y)
9731 {
9732   if (game.gems_still_needed > 0 ||
9733       game.sokoban_fields_still_needed > 0 ||
9734       game.sokoban_objects_still_needed > 0 ||
9735       game.lights_still_needed > 0)
9736   {
9737     int element = Tile[x][y];
9738     int graphic = el2img(element);
9739
9740     if (IS_ANIMATED(graphic))
9741       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9742
9743     return;
9744   }
9745
9746   // do not re-open exit door closed after last player
9747   if (game.all_players_gone)
9748     return;
9749
9750   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9751
9752   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9753 }
9754
9755 static void CheckExitSteelEM(int x, int y)
9756 {
9757   if (game.gems_still_needed > 0 ||
9758       game.sokoban_fields_still_needed > 0 ||
9759       game.sokoban_objects_still_needed > 0 ||
9760       game.lights_still_needed > 0)
9761   {
9762     int element = Tile[x][y];
9763     int graphic = el2img(element);
9764
9765     if (IS_ANIMATED(graphic))
9766       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9767
9768     return;
9769   }
9770
9771   // do not re-open exit door closed after last player
9772   if (game.all_players_gone)
9773     return;
9774
9775   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9776
9777   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9778 }
9779
9780 static void CheckExitSP(int x, int y)
9781 {
9782   if (game.gems_still_needed > 0)
9783   {
9784     int element = Tile[x][y];
9785     int graphic = el2img(element);
9786
9787     if (IS_ANIMATED(graphic))
9788       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9789
9790     return;
9791   }
9792
9793   // do not re-open exit door closed after last player
9794   if (game.all_players_gone)
9795     return;
9796
9797   Tile[x][y] = EL_SP_EXIT_OPENING;
9798
9799   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9800 }
9801
9802 static void CloseAllOpenTimegates(void)
9803 {
9804   int x, y;
9805
9806   SCAN_PLAYFIELD(x, y)
9807   {
9808     int element = Tile[x][y];
9809
9810     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9811     {
9812       Tile[x][y] = EL_TIMEGATE_CLOSING;
9813
9814       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9815     }
9816   }
9817 }
9818
9819 static void DrawTwinkleOnField(int x, int y)
9820 {
9821   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9822     return;
9823
9824   if (Tile[x][y] == EL_BD_DIAMOND)
9825     return;
9826
9827   if (MovDelay[x][y] == 0)      // next animation frame
9828     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9829
9830   if (MovDelay[x][y] != 0)      // wait some time before next frame
9831   {
9832     MovDelay[x][y]--;
9833
9834     DrawLevelElementAnimation(x, y, Tile[x][y]);
9835
9836     if (MovDelay[x][y] != 0)
9837     {
9838       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9839                                            10 - MovDelay[x][y]);
9840
9841       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9842     }
9843   }
9844 }
9845
9846 static void WallGrowing(int x, int y)
9847 {
9848   int delay = 6;
9849
9850   if (!MovDelay[x][y])          // next animation frame
9851     MovDelay[x][y] = 3 * delay;
9852
9853   if (MovDelay[x][y])           // wait some time before next frame
9854   {
9855     MovDelay[x][y]--;
9856
9857     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9858     {
9859       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9860       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9861
9862       DrawLevelGraphic(x, y, graphic, frame);
9863     }
9864
9865     if (!MovDelay[x][y])
9866     {
9867       if (MovDir[x][y] == MV_LEFT)
9868       {
9869         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9870           TEST_DrawLevelField(x - 1, y);
9871       }
9872       else if (MovDir[x][y] == MV_RIGHT)
9873       {
9874         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9875           TEST_DrawLevelField(x + 1, y);
9876       }
9877       else if (MovDir[x][y] == MV_UP)
9878       {
9879         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9880           TEST_DrawLevelField(x, y - 1);
9881       }
9882       else
9883       {
9884         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9885           TEST_DrawLevelField(x, y + 1);
9886       }
9887
9888       Tile[x][y] = Store[x][y];
9889       Store[x][y] = 0;
9890       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9891       TEST_DrawLevelField(x, y);
9892     }
9893   }
9894 }
9895
9896 static void CheckWallGrowing(int ax, int ay)
9897 {
9898   int element = Tile[ax][ay];
9899   int graphic = el2img(element);
9900   boolean free_top    = FALSE;
9901   boolean free_bottom = FALSE;
9902   boolean free_left   = FALSE;
9903   boolean free_right  = FALSE;
9904   boolean stop_top    = FALSE;
9905   boolean stop_bottom = FALSE;
9906   boolean stop_left   = FALSE;
9907   boolean stop_right  = FALSE;
9908   boolean new_wall    = FALSE;
9909
9910   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9911                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9912                            element == EL_EXPANDABLE_STEELWALL_ANY);
9913
9914   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9915                              element == EL_EXPANDABLE_WALL_ANY ||
9916                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9917                              element == EL_EXPANDABLE_STEELWALL_ANY);
9918
9919   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9920                              element == EL_EXPANDABLE_WALL_ANY ||
9921                              element == EL_EXPANDABLE_WALL ||
9922                              element == EL_BD_EXPANDABLE_WALL ||
9923                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9924                              element == EL_EXPANDABLE_STEELWALL_ANY);
9925
9926   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9927                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9928
9929   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9930                              element == EL_EXPANDABLE_WALL ||
9931                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9932
9933   int wall_growing = (is_steelwall ?
9934                       EL_EXPANDABLE_STEELWALL_GROWING :
9935                       EL_EXPANDABLE_WALL_GROWING);
9936
9937   int gfx_wall_growing_up    = (is_steelwall ?
9938                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9939                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9940   int gfx_wall_growing_down  = (is_steelwall ?
9941                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9942                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9943   int gfx_wall_growing_left  = (is_steelwall ?
9944                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9945                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9946   int gfx_wall_growing_right = (is_steelwall ?
9947                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9948                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9949
9950   if (IS_ANIMATED(graphic))
9951     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9952
9953   if (!MovDelay[ax][ay])        // start building new wall
9954     MovDelay[ax][ay] = 6;
9955
9956   if (MovDelay[ax][ay])         // wait some time before building new wall
9957   {
9958     MovDelay[ax][ay]--;
9959     if (MovDelay[ax][ay])
9960       return;
9961   }
9962
9963   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9964     free_top = TRUE;
9965   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9966     free_bottom = TRUE;
9967   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9968     free_left = TRUE;
9969   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9970     free_right = TRUE;
9971
9972   if (grow_vertical)
9973   {
9974     if (free_top)
9975     {
9976       Tile[ax][ay - 1] = wall_growing;
9977       Store[ax][ay - 1] = element;
9978       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9979
9980       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9981         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9982
9983       new_wall = TRUE;
9984     }
9985
9986     if (free_bottom)
9987     {
9988       Tile[ax][ay + 1] = wall_growing;
9989       Store[ax][ay + 1] = element;
9990       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9991
9992       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9993         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9994
9995       new_wall = TRUE;
9996     }
9997   }
9998
9999   if (grow_horizontal)
10000   {
10001     if (free_left)
10002     {
10003       Tile[ax - 1][ay] = wall_growing;
10004       Store[ax - 1][ay] = element;
10005       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
10006
10007       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
10008         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
10009
10010       new_wall = TRUE;
10011     }
10012
10013     if (free_right)
10014     {
10015       Tile[ax + 1][ay] = wall_growing;
10016       Store[ax + 1][ay] = element;
10017       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10018
10019       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10020         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10021
10022       new_wall = TRUE;
10023     }
10024   }
10025
10026   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10027     TEST_DrawLevelField(ax, ay);
10028
10029   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10030     stop_top = TRUE;
10031   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10032     stop_bottom = TRUE;
10033   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10034     stop_left = TRUE;
10035   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10036     stop_right = TRUE;
10037
10038   if (((stop_top && stop_bottom) || stop_horizontal) &&
10039       ((stop_left && stop_right) || stop_vertical))
10040     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10041
10042   if (new_wall)
10043     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10044 }
10045
10046 static void CheckForDragon(int x, int y)
10047 {
10048   int i, j;
10049   boolean dragon_found = FALSE;
10050   struct XY *xy = xy_topdown;
10051
10052   for (i = 0; i < NUM_DIRECTIONS; i++)
10053   {
10054     for (j = 0; j < 4; j++)
10055     {
10056       int xx = x + j * xy[i].x;
10057       int yy = y + j * xy[i].y;
10058
10059       if (IN_LEV_FIELD(xx, yy) &&
10060           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10061       {
10062         if (Tile[xx][yy] == EL_DRAGON)
10063           dragon_found = TRUE;
10064       }
10065       else
10066         break;
10067     }
10068   }
10069
10070   if (!dragon_found)
10071   {
10072     for (i = 0; i < NUM_DIRECTIONS; i++)
10073     {
10074       for (j = 0; j < 3; j++)
10075       {
10076         int xx = x + j * xy[i].x;
10077         int yy = y + j * xy[i].y;
10078
10079         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10080         {
10081           Tile[xx][yy] = EL_EMPTY;
10082           TEST_DrawLevelField(xx, yy);
10083         }
10084         else
10085           break;
10086       }
10087     }
10088   }
10089 }
10090
10091 static void InitBuggyBase(int x, int y)
10092 {
10093   int element = Tile[x][y];
10094   int activating_delay = FRAMES_PER_SECOND / 4;
10095
10096   ChangeDelay[x][y] =
10097     (element == EL_SP_BUGGY_BASE ?
10098      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10099      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10100      activating_delay :
10101      element == EL_SP_BUGGY_BASE_ACTIVE ?
10102      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10103 }
10104
10105 static void WarnBuggyBase(int x, int y)
10106 {
10107   int i;
10108   struct XY *xy = xy_topdown;
10109
10110   for (i = 0; i < NUM_DIRECTIONS; i++)
10111   {
10112     int xx = x + xy[i].x;
10113     int yy = y + xy[i].y;
10114
10115     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10116     {
10117       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10118
10119       break;
10120     }
10121   }
10122 }
10123
10124 static void InitTrap(int x, int y)
10125 {
10126   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10127 }
10128
10129 static void ActivateTrap(int x, int y)
10130 {
10131   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10132 }
10133
10134 static void ChangeActiveTrap(int x, int y)
10135 {
10136   int graphic = IMG_TRAP_ACTIVE;
10137
10138   // if new animation frame was drawn, correct crumbled sand border
10139   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10140     TEST_DrawLevelFieldCrumbled(x, y);
10141 }
10142
10143 static int getSpecialActionElement(int element, int number, int base_element)
10144 {
10145   return (element != EL_EMPTY ? element :
10146           number != -1 ? base_element + number - 1 :
10147           EL_EMPTY);
10148 }
10149
10150 static int getModifiedActionNumber(int value_old, int operator, int operand,
10151                                    int value_min, int value_max)
10152 {
10153   int value_new = (operator == CA_MODE_SET      ? operand :
10154                    operator == CA_MODE_ADD      ? value_old + operand :
10155                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10156                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10157                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10158                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10159                    value_old);
10160
10161   return (value_new < value_min ? value_min :
10162           value_new > value_max ? value_max :
10163           value_new);
10164 }
10165
10166 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10167 {
10168   struct ElementInfo *ei = &element_info[element];
10169   struct ElementChangeInfo *change = &ei->change_page[page];
10170   int target_element = change->target_element;
10171   int action_type = change->action_type;
10172   int action_mode = change->action_mode;
10173   int action_arg = change->action_arg;
10174   int action_element = change->action_element;
10175   int i;
10176
10177   if (!change->has_action)
10178     return;
10179
10180   // ---------- determine action paramater values -----------------------------
10181
10182   int level_time_value =
10183     (level.time > 0 ? TimeLeft :
10184      TimePlayed);
10185
10186   int action_arg_element_raw =
10187     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10188      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10189      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10190      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10191      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10192      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10193      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10194      EL_EMPTY);
10195   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10196
10197   int action_arg_direction =
10198     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10199      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10200      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10201      change->actual_trigger_side :
10202      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10203      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10204      MV_NONE);
10205
10206   int action_arg_number_min =
10207     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10208      CA_ARG_MIN);
10209
10210   int action_arg_number_max =
10211     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10212      action_type == CA_SET_LEVEL_GEMS ? 999 :
10213      action_type == CA_SET_LEVEL_TIME ? 9999 :
10214      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10215      action_type == CA_SET_CE_VALUE ? 9999 :
10216      action_type == CA_SET_CE_SCORE ? 9999 :
10217      CA_ARG_MAX);
10218
10219   int action_arg_number_reset =
10220     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10221      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10222      action_type == CA_SET_LEVEL_TIME ? level.time :
10223      action_type == CA_SET_LEVEL_SCORE ? 0 :
10224      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10225      action_type == CA_SET_CE_SCORE ? 0 :
10226      0);
10227
10228   int action_arg_number =
10229     (action_arg <= CA_ARG_MAX ? action_arg :
10230      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10231      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10232      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10233      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10234      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10235      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10236      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10237      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10238      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10239      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10240      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10241      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10242      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10243      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10244      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10245      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10246      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10247      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10248      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10249      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10250      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10251      -1);
10252
10253   int action_arg_number_old =
10254     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10255      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10256      action_type == CA_SET_LEVEL_SCORE ? game.score :
10257      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10258      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10259      0);
10260
10261   int action_arg_number_new =
10262     getModifiedActionNumber(action_arg_number_old,
10263                             action_mode, action_arg_number,
10264                             action_arg_number_min, action_arg_number_max);
10265
10266   int trigger_player_bits =
10267     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10268      change->actual_trigger_player_bits : change->trigger_player);
10269
10270   int action_arg_player_bits =
10271     (action_arg >= CA_ARG_PLAYER_1 &&
10272      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10273      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10274      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10275      PLAYER_BITS_ANY);
10276
10277   // ---------- execute action  -----------------------------------------------
10278
10279   switch (action_type)
10280   {
10281     case CA_NO_ACTION:
10282     {
10283       return;
10284     }
10285
10286     // ---------- level actions  ----------------------------------------------
10287
10288     case CA_RESTART_LEVEL:
10289     {
10290       game.restart_level = TRUE;
10291
10292       break;
10293     }
10294
10295     case CA_SHOW_ENVELOPE:
10296     {
10297       int element = getSpecialActionElement(action_arg_element,
10298                                             action_arg_number, EL_ENVELOPE_1);
10299
10300       if (IS_ENVELOPE(element))
10301         local_player->show_envelope = element;
10302
10303       break;
10304     }
10305
10306     case CA_SET_LEVEL_TIME:
10307     {
10308       if (level.time > 0)       // only modify limited time value
10309       {
10310         TimeLeft = action_arg_number_new;
10311
10312         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10313
10314         DisplayGameControlValues();
10315
10316         if (!TimeLeft && game.time_limit)
10317           for (i = 0; i < MAX_PLAYERS; i++)
10318             KillPlayer(&stored_player[i]);
10319       }
10320
10321       break;
10322     }
10323
10324     case CA_SET_LEVEL_SCORE:
10325     {
10326       game.score = action_arg_number_new;
10327
10328       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10329
10330       DisplayGameControlValues();
10331
10332       break;
10333     }
10334
10335     case CA_SET_LEVEL_GEMS:
10336     {
10337       game.gems_still_needed = action_arg_number_new;
10338
10339       game.snapshot.collected_item = TRUE;
10340
10341       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10342
10343       DisplayGameControlValues();
10344
10345       break;
10346     }
10347
10348     case CA_SET_LEVEL_WIND:
10349     {
10350       game.wind_direction = action_arg_direction;
10351
10352       break;
10353     }
10354
10355     case CA_SET_LEVEL_RANDOM_SEED:
10356     {
10357       // ensure that setting a new random seed while playing is predictable
10358       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10359
10360       break;
10361     }
10362
10363     // ---------- player actions  ---------------------------------------------
10364
10365     case CA_MOVE_PLAYER:
10366     case CA_MOVE_PLAYER_NEW:
10367     {
10368       // automatically move to the next field in specified direction
10369       for (i = 0; i < MAX_PLAYERS; i++)
10370         if (trigger_player_bits & (1 << i))
10371           if (action_type == CA_MOVE_PLAYER ||
10372               stored_player[i].MovPos == 0)
10373             stored_player[i].programmed_action = action_arg_direction;
10374
10375       break;
10376     }
10377
10378     case CA_EXIT_PLAYER:
10379     {
10380       for (i = 0; i < MAX_PLAYERS; i++)
10381         if (action_arg_player_bits & (1 << i))
10382           ExitPlayer(&stored_player[i]);
10383
10384       if (game.players_still_needed == 0)
10385         LevelSolved();
10386
10387       break;
10388     }
10389
10390     case CA_KILL_PLAYER:
10391     {
10392       for (i = 0; i < MAX_PLAYERS; i++)
10393         if (action_arg_player_bits & (1 << i))
10394           KillPlayer(&stored_player[i]);
10395
10396       break;
10397     }
10398
10399     case CA_SET_PLAYER_KEYS:
10400     {
10401       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10402       int element = getSpecialActionElement(action_arg_element,
10403                                             action_arg_number, EL_KEY_1);
10404
10405       if (IS_KEY(element))
10406       {
10407         for (i = 0; i < MAX_PLAYERS; i++)
10408         {
10409           if (trigger_player_bits & (1 << i))
10410           {
10411             stored_player[i].key[KEY_NR(element)] = key_state;
10412
10413             DrawGameDoorValues();
10414           }
10415         }
10416       }
10417
10418       break;
10419     }
10420
10421     case CA_SET_PLAYER_SPEED:
10422     {
10423       for (i = 0; i < MAX_PLAYERS; i++)
10424       {
10425         if (trigger_player_bits & (1 << i))
10426         {
10427           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10428
10429           if (action_arg == CA_ARG_SPEED_FASTER &&
10430               stored_player[i].cannot_move)
10431           {
10432             action_arg_number = STEPSIZE_VERY_SLOW;
10433           }
10434           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10435                    action_arg == CA_ARG_SPEED_FASTER)
10436           {
10437             action_arg_number = 2;
10438             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10439                            CA_MODE_MULTIPLY);
10440           }
10441           else if (action_arg == CA_ARG_NUMBER_RESET)
10442           {
10443             action_arg_number = level.initial_player_stepsize[i];
10444           }
10445
10446           move_stepsize =
10447             getModifiedActionNumber(move_stepsize,
10448                                     action_mode,
10449                                     action_arg_number,
10450                                     action_arg_number_min,
10451                                     action_arg_number_max);
10452
10453           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10454         }
10455       }
10456
10457       break;
10458     }
10459
10460     case CA_SET_PLAYER_SHIELD:
10461     {
10462       for (i = 0; i < MAX_PLAYERS; i++)
10463       {
10464         if (trigger_player_bits & (1 << i))
10465         {
10466           if (action_arg == CA_ARG_SHIELD_OFF)
10467           {
10468             stored_player[i].shield_normal_time_left = 0;
10469             stored_player[i].shield_deadly_time_left = 0;
10470           }
10471           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10472           {
10473             stored_player[i].shield_normal_time_left = 999999;
10474           }
10475           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10476           {
10477             stored_player[i].shield_normal_time_left = 999999;
10478             stored_player[i].shield_deadly_time_left = 999999;
10479           }
10480         }
10481       }
10482
10483       break;
10484     }
10485
10486     case CA_SET_PLAYER_GRAVITY:
10487     {
10488       for (i = 0; i < MAX_PLAYERS; i++)
10489       {
10490         if (trigger_player_bits & (1 << i))
10491         {
10492           stored_player[i].gravity =
10493             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10494              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10495              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10496              stored_player[i].gravity);
10497         }
10498       }
10499
10500       break;
10501     }
10502
10503     case CA_SET_PLAYER_ARTWORK:
10504     {
10505       for (i = 0; i < MAX_PLAYERS; i++)
10506       {
10507         if (trigger_player_bits & (1 << i))
10508         {
10509           int artwork_element = action_arg_element;
10510
10511           if (action_arg == CA_ARG_ELEMENT_RESET)
10512             artwork_element =
10513               (level.use_artwork_element[i] ? level.artwork_element[i] :
10514                stored_player[i].element_nr);
10515
10516           if (stored_player[i].artwork_element != artwork_element)
10517             stored_player[i].Frame = 0;
10518
10519           stored_player[i].artwork_element = artwork_element;
10520
10521           SetPlayerWaiting(&stored_player[i], FALSE);
10522
10523           // set number of special actions for bored and sleeping animation
10524           stored_player[i].num_special_action_bored =
10525             get_num_special_action(artwork_element,
10526                                    ACTION_BORING_1, ACTION_BORING_LAST);
10527           stored_player[i].num_special_action_sleeping =
10528             get_num_special_action(artwork_element,
10529                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10530         }
10531       }
10532
10533       break;
10534     }
10535
10536     case CA_SET_PLAYER_INVENTORY:
10537     {
10538       for (i = 0; i < MAX_PLAYERS; i++)
10539       {
10540         struct PlayerInfo *player = &stored_player[i];
10541         int j, k;
10542
10543         if (trigger_player_bits & (1 << i))
10544         {
10545           int inventory_element = action_arg_element;
10546
10547           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10548               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10549               action_arg == CA_ARG_ELEMENT_ACTION)
10550           {
10551             int element = inventory_element;
10552             int collect_count = element_info[element].collect_count_initial;
10553
10554             if (!IS_CUSTOM_ELEMENT(element))
10555               collect_count = 1;
10556
10557             if (collect_count == 0)
10558               player->inventory_infinite_element = element;
10559             else
10560               for (k = 0; k < collect_count; k++)
10561                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10562                   player->inventory_element[player->inventory_size++] =
10563                     element;
10564           }
10565           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10566                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10567                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10568           {
10569             if (player->inventory_infinite_element != EL_UNDEFINED &&
10570                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10571                                      action_arg_element_raw))
10572               player->inventory_infinite_element = EL_UNDEFINED;
10573
10574             for (k = 0, j = 0; j < player->inventory_size; j++)
10575             {
10576               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10577                                         action_arg_element_raw))
10578                 player->inventory_element[k++] = player->inventory_element[j];
10579             }
10580
10581             player->inventory_size = k;
10582           }
10583           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10584           {
10585             if (player->inventory_size > 0)
10586             {
10587               for (j = 0; j < player->inventory_size - 1; j++)
10588                 player->inventory_element[j] = player->inventory_element[j + 1];
10589
10590               player->inventory_size--;
10591             }
10592           }
10593           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10594           {
10595             if (player->inventory_size > 0)
10596               player->inventory_size--;
10597           }
10598           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10599           {
10600             player->inventory_infinite_element = EL_UNDEFINED;
10601             player->inventory_size = 0;
10602           }
10603           else if (action_arg == CA_ARG_INVENTORY_RESET)
10604           {
10605             player->inventory_infinite_element = EL_UNDEFINED;
10606             player->inventory_size = 0;
10607
10608             if (level.use_initial_inventory[i])
10609             {
10610               for (j = 0; j < level.initial_inventory_size[i]; j++)
10611               {
10612                 int element = level.initial_inventory_content[i][j];
10613                 int collect_count = element_info[element].collect_count_initial;
10614
10615                 if (!IS_CUSTOM_ELEMENT(element))
10616                   collect_count = 1;
10617
10618                 if (collect_count == 0)
10619                   player->inventory_infinite_element = element;
10620                 else
10621                   for (k = 0; k < collect_count; k++)
10622                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10623                       player->inventory_element[player->inventory_size++] =
10624                         element;
10625               }
10626             }
10627           }
10628         }
10629       }
10630
10631       break;
10632     }
10633
10634     // ---------- CE actions  -------------------------------------------------
10635
10636     case CA_SET_CE_VALUE:
10637     {
10638       int last_ce_value = CustomValue[x][y];
10639
10640       CustomValue[x][y] = action_arg_number_new;
10641
10642       if (CustomValue[x][y] != last_ce_value)
10643       {
10644         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10645         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10646
10647         if (CustomValue[x][y] == 0)
10648         {
10649           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10650           ChangeCount[x][y] = 0;        // allow at least one more change
10651
10652           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10653           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10654         }
10655       }
10656
10657       break;
10658     }
10659
10660     case CA_SET_CE_SCORE:
10661     {
10662       int last_ce_score = ei->collect_score;
10663
10664       ei->collect_score = action_arg_number_new;
10665
10666       if (ei->collect_score != last_ce_score)
10667       {
10668         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10669         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10670
10671         if (ei->collect_score == 0)
10672         {
10673           int xx, yy;
10674
10675           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10676           ChangeCount[x][y] = 0;        // allow at least one more change
10677
10678           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10679           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10680
10681           /*
10682             This is a very special case that seems to be a mixture between
10683             CheckElementChange() and CheckTriggeredElementChange(): while
10684             the first one only affects single elements that are triggered
10685             directly, the second one affects multiple elements in the playfield
10686             that are triggered indirectly by another element. This is a third
10687             case: Changing the CE score always affects multiple identical CEs,
10688             so every affected CE must be checked, not only the single CE for
10689             which the CE score was changed in the first place (as every instance
10690             of that CE shares the same CE score, and therefore also can change)!
10691           */
10692           SCAN_PLAYFIELD(xx, yy)
10693           {
10694             if (Tile[xx][yy] == element)
10695               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10696                                  CE_SCORE_GETS_ZERO);
10697           }
10698         }
10699       }
10700
10701       break;
10702     }
10703
10704     case CA_SET_CE_ARTWORK:
10705     {
10706       int artwork_element = action_arg_element;
10707       boolean reset_frame = FALSE;
10708       int xx, yy;
10709
10710       if (action_arg == CA_ARG_ELEMENT_RESET)
10711         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10712                            element);
10713
10714       if (ei->gfx_element != artwork_element)
10715         reset_frame = TRUE;
10716
10717       ei->gfx_element = artwork_element;
10718
10719       SCAN_PLAYFIELD(xx, yy)
10720       {
10721         if (Tile[xx][yy] == element)
10722         {
10723           if (reset_frame)
10724           {
10725             ResetGfxAnimation(xx, yy);
10726             ResetRandomAnimationValue(xx, yy);
10727           }
10728
10729           TEST_DrawLevelField(xx, yy);
10730         }
10731       }
10732
10733       break;
10734     }
10735
10736     // ---------- engine actions  ---------------------------------------------
10737
10738     case CA_SET_ENGINE_SCAN_MODE:
10739     {
10740       InitPlayfieldScanMode(action_arg);
10741
10742       break;
10743     }
10744
10745     default:
10746       break;
10747   }
10748 }
10749
10750 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10751 {
10752   int old_element = Tile[x][y];
10753   int new_element = GetElementFromGroupElement(element);
10754   int previous_move_direction = MovDir[x][y];
10755   int last_ce_value = CustomValue[x][y];
10756   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10757   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10758   boolean add_player_onto_element = (new_element_is_player &&
10759                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10760                                      IS_WALKABLE(old_element));
10761
10762   if (!add_player_onto_element)
10763   {
10764     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10765       RemoveMovingField(x, y);
10766     else
10767       RemoveField(x, y);
10768
10769     Tile[x][y] = new_element;
10770
10771     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10772       MovDir[x][y] = previous_move_direction;
10773
10774     if (element_info[new_element].use_last_ce_value)
10775       CustomValue[x][y] = last_ce_value;
10776
10777     InitField_WithBug1(x, y, FALSE);
10778
10779     new_element = Tile[x][y];   // element may have changed
10780
10781     ResetGfxAnimation(x, y);
10782     ResetRandomAnimationValue(x, y);
10783
10784     TEST_DrawLevelField(x, y);
10785
10786     if (GFX_CRUMBLED(new_element))
10787       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10788
10789     if (old_element == EL_EXPLOSION)
10790     {
10791       Store[x][y] = Store2[x][y] = 0;
10792
10793       // check if new element replaces an exploding player, requiring cleanup
10794       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10795         StorePlayer[x][y] = 0;
10796     }
10797
10798     // check if element under the player changes from accessible to unaccessible
10799     // (needed for special case of dropping element which then changes)
10800     // (must be checked after creating new element for walkable group elements)
10801     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10802         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10803     {
10804       KillPlayer(PLAYERINFO(x, y));
10805
10806       return;
10807     }
10808   }
10809
10810   // "ChangeCount" not set yet to allow "entered by player" change one time
10811   if (new_element_is_player)
10812     RelocatePlayer(x, y, new_element);
10813
10814   if (is_change)
10815     ChangeCount[x][y]++;        // count number of changes in the same frame
10816
10817   TestIfBadThingTouchesPlayer(x, y);
10818   TestIfPlayerTouchesCustomElement(x, y);
10819   TestIfElementTouchesCustomElement(x, y);
10820 }
10821
10822 static void CreateField(int x, int y, int element)
10823 {
10824   CreateFieldExt(x, y, element, FALSE);
10825 }
10826
10827 static void CreateElementFromChange(int x, int y, int element)
10828 {
10829   element = GET_VALID_RUNTIME_ELEMENT(element);
10830
10831   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10832   {
10833     int old_element = Tile[x][y];
10834
10835     // prevent changed element from moving in same engine frame
10836     // unless both old and new element can either fall or move
10837     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10838         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10839       Stop[x][y] = TRUE;
10840   }
10841
10842   CreateFieldExt(x, y, element, TRUE);
10843 }
10844
10845 static boolean ChangeElement(int x, int y, int element, int page)
10846 {
10847   struct ElementInfo *ei = &element_info[element];
10848   struct ElementChangeInfo *change = &ei->change_page[page];
10849   int ce_value = CustomValue[x][y];
10850   int ce_score = ei->collect_score;
10851   int target_element;
10852   int old_element = Tile[x][y];
10853
10854   // always use default change event to prevent running into a loop
10855   if (ChangeEvent[x][y] == -1)
10856     ChangeEvent[x][y] = CE_DELAY;
10857
10858   if (ChangeEvent[x][y] == CE_DELAY)
10859   {
10860     // reset actual trigger element, trigger player and action element
10861     change->actual_trigger_element = EL_EMPTY;
10862     change->actual_trigger_player = EL_EMPTY;
10863     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10864     change->actual_trigger_side = CH_SIDE_NONE;
10865     change->actual_trigger_ce_value = 0;
10866     change->actual_trigger_ce_score = 0;
10867     change->actual_trigger_x = -1;
10868     change->actual_trigger_y = -1;
10869   }
10870
10871   // do not change elements more than a specified maximum number of changes
10872   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10873     return FALSE;
10874
10875   ChangeCount[x][y]++;          // count number of changes in the same frame
10876
10877   if (ei->has_anim_event)
10878     HandleGlobalAnimEventByElementChange(element, page, x, y,
10879                                          change->actual_trigger_x,
10880                                          change->actual_trigger_y);
10881
10882   if (change->explode)
10883   {
10884     Bang(x, y);
10885
10886     return TRUE;
10887   }
10888
10889   if (change->use_target_content)
10890   {
10891     boolean complete_replace = TRUE;
10892     boolean can_replace[3][3];
10893     int xx, yy;
10894
10895     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10896     {
10897       boolean is_empty;
10898       boolean is_walkable;
10899       boolean is_diggable;
10900       boolean is_collectible;
10901       boolean is_removable;
10902       boolean is_destructible;
10903       int ex = x + xx - 1;
10904       int ey = y + yy - 1;
10905       int content_element = change->target_content.e[xx][yy];
10906       int e;
10907
10908       can_replace[xx][yy] = TRUE;
10909
10910       if (ex == x && ey == y)   // do not check changing element itself
10911         continue;
10912
10913       if (content_element == EL_EMPTY_SPACE)
10914       {
10915         can_replace[xx][yy] = FALSE;    // do not replace border with space
10916
10917         continue;
10918       }
10919
10920       if (!IN_LEV_FIELD(ex, ey))
10921       {
10922         can_replace[xx][yy] = FALSE;
10923         complete_replace = FALSE;
10924
10925         continue;
10926       }
10927
10928       e = Tile[ex][ey];
10929
10930       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10931         e = MovingOrBlocked2Element(ex, ey);
10932
10933       is_empty = (IS_FREE(ex, ey) ||
10934                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10935
10936       is_walkable     = (is_empty || IS_WALKABLE(e));
10937       is_diggable     = (is_empty || IS_DIGGABLE(e));
10938       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10939       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10940       is_removable    = (is_diggable || is_collectible);
10941
10942       can_replace[xx][yy] =
10943         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10944           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10945           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10946           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10947           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10948           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10949          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10950
10951       if (!can_replace[xx][yy])
10952         complete_replace = FALSE;
10953     }
10954
10955     if (!change->only_if_complete || complete_replace)
10956     {
10957       boolean something_has_changed = FALSE;
10958
10959       if (change->only_if_complete && change->use_random_replace &&
10960           RND(100) < change->random_percentage)
10961         return FALSE;
10962
10963       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10964       {
10965         int ex = x + xx - 1;
10966         int ey = y + yy - 1;
10967         int content_element;
10968
10969         if (can_replace[xx][yy] && (!change->use_random_replace ||
10970                                     RND(100) < change->random_percentage))
10971         {
10972           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10973             RemoveMovingField(ex, ey);
10974
10975           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10976
10977           content_element = change->target_content.e[xx][yy];
10978           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10979                                               ce_value, ce_score);
10980
10981           CreateElementFromChange(ex, ey, target_element);
10982
10983           something_has_changed = TRUE;
10984
10985           // for symmetry reasons, freeze newly created border elements
10986           if (ex != x || ey != y)
10987             Stop[ex][ey] = TRUE;        // no more moving in this frame
10988         }
10989       }
10990
10991       if (something_has_changed)
10992       {
10993         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10994         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10995       }
10996     }
10997   }
10998   else
10999   {
11000     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
11001                                         ce_value, ce_score);
11002
11003     if (element == EL_DIAGONAL_GROWING ||
11004         element == EL_DIAGONAL_SHRINKING)
11005     {
11006       target_element = Store[x][y];
11007
11008       Store[x][y] = EL_EMPTY;
11009     }
11010
11011     // special case: element changes to player (and may be kept if walkable)
11012     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11013       CreateElementFromChange(x, y, EL_EMPTY);
11014
11015     CreateElementFromChange(x, y, target_element);
11016
11017     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11018     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11019   }
11020
11021   // this uses direct change before indirect change
11022   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11023
11024   return TRUE;
11025 }
11026
11027 static void HandleElementChange(int x, int y, int page)
11028 {
11029   int element = MovingOrBlocked2Element(x, y);
11030   struct ElementInfo *ei = &element_info[element];
11031   struct ElementChangeInfo *change = &ei->change_page[page];
11032   boolean handle_action_before_change = FALSE;
11033
11034 #ifdef DEBUG
11035   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11036       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11037   {
11038     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11039           x, y, element, element_info[element].token_name);
11040     Debug("game:playing:HandleElementChange", "This should never happen!");
11041   }
11042 #endif
11043
11044   // this can happen with classic bombs on walkable, changing elements
11045   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11046   {
11047     return;
11048   }
11049
11050   if (ChangeDelay[x][y] == 0)           // initialize element change
11051   {
11052     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11053
11054     if (change->can_change)
11055     {
11056       // !!! not clear why graphic animation should be reset at all here !!!
11057       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11058       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11059
11060       /*
11061         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11062
11063         When using an animation frame delay of 1 (this only happens with
11064         "sp_zonk.moving.left/right" in the classic graphics), the default
11065         (non-moving) animation shows wrong animation frames (while the
11066         moving animation, like "sp_zonk.moving.left/right", is correct,
11067         so this graphical bug never shows up with the classic graphics).
11068         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11069         be drawn instead of the correct frames 0,1,2,3. This is caused by
11070         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11071         an element change: First when the change delay ("ChangeDelay[][]")
11072         counter has reached zero after decrementing, then a second time in
11073         the next frame (after "GfxFrame[][]" was already incremented) when
11074         "ChangeDelay[][]" is reset to the initial delay value again.
11075
11076         This causes frame 0 to be drawn twice, while the last frame won't
11077         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11078
11079         As some animations may already be cleverly designed around this bug
11080         (at least the "Snake Bite" snake tail animation does this), it cannot
11081         simply be fixed here without breaking such existing animations.
11082         Unfortunately, it cannot easily be detected if a graphics set was
11083         designed "before" or "after" the bug was fixed. As a workaround,
11084         a new graphics set option "game.graphics_engine_version" was added
11085         to be able to specify the game's major release version for which the
11086         graphics set was designed, which can then be used to decide if the
11087         bugfix should be used (version 4 and above) or not (version 3 or
11088         below, or if no version was specified at all, as with old sets).
11089
11090         (The wrong/fixed animation frames can be tested with the test level set
11091         "test_gfxframe" and level "000", which contains a specially prepared
11092         custom element at level position (x/y) == (11/9) which uses the zonk
11093         animation mentioned above. Using "game.graphics_engine_version: 4"
11094         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11095         This can also be seen from the debug output for this test element.)
11096       */
11097
11098       // when a custom element is about to change (for example by change delay),
11099       // do not reset graphic animation when the custom element is moving
11100       if (game.graphics_engine_version < 4 &&
11101           !IS_MOVING(x, y))
11102       {
11103         ResetGfxAnimation(x, y);
11104         ResetRandomAnimationValue(x, y);
11105       }
11106
11107       if (change->pre_change_function)
11108         change->pre_change_function(x, y);
11109     }
11110   }
11111
11112   ChangeDelay[x][y]--;
11113
11114   if (ChangeDelay[x][y] != 0)           // continue element change
11115   {
11116     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11117
11118     // also needed if CE can not change, but has CE delay with CE action
11119     if (IS_ANIMATED(graphic))
11120       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11121
11122     if (change->can_change)
11123     {
11124       if (change->change_function)
11125         change->change_function(x, y);
11126     }
11127   }
11128   else                                  // finish element change
11129   {
11130     if (ChangePage[x][y] != -1)         // remember page from delayed change
11131     {
11132       page = ChangePage[x][y];
11133       ChangePage[x][y] = -1;
11134
11135       change = &ei->change_page[page];
11136     }
11137
11138     if (IS_MOVING(x, y))                // never change a running system ;-)
11139     {
11140       ChangeDelay[x][y] = 1;            // try change after next move step
11141       ChangePage[x][y] = page;          // remember page to use for change
11142
11143       return;
11144     }
11145
11146     // special case: set new level random seed before changing element
11147     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11148       handle_action_before_change = TRUE;
11149
11150     if (change->has_action && handle_action_before_change)
11151       ExecuteCustomElementAction(x, y, element, page);
11152
11153     if (change->can_change)
11154     {
11155       if (ChangeElement(x, y, element, page))
11156       {
11157         if (change->post_change_function)
11158           change->post_change_function(x, y);
11159       }
11160     }
11161
11162     if (change->has_action && !handle_action_before_change)
11163       ExecuteCustomElementAction(x, y, element, page);
11164   }
11165 }
11166
11167 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11168                                               int trigger_element,
11169                                               int trigger_event,
11170                                               int trigger_player,
11171                                               int trigger_side,
11172                                               int trigger_page)
11173 {
11174   boolean change_done_any = FALSE;
11175   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11176   int i;
11177
11178   if (!(trigger_events[trigger_element][trigger_event]))
11179     return FALSE;
11180
11181   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11182
11183   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11184   {
11185     int element = EL_CUSTOM_START + i;
11186     boolean change_done = FALSE;
11187     int p;
11188
11189     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11190         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11191       continue;
11192
11193     for (p = 0; p < element_info[element].num_change_pages; p++)
11194     {
11195       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11196
11197       if (change->can_change_or_has_action &&
11198           change->has_event[trigger_event] &&
11199           change->trigger_side & trigger_side &&
11200           change->trigger_player & trigger_player &&
11201           change->trigger_page & trigger_page_bits &&
11202           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11203       {
11204         change->actual_trigger_element = trigger_element;
11205         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11206         change->actual_trigger_player_bits = trigger_player;
11207         change->actual_trigger_side = trigger_side;
11208         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11209         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11210         change->actual_trigger_x = trigger_x;
11211         change->actual_trigger_y = trigger_y;
11212
11213         if ((change->can_change && !change_done) || change->has_action)
11214         {
11215           int x, y;
11216
11217           SCAN_PLAYFIELD(x, y)
11218           {
11219             if (Tile[x][y] == element)
11220             {
11221               if (change->can_change && !change_done)
11222               {
11223                 // if element already changed in this frame, not only prevent
11224                 // another element change (checked in ChangeElement()), but
11225                 // also prevent additional element actions for this element
11226
11227                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11228                     !level.use_action_after_change_bug)
11229                   continue;
11230
11231                 ChangeDelay[x][y] = 1;
11232                 ChangeEvent[x][y] = trigger_event;
11233
11234                 HandleElementChange(x, y, p);
11235               }
11236               else if (change->has_action)
11237               {
11238                 // if element already changed in this frame, not only prevent
11239                 // another element change (checked in ChangeElement()), but
11240                 // also prevent additional element actions for this element
11241
11242                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11243                     !level.use_action_after_change_bug)
11244                   continue;
11245
11246                 ExecuteCustomElementAction(x, y, element, p);
11247                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11248               }
11249             }
11250           }
11251
11252           if (change->can_change)
11253           {
11254             change_done = TRUE;
11255             change_done_any = TRUE;
11256           }
11257         }
11258       }
11259     }
11260   }
11261
11262   RECURSION_LOOP_DETECTION_END();
11263
11264   return change_done_any;
11265 }
11266
11267 static boolean CheckElementChangeExt(int x, int y,
11268                                      int element,
11269                                      int trigger_element,
11270                                      int trigger_event,
11271                                      int trigger_player,
11272                                      int trigger_side)
11273 {
11274   boolean change_done = FALSE;
11275   int p;
11276
11277   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11278       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11279     return FALSE;
11280
11281   if (Tile[x][y] == EL_BLOCKED)
11282   {
11283     Blocked2Moving(x, y, &x, &y);
11284     element = Tile[x][y];
11285   }
11286
11287   // check if element has already changed or is about to change after moving
11288   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11289        Tile[x][y] != element) ||
11290
11291       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11292        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11293         ChangePage[x][y] != -1)))
11294     return FALSE;
11295
11296   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11297
11298   for (p = 0; p < element_info[element].num_change_pages; p++)
11299   {
11300     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11301
11302     /* check trigger element for all events where the element that is checked
11303        for changing interacts with a directly adjacent element -- this is
11304        different to element changes that affect other elements to change on the
11305        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11306     boolean check_trigger_element =
11307       (trigger_event == CE_NEXT_TO_X ||
11308        trigger_event == CE_TOUCHING_X ||
11309        trigger_event == CE_HITTING_X ||
11310        trigger_event == CE_HIT_BY_X ||
11311        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11312
11313     if (change->can_change_or_has_action &&
11314         change->has_event[trigger_event] &&
11315         change->trigger_side & trigger_side &&
11316         change->trigger_player & trigger_player &&
11317         (!check_trigger_element ||
11318          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11319     {
11320       change->actual_trigger_element = trigger_element;
11321       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11322       change->actual_trigger_player_bits = trigger_player;
11323       change->actual_trigger_side = trigger_side;
11324       change->actual_trigger_ce_value = CustomValue[x][y];
11325       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11326       change->actual_trigger_x = x;
11327       change->actual_trigger_y = y;
11328
11329       // special case: trigger element not at (x,y) position for some events
11330       if (check_trigger_element)
11331       {
11332         static struct
11333         {
11334           int dx, dy;
11335         } move_xy[] =
11336           {
11337             {  0,  0 },
11338             { -1,  0 },
11339             { +1,  0 },
11340             {  0,  0 },
11341             {  0, -1 },
11342             {  0,  0 }, { 0, 0 }, { 0, 0 },
11343             {  0, +1 }
11344           };
11345
11346         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11347         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11348
11349         change->actual_trigger_ce_value = CustomValue[xx][yy];
11350         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11351         change->actual_trigger_x = xx;
11352         change->actual_trigger_y = yy;
11353       }
11354
11355       if (change->can_change && !change_done)
11356       {
11357         ChangeDelay[x][y] = 1;
11358         ChangeEvent[x][y] = trigger_event;
11359
11360         HandleElementChange(x, y, p);
11361
11362         change_done = TRUE;
11363       }
11364       else if (change->has_action)
11365       {
11366         ExecuteCustomElementAction(x, y, element, p);
11367         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11368       }
11369     }
11370   }
11371
11372   RECURSION_LOOP_DETECTION_END();
11373
11374   return change_done;
11375 }
11376
11377 static void PlayPlayerSound(struct PlayerInfo *player)
11378 {
11379   int jx = player->jx, jy = player->jy;
11380   int sound_element = player->artwork_element;
11381   int last_action = player->last_action_waiting;
11382   int action = player->action_waiting;
11383
11384   if (player->is_waiting)
11385   {
11386     if (action != last_action)
11387       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11388     else
11389       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11390   }
11391   else
11392   {
11393     if (action != last_action)
11394       StopSound(element_info[sound_element].sound[last_action]);
11395
11396     if (last_action == ACTION_SLEEPING)
11397       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11398   }
11399 }
11400
11401 static void PlayAllPlayersSound(void)
11402 {
11403   int i;
11404
11405   for (i = 0; i < MAX_PLAYERS; i++)
11406     if (stored_player[i].active)
11407       PlayPlayerSound(&stored_player[i]);
11408 }
11409
11410 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11411 {
11412   boolean last_waiting = player->is_waiting;
11413   int move_dir = player->MovDir;
11414
11415   player->dir_waiting = move_dir;
11416   player->last_action_waiting = player->action_waiting;
11417
11418   if (is_waiting)
11419   {
11420     if (!last_waiting)          // not waiting -> waiting
11421     {
11422       player->is_waiting = TRUE;
11423
11424       player->frame_counter_bored =
11425         FrameCounter +
11426         game.player_boring_delay_fixed +
11427         GetSimpleRandom(game.player_boring_delay_random);
11428       player->frame_counter_sleeping =
11429         FrameCounter +
11430         game.player_sleeping_delay_fixed +
11431         GetSimpleRandom(game.player_sleeping_delay_random);
11432
11433       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11434     }
11435
11436     if (game.player_sleeping_delay_fixed +
11437         game.player_sleeping_delay_random > 0 &&
11438         player->anim_delay_counter == 0 &&
11439         player->post_delay_counter == 0 &&
11440         FrameCounter >= player->frame_counter_sleeping)
11441       player->is_sleeping = TRUE;
11442     else if (game.player_boring_delay_fixed +
11443              game.player_boring_delay_random > 0 &&
11444              FrameCounter >= player->frame_counter_bored)
11445       player->is_bored = TRUE;
11446
11447     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11448                               player->is_bored ? ACTION_BORING :
11449                               ACTION_WAITING);
11450
11451     if (player->is_sleeping && player->use_murphy)
11452     {
11453       // special case for sleeping Murphy when leaning against non-free tile
11454
11455       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11456           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11457            !IS_MOVING(player->jx - 1, player->jy)))
11458         move_dir = MV_LEFT;
11459       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11460                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11461                 !IS_MOVING(player->jx + 1, player->jy)))
11462         move_dir = MV_RIGHT;
11463       else
11464         player->is_sleeping = FALSE;
11465
11466       player->dir_waiting = move_dir;
11467     }
11468
11469     if (player->is_sleeping)
11470     {
11471       if (player->num_special_action_sleeping > 0)
11472       {
11473         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11474         {
11475           int last_special_action = player->special_action_sleeping;
11476           int num_special_action = player->num_special_action_sleeping;
11477           int special_action =
11478             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11479              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11480              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11481              last_special_action + 1 : ACTION_SLEEPING);
11482           int special_graphic =
11483             el_act_dir2img(player->artwork_element, special_action, move_dir);
11484
11485           player->anim_delay_counter =
11486             graphic_info[special_graphic].anim_delay_fixed +
11487             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11488           player->post_delay_counter =
11489             graphic_info[special_graphic].post_delay_fixed +
11490             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11491
11492           player->special_action_sleeping = special_action;
11493         }
11494
11495         if (player->anim_delay_counter > 0)
11496         {
11497           player->action_waiting = player->special_action_sleeping;
11498           player->anim_delay_counter--;
11499         }
11500         else if (player->post_delay_counter > 0)
11501         {
11502           player->post_delay_counter--;
11503         }
11504       }
11505     }
11506     else if (player->is_bored)
11507     {
11508       if (player->num_special_action_bored > 0)
11509       {
11510         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11511         {
11512           int special_action =
11513             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11514           int special_graphic =
11515             el_act_dir2img(player->artwork_element, special_action, move_dir);
11516
11517           player->anim_delay_counter =
11518             graphic_info[special_graphic].anim_delay_fixed +
11519             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11520           player->post_delay_counter =
11521             graphic_info[special_graphic].post_delay_fixed +
11522             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11523
11524           player->special_action_bored = special_action;
11525         }
11526
11527         if (player->anim_delay_counter > 0)
11528         {
11529           player->action_waiting = player->special_action_bored;
11530           player->anim_delay_counter--;
11531         }
11532         else if (player->post_delay_counter > 0)
11533         {
11534           player->post_delay_counter--;
11535         }
11536       }
11537     }
11538   }
11539   else if (last_waiting)        // waiting -> not waiting
11540   {
11541     player->is_waiting = FALSE;
11542     player->is_bored = FALSE;
11543     player->is_sleeping = FALSE;
11544
11545     player->frame_counter_bored = -1;
11546     player->frame_counter_sleeping = -1;
11547
11548     player->anim_delay_counter = 0;
11549     player->post_delay_counter = 0;
11550
11551     player->dir_waiting = player->MovDir;
11552     player->action_waiting = ACTION_DEFAULT;
11553
11554     player->special_action_bored = ACTION_DEFAULT;
11555     player->special_action_sleeping = ACTION_DEFAULT;
11556   }
11557 }
11558
11559 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11560 {
11561   if ((!player->is_moving  && player->was_moving) ||
11562       (player->MovPos == 0 && player->was_moving) ||
11563       (player->is_snapping && !player->was_snapping) ||
11564       (player->is_dropping && !player->was_dropping))
11565   {
11566     if (!CheckSaveEngineSnapshotToList())
11567       return;
11568
11569     player->was_moving = FALSE;
11570     player->was_snapping = TRUE;
11571     player->was_dropping = TRUE;
11572   }
11573   else
11574   {
11575     if (player->is_moving)
11576       player->was_moving = TRUE;
11577
11578     if (!player->is_snapping)
11579       player->was_snapping = FALSE;
11580
11581     if (!player->is_dropping)
11582       player->was_dropping = FALSE;
11583   }
11584
11585   static struct MouseActionInfo mouse_action_last = { 0 };
11586   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11587   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11588
11589   if (new_released)
11590     CheckSaveEngineSnapshotToList();
11591
11592   mouse_action_last = mouse_action;
11593 }
11594
11595 static void CheckSingleStepMode(struct PlayerInfo *player)
11596 {
11597   if (tape.single_step && tape.recording && !tape.pausing)
11598   {
11599     // as it is called "single step mode", just return to pause mode when the
11600     // player stopped moving after one tile (or never starts moving at all)
11601     // (reverse logic needed here in case single step mode used in team mode)
11602     if (player->is_moving ||
11603         player->is_pushing ||
11604         player->is_dropping_pressed ||
11605         player->effective_mouse_action.button)
11606       game.enter_single_step_mode = FALSE;
11607   }
11608
11609   CheckSaveEngineSnapshot(player);
11610 }
11611
11612 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11613 {
11614   int left      = player_action & JOY_LEFT;
11615   int right     = player_action & JOY_RIGHT;
11616   int up        = player_action & JOY_UP;
11617   int down      = player_action & JOY_DOWN;
11618   int button1   = player_action & JOY_BUTTON_1;
11619   int button2   = player_action & JOY_BUTTON_2;
11620   int dx        = (left ? -1 : right ? 1 : 0);
11621   int dy        = (up   ? -1 : down  ? 1 : 0);
11622
11623   if (!player->active || tape.pausing)
11624     return 0;
11625
11626   if (player_action)
11627   {
11628     if (button1)
11629       SnapField(player, dx, dy);
11630     else
11631     {
11632       if (button2)
11633         DropElement(player);
11634
11635       MovePlayer(player, dx, dy);
11636     }
11637
11638     CheckSingleStepMode(player);
11639
11640     SetPlayerWaiting(player, FALSE);
11641
11642     return player_action;
11643   }
11644   else
11645   {
11646     // no actions for this player (no input at player's configured device)
11647
11648     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11649     SnapField(player, 0, 0);
11650     CheckGravityMovementWhenNotMoving(player);
11651
11652     if (player->MovPos == 0)
11653       SetPlayerWaiting(player, TRUE);
11654
11655     if (player->MovPos == 0)    // needed for tape.playing
11656       player->is_moving = FALSE;
11657
11658     player->is_dropping = FALSE;
11659     player->is_dropping_pressed = FALSE;
11660     player->drop_pressed_delay = 0;
11661
11662     CheckSingleStepMode(player);
11663
11664     return 0;
11665   }
11666 }
11667
11668 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11669                                          byte *tape_action)
11670 {
11671   if (!tape.use_mouse_actions)
11672     return;
11673
11674   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11675   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11676   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11677 }
11678
11679 static void SetTapeActionFromMouseAction(byte *tape_action,
11680                                          struct MouseActionInfo *mouse_action)
11681 {
11682   if (!tape.use_mouse_actions)
11683     return;
11684
11685   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11686   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11687   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11688 }
11689
11690 static void CheckLevelSolved(void)
11691 {
11692   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11693   {
11694     if (game_bd.level_solved &&
11695         !game_bd.game_over)                             // game won
11696     {
11697       LevelSolved();
11698
11699       game_bd.game_over = TRUE;
11700
11701       game.all_players_gone = TRUE;
11702     }
11703
11704     if (game_bd.game_over)                              // game lost
11705       game.all_players_gone = TRUE;
11706   }
11707   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11708   {
11709     if (game_em.level_solved &&
11710         !game_em.game_over)                             // game won
11711     {
11712       LevelSolved();
11713
11714       game_em.game_over = TRUE;
11715
11716       game.all_players_gone = TRUE;
11717     }
11718
11719     if (game_em.game_over)                              // game lost
11720       game.all_players_gone = TRUE;
11721   }
11722   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11723   {
11724     if (game_sp.level_solved &&
11725         !game_sp.game_over)                             // game won
11726     {
11727       LevelSolved();
11728
11729       game_sp.game_over = TRUE;
11730
11731       game.all_players_gone = TRUE;
11732     }
11733
11734     if (game_sp.game_over)                              // game lost
11735       game.all_players_gone = TRUE;
11736   }
11737   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11738   {
11739     if (game_mm.level_solved &&
11740         !game_mm.game_over)                             // game won
11741     {
11742       LevelSolved();
11743
11744       game_mm.game_over = TRUE;
11745
11746       game.all_players_gone = TRUE;
11747     }
11748
11749     if (game_mm.game_over)                              // game lost
11750       game.all_players_gone = TRUE;
11751   }
11752 }
11753
11754 static void PlayTimeoutSound(int seconds_left)
11755 {
11756   // will be played directly by BD engine (for classic bonus time sounds)
11757   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11758     return;
11759
11760   // try to use individual "running out of time" sound for each second left
11761   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11762
11763   // if special sound per second not defined, use default sound
11764   if (getSoundInfoEntryFilename(sound) == NULL)
11765     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11766
11767   // if out of time, but player still alive, play special "timeout" sound, if defined
11768   if (seconds_left == 0 && !checkGameFailed())
11769     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11770       sound = SND_GAME_TIMEOUT;
11771
11772   PlaySound(sound);
11773 }
11774
11775 static void CheckLevelTime_StepCounter(void)
11776 {
11777   int i;
11778
11779   TimePlayed++;
11780
11781   if (TimeLeft > 0)
11782   {
11783     TimeLeft--;
11784
11785     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11786       PlayTimeoutSound(TimeLeft);
11787
11788     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11789
11790     DisplayGameControlValues();
11791
11792     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11793       for (i = 0; i < MAX_PLAYERS; i++)
11794         KillPlayer(&stored_player[i]);
11795   }
11796   else if (game.no_level_time_limit && !game.all_players_gone)
11797   {
11798     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11799
11800     DisplayGameControlValues();
11801   }
11802 }
11803
11804 static void CheckLevelTime(void)
11805 {
11806   int frames_per_second = FRAMES_PER_SECOND;
11807   int i;
11808
11809   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11810   {
11811     // level time may be running slower in native BD engine
11812     frames_per_second = getFramesPerSecond_BD();
11813
11814     // if native engine time changed, force main engine time change
11815     if (getTimeLeft_BD() < TimeLeft)
11816       TimeFrames = frames_per_second;
11817
11818     // if last second running, wait for native engine time to exactly reach zero
11819     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11820       TimeFrames = frames_per_second - 1;
11821   }
11822
11823   if (TimeFrames >= frames_per_second)
11824   {
11825     TimeFrames = 0;
11826
11827     for (i = 0; i < MAX_PLAYERS; i++)
11828     {
11829       struct PlayerInfo *player = &stored_player[i];
11830
11831       if (SHIELD_ON(player))
11832       {
11833         player->shield_normal_time_left--;
11834
11835         if (player->shield_deadly_time_left > 0)
11836           player->shield_deadly_time_left--;
11837       }
11838     }
11839
11840     if (!game.LevelSolved && !level.use_step_counter)
11841     {
11842       TimePlayed++;
11843
11844       if (TimeLeft > 0)
11845       {
11846         TimeLeft--;
11847
11848         if (TimeLeft <= 10 && game.time_limit)
11849           PlayTimeoutSound(TimeLeft);
11850
11851         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11852            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11853
11854         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11855
11856         if (!TimeLeft && game.time_limit)
11857         {
11858           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11859           {
11860             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11861               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11862           }
11863           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11864           {
11865             game_em.lev->killed_out_of_time = TRUE;
11866           }
11867           else
11868           {
11869             for (i = 0; i < MAX_PLAYERS; i++)
11870               KillPlayer(&stored_player[i]);
11871           }
11872         }
11873       }
11874       else if (game.no_level_time_limit && !game.all_players_gone)
11875       {
11876         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11877       }
11878
11879       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11880     }
11881   }
11882
11883   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11884   {
11885     TapeTimeFrames = 0;
11886     TapeTime++;
11887
11888     if (tape.recording || tape.playing)
11889       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11890   }
11891
11892   if (tape.recording || tape.playing)
11893     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11894
11895   UpdateAndDisplayGameControlValues();
11896 }
11897
11898 void AdvanceFrameAndPlayerCounters(int player_nr)
11899 {
11900   int i;
11901
11902   // handle game and tape time differently for native BD game engine
11903
11904   // tape time is running in native BD engine even if player is not hatched yet
11905   if (!checkGameRunning())
11906     return;
11907
11908   // advance frame counters (global frame counter and tape time frame counter)
11909   FrameCounter++;
11910   TapeTimeFrames++;
11911
11912   // level time is running in native BD engine after player is being hatched
11913   if (!checkGamePlaying())
11914     return;
11915
11916   // advance time frame counter (used to control available time to solve level)
11917   TimeFrames++;
11918
11919   // advance player counters (counters for move delay, move animation etc.)
11920   for (i = 0; i < MAX_PLAYERS; i++)
11921   {
11922     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11923     int move_delay_value = stored_player[i].move_delay_value;
11924     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11925
11926     if (!advance_player_counters)       // not all players may be affected
11927       continue;
11928
11929     if (move_frames == 0)       // less than one move per game frame
11930     {
11931       int stepsize = TILEX / move_delay_value;
11932       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11933       int count = (stored_player[i].is_moving ?
11934                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11935
11936       if (count % delay == 0)
11937         move_frames = 1;
11938     }
11939
11940     stored_player[i].Frame += move_frames;
11941
11942     if (stored_player[i].MovPos != 0)
11943       stored_player[i].StepFrame += move_frames;
11944
11945     if (stored_player[i].move_delay > 0)
11946       stored_player[i].move_delay--;
11947
11948     // due to bugs in previous versions, counter must count up, not down
11949     if (stored_player[i].push_delay != -1)
11950       stored_player[i].push_delay++;
11951
11952     if (stored_player[i].drop_delay > 0)
11953       stored_player[i].drop_delay--;
11954
11955     if (stored_player[i].is_dropping_pressed)
11956       stored_player[i].drop_pressed_delay++;
11957   }
11958 }
11959
11960 void AdvanceFrameCounter(void)
11961 {
11962   FrameCounter++;
11963 }
11964
11965 void AdvanceGfxFrame(void)
11966 {
11967   int x, y;
11968
11969   SCAN_PLAYFIELD(x, y)
11970   {
11971     GfxFrame[x][y]++;
11972   }
11973 }
11974
11975 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11976                               struct MouseActionInfo *mouse_action_last)
11977 {
11978   if (mouse_action->button)
11979   {
11980     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11981     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11982     int x = mouse_action->lx;
11983     int y = mouse_action->ly;
11984     int element = Tile[x][y];
11985
11986     if (new_button)
11987     {
11988       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11989       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11990                                          ch_button);
11991     }
11992
11993     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11994     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11995                                        ch_button);
11996
11997     if (level.use_step_counter)
11998     {
11999       boolean counted_click = FALSE;
12000
12001       // element clicked that can change when clicked/pressed
12002       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12003           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12004            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12005         counted_click = TRUE;
12006
12007       // element clicked that can trigger change when clicked/pressed
12008       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12009           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12010         counted_click = TRUE;
12011
12012       if (new_button && counted_click)
12013         CheckLevelTime_StepCounter();
12014     }
12015   }
12016 }
12017
12018 void StartGameActions(boolean init_network_game, boolean record_tape,
12019                       int random_seed)
12020 {
12021   unsigned int new_random_seed = InitRND(random_seed);
12022
12023   if (record_tape)
12024     TapeStartRecording(new_random_seed);
12025
12026   if (setup.auto_pause_on_start && !tape.pausing)
12027     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12028
12029   if (init_network_game)
12030   {
12031     SendToServer_LevelFile();
12032     SendToServer_StartPlaying();
12033
12034     return;
12035   }
12036
12037   InitGame();
12038 }
12039
12040 static void GameActionsExt(void)
12041 {
12042 #if 0
12043   static unsigned int game_frame_delay = 0;
12044 #endif
12045   unsigned int game_frame_delay_value;
12046   byte *recorded_player_action;
12047   byte summarized_player_action = 0;
12048   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12049   int i;
12050
12051   // detect endless loops, caused by custom element programming
12052   if (recursion_loop_detected && recursion_loop_depth == 0)
12053   {
12054     char *message = getStringCat3("Internal Error! Element ",
12055                                   EL_NAME(recursion_loop_element),
12056                                   " caused endless loop! Quit the game?");
12057
12058     Warn("element '%s' caused endless loop in game engine",
12059          EL_NAME(recursion_loop_element));
12060
12061     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12062
12063     recursion_loop_detected = FALSE;    // if game should be continued
12064
12065     free(message);
12066
12067     return;
12068   }
12069
12070   if (game.restart_level)
12071     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12072
12073   CheckLevelSolved();
12074
12075   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12076     GameWon();
12077
12078   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12079     TapeStop();
12080
12081   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12082     return;
12083
12084   game_frame_delay_value =
12085     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12086
12087   if (tape.playing && tape.warp_forward && !tape.pausing)
12088     game_frame_delay_value = 0;
12089
12090   SetVideoFrameDelay(game_frame_delay_value);
12091
12092   // (de)activate virtual buttons depending on current game status
12093   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12094   {
12095     if (game.all_players_gone)  // if no players there to be controlled anymore
12096       SetOverlayActive(FALSE);
12097     else if (!tape.playing)     // if game continues after tape stopped playing
12098       SetOverlayActive(TRUE);
12099   }
12100
12101 #if 0
12102 #if 0
12103   // ---------- main game synchronization point ----------
12104
12105   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12106
12107   Debug("game:playing:skip", "skip == %d", skip);
12108
12109 #else
12110   // ---------- main game synchronization point ----------
12111
12112   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12113 #endif
12114 #endif
12115
12116   if (network_playing && !network_player_action_received)
12117   {
12118     // try to get network player actions in time
12119
12120     // last chance to get network player actions without main loop delay
12121     HandleNetworking();
12122
12123     // game was quit by network peer
12124     if (game_status != GAME_MODE_PLAYING)
12125       return;
12126
12127     // check if network player actions still missing and game still running
12128     if (!network_player_action_received && !checkGameEnded())
12129       return;           // failed to get network player actions in time
12130
12131     // do not yet reset "network_player_action_received" (for tape.pausing)
12132   }
12133
12134   if (tape.pausing)
12135     return;
12136
12137   // at this point we know that we really continue executing the game
12138
12139   network_player_action_received = FALSE;
12140
12141   // when playing tape, read previously recorded player input from tape data
12142   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12143
12144   local_player->effective_mouse_action = local_player->mouse_action;
12145
12146   if (recorded_player_action != NULL)
12147     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12148                                  recorded_player_action);
12149
12150   // TapePlayAction() may return NULL when toggling to "pause before death"
12151   if (tape.pausing)
12152     return;
12153
12154   if (tape.set_centered_player)
12155   {
12156     game.centered_player_nr_next = tape.centered_player_nr_next;
12157     game.set_centered_player = TRUE;
12158   }
12159
12160   for (i = 0; i < MAX_PLAYERS; i++)
12161   {
12162     summarized_player_action |= stored_player[i].action;
12163
12164     if (!network_playing && (game.team_mode || tape.playing))
12165       stored_player[i].effective_action = stored_player[i].action;
12166   }
12167
12168   if (network_playing && !checkGameEnded())
12169     SendToServer_MovePlayer(summarized_player_action);
12170
12171   // summarize all actions at local players mapped input device position
12172   // (this allows using different input devices in single player mode)
12173   if (!network.enabled && !game.team_mode)
12174     stored_player[map_player_action[local_player->index_nr]].effective_action =
12175       summarized_player_action;
12176
12177   // summarize all actions at centered player in local team mode
12178   if (tape.recording &&
12179       setup.team_mode && !network.enabled &&
12180       setup.input_on_focus &&
12181       game.centered_player_nr != -1)
12182   {
12183     for (i = 0; i < MAX_PLAYERS; i++)
12184       stored_player[map_player_action[i]].effective_action =
12185         (i == game.centered_player_nr ? summarized_player_action : 0);
12186   }
12187
12188   if (recorded_player_action != NULL)
12189     for (i = 0; i < MAX_PLAYERS; i++)
12190       stored_player[i].effective_action = recorded_player_action[i];
12191
12192   for (i = 0; i < MAX_PLAYERS; i++)
12193   {
12194     tape_action[i] = stored_player[i].effective_action;
12195
12196     /* (this may happen in the RND game engine if a player was not present on
12197        the playfield on level start, but appeared later from a custom element */
12198     if (setup.team_mode &&
12199         tape.recording &&
12200         tape_action[i] &&
12201         !tape.player_participates[i])
12202       tape.player_participates[i] = TRUE;
12203   }
12204
12205   SetTapeActionFromMouseAction(tape_action,
12206                                &local_player->effective_mouse_action);
12207
12208   // only record actions from input devices, but not programmed actions
12209   if (tape.recording)
12210     TapeRecordAction(tape_action);
12211
12212   // remember if game was played (especially after tape stopped playing)
12213   if (!tape.playing && summarized_player_action && !checkGameFailed())
12214     game.GamePlayed = TRUE;
12215
12216 #if USE_NEW_PLAYER_ASSIGNMENTS
12217   // !!! also map player actions in single player mode !!!
12218   // if (game.team_mode)
12219   if (1)
12220   {
12221     byte mapped_action[MAX_PLAYERS];
12222
12223 #if DEBUG_PLAYER_ACTIONS
12224     for (i = 0; i < MAX_PLAYERS; i++)
12225       DebugContinued("", "%d, ", stored_player[i].effective_action);
12226 #endif
12227
12228     for (i = 0; i < MAX_PLAYERS; i++)
12229       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12230
12231     for (i = 0; i < MAX_PLAYERS; i++)
12232       stored_player[i].effective_action = mapped_action[i];
12233
12234 #if DEBUG_PLAYER_ACTIONS
12235     DebugContinued("", "=> ");
12236     for (i = 0; i < MAX_PLAYERS; i++)
12237       DebugContinued("", "%d, ", stored_player[i].effective_action);
12238     DebugContinued("game:playing:player", "\n");
12239 #endif
12240   }
12241 #if DEBUG_PLAYER_ACTIONS
12242   else
12243   {
12244     for (i = 0; i < MAX_PLAYERS; i++)
12245       DebugContinued("", "%d, ", stored_player[i].effective_action);
12246     DebugContinued("game:playing:player", "\n");
12247   }
12248 #endif
12249 #endif
12250
12251   for (i = 0; i < MAX_PLAYERS; i++)
12252   {
12253     // allow engine snapshot in case of changed movement attempt
12254     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12255         (stored_player[i].effective_action & KEY_MOTION))
12256       game.snapshot.changed_action = TRUE;
12257
12258     // allow engine snapshot in case of snapping/dropping attempt
12259     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12260         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12261       game.snapshot.changed_action = TRUE;
12262
12263     game.snapshot.last_action[i] = stored_player[i].effective_action;
12264   }
12265
12266   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12267   {
12268     GameActions_BD_Main();
12269   }
12270   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12271   {
12272     GameActions_EM_Main();
12273   }
12274   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12275   {
12276     GameActions_SP_Main();
12277   }
12278   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12279   {
12280     GameActions_MM_Main();
12281   }
12282   else
12283   {
12284     GameActions_RND_Main();
12285   }
12286
12287   BlitScreenToBitmap(backbuffer);
12288
12289   CheckLevelSolved();
12290   CheckLevelTime();
12291
12292   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12293
12294   if (global.show_frames_per_second)
12295   {
12296     static unsigned int fps_counter = 0;
12297     static int fps_frames = 0;
12298     unsigned int fps_delay_ms = Counter() - fps_counter;
12299
12300     fps_frames++;
12301
12302     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12303     {
12304       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12305
12306       fps_frames = 0;
12307       fps_counter = Counter();
12308
12309       // always draw FPS to screen after FPS value was updated
12310       redraw_mask |= REDRAW_FPS;
12311     }
12312
12313     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12314     if (GetDrawDeactivationMask() == REDRAW_NONE)
12315       redraw_mask |= REDRAW_FPS;
12316   }
12317 }
12318
12319 static void GameActions_CheckSaveEngineSnapshot(void)
12320 {
12321   if (!game.snapshot.save_snapshot)
12322     return;
12323
12324   // clear flag for saving snapshot _before_ saving snapshot
12325   game.snapshot.save_snapshot = FALSE;
12326
12327   SaveEngineSnapshotToList();
12328 }
12329
12330 void GameActions(void)
12331 {
12332   GameActionsExt();
12333
12334   GameActions_CheckSaveEngineSnapshot();
12335 }
12336
12337 void GameActions_BD_Main(void)
12338 {
12339   byte effective_action[MAX_PLAYERS];
12340   int i;
12341
12342   for (i = 0; i < MAX_PLAYERS; i++)
12343     effective_action[i] = stored_player[i].effective_action;
12344
12345   GameActions_BD(effective_action);
12346 }
12347
12348 void GameActions_EM_Main(void)
12349 {
12350   byte effective_action[MAX_PLAYERS];
12351   int i;
12352
12353   for (i = 0; i < MAX_PLAYERS; i++)
12354     effective_action[i] = stored_player[i].effective_action;
12355
12356   GameActions_EM(effective_action);
12357 }
12358
12359 void GameActions_SP_Main(void)
12360 {
12361   byte effective_action[MAX_PLAYERS];
12362   int i;
12363
12364   for (i = 0; i < MAX_PLAYERS; i++)
12365     effective_action[i] = stored_player[i].effective_action;
12366
12367   GameActions_SP(effective_action);
12368
12369   for (i = 0; i < MAX_PLAYERS; i++)
12370   {
12371     if (stored_player[i].force_dropping)
12372       stored_player[i].action |= KEY_BUTTON_DROP;
12373
12374     stored_player[i].force_dropping = FALSE;
12375   }
12376 }
12377
12378 void GameActions_MM_Main(void)
12379 {
12380   AdvanceGfxFrame();
12381
12382   GameActions_MM(local_player->effective_mouse_action);
12383 }
12384
12385 void GameActions_RND_Main(void)
12386 {
12387   GameActions_RND();
12388 }
12389
12390 void GameActions_RND(void)
12391 {
12392   static struct MouseActionInfo mouse_action_last = { 0 };
12393   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12394   int magic_wall_x = 0, magic_wall_y = 0;
12395   int i, x, y, element, graphic, last_gfx_frame;
12396
12397   InitPlayfieldScanModeVars();
12398
12399   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12400   {
12401     SCAN_PLAYFIELD(x, y)
12402     {
12403       ChangeCount[x][y] = 0;
12404       ChangeEvent[x][y] = -1;
12405     }
12406   }
12407
12408   if (game.set_centered_player)
12409   {
12410     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12411
12412     // switching to "all players" only possible if all players fit to screen
12413     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12414     {
12415       game.centered_player_nr_next = game.centered_player_nr;
12416       game.set_centered_player = FALSE;
12417     }
12418
12419     // do not switch focus to non-existing (or non-active) player
12420     if (game.centered_player_nr_next >= 0 &&
12421         !stored_player[game.centered_player_nr_next].active)
12422     {
12423       game.centered_player_nr_next = game.centered_player_nr;
12424       game.set_centered_player = FALSE;
12425     }
12426   }
12427
12428   if (game.set_centered_player &&
12429       ScreenMovPos == 0)        // screen currently aligned at tile position
12430   {
12431     int sx, sy;
12432
12433     if (game.centered_player_nr_next == -1)
12434     {
12435       setScreenCenteredToAllPlayers(&sx, &sy);
12436     }
12437     else
12438     {
12439       sx = stored_player[game.centered_player_nr_next].jx;
12440       sy = stored_player[game.centered_player_nr_next].jy;
12441     }
12442
12443     game.centered_player_nr = game.centered_player_nr_next;
12444     game.set_centered_player = FALSE;
12445
12446     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12447     DrawGameDoorValues();
12448   }
12449
12450   // check single step mode (set flag and clear again if any player is active)
12451   game.enter_single_step_mode =
12452     (tape.single_step && tape.recording && !tape.pausing);
12453
12454   for (i = 0; i < MAX_PLAYERS; i++)
12455   {
12456     int actual_player_action = stored_player[i].effective_action;
12457
12458 #if 1
12459     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12460        - rnd_equinox_tetrachloride 048
12461        - rnd_equinox_tetrachloride_ii 096
12462        - rnd_emanuel_schmieg 002
12463        - doctor_sloan_ww 001, 020
12464     */
12465     if (stored_player[i].MovPos == 0)
12466       CheckGravityMovement(&stored_player[i]);
12467 #endif
12468
12469     // overwrite programmed action with tape action
12470     if (stored_player[i].programmed_action)
12471       actual_player_action = stored_player[i].programmed_action;
12472
12473     PlayerActions(&stored_player[i], actual_player_action);
12474
12475     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12476   }
12477
12478   // single step pause mode may already have been toggled by "ScrollPlayer()"
12479   if (game.enter_single_step_mode && !tape.pausing)
12480     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12481
12482   ScrollScreen(NULL, SCROLL_GO_ON);
12483
12484   /* for backwards compatibility, the following code emulates a fixed bug that
12485      occured when pushing elements (causing elements that just made their last
12486      pushing step to already (if possible) make their first falling step in the
12487      same game frame, which is bad); this code is also needed to use the famous
12488      "spring push bug" which is used in older levels and might be wanted to be
12489      used also in newer levels, but in this case the buggy pushing code is only
12490      affecting the "spring" element and no other elements */
12491
12492   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12493   {
12494     for (i = 0; i < MAX_PLAYERS; i++)
12495     {
12496       struct PlayerInfo *player = &stored_player[i];
12497       int x = player->jx;
12498       int y = player->jy;
12499
12500       if (player->active && player->is_pushing && player->is_moving &&
12501           IS_MOVING(x, y) &&
12502           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12503            Tile[x][y] == EL_SPRING))
12504       {
12505         ContinueMoving(x, y);
12506
12507         // continue moving after pushing (this is actually a bug)
12508         if (!IS_MOVING(x, y))
12509           Stop[x][y] = FALSE;
12510       }
12511     }
12512   }
12513
12514   SCAN_PLAYFIELD(x, y)
12515   {
12516     Last[x][y] = Tile[x][y];
12517
12518     ChangeCount[x][y] = 0;
12519     ChangeEvent[x][y] = -1;
12520
12521     // this must be handled before main playfield loop
12522     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12523     {
12524       MovDelay[x][y]--;
12525       if (MovDelay[x][y] <= 0)
12526         RemoveField(x, y);
12527     }
12528
12529     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12530     {
12531       MovDelay[x][y]--;
12532       if (MovDelay[x][y] <= 0)
12533       {
12534         int element = Store[x][y];
12535         int move_direction = MovDir[x][y];
12536         int player_index_bit = Store2[x][y];
12537
12538         Store[x][y] = 0;
12539         Store2[x][y] = 0;
12540
12541         RemoveField(x, y);
12542         TEST_DrawLevelField(x, y);
12543
12544         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12545
12546         if (IS_ENVELOPE(element))
12547           local_player->show_envelope = element;
12548       }
12549     }
12550
12551 #if DEBUG
12552     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12553     {
12554       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12555             x, y);
12556       Debug("game:playing:GameActions_RND", "This should never happen!");
12557
12558       ChangePage[x][y] = -1;
12559     }
12560 #endif
12561
12562     Stop[x][y] = FALSE;
12563     if (WasJustMoving[x][y] > 0)
12564       WasJustMoving[x][y]--;
12565     if (WasJustFalling[x][y] > 0)
12566       WasJustFalling[x][y]--;
12567     if (CheckCollision[x][y] > 0)
12568       CheckCollision[x][y]--;
12569     if (CheckImpact[x][y] > 0)
12570       CheckImpact[x][y]--;
12571
12572     GfxFrame[x][y]++;
12573
12574     /* reset finished pushing action (not done in ContinueMoving() to allow
12575        continuous pushing animation for elements with zero push delay) */
12576     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12577     {
12578       ResetGfxAnimation(x, y);
12579       TEST_DrawLevelField(x, y);
12580     }
12581
12582 #if DEBUG
12583     if (IS_BLOCKED(x, y))
12584     {
12585       int oldx, oldy;
12586
12587       Blocked2Moving(x, y, &oldx, &oldy);
12588       if (!IS_MOVING(oldx, oldy))
12589       {
12590         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12591         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12592         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12593         Debug("game:playing:GameActions_RND", "This should never happen!");
12594       }
12595     }
12596 #endif
12597   }
12598
12599   HandleMouseAction(&mouse_action, &mouse_action_last);
12600
12601   SCAN_PLAYFIELD(x, y)
12602   {
12603     element = Tile[x][y];
12604     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12605     last_gfx_frame = GfxFrame[x][y];
12606
12607     if (element == EL_EMPTY)
12608       graphic = el2img(GfxElementEmpty[x][y]);
12609
12610     ResetGfxFrame(x, y);
12611
12612     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12613       DrawLevelGraphicAnimation(x, y, graphic);
12614
12615     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12616         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12617       ResetRandomAnimationValue(x, y);
12618
12619     SetRandomAnimationValue(x, y);
12620
12621     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12622
12623     if (IS_INACTIVE(element))
12624     {
12625       if (IS_ANIMATED(graphic))
12626         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12627
12628       continue;
12629     }
12630
12631     // this may take place after moving, so 'element' may have changed
12632     if (IS_CHANGING(x, y) &&
12633         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12634     {
12635       int page = element_info[element].event_page_nr[CE_DELAY];
12636
12637       HandleElementChange(x, y, page);
12638
12639       element = Tile[x][y];
12640       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12641     }
12642
12643     CheckNextToConditions(x, y);
12644
12645     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12646     {
12647       StartMoving(x, y);
12648
12649       element = Tile[x][y];
12650       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12651
12652       if (IS_ANIMATED(graphic) &&
12653           !IS_MOVING(x, y) &&
12654           !Stop[x][y])
12655         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12656
12657       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12658         TEST_DrawTwinkleOnField(x, y);
12659     }
12660     else if (element == EL_ACID)
12661     {
12662       if (!Stop[x][y])
12663         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12664     }
12665     else if ((element == EL_EXIT_OPEN ||
12666               element == EL_EM_EXIT_OPEN ||
12667               element == EL_SP_EXIT_OPEN ||
12668               element == EL_STEEL_EXIT_OPEN ||
12669               element == EL_EM_STEEL_EXIT_OPEN ||
12670               element == EL_SP_TERMINAL ||
12671               element == EL_SP_TERMINAL_ACTIVE ||
12672               element == EL_EXTRA_TIME ||
12673               element == EL_SHIELD_NORMAL ||
12674               element == EL_SHIELD_DEADLY) &&
12675              IS_ANIMATED(graphic))
12676       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12677     else if (IS_MOVING(x, y))
12678       ContinueMoving(x, y);
12679     else if (IS_ACTIVE_BOMB(element))
12680       CheckDynamite(x, y);
12681     else if (element == EL_AMOEBA_GROWING)
12682       AmoebaGrowing(x, y);
12683     else if (element == EL_AMOEBA_SHRINKING)
12684       AmoebaShrinking(x, y);
12685
12686 #if !USE_NEW_AMOEBA_CODE
12687     else if (IS_AMOEBALIVE(element))
12688       AmoebaReproduce(x, y);
12689 #endif
12690
12691     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12692       Life(x, y);
12693     else if (element == EL_EXIT_CLOSED)
12694       CheckExit(x, y);
12695     else if (element == EL_EM_EXIT_CLOSED)
12696       CheckExitEM(x, y);
12697     else if (element == EL_STEEL_EXIT_CLOSED)
12698       CheckExitSteel(x, y);
12699     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12700       CheckExitSteelEM(x, y);
12701     else if (element == EL_SP_EXIT_CLOSED)
12702       CheckExitSP(x, y);
12703     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12704              element == EL_EXPANDABLE_STEELWALL_GROWING)
12705       WallGrowing(x, y);
12706     else if (element == EL_EXPANDABLE_WALL ||
12707              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12708              element == EL_EXPANDABLE_WALL_VERTICAL ||
12709              element == EL_EXPANDABLE_WALL_ANY ||
12710              element == EL_BD_EXPANDABLE_WALL ||
12711              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12712              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12713              element == EL_EXPANDABLE_STEELWALL_ANY)
12714       CheckWallGrowing(x, y);
12715     else if (element == EL_FLAMES)
12716       CheckForDragon(x, y);
12717     else if (element == EL_EXPLOSION)
12718       ; // drawing of correct explosion animation is handled separately
12719     else if (element == EL_ELEMENT_SNAPPING ||
12720              element == EL_DIAGONAL_SHRINKING ||
12721              element == EL_DIAGONAL_GROWING)
12722     {
12723       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12724
12725       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12726     }
12727     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12728       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12729
12730     if (IS_BELT_ACTIVE(element))
12731       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12732
12733     if (game.magic_wall_active)
12734     {
12735       int jx = local_player->jx, jy = local_player->jy;
12736
12737       // play the element sound at the position nearest to the player
12738       if ((element == EL_MAGIC_WALL_FULL ||
12739            element == EL_MAGIC_WALL_ACTIVE ||
12740            element == EL_MAGIC_WALL_EMPTYING ||
12741            element == EL_BD_MAGIC_WALL_FULL ||
12742            element == EL_BD_MAGIC_WALL_ACTIVE ||
12743            element == EL_BD_MAGIC_WALL_EMPTYING ||
12744            element == EL_DC_MAGIC_WALL_FULL ||
12745            element == EL_DC_MAGIC_WALL_ACTIVE ||
12746            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12747           ABS(x - jx) + ABS(y - jy) <
12748           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12749       {
12750         magic_wall_x = x;
12751         magic_wall_y = y;
12752       }
12753     }
12754   }
12755
12756 #if USE_NEW_AMOEBA_CODE
12757   // new experimental amoeba growth stuff
12758   if (!(FrameCounter % 8))
12759   {
12760     static unsigned int random = 1684108901;
12761
12762     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12763     {
12764       x = RND(lev_fieldx);
12765       y = RND(lev_fieldy);
12766       element = Tile[x][y];
12767
12768       if (!IS_PLAYER(x, y) &&
12769           (element == EL_EMPTY ||
12770            CAN_GROW_INTO(element) ||
12771            element == EL_QUICKSAND_EMPTY ||
12772            element == EL_QUICKSAND_FAST_EMPTY ||
12773            element == EL_ACID_SPLASH_LEFT ||
12774            element == EL_ACID_SPLASH_RIGHT))
12775       {
12776         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12777             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12778             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12779             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12780           Tile[x][y] = EL_AMOEBA_DROP;
12781       }
12782
12783       random = random * 129 + 1;
12784     }
12785   }
12786 #endif
12787
12788   game.explosions_delayed = FALSE;
12789
12790   SCAN_PLAYFIELD(x, y)
12791   {
12792     element = Tile[x][y];
12793
12794     if (ExplodeField[x][y])
12795       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12796     else if (element == EL_EXPLOSION)
12797       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12798
12799     ExplodeField[x][y] = EX_TYPE_NONE;
12800   }
12801
12802   game.explosions_delayed = TRUE;
12803
12804   if (game.magic_wall_active)
12805   {
12806     if (!(game.magic_wall_time_left % 4))
12807     {
12808       int element = Tile[magic_wall_x][magic_wall_y];
12809
12810       if (element == EL_BD_MAGIC_WALL_FULL ||
12811           element == EL_BD_MAGIC_WALL_ACTIVE ||
12812           element == EL_BD_MAGIC_WALL_EMPTYING)
12813         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12814       else if (element == EL_DC_MAGIC_WALL_FULL ||
12815                element == EL_DC_MAGIC_WALL_ACTIVE ||
12816                element == EL_DC_MAGIC_WALL_EMPTYING)
12817         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12818       else
12819         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12820     }
12821
12822     if (game.magic_wall_time_left > 0)
12823     {
12824       game.magic_wall_time_left--;
12825
12826       if (!game.magic_wall_time_left)
12827       {
12828         SCAN_PLAYFIELD(x, y)
12829         {
12830           element = Tile[x][y];
12831
12832           if (element == EL_MAGIC_WALL_ACTIVE ||
12833               element == EL_MAGIC_WALL_FULL)
12834           {
12835             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12836             TEST_DrawLevelField(x, y);
12837           }
12838           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12839                    element == EL_BD_MAGIC_WALL_FULL)
12840           {
12841             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12842             TEST_DrawLevelField(x, y);
12843           }
12844           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12845                    element == EL_DC_MAGIC_WALL_FULL)
12846           {
12847             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12848             TEST_DrawLevelField(x, y);
12849           }
12850         }
12851
12852         game.magic_wall_active = FALSE;
12853       }
12854     }
12855   }
12856
12857   if (game.light_time_left > 0)
12858   {
12859     game.light_time_left--;
12860
12861     if (game.light_time_left == 0)
12862       RedrawAllLightSwitchesAndInvisibleElements();
12863   }
12864
12865   if (game.timegate_time_left > 0)
12866   {
12867     game.timegate_time_left--;
12868
12869     if (game.timegate_time_left == 0)
12870       CloseAllOpenTimegates();
12871   }
12872
12873   if (game.lenses_time_left > 0)
12874   {
12875     game.lenses_time_left--;
12876
12877     if (game.lenses_time_left == 0)
12878       RedrawAllInvisibleElementsForLenses();
12879   }
12880
12881   if (game.magnify_time_left > 0)
12882   {
12883     game.magnify_time_left--;
12884
12885     if (game.magnify_time_left == 0)
12886       RedrawAllInvisibleElementsForMagnifier();
12887   }
12888
12889   for (i = 0; i < MAX_PLAYERS; i++)
12890   {
12891     struct PlayerInfo *player = &stored_player[i];
12892
12893     if (SHIELD_ON(player))
12894     {
12895       if (player->shield_deadly_time_left)
12896         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12897       else if (player->shield_normal_time_left)
12898         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12899     }
12900   }
12901
12902 #if USE_DELAYED_GFX_REDRAW
12903   SCAN_PLAYFIELD(x, y)
12904   {
12905     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12906     {
12907       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12908          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12909
12910       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12911         DrawLevelField(x, y);
12912
12913       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12914         DrawLevelFieldCrumbled(x, y);
12915
12916       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12917         DrawLevelFieldCrumbledNeighbours(x, y);
12918
12919       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12920         DrawTwinkleOnField(x, y);
12921     }
12922
12923     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12924   }
12925 #endif
12926
12927   DrawAllPlayers();
12928   PlayAllPlayersSound();
12929
12930   for (i = 0; i < MAX_PLAYERS; i++)
12931   {
12932     struct PlayerInfo *player = &stored_player[i];
12933
12934     if (player->show_envelope != 0 && (!player->active ||
12935                                        player->MovPos == 0))
12936     {
12937       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12938
12939       player->show_envelope = 0;
12940     }
12941   }
12942
12943   // use random number generator in every frame to make it less predictable
12944   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12945     RND(1);
12946
12947   mouse_action_last = mouse_action;
12948 }
12949
12950 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12951 {
12952   int min_x = x, min_y = y, max_x = x, max_y = y;
12953   int scr_fieldx = getScreenFieldSizeX();
12954   int scr_fieldy = getScreenFieldSizeY();
12955   int i;
12956
12957   for (i = 0; i < MAX_PLAYERS; i++)
12958   {
12959     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12960
12961     if (!stored_player[i].active || &stored_player[i] == player)
12962       continue;
12963
12964     min_x = MIN(min_x, jx);
12965     min_y = MIN(min_y, jy);
12966     max_x = MAX(max_x, jx);
12967     max_y = MAX(max_y, jy);
12968   }
12969
12970   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12971 }
12972
12973 static boolean AllPlayersInVisibleScreen(void)
12974 {
12975   int i;
12976
12977   for (i = 0; i < MAX_PLAYERS; i++)
12978   {
12979     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12980
12981     if (!stored_player[i].active)
12982       continue;
12983
12984     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12985       return FALSE;
12986   }
12987
12988   return TRUE;
12989 }
12990
12991 void ScrollLevel(int dx, int dy)
12992 {
12993   int scroll_offset = 2 * TILEX_VAR;
12994   int x, y;
12995
12996   BlitBitmap(drawto_field, drawto_field,
12997              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12998              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12999              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
13000              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
13001              FX + TILEX_VAR * (dx == 1) - scroll_offset,
13002              FY + TILEY_VAR * (dy == 1) - scroll_offset);
13003
13004   if (dx != 0)
13005   {
13006     x = (dx == 1 ? BX1 : BX2);
13007     for (y = BY1; y <= BY2; y++)
13008       DrawScreenField(x, y);
13009   }
13010
13011   if (dy != 0)
13012   {
13013     y = (dy == 1 ? BY1 : BY2);
13014     for (x = BX1; x <= BX2; x++)
13015       DrawScreenField(x, y);
13016   }
13017
13018   redraw_mask |= REDRAW_FIELD;
13019 }
13020
13021 static boolean canFallDown(struct PlayerInfo *player)
13022 {
13023   int jx = player->jx, jy = player->jy;
13024
13025   return (IN_LEV_FIELD(jx, jy + 1) &&
13026           (IS_FREE(jx, jy + 1) ||
13027            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13028           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13029           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13030 }
13031
13032 static boolean canPassField(int x, int y, int move_dir)
13033 {
13034   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13035   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13036   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13037   int nextx = x + dx;
13038   int nexty = y + dy;
13039   int element = Tile[x][y];
13040
13041   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13042           !CAN_MOVE(element) &&
13043           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13044           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13045           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13046 }
13047
13048 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13049 {
13050   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13051   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13052   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13053   int newx = x + dx;
13054   int newy = y + dy;
13055
13056   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13057           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13058           (IS_DIGGABLE(Tile[newx][newy]) ||
13059            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13060            canPassField(newx, newy, move_dir)));
13061 }
13062
13063 static void CheckGravityMovement(struct PlayerInfo *player)
13064 {
13065   if (player->gravity && !player->programmed_action)
13066   {
13067     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13068     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13069     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13070     int jx = player->jx, jy = player->jy;
13071     boolean player_is_moving_to_valid_field =
13072       (!player_is_snapping &&
13073        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13074         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13075     boolean player_can_fall_down = canFallDown(player);
13076
13077     if (player_can_fall_down &&
13078         !player_is_moving_to_valid_field)
13079       player->programmed_action = MV_DOWN;
13080   }
13081 }
13082
13083 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13084 {
13085   return CheckGravityMovement(player);
13086
13087   if (player->gravity && !player->programmed_action)
13088   {
13089     int jx = player->jx, jy = player->jy;
13090     boolean field_under_player_is_free =
13091       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13092     boolean player_is_standing_on_valid_field =
13093       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13094        (IS_WALKABLE(Tile[jx][jy]) &&
13095         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13096
13097     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13098       player->programmed_action = MV_DOWN;
13099   }
13100 }
13101
13102 /*
13103   MovePlayerOneStep()
13104   -----------------------------------------------------------------------------
13105   dx, dy:               direction (non-diagonal) to try to move the player to
13106   real_dx, real_dy:     direction as read from input device (can be diagonal)
13107 */
13108
13109 boolean MovePlayerOneStep(struct PlayerInfo *player,
13110                           int dx, int dy, int real_dx, int real_dy)
13111 {
13112   int jx = player->jx, jy = player->jy;
13113   int new_jx = jx + dx, new_jy = jy + dy;
13114   int can_move;
13115   boolean player_can_move = !player->cannot_move;
13116
13117   if (!player->active || (!dx && !dy))
13118     return MP_NO_ACTION;
13119
13120   player->MovDir = (dx < 0 ? MV_LEFT :
13121                     dx > 0 ? MV_RIGHT :
13122                     dy < 0 ? MV_UP :
13123                     dy > 0 ? MV_DOWN :  MV_NONE);
13124
13125   if (!IN_LEV_FIELD(new_jx, new_jy))
13126     return MP_NO_ACTION;
13127
13128   if (!player_can_move)
13129   {
13130     if (player->MovPos == 0)
13131     {
13132       player->is_moving = FALSE;
13133       player->is_digging = FALSE;
13134       player->is_collecting = FALSE;
13135       player->is_snapping = FALSE;
13136       player->is_pushing = FALSE;
13137     }
13138   }
13139
13140   if (!network.enabled && game.centered_player_nr == -1 &&
13141       !AllPlayersInSight(player, new_jx, new_jy))
13142     return MP_NO_ACTION;
13143
13144   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13145   if (can_move != MP_MOVING)
13146     return can_move;
13147
13148   // check if DigField() has caused relocation of the player
13149   if (player->jx != jx || player->jy != jy)
13150     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13151
13152   StorePlayer[jx][jy] = 0;
13153   player->last_jx = jx;
13154   player->last_jy = jy;
13155   player->jx = new_jx;
13156   player->jy = new_jy;
13157   StorePlayer[new_jx][new_jy] = player->element_nr;
13158
13159   if (player->move_delay_value_next != -1)
13160   {
13161     player->move_delay_value = player->move_delay_value_next;
13162     player->move_delay_value_next = -1;
13163   }
13164
13165   player->MovPos =
13166     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13167
13168   player->step_counter++;
13169
13170   PlayerVisit[jx][jy] = FrameCounter;
13171
13172   player->is_moving = TRUE;
13173
13174 #if 1
13175   // should better be called in MovePlayer(), but this breaks some tapes
13176   ScrollPlayer(player, SCROLL_INIT);
13177 #endif
13178
13179   return MP_MOVING;
13180 }
13181
13182 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13183 {
13184   int jx = player->jx, jy = player->jy;
13185   int old_jx = jx, old_jy = jy;
13186   int moved = MP_NO_ACTION;
13187
13188   if (!player->active)
13189     return FALSE;
13190
13191   if (!dx && !dy)
13192   {
13193     if (player->MovPos == 0)
13194     {
13195       player->is_moving = FALSE;
13196       player->is_digging = FALSE;
13197       player->is_collecting = FALSE;
13198       player->is_snapping = FALSE;
13199       player->is_pushing = FALSE;
13200     }
13201
13202     return FALSE;
13203   }
13204
13205   if (player->move_delay > 0)
13206     return FALSE;
13207
13208   player->move_delay = -1;              // set to "uninitialized" value
13209
13210   // store if player is automatically moved to next field
13211   player->is_auto_moving = (player->programmed_action != MV_NONE);
13212
13213   // remove the last programmed player action
13214   player->programmed_action = 0;
13215
13216   if (player->MovPos)
13217   {
13218     // should only happen if pre-1.2 tape recordings are played
13219     // this is only for backward compatibility
13220
13221     int original_move_delay_value = player->move_delay_value;
13222
13223 #if DEBUG
13224     Debug("game:playing:MovePlayer",
13225           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13226           tape.counter);
13227 #endif
13228
13229     // scroll remaining steps with finest movement resolution
13230     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13231
13232     while (player->MovPos)
13233     {
13234       ScrollPlayer(player, SCROLL_GO_ON);
13235       ScrollScreen(NULL, SCROLL_GO_ON);
13236
13237       AdvanceFrameAndPlayerCounters(player->index_nr);
13238
13239       DrawAllPlayers();
13240       BackToFront_WithFrameDelay(0);
13241     }
13242
13243     player->move_delay_value = original_move_delay_value;
13244   }
13245
13246   player->is_active = FALSE;
13247
13248   if (player->last_move_dir & MV_HORIZONTAL)
13249   {
13250     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13251       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13252   }
13253   else
13254   {
13255     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13256       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13257   }
13258
13259   if (!moved && !player->is_active)
13260   {
13261     player->is_moving = FALSE;
13262     player->is_digging = FALSE;
13263     player->is_collecting = FALSE;
13264     player->is_snapping = FALSE;
13265     player->is_pushing = FALSE;
13266   }
13267
13268   jx = player->jx;
13269   jy = player->jy;
13270
13271   if (moved & MP_MOVING && !ScreenMovPos &&
13272       (player->index_nr == game.centered_player_nr ||
13273        game.centered_player_nr == -1))
13274   {
13275     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13276
13277     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13278     {
13279       // actual player has left the screen -- scroll in that direction
13280       if (jx != old_jx)         // player has moved horizontally
13281         scroll_x += (jx - old_jx);
13282       else                      // player has moved vertically
13283         scroll_y += (jy - old_jy);
13284     }
13285     else
13286     {
13287       int offset_raw = game.scroll_delay_value;
13288
13289       if (jx != old_jx)         // player has moved horizontally
13290       {
13291         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13292         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13293         int new_scroll_x = jx - MIDPOSX + offset_x;
13294
13295         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13296             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13297           scroll_x = new_scroll_x;
13298
13299         // don't scroll over playfield boundaries
13300         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13301
13302         // don't scroll more than one field at a time
13303         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13304
13305         // don't scroll against the player's moving direction
13306         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13307             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13308           scroll_x = old_scroll_x;
13309       }
13310       else                      // player has moved vertically
13311       {
13312         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13313         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13314         int new_scroll_y = jy - MIDPOSY + offset_y;
13315
13316         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13317             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13318           scroll_y = new_scroll_y;
13319
13320         // don't scroll over playfield boundaries
13321         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13322
13323         // don't scroll more than one field at a time
13324         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13325
13326         // don't scroll against the player's moving direction
13327         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13328             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13329           scroll_y = old_scroll_y;
13330       }
13331     }
13332
13333     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13334     {
13335       if (!network.enabled && game.centered_player_nr == -1 &&
13336           !AllPlayersInVisibleScreen())
13337       {
13338         scroll_x = old_scroll_x;
13339         scroll_y = old_scroll_y;
13340       }
13341       else
13342       {
13343         ScrollScreen(player, SCROLL_INIT);
13344         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13345       }
13346     }
13347   }
13348
13349   player->StepFrame = 0;
13350
13351   if (moved & MP_MOVING)
13352   {
13353     if (old_jx != jx && old_jy == jy)
13354       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13355     else if (old_jx == jx && old_jy != jy)
13356       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13357
13358     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13359
13360     player->last_move_dir = player->MovDir;
13361     player->is_moving = TRUE;
13362     player->is_snapping = FALSE;
13363     player->is_switching = FALSE;
13364     player->is_dropping = FALSE;
13365     player->is_dropping_pressed = FALSE;
13366     player->drop_pressed_delay = 0;
13367
13368 #if 0
13369     // should better be called here than above, but this breaks some tapes
13370     ScrollPlayer(player, SCROLL_INIT);
13371 #endif
13372   }
13373   else
13374   {
13375     CheckGravityMovementWhenNotMoving(player);
13376
13377     player->is_moving = FALSE;
13378
13379     /* at this point, the player is allowed to move, but cannot move right now
13380        (e.g. because of something blocking the way) -- ensure that the player
13381        is also allowed to move in the next frame (in old versions before 3.1.1,
13382        the player was forced to wait again for eight frames before next try) */
13383
13384     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13385       player->move_delay = 0;   // allow direct movement in the next frame
13386   }
13387
13388   if (player->move_delay == -1)         // not yet initialized by DigField()
13389     player->move_delay = player->move_delay_value;
13390
13391   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13392   {
13393     TestIfPlayerTouchesBadThing(jx, jy);
13394     TestIfPlayerTouchesCustomElement(jx, jy);
13395   }
13396
13397   if (!player->active)
13398     RemovePlayer(player);
13399
13400   return moved;
13401 }
13402
13403 void ScrollPlayer(struct PlayerInfo *player, int mode)
13404 {
13405   int jx = player->jx, jy = player->jy;
13406   int last_jx = player->last_jx, last_jy = player->last_jy;
13407   int move_stepsize = TILEX / player->move_delay_value;
13408
13409   if (!player->active)
13410     return;
13411
13412   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13413     return;
13414
13415   if (mode == SCROLL_INIT)
13416   {
13417     player->actual_frame_counter.count = FrameCounter;
13418     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13419
13420     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13421         Tile[last_jx][last_jy] == EL_EMPTY)
13422     {
13423       int last_field_block_delay = 0;   // start with no blocking at all
13424       int block_delay_adjustment = player->block_delay_adjustment;
13425
13426       // if player blocks last field, add delay for exactly one move
13427       if (player->block_last_field)
13428       {
13429         last_field_block_delay += player->move_delay_value;
13430
13431         // when blocking enabled, prevent moving up despite gravity
13432         if (player->gravity && player->MovDir == MV_UP)
13433           block_delay_adjustment = -1;
13434       }
13435
13436       // add block delay adjustment (also possible when not blocking)
13437       last_field_block_delay += block_delay_adjustment;
13438
13439       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13440       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13441     }
13442
13443     if (player->MovPos != 0)    // player has not yet reached destination
13444       return;
13445   }
13446   else if (!FrameReached(&player->actual_frame_counter))
13447     return;
13448
13449   if (player->MovPos != 0)
13450   {
13451     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13452     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13453
13454     // before DrawPlayer() to draw correct player graphic for this case
13455     if (player->MovPos == 0)
13456       CheckGravityMovement(player);
13457   }
13458
13459   if (player->MovPos == 0)      // player reached destination field
13460   {
13461     if (player->move_delay_reset_counter > 0)
13462     {
13463       player->move_delay_reset_counter--;
13464
13465       if (player->move_delay_reset_counter == 0)
13466       {
13467         // continue with normal speed after quickly moving through gate
13468         HALVE_PLAYER_SPEED(player);
13469
13470         // be able to make the next move without delay
13471         player->move_delay = 0;
13472       }
13473     }
13474
13475     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13476         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13477         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13478         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13479         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13480         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13481         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13482         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13483     {
13484       ExitPlayer(player);
13485
13486       if (game.players_still_needed == 0 &&
13487           (game.friends_still_needed == 0 ||
13488            IS_SP_ELEMENT(Tile[jx][jy])))
13489         LevelSolved();
13490     }
13491
13492     player->last_jx = jx;
13493     player->last_jy = jy;
13494
13495     // this breaks one level: "machine", level 000
13496     {
13497       int move_direction = player->MovDir;
13498       int enter_side = MV_DIR_OPPOSITE(move_direction);
13499       int leave_side = move_direction;
13500       int old_jx = last_jx;
13501       int old_jy = last_jy;
13502       int old_element = Tile[old_jx][old_jy];
13503       int new_element = Tile[jx][jy];
13504
13505       if (IS_CUSTOM_ELEMENT(old_element))
13506         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13507                                    CE_LEFT_BY_PLAYER,
13508                                    player->index_bit, leave_side);
13509
13510       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13511                                           CE_PLAYER_LEAVES_X,
13512                                           player->index_bit, leave_side);
13513
13514       // needed because pushed element has not yet reached its destination,
13515       // so it would trigger a change event at its previous field location
13516       if (!player->is_pushing)
13517       {
13518         if (IS_CUSTOM_ELEMENT(new_element))
13519           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13520                                      player->index_bit, enter_side);
13521
13522         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13523                                             CE_PLAYER_ENTERS_X,
13524                                             player->index_bit, enter_side);
13525       }
13526
13527       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13528                                         CE_MOVE_OF_X, move_direction);
13529     }
13530
13531     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13532     {
13533       TestIfPlayerTouchesBadThing(jx, jy);
13534       TestIfPlayerTouchesCustomElement(jx, jy);
13535
13536       // needed because pushed element has not yet reached its destination,
13537       // so it would trigger a change event at its previous field location
13538       if (!player->is_pushing)
13539         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13540
13541       if (level.finish_dig_collect &&
13542           (player->is_digging || player->is_collecting))
13543       {
13544         int last_element = player->last_removed_element;
13545         int move_direction = player->MovDir;
13546         int enter_side = MV_DIR_OPPOSITE(move_direction);
13547         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13548                             CE_PLAYER_COLLECTS_X);
13549
13550         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13551                                             player->index_bit, enter_side);
13552
13553         player->last_removed_element = EL_UNDEFINED;
13554       }
13555
13556       if (!player->active)
13557         RemovePlayer(player);
13558     }
13559
13560     if (level.use_step_counter)
13561       CheckLevelTime_StepCounter();
13562
13563     if (tape.single_step && tape.recording && !tape.pausing &&
13564         !player->programmed_action)
13565       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13566
13567     if (!player->programmed_action)
13568       CheckSaveEngineSnapshot(player);
13569   }
13570 }
13571
13572 void ScrollScreen(struct PlayerInfo *player, int mode)
13573 {
13574   static DelayCounter screen_frame_counter = { 0 };
13575
13576   if (mode == SCROLL_INIT)
13577   {
13578     // set scrolling step size according to actual player's moving speed
13579     ScrollStepSize = TILEX / player->move_delay_value;
13580
13581     screen_frame_counter.count = FrameCounter;
13582     screen_frame_counter.value = 1;
13583
13584     ScreenMovDir = player->MovDir;
13585     ScreenMovPos = player->MovPos;
13586     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13587     return;
13588   }
13589   else if (!FrameReached(&screen_frame_counter))
13590     return;
13591
13592   if (ScreenMovPos)
13593   {
13594     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13595     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13596     redraw_mask |= REDRAW_FIELD;
13597   }
13598   else
13599     ScreenMovDir = MV_NONE;
13600 }
13601
13602 void CheckNextToConditions(int x, int y)
13603 {
13604   int element = Tile[x][y];
13605
13606   if (IS_PLAYER(x, y))
13607     TestIfPlayerNextToCustomElement(x, y);
13608
13609   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13610       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13611     TestIfElementNextToCustomElement(x, y);
13612 }
13613
13614 void TestIfPlayerNextToCustomElement(int x, int y)
13615 {
13616   struct XY *xy = xy_topdown;
13617   static int trigger_sides[4][2] =
13618   {
13619     // center side       border side
13620     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13621     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13622     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13623     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13624   };
13625   int i;
13626
13627   if (!IS_PLAYER(x, y))
13628     return;
13629
13630   struct PlayerInfo *player = PLAYERINFO(x, y);
13631
13632   if (player->is_moving)
13633     return;
13634
13635   for (i = 0; i < NUM_DIRECTIONS; i++)
13636   {
13637     int xx = x + xy[i].x;
13638     int yy = y + xy[i].y;
13639     int border_side = trigger_sides[i][1];
13640     int border_element;
13641
13642     if (!IN_LEV_FIELD(xx, yy))
13643       continue;
13644
13645     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13646       continue;         // center and border element not connected
13647
13648     border_element = Tile[xx][yy];
13649
13650     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13651                                player->index_bit, border_side);
13652     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13653                                         CE_PLAYER_NEXT_TO_X,
13654                                         player->index_bit, border_side);
13655
13656     /* use player element that is initially defined in the level playfield,
13657        not the player element that corresponds to the runtime player number
13658        (example: a level that contains EL_PLAYER_3 as the only player would
13659        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13660
13661     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13662                              CE_NEXT_TO_X, border_side);
13663   }
13664 }
13665
13666 void TestIfPlayerTouchesCustomElement(int x, int y)
13667 {
13668   struct XY *xy = xy_topdown;
13669   static int trigger_sides[4][2] =
13670   {
13671     // center side       border side
13672     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13673     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13674     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13675     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13676   };
13677   static int touch_dir[4] =
13678   {
13679     MV_LEFT | MV_RIGHT,
13680     MV_UP   | MV_DOWN,
13681     MV_UP   | MV_DOWN,
13682     MV_LEFT | MV_RIGHT
13683   };
13684   int center_element = Tile[x][y];      // should always be non-moving!
13685   int i;
13686
13687   for (i = 0; i < NUM_DIRECTIONS; i++)
13688   {
13689     int xx = x + xy[i].x;
13690     int yy = y + xy[i].y;
13691     int center_side = trigger_sides[i][0];
13692     int border_side = trigger_sides[i][1];
13693     int border_element;
13694
13695     if (!IN_LEV_FIELD(xx, yy))
13696       continue;
13697
13698     if (IS_PLAYER(x, y))                // player found at center element
13699     {
13700       struct PlayerInfo *player = PLAYERINFO(x, y);
13701
13702       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13703         border_element = Tile[xx][yy];          // may be moving!
13704       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13705         border_element = Tile[xx][yy];
13706       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13707         border_element = MovingOrBlocked2Element(xx, yy);
13708       else
13709         continue;               // center and border element do not touch
13710
13711       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13712                                  player->index_bit, border_side);
13713       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13714                                           CE_PLAYER_TOUCHES_X,
13715                                           player->index_bit, border_side);
13716
13717       {
13718         /* use player element that is initially defined in the level playfield,
13719            not the player element that corresponds to the runtime player number
13720            (example: a level that contains EL_PLAYER_3 as the only player would
13721            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13722         int player_element = PLAYERINFO(x, y)->initial_element;
13723
13724         // as element "X" is the player here, check opposite (center) side
13725         CheckElementChangeBySide(xx, yy, border_element, player_element,
13726                                  CE_TOUCHING_X, center_side);
13727       }
13728     }
13729     else if (IS_PLAYER(xx, yy))         // player found at border element
13730     {
13731       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13732
13733       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13734       {
13735         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13736           continue;             // center and border element do not touch
13737       }
13738
13739       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13740                                  player->index_bit, center_side);
13741       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13742                                           CE_PLAYER_TOUCHES_X,
13743                                           player->index_bit, center_side);
13744
13745       {
13746         /* use player element that is initially defined in the level playfield,
13747            not the player element that corresponds to the runtime player number
13748            (example: a level that contains EL_PLAYER_3 as the only player would
13749            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13750         int player_element = PLAYERINFO(xx, yy)->initial_element;
13751
13752         // as element "X" is the player here, check opposite (border) side
13753         CheckElementChangeBySide(x, y, center_element, player_element,
13754                                  CE_TOUCHING_X, border_side);
13755       }
13756
13757       break;
13758     }
13759   }
13760 }
13761
13762 void TestIfElementNextToCustomElement(int x, int y)
13763 {
13764   struct XY *xy = xy_topdown;
13765   static int trigger_sides[4][2] =
13766   {
13767     // center side      border side
13768     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13769     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13770     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13771     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13772   };
13773   int center_element = Tile[x][y];      // should always be non-moving!
13774   int i;
13775
13776   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13777     return;
13778
13779   for (i = 0; i < NUM_DIRECTIONS; i++)
13780   {
13781     int xx = x + xy[i].x;
13782     int yy = y + xy[i].y;
13783     int border_side = trigger_sides[i][1];
13784     int border_element;
13785
13786     if (!IN_LEV_FIELD(xx, yy))
13787       continue;
13788
13789     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13790       continue;                 // center and border element not connected
13791
13792     border_element = Tile[xx][yy];
13793
13794     // check for change of center element (but change it only once)
13795     if (CheckElementChangeBySide(x, y, center_element, border_element,
13796                                  CE_NEXT_TO_X, border_side))
13797       break;
13798   }
13799 }
13800
13801 void TestIfElementTouchesCustomElement(int x, int y)
13802 {
13803   struct XY *xy = xy_topdown;
13804   static int trigger_sides[4][2] =
13805   {
13806     // center side      border side
13807     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13808     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13809     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13810     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13811   };
13812   static int touch_dir[4] =
13813   {
13814     MV_LEFT | MV_RIGHT,
13815     MV_UP   | MV_DOWN,
13816     MV_UP   | MV_DOWN,
13817     MV_LEFT | MV_RIGHT
13818   };
13819   boolean change_center_element = FALSE;
13820   int center_element = Tile[x][y];      // should always be non-moving!
13821   int border_element_old[NUM_DIRECTIONS];
13822   int i;
13823
13824   for (i = 0; i < NUM_DIRECTIONS; i++)
13825   {
13826     int xx = x + xy[i].x;
13827     int yy = y + xy[i].y;
13828     int border_element;
13829
13830     border_element_old[i] = -1;
13831
13832     if (!IN_LEV_FIELD(xx, yy))
13833       continue;
13834
13835     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13836       border_element = Tile[xx][yy];    // may be moving!
13837     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13838       border_element = Tile[xx][yy];
13839     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13840       border_element = MovingOrBlocked2Element(xx, yy);
13841     else
13842       continue;                 // center and border element do not touch
13843
13844     border_element_old[i] = border_element;
13845   }
13846
13847   for (i = 0; i < NUM_DIRECTIONS; i++)
13848   {
13849     int xx = x + xy[i].x;
13850     int yy = y + xy[i].y;
13851     int center_side = trigger_sides[i][0];
13852     int border_element = border_element_old[i];
13853
13854     if (border_element == -1)
13855       continue;
13856
13857     // check for change of border element
13858     CheckElementChangeBySide(xx, yy, border_element, center_element,
13859                              CE_TOUCHING_X, center_side);
13860
13861     // (center element cannot be player, so we don't have to check this here)
13862   }
13863
13864   for (i = 0; i < NUM_DIRECTIONS; i++)
13865   {
13866     int xx = x + xy[i].x;
13867     int yy = y + xy[i].y;
13868     int border_side = trigger_sides[i][1];
13869     int border_element = border_element_old[i];
13870
13871     if (border_element == -1)
13872       continue;
13873
13874     // check for change of center element (but change it only once)
13875     if (!change_center_element)
13876       change_center_element =
13877         CheckElementChangeBySide(x, y, center_element, border_element,
13878                                  CE_TOUCHING_X, border_side);
13879
13880     if (IS_PLAYER(xx, yy))
13881     {
13882       /* use player element that is initially defined in the level playfield,
13883          not the player element that corresponds to the runtime player number
13884          (example: a level that contains EL_PLAYER_3 as the only player would
13885          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13886       int player_element = PLAYERINFO(xx, yy)->initial_element;
13887
13888       // as element "X" is the player here, check opposite (border) side
13889       CheckElementChangeBySide(x, y, center_element, player_element,
13890                                CE_TOUCHING_X, border_side);
13891     }
13892   }
13893 }
13894
13895 void TestIfElementHitsCustomElement(int x, int y, int direction)
13896 {
13897   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13898   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13899   int hitx = x + dx, hity = y + dy;
13900   int hitting_element = Tile[x][y];
13901   int touched_element;
13902
13903   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13904     return;
13905
13906   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13907                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13908
13909   if (IN_LEV_FIELD(hitx, hity))
13910   {
13911     int opposite_direction = MV_DIR_OPPOSITE(direction);
13912     int hitting_side = direction;
13913     int touched_side = opposite_direction;
13914     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13915                           MovDir[hitx][hity] != direction ||
13916                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13917
13918     object_hit = TRUE;
13919
13920     if (object_hit)
13921     {
13922       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13923                                CE_HITTING_X, touched_side);
13924
13925       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13926                                CE_HIT_BY_X, hitting_side);
13927
13928       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13929                                CE_HIT_BY_SOMETHING, opposite_direction);
13930
13931       if (IS_PLAYER(hitx, hity))
13932       {
13933         /* use player element that is initially defined in the level playfield,
13934            not the player element that corresponds to the runtime player number
13935            (example: a level that contains EL_PLAYER_3 as the only player would
13936            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13937         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13938
13939         CheckElementChangeBySide(x, y, hitting_element, player_element,
13940                                  CE_HITTING_X, touched_side);
13941       }
13942     }
13943   }
13944
13945   // "hitting something" is also true when hitting the playfield border
13946   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13947                            CE_HITTING_SOMETHING, direction);
13948 }
13949
13950 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13951 {
13952   int i, kill_x = -1, kill_y = -1;
13953
13954   int bad_element = -1;
13955   struct XY *test_xy = xy_topdown;
13956   static int test_dir[4] =
13957   {
13958     MV_UP,
13959     MV_LEFT,
13960     MV_RIGHT,
13961     MV_DOWN
13962   };
13963
13964   for (i = 0; i < NUM_DIRECTIONS; i++)
13965   {
13966     int test_x, test_y, test_move_dir, test_element;
13967
13968     test_x = good_x + test_xy[i].x;
13969     test_y = good_y + test_xy[i].y;
13970
13971     if (!IN_LEV_FIELD(test_x, test_y))
13972       continue;
13973
13974     test_move_dir =
13975       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13976
13977     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13978
13979     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13980        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13981     */
13982     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13983         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13984     {
13985       kill_x = test_x;
13986       kill_y = test_y;
13987       bad_element = test_element;
13988
13989       break;
13990     }
13991   }
13992
13993   if (kill_x != -1 || kill_y != -1)
13994   {
13995     if (IS_PLAYER(good_x, good_y))
13996     {
13997       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13998
13999       if (player->shield_deadly_time_left > 0 &&
14000           !IS_INDESTRUCTIBLE(bad_element))
14001         Bang(kill_x, kill_y);
14002       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
14003         KillPlayer(player);
14004     }
14005     else
14006       Bang(good_x, good_y);
14007   }
14008 }
14009
14010 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14011 {
14012   int i, kill_x = -1, kill_y = -1;
14013   int bad_element = Tile[bad_x][bad_y];
14014   struct XY *test_xy = xy_topdown;
14015   static int touch_dir[4] =
14016   {
14017     MV_LEFT | MV_RIGHT,
14018     MV_UP   | MV_DOWN,
14019     MV_UP   | MV_DOWN,
14020     MV_LEFT | MV_RIGHT
14021   };
14022   static int test_dir[4] =
14023   {
14024     MV_UP,
14025     MV_LEFT,
14026     MV_RIGHT,
14027     MV_DOWN
14028   };
14029
14030   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14031     return;
14032
14033   for (i = 0; i < NUM_DIRECTIONS; i++)
14034   {
14035     int test_x, test_y, test_move_dir, test_element;
14036
14037     test_x = bad_x + test_xy[i].x;
14038     test_y = bad_y + test_xy[i].y;
14039
14040     if (!IN_LEV_FIELD(test_x, test_y))
14041       continue;
14042
14043     test_move_dir =
14044       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14045
14046     test_element = Tile[test_x][test_y];
14047
14048     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14049        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14050     */
14051     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14052         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14053     {
14054       // good thing is player or penguin that does not move away
14055       if (IS_PLAYER(test_x, test_y))
14056       {
14057         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14058
14059         if (bad_element == EL_ROBOT && player->is_moving)
14060           continue;     // robot does not kill player if he is moving
14061
14062         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14063         {
14064           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14065             continue;           // center and border element do not touch
14066         }
14067
14068         kill_x = test_x;
14069         kill_y = test_y;
14070
14071         break;
14072       }
14073       else if (test_element == EL_PENGUIN)
14074       {
14075         kill_x = test_x;
14076         kill_y = test_y;
14077
14078         break;
14079       }
14080     }
14081   }
14082
14083   if (kill_x != -1 || kill_y != -1)
14084   {
14085     if (IS_PLAYER(kill_x, kill_y))
14086     {
14087       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14088
14089       if (player->shield_deadly_time_left > 0 &&
14090           !IS_INDESTRUCTIBLE(bad_element))
14091         Bang(bad_x, bad_y);
14092       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14093         KillPlayer(player);
14094     }
14095     else
14096       Bang(kill_x, kill_y);
14097   }
14098 }
14099
14100 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14101 {
14102   int bad_element = Tile[bad_x][bad_y];
14103   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14104   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14105   int test_x = bad_x + dx, test_y = bad_y + dy;
14106   int test_move_dir, test_element;
14107   int kill_x = -1, kill_y = -1;
14108
14109   if (!IN_LEV_FIELD(test_x, test_y))
14110     return;
14111
14112   test_move_dir =
14113     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14114
14115   test_element = Tile[test_x][test_y];
14116
14117   if (test_move_dir != bad_move_dir)
14118   {
14119     // good thing can be player or penguin that does not move away
14120     if (IS_PLAYER(test_x, test_y))
14121     {
14122       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14123
14124       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14125          player as being hit when he is moving towards the bad thing, because
14126          the "get hit by" condition would be lost after the player stops) */
14127       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14128         return;         // player moves away from bad thing
14129
14130       kill_x = test_x;
14131       kill_y = test_y;
14132     }
14133     else if (test_element == EL_PENGUIN)
14134     {
14135       kill_x = test_x;
14136       kill_y = test_y;
14137     }
14138   }
14139
14140   if (kill_x != -1 || kill_y != -1)
14141   {
14142     if (IS_PLAYER(kill_x, kill_y))
14143     {
14144       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14145
14146       if (player->shield_deadly_time_left > 0 &&
14147           !IS_INDESTRUCTIBLE(bad_element))
14148         Bang(bad_x, bad_y);
14149       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14150         KillPlayer(player);
14151     }
14152     else
14153       Bang(kill_x, kill_y);
14154   }
14155 }
14156
14157 void TestIfPlayerTouchesBadThing(int x, int y)
14158 {
14159   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14160 }
14161
14162 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14163 {
14164   TestIfGoodThingHitsBadThing(x, y, move_dir);
14165 }
14166
14167 void TestIfBadThingTouchesPlayer(int x, int y)
14168 {
14169   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14170 }
14171
14172 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14173 {
14174   TestIfBadThingHitsGoodThing(x, y, move_dir);
14175 }
14176
14177 void TestIfFriendTouchesBadThing(int x, int y)
14178 {
14179   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14180 }
14181
14182 void TestIfBadThingTouchesFriend(int x, int y)
14183 {
14184   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14185 }
14186
14187 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14188 {
14189   int i, kill_x = bad_x, kill_y = bad_y;
14190   struct XY *xy = xy_topdown;
14191
14192   for (i = 0; i < NUM_DIRECTIONS; i++)
14193   {
14194     int x, y, element;
14195
14196     x = bad_x + xy[i].x;
14197     y = bad_y + xy[i].y;
14198     if (!IN_LEV_FIELD(x, y))
14199       continue;
14200
14201     element = Tile[x][y];
14202     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14203         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14204     {
14205       kill_x = x;
14206       kill_y = y;
14207       break;
14208     }
14209   }
14210
14211   if (kill_x != bad_x || kill_y != bad_y)
14212     Bang(bad_x, bad_y);
14213 }
14214
14215 void KillPlayer(struct PlayerInfo *player)
14216 {
14217   int jx = player->jx, jy = player->jy;
14218
14219   if (!player->active)
14220     return;
14221
14222 #if 0
14223   Debug("game:playing:KillPlayer",
14224         "0: killed == %d, active == %d, reanimated == %d",
14225         player->killed, player->active, player->reanimated);
14226 #endif
14227
14228   /* the following code was introduced to prevent an infinite loop when calling
14229      -> Bang()
14230      -> CheckTriggeredElementChangeExt()
14231      -> ExecuteCustomElementAction()
14232      -> KillPlayer()
14233      -> (infinitely repeating the above sequence of function calls)
14234      which occurs when killing the player while having a CE with the setting
14235      "kill player X when explosion of <player X>"; the solution using a new
14236      field "player->killed" was chosen for backwards compatibility, although
14237      clever use of the fields "player->active" etc. would probably also work */
14238 #if 1
14239   if (player->killed)
14240     return;
14241 #endif
14242
14243   player->killed = TRUE;
14244
14245   // remove accessible field at the player's position
14246   RemoveField(jx, jy);
14247
14248   // deactivate shield (else Bang()/Explode() would not work right)
14249   player->shield_normal_time_left = 0;
14250   player->shield_deadly_time_left = 0;
14251
14252 #if 0
14253   Debug("game:playing:KillPlayer",
14254         "1: killed == %d, active == %d, reanimated == %d",
14255         player->killed, player->active, player->reanimated);
14256 #endif
14257
14258   Bang(jx, jy);
14259
14260 #if 0
14261   Debug("game:playing:KillPlayer",
14262         "2: killed == %d, active == %d, reanimated == %d",
14263         player->killed, player->active, player->reanimated);
14264 #endif
14265
14266   if (player->reanimated)       // killed player may have been reanimated
14267     player->killed = player->reanimated = FALSE;
14268   else
14269     BuryPlayer(player);
14270 }
14271
14272 static void KillPlayerUnlessEnemyProtected(int x, int y)
14273 {
14274   if (!PLAYER_ENEMY_PROTECTED(x, y))
14275     KillPlayer(PLAYERINFO(x, y));
14276 }
14277
14278 static void KillPlayerUnlessExplosionProtected(int x, int y)
14279 {
14280   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14281     KillPlayer(PLAYERINFO(x, y));
14282 }
14283
14284 void BuryPlayer(struct PlayerInfo *player)
14285 {
14286   int jx = player->jx, jy = player->jy;
14287
14288   if (!player->active)
14289     return;
14290
14291   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14292
14293   RemovePlayer(player);
14294
14295   player->buried = TRUE;
14296
14297   if (game.all_players_gone)
14298     game.GameOver = TRUE;
14299 }
14300
14301 void RemovePlayer(struct PlayerInfo *player)
14302 {
14303   int jx = player->jx, jy = player->jy;
14304   int i, found = FALSE;
14305
14306   player->present = FALSE;
14307   player->active = FALSE;
14308
14309   // required for some CE actions (even if the player is not active anymore)
14310   player->MovPos = 0;
14311
14312   if (!ExplodeField[jx][jy])
14313     StorePlayer[jx][jy] = 0;
14314
14315   if (player->is_moving)
14316     TEST_DrawLevelField(player->last_jx, player->last_jy);
14317
14318   for (i = 0; i < MAX_PLAYERS; i++)
14319     if (stored_player[i].active)
14320       found = TRUE;
14321
14322   if (!found)
14323   {
14324     game.all_players_gone = TRUE;
14325     game.GameOver = TRUE;
14326   }
14327
14328   game.exit_x = game.robot_wheel_x = jx;
14329   game.exit_y = game.robot_wheel_y = jy;
14330 }
14331
14332 void ExitPlayer(struct PlayerInfo *player)
14333 {
14334   DrawPlayer(player);   // needed here only to cleanup last field
14335   RemovePlayer(player);
14336
14337   if (game.players_still_needed > 0)
14338     game.players_still_needed--;
14339 }
14340
14341 static void SetFieldForSnapping(int x, int y, int element, int direction,
14342                                 int player_index_bit)
14343 {
14344   struct ElementInfo *ei = &element_info[element];
14345   int direction_bit = MV_DIR_TO_BIT(direction);
14346   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14347   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14348                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14349
14350   Tile[x][y] = EL_ELEMENT_SNAPPING;
14351   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14352   MovDir[x][y] = direction;
14353   Store[x][y] = element;
14354   Store2[x][y] = player_index_bit;
14355
14356   ResetGfxAnimation(x, y);
14357
14358   GfxElement[x][y] = element;
14359   GfxAction[x][y] = action;
14360   GfxDir[x][y] = direction;
14361   GfxFrame[x][y] = -1;
14362 }
14363
14364 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14365                                    int player_index_bit)
14366 {
14367   TestIfElementTouchesCustomElement(x, y);      // for empty space
14368
14369   if (level.finish_dig_collect)
14370   {
14371     int dig_side = MV_DIR_OPPOSITE(direction);
14372     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14373                         CE_PLAYER_COLLECTS_X);
14374
14375     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14376                                         player_index_bit, dig_side);
14377     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14378                                         player_index_bit, dig_side);
14379   }
14380 }
14381
14382 /*
14383   =============================================================================
14384   checkDiagonalPushing()
14385   -----------------------------------------------------------------------------
14386   check if diagonal input device direction results in pushing of object
14387   (by checking if the alternative direction is walkable, diggable, ...)
14388   =============================================================================
14389 */
14390
14391 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14392                                     int x, int y, int real_dx, int real_dy)
14393 {
14394   int jx, jy, dx, dy, xx, yy;
14395
14396   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14397     return TRUE;
14398
14399   // diagonal direction: check alternative direction
14400   jx = player->jx;
14401   jy = player->jy;
14402   dx = x - jx;
14403   dy = y - jy;
14404   xx = jx + (dx == 0 ? real_dx : 0);
14405   yy = jy + (dy == 0 ? real_dy : 0);
14406
14407   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14408 }
14409
14410 /*
14411   =============================================================================
14412   DigField()
14413   -----------------------------------------------------------------------------
14414   x, y:                 field next to player (non-diagonal) to try to dig to
14415   real_dx, real_dy:     direction as read from input device (can be diagonal)
14416   =============================================================================
14417 */
14418
14419 static int DigField(struct PlayerInfo *player,
14420                     int oldx, int oldy, int x, int y,
14421                     int real_dx, int real_dy, int mode)
14422 {
14423   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14424   boolean player_was_pushing = player->is_pushing;
14425   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14426   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14427   int jx = oldx, jy = oldy;
14428   int dx = x - jx, dy = y - jy;
14429   int nextx = x + dx, nexty = y + dy;
14430   int move_direction = (dx == -1 ? MV_LEFT  :
14431                         dx == +1 ? MV_RIGHT :
14432                         dy == -1 ? MV_UP    :
14433                         dy == +1 ? MV_DOWN  : MV_NONE);
14434   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14435   int dig_side = MV_DIR_OPPOSITE(move_direction);
14436   int old_element = Tile[jx][jy];
14437   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14438   int collect_count;
14439
14440   if (is_player)                // function can also be called by EL_PENGUIN
14441   {
14442     if (player->MovPos == 0)
14443     {
14444       player->is_digging = FALSE;
14445       player->is_collecting = FALSE;
14446     }
14447
14448     if (player->MovPos == 0)    // last pushing move finished
14449       player->is_pushing = FALSE;
14450
14451     if (mode == DF_NO_PUSH)     // player just stopped pushing
14452     {
14453       player->is_switching = FALSE;
14454       player->push_delay = -1;
14455
14456       return MP_NO_ACTION;
14457     }
14458   }
14459   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14460     old_element = Back[jx][jy];
14461
14462   // in case of element dropped at player position, check background
14463   else if (Back[jx][jy] != EL_EMPTY &&
14464            game.engine_version >= VERSION_IDENT(2,2,0,0))
14465     old_element = Back[jx][jy];
14466
14467   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14468     return MP_NO_ACTION;        // field has no opening in this direction
14469
14470   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14471     return MP_NO_ACTION;        // field has no opening in this direction
14472
14473   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14474   {
14475     SplashAcid(x, y);
14476
14477     Tile[jx][jy] = player->artwork_element;
14478     InitMovingField(jx, jy, MV_DOWN);
14479     Store[jx][jy] = EL_ACID;
14480     ContinueMoving(jx, jy);
14481     BuryPlayer(player);
14482
14483     return MP_DONT_RUN_INTO;
14484   }
14485
14486   if (player_can_move && DONT_RUN_INTO(element))
14487   {
14488     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14489
14490     return MP_DONT_RUN_INTO;
14491   }
14492
14493   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14494     return MP_NO_ACTION;
14495
14496   collect_count = element_info[element].collect_count_initial;
14497
14498   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14499     return MP_NO_ACTION;
14500
14501   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14502     player_can_move = player_can_move_or_snap;
14503
14504   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14505       game.engine_version >= VERSION_IDENT(2,2,0,0))
14506   {
14507     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14508                                player->index_bit, dig_side);
14509     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14510                                         player->index_bit, dig_side);
14511
14512     if (element == EL_DC_LANDMINE)
14513       Bang(x, y);
14514
14515     if (Tile[x][y] != element)          // field changed by snapping
14516       return MP_ACTION;
14517
14518     return MP_NO_ACTION;
14519   }
14520
14521   if (player->gravity && is_player && !player->is_auto_moving &&
14522       canFallDown(player) && move_direction != MV_DOWN &&
14523       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14524     return MP_NO_ACTION;        // player cannot walk here due to gravity
14525
14526   if (player_can_move &&
14527       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14528   {
14529     int sound_element = SND_ELEMENT(element);
14530     int sound_action = ACTION_WALKING;
14531
14532     if (IS_RND_GATE(element))
14533     {
14534       if (!player->key[RND_GATE_NR(element)])
14535         return MP_NO_ACTION;
14536     }
14537     else if (IS_RND_GATE_GRAY(element))
14538     {
14539       if (!player->key[RND_GATE_GRAY_NR(element)])
14540         return MP_NO_ACTION;
14541     }
14542     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14543     {
14544       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14545         return MP_NO_ACTION;
14546     }
14547     else if (element == EL_EXIT_OPEN ||
14548              element == EL_EM_EXIT_OPEN ||
14549              element == EL_EM_EXIT_OPENING ||
14550              element == EL_STEEL_EXIT_OPEN ||
14551              element == EL_EM_STEEL_EXIT_OPEN ||
14552              element == EL_EM_STEEL_EXIT_OPENING ||
14553              element == EL_SP_EXIT_OPEN ||
14554              element == EL_SP_EXIT_OPENING)
14555     {
14556       sound_action = ACTION_PASSING;    // player is passing exit
14557     }
14558     else if (element == EL_EMPTY)
14559     {
14560       sound_action = ACTION_MOVING;             // nothing to walk on
14561     }
14562
14563     // play sound from background or player, whatever is available
14564     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14565       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14566     else
14567       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14568   }
14569   else if (player_can_move &&
14570            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14571   {
14572     if (!ACCESS_FROM(element, opposite_direction))
14573       return MP_NO_ACTION;      // field not accessible from this direction
14574
14575     if (CAN_MOVE(element))      // only fixed elements can be passed!
14576       return MP_NO_ACTION;
14577
14578     if (IS_EM_GATE(element))
14579     {
14580       if (!player->key[EM_GATE_NR(element)])
14581         return MP_NO_ACTION;
14582     }
14583     else if (IS_EM_GATE_GRAY(element))
14584     {
14585       if (!player->key[EM_GATE_GRAY_NR(element)])
14586         return MP_NO_ACTION;
14587     }
14588     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14589     {
14590       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14591         return MP_NO_ACTION;
14592     }
14593     else if (IS_EMC_GATE(element))
14594     {
14595       if (!player->key[EMC_GATE_NR(element)])
14596         return MP_NO_ACTION;
14597     }
14598     else if (IS_EMC_GATE_GRAY(element))
14599     {
14600       if (!player->key[EMC_GATE_GRAY_NR(element)])
14601         return MP_NO_ACTION;
14602     }
14603     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14604     {
14605       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14606         return MP_NO_ACTION;
14607     }
14608     else if (element == EL_DC_GATE_WHITE ||
14609              element == EL_DC_GATE_WHITE_GRAY ||
14610              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14611     {
14612       if (player->num_white_keys == 0)
14613         return MP_NO_ACTION;
14614
14615       player->num_white_keys--;
14616     }
14617     else if (IS_SP_PORT(element))
14618     {
14619       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14620           element == EL_SP_GRAVITY_PORT_RIGHT ||
14621           element == EL_SP_GRAVITY_PORT_UP ||
14622           element == EL_SP_GRAVITY_PORT_DOWN)
14623         player->gravity = !player->gravity;
14624       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14625                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14626                element == EL_SP_GRAVITY_ON_PORT_UP ||
14627                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14628         player->gravity = TRUE;
14629       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14630                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14631                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14632                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14633         player->gravity = FALSE;
14634     }
14635
14636     // automatically move to the next field with double speed
14637     player->programmed_action = move_direction;
14638
14639     if (player->move_delay_reset_counter == 0)
14640     {
14641       player->move_delay_reset_counter = 2;     // two double speed steps
14642
14643       DOUBLE_PLAYER_SPEED(player);
14644     }
14645
14646     PlayLevelSoundAction(x, y, ACTION_PASSING);
14647   }
14648   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14649   {
14650     RemoveField(x, y);
14651
14652     if (mode != DF_SNAP)
14653     {
14654       GfxElement[x][y] = GFX_ELEMENT(element);
14655       player->is_digging = TRUE;
14656     }
14657
14658     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14659
14660     // use old behaviour for old levels (digging)
14661     if (!level.finish_dig_collect)
14662     {
14663       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14664                                           player->index_bit, dig_side);
14665
14666       // if digging triggered player relocation, finish digging tile
14667       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14668         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14669     }
14670
14671     if (mode == DF_SNAP)
14672     {
14673       if (level.block_snap_field)
14674         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14675       else
14676         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14677
14678       // use old behaviour for old levels (snapping)
14679       if (!level.finish_dig_collect)
14680         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14681                                             player->index_bit, dig_side);
14682     }
14683   }
14684   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14685   {
14686     RemoveField(x, y);
14687
14688     if (is_player && mode != DF_SNAP)
14689     {
14690       GfxElement[x][y] = element;
14691       player->is_collecting = TRUE;
14692     }
14693
14694     if (element == EL_SPEED_PILL)
14695     {
14696       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14697     }
14698     else if (element == EL_EXTRA_TIME && level.time > 0)
14699     {
14700       TimeLeft += level.extra_time;
14701
14702       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14703
14704       DisplayGameControlValues();
14705     }
14706     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14707     {
14708       int shield_time = (element == EL_SHIELD_DEADLY ?
14709                          level.shield_deadly_time :
14710                          level.shield_normal_time);
14711
14712       player->shield_normal_time_left += shield_time;
14713       if (element == EL_SHIELD_DEADLY)
14714         player->shield_deadly_time_left += shield_time;
14715     }
14716     else if (element == EL_DYNAMITE ||
14717              element == EL_EM_DYNAMITE ||
14718              element == EL_SP_DISK_RED)
14719     {
14720       if (player->inventory_size < MAX_INVENTORY_SIZE)
14721         player->inventory_element[player->inventory_size++] = element;
14722
14723       DrawGameDoorValues();
14724     }
14725     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14726     {
14727       player->dynabomb_count++;
14728       player->dynabombs_left++;
14729     }
14730     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14731     {
14732       player->dynabomb_size++;
14733     }
14734     else if (element == EL_DYNABOMB_INCREASE_POWER)
14735     {
14736       player->dynabomb_xl = TRUE;
14737     }
14738     else if (IS_KEY(element))
14739     {
14740       player->key[KEY_NR(element)] = TRUE;
14741
14742       DrawGameDoorValues();
14743     }
14744     else if (element == EL_DC_KEY_WHITE)
14745     {
14746       player->num_white_keys++;
14747
14748       // display white keys?
14749       // DrawGameDoorValues();
14750     }
14751     else if (IS_ENVELOPE(element))
14752     {
14753       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14754
14755       if (!wait_for_snapping)
14756         player->show_envelope = element;
14757     }
14758     else if (element == EL_EMC_LENSES)
14759     {
14760       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14761
14762       RedrawAllInvisibleElementsForLenses();
14763     }
14764     else if (element == EL_EMC_MAGNIFIER)
14765     {
14766       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14767
14768       RedrawAllInvisibleElementsForMagnifier();
14769     }
14770     else if (IS_DROPPABLE(element) ||
14771              IS_THROWABLE(element))     // can be collected and dropped
14772     {
14773       int i;
14774
14775       if (collect_count == 0)
14776         player->inventory_infinite_element = element;
14777       else
14778         for (i = 0; i < collect_count; i++)
14779           if (player->inventory_size < MAX_INVENTORY_SIZE)
14780             player->inventory_element[player->inventory_size++] = element;
14781
14782       DrawGameDoorValues();
14783     }
14784     else if (collect_count > 0)
14785     {
14786       game.gems_still_needed -= collect_count;
14787       if (game.gems_still_needed < 0)
14788         game.gems_still_needed = 0;
14789
14790       game.snapshot.collected_item = TRUE;
14791
14792       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14793
14794       DisplayGameControlValues();
14795     }
14796
14797     RaiseScoreElement(element);
14798     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14799
14800     // use old behaviour for old levels (collecting)
14801     if (!level.finish_dig_collect && is_player)
14802     {
14803       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14804                                           player->index_bit, dig_side);
14805
14806       // if collecting triggered player relocation, finish collecting tile
14807       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14808         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14809     }
14810
14811     if (mode == DF_SNAP)
14812     {
14813       if (level.block_snap_field)
14814         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14815       else
14816         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14817
14818       // use old behaviour for old levels (snapping)
14819       if (!level.finish_dig_collect)
14820         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14821                                             player->index_bit, dig_side);
14822     }
14823   }
14824   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14825   {
14826     if (mode == DF_SNAP && element != EL_BD_ROCK)
14827       return MP_NO_ACTION;
14828
14829     if (CAN_FALL(element) && dy)
14830       return MP_NO_ACTION;
14831
14832     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14833         !(element == EL_SPRING && level.use_spring_bug))
14834       return MP_NO_ACTION;
14835
14836     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14837         ((move_direction & MV_VERTICAL &&
14838           ((element_info[element].move_pattern & MV_LEFT &&
14839             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14840            (element_info[element].move_pattern & MV_RIGHT &&
14841             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14842          (move_direction & MV_HORIZONTAL &&
14843           ((element_info[element].move_pattern & MV_UP &&
14844             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14845            (element_info[element].move_pattern & MV_DOWN &&
14846             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14847       return MP_NO_ACTION;
14848
14849     // do not push elements already moving away faster than player
14850     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14851         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14852       return MP_NO_ACTION;
14853
14854     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14855     {
14856       if (player->push_delay_value == -1 || !player_was_pushing)
14857         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14858     }
14859     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14860     {
14861       if (player->push_delay_value == -1)
14862         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14863     }
14864     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14865     {
14866       if (!player->is_pushing)
14867         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14868     }
14869
14870     player->is_pushing = TRUE;
14871     player->is_active = TRUE;
14872
14873     if (!(IN_LEV_FIELD(nextx, nexty) &&
14874           (IS_FREE(nextx, nexty) ||
14875            (IS_SB_ELEMENT(element) &&
14876             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14877            (IS_CUSTOM_ELEMENT(element) &&
14878             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14879       return MP_NO_ACTION;
14880
14881     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14882       return MP_NO_ACTION;
14883
14884     if (player->push_delay == -1)       // new pushing; restart delay
14885       player->push_delay = 0;
14886
14887     if (player->push_delay < player->push_delay_value &&
14888         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14889         element != EL_SPRING && element != EL_BALLOON)
14890     {
14891       // make sure that there is no move delay before next try to push
14892       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14893         player->move_delay = 0;
14894
14895       return MP_NO_ACTION;
14896     }
14897
14898     if (IS_CUSTOM_ELEMENT(element) &&
14899         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14900     {
14901       if (!DigFieldByCE(nextx, nexty, element))
14902         return MP_NO_ACTION;
14903     }
14904
14905     if (IS_SB_ELEMENT(element))
14906     {
14907       boolean sokoban_task_solved = FALSE;
14908
14909       if (element == EL_SOKOBAN_FIELD_FULL)
14910       {
14911         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14912
14913         IncrementSokobanFieldsNeeded();
14914         IncrementSokobanObjectsNeeded();
14915       }
14916
14917       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14918       {
14919         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14920
14921         DecrementSokobanFieldsNeeded();
14922         DecrementSokobanObjectsNeeded();
14923
14924         // sokoban object was pushed from empty field to sokoban field
14925         if (Back[x][y] == EL_EMPTY)
14926           sokoban_task_solved = TRUE;
14927       }
14928
14929       Tile[x][y] = EL_SOKOBAN_OBJECT;
14930
14931       if (Back[x][y] == Back[nextx][nexty])
14932         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14933       else if (Back[x][y] != 0)
14934         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14935                                     ACTION_EMPTYING);
14936       else
14937         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14938                                     ACTION_FILLING);
14939
14940       if (sokoban_task_solved &&
14941           game.sokoban_fields_still_needed == 0 &&
14942           game.sokoban_objects_still_needed == 0 &&
14943           level.auto_exit_sokoban)
14944       {
14945         game.players_still_needed = 0;
14946
14947         LevelSolved();
14948
14949         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14950       }
14951     }
14952     else
14953       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14954
14955     InitMovingField(x, y, move_direction);
14956     GfxAction[x][y] = ACTION_PUSHING;
14957
14958     if (mode == DF_SNAP)
14959       ContinueMoving(x, y);
14960     else
14961       MovPos[x][y] = (dx != 0 ? dx : dy);
14962
14963     Pushed[x][y] = TRUE;
14964     Pushed[nextx][nexty] = TRUE;
14965
14966     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14967       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14968     else
14969       player->push_delay_value = -1;    // get new value later
14970
14971     // check for element change _after_ element has been pushed
14972     if (game.use_change_when_pushing_bug)
14973     {
14974       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14975                                  player->index_bit, dig_side);
14976       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14977                                           player->index_bit, dig_side);
14978     }
14979   }
14980   else if (IS_SWITCHABLE(element))
14981   {
14982     if (PLAYER_SWITCHING(player, x, y))
14983     {
14984       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14985                                           player->index_bit, dig_side);
14986
14987       return MP_ACTION;
14988     }
14989
14990     player->is_switching = TRUE;
14991     player->switch_x = x;
14992     player->switch_y = y;
14993
14994     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14995
14996     if (element == EL_ROBOT_WHEEL)
14997     {
14998       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14999
15000       game.robot_wheel_x = x;
15001       game.robot_wheel_y = y;
15002       game.robot_wheel_active = TRUE;
15003
15004       TEST_DrawLevelField(x, y);
15005     }
15006     else if (element == EL_SP_TERMINAL)
15007     {
15008       int xx, yy;
15009
15010       SCAN_PLAYFIELD(xx, yy)
15011       {
15012         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15013         {
15014           Bang(xx, yy);
15015         }
15016         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15017         {
15018           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15019
15020           ResetGfxAnimation(xx, yy);
15021           TEST_DrawLevelField(xx, yy);
15022         }
15023       }
15024     }
15025     else if (IS_BELT_SWITCH(element))
15026     {
15027       ToggleBeltSwitch(x, y);
15028     }
15029     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15030              element == EL_SWITCHGATE_SWITCH_DOWN ||
15031              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15032              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15033     {
15034       ToggleSwitchgateSwitch();
15035     }
15036     else if (element == EL_LIGHT_SWITCH ||
15037              element == EL_LIGHT_SWITCH_ACTIVE)
15038     {
15039       ToggleLightSwitch(x, y);
15040     }
15041     else if (element == EL_TIMEGATE_SWITCH ||
15042              element == EL_DC_TIMEGATE_SWITCH)
15043     {
15044       ActivateTimegateSwitch(x, y);
15045     }
15046     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15047              element == EL_BALLOON_SWITCH_RIGHT ||
15048              element == EL_BALLOON_SWITCH_UP    ||
15049              element == EL_BALLOON_SWITCH_DOWN  ||
15050              element == EL_BALLOON_SWITCH_NONE  ||
15051              element == EL_BALLOON_SWITCH_ANY)
15052     {
15053       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15054                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15055                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15056                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15057                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15058                              move_direction);
15059     }
15060     else if (element == EL_LAMP)
15061     {
15062       Tile[x][y] = EL_LAMP_ACTIVE;
15063       game.lights_still_needed--;
15064
15065       ResetGfxAnimation(x, y);
15066       TEST_DrawLevelField(x, y);
15067     }
15068     else if (element == EL_TIME_ORB_FULL)
15069     {
15070       Tile[x][y] = EL_TIME_ORB_EMPTY;
15071
15072       if (level.time > 0 || level.use_time_orb_bug)
15073       {
15074         TimeLeft += level.time_orb_time;
15075         game.no_level_time_limit = FALSE;
15076
15077         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15078
15079         DisplayGameControlValues();
15080       }
15081
15082       ResetGfxAnimation(x, y);
15083       TEST_DrawLevelField(x, y);
15084     }
15085     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15086              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15087     {
15088       int xx, yy;
15089
15090       game.ball_active = !game.ball_active;
15091
15092       SCAN_PLAYFIELD(xx, yy)
15093       {
15094         int e = Tile[xx][yy];
15095
15096         if (game.ball_active)
15097         {
15098           if (e == EL_EMC_MAGIC_BALL)
15099             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15100           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15101             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15102         }
15103         else
15104         {
15105           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15106             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15107           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15108             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15109         }
15110       }
15111     }
15112
15113     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15114                                         player->index_bit, dig_side);
15115
15116     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15117                                         player->index_bit, dig_side);
15118
15119     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15120                                         player->index_bit, dig_side);
15121
15122     return MP_ACTION;
15123   }
15124   else
15125   {
15126     if (!PLAYER_SWITCHING(player, x, y))
15127     {
15128       player->is_switching = TRUE;
15129       player->switch_x = x;
15130       player->switch_y = y;
15131
15132       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15133                                  player->index_bit, dig_side);
15134       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15135                                           player->index_bit, dig_side);
15136
15137       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15138                                  player->index_bit, dig_side);
15139       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15140                                           player->index_bit, dig_side);
15141     }
15142
15143     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15144                                player->index_bit, dig_side);
15145     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15146                                         player->index_bit, dig_side);
15147
15148     return MP_NO_ACTION;
15149   }
15150
15151   player->push_delay = -1;
15152
15153   if (is_player)                // function can also be called by EL_PENGUIN
15154   {
15155     if (Tile[x][y] != element)          // really digged/collected something
15156     {
15157       player->is_collecting = !player->is_digging;
15158       player->is_active = TRUE;
15159
15160       player->last_removed_element = element;
15161     }
15162   }
15163
15164   return MP_MOVING;
15165 }
15166
15167 static boolean DigFieldByCE(int x, int y, int digging_element)
15168 {
15169   int element = Tile[x][y];
15170
15171   if (!IS_FREE(x, y))
15172   {
15173     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15174                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15175                   ACTION_BREAKING);
15176
15177     // no element can dig solid indestructible elements
15178     if (IS_INDESTRUCTIBLE(element) &&
15179         !IS_DIGGABLE(element) &&
15180         !IS_COLLECTIBLE(element))
15181       return FALSE;
15182
15183     if (AmoebaNr[x][y] &&
15184         (element == EL_AMOEBA_FULL ||
15185          element == EL_BD_AMOEBA ||
15186          element == EL_AMOEBA_GROWING))
15187     {
15188       AmoebaCnt[AmoebaNr[x][y]]--;
15189       AmoebaCnt2[AmoebaNr[x][y]]--;
15190     }
15191
15192     if (IS_MOVING(x, y))
15193       RemoveMovingField(x, y);
15194     else
15195     {
15196       RemoveField(x, y);
15197       TEST_DrawLevelField(x, y);
15198     }
15199
15200     // if digged element was about to explode, prevent the explosion
15201     ExplodeField[x][y] = EX_TYPE_NONE;
15202
15203     PlayLevelSoundAction(x, y, action);
15204   }
15205
15206   Store[x][y] = EL_EMPTY;
15207
15208   // this makes it possible to leave the removed element again
15209   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15210     Store[x][y] = element;
15211
15212   return TRUE;
15213 }
15214
15215 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15216 {
15217   int jx = player->jx, jy = player->jy;
15218   int x = jx + dx, y = jy + dy;
15219   int snap_direction = (dx == -1 ? MV_LEFT  :
15220                         dx == +1 ? MV_RIGHT :
15221                         dy == -1 ? MV_UP    :
15222                         dy == +1 ? MV_DOWN  : MV_NONE);
15223   boolean can_continue_snapping = (level.continuous_snapping &&
15224                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15225
15226   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15227     return FALSE;
15228
15229   if (!player->active || !IN_LEV_FIELD(x, y))
15230     return FALSE;
15231
15232   if (dx && dy)
15233     return FALSE;
15234
15235   if (!dx && !dy)
15236   {
15237     if (player->MovPos == 0)
15238       player->is_pushing = FALSE;
15239
15240     player->is_snapping = FALSE;
15241
15242     if (player->MovPos == 0)
15243     {
15244       player->is_moving = FALSE;
15245       player->is_digging = FALSE;
15246       player->is_collecting = FALSE;
15247     }
15248
15249     return FALSE;
15250   }
15251
15252   // prevent snapping with already pressed snap key when not allowed
15253   if (player->is_snapping && !can_continue_snapping)
15254     return FALSE;
15255
15256   player->MovDir = snap_direction;
15257
15258   if (player->MovPos == 0)
15259   {
15260     player->is_moving = FALSE;
15261     player->is_digging = FALSE;
15262     player->is_collecting = FALSE;
15263   }
15264
15265   player->is_dropping = FALSE;
15266   player->is_dropping_pressed = FALSE;
15267   player->drop_pressed_delay = 0;
15268
15269   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15270     return FALSE;
15271
15272   player->is_snapping = TRUE;
15273   player->is_active = TRUE;
15274
15275   if (player->MovPos == 0)
15276   {
15277     player->is_moving = FALSE;
15278     player->is_digging = FALSE;
15279     player->is_collecting = FALSE;
15280   }
15281
15282   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15283     TEST_DrawLevelField(player->last_jx, player->last_jy);
15284
15285   TEST_DrawLevelField(x, y);
15286
15287   return TRUE;
15288 }
15289
15290 static boolean DropElement(struct PlayerInfo *player)
15291 {
15292   int old_element, new_element;
15293   int dropx = player->jx, dropy = player->jy;
15294   int drop_direction = player->MovDir;
15295   int drop_side = drop_direction;
15296   int drop_element = get_next_dropped_element(player);
15297
15298   /* do not drop an element on top of another element; when holding drop key
15299      pressed without moving, dropped element must move away before the next
15300      element can be dropped (this is especially important if the next element
15301      is dynamite, which can be placed on background for historical reasons) */
15302   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15303     return MP_ACTION;
15304
15305   if (IS_THROWABLE(drop_element))
15306   {
15307     dropx += GET_DX_FROM_DIR(drop_direction);
15308     dropy += GET_DY_FROM_DIR(drop_direction);
15309
15310     if (!IN_LEV_FIELD(dropx, dropy))
15311       return FALSE;
15312   }
15313
15314   old_element = Tile[dropx][dropy];     // old element at dropping position
15315   new_element = drop_element;           // default: no change when dropping
15316
15317   // check if player is active, not moving and ready to drop
15318   if (!player->active || player->MovPos || player->drop_delay > 0)
15319     return FALSE;
15320
15321   // check if player has anything that can be dropped
15322   if (new_element == EL_UNDEFINED)
15323     return FALSE;
15324
15325   // only set if player has anything that can be dropped
15326   player->is_dropping_pressed = TRUE;
15327
15328   // check if drop key was pressed long enough for EM style dynamite
15329   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15330     return FALSE;
15331
15332   // check if anything can be dropped at the current position
15333   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15334     return FALSE;
15335
15336   // collected custom elements can only be dropped on empty fields
15337   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15338     return FALSE;
15339
15340   if (old_element != EL_EMPTY)
15341     Back[dropx][dropy] = old_element;   // store old element on this field
15342
15343   ResetGfxAnimation(dropx, dropy);
15344   ResetRandomAnimationValue(dropx, dropy);
15345
15346   if (player->inventory_size > 0 ||
15347       player->inventory_infinite_element != EL_UNDEFINED)
15348   {
15349     if (player->inventory_size > 0)
15350     {
15351       player->inventory_size--;
15352
15353       DrawGameDoorValues();
15354
15355       if (new_element == EL_DYNAMITE)
15356         new_element = EL_DYNAMITE_ACTIVE;
15357       else if (new_element == EL_EM_DYNAMITE)
15358         new_element = EL_EM_DYNAMITE_ACTIVE;
15359       else if (new_element == EL_SP_DISK_RED)
15360         new_element = EL_SP_DISK_RED_ACTIVE;
15361     }
15362
15363     Tile[dropx][dropy] = new_element;
15364
15365     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15366       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15367                           el2img(Tile[dropx][dropy]), 0);
15368
15369     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15370
15371     // needed if previous element just changed to "empty" in the last frame
15372     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15373
15374     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15375                                player->index_bit, drop_side);
15376     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15377                                         CE_PLAYER_DROPS_X,
15378                                         player->index_bit, drop_side);
15379
15380     TestIfElementTouchesCustomElement(dropx, dropy);
15381   }
15382   else          // player is dropping a dyna bomb
15383   {
15384     player->dynabombs_left--;
15385
15386     Tile[dropx][dropy] = new_element;
15387
15388     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15389       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15390                           el2img(Tile[dropx][dropy]), 0);
15391
15392     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15393   }
15394
15395   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15396     InitField_WithBug1(dropx, dropy, FALSE);
15397
15398   new_element = Tile[dropx][dropy];     // element might have changed
15399
15400   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15401       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15402   {
15403     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15404       MovDir[dropx][dropy] = drop_direction;
15405
15406     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15407
15408     // do not cause impact style collision by dropping elements that can fall
15409     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15410   }
15411
15412   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15413   player->is_dropping = TRUE;
15414
15415   player->drop_pressed_delay = 0;
15416   player->is_dropping_pressed = FALSE;
15417
15418   player->drop_x = dropx;
15419   player->drop_y = dropy;
15420
15421   return TRUE;
15422 }
15423
15424 // ----------------------------------------------------------------------------
15425 // game sound playing functions
15426 // ----------------------------------------------------------------------------
15427
15428 static int *loop_sound_frame = NULL;
15429 static int *loop_sound_volume = NULL;
15430
15431 void InitPlayLevelSound(void)
15432 {
15433   int num_sounds = getSoundListSize();
15434
15435   checked_free(loop_sound_frame);
15436   checked_free(loop_sound_volume);
15437
15438   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15439   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15440 }
15441
15442 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15443 {
15444   int sx = SCREENX(x), sy = SCREENY(y);
15445   int volume, stereo_position;
15446   int max_distance = 8;
15447   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15448
15449   if ((!setup.sound_simple && !is_loop_sound) ||
15450       (!setup.sound_loops && is_loop_sound))
15451     return;
15452
15453   if (!IN_LEV_FIELD(x, y) ||
15454       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15455       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15456     return;
15457
15458   volume = SOUND_MAX_VOLUME;
15459
15460   if (!IN_SCR_FIELD(sx, sy))
15461   {
15462     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15463     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15464
15465     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15466   }
15467
15468   stereo_position = (SOUND_MAX_LEFT +
15469                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15470                      (SCR_FIELDX + 2 * max_distance));
15471
15472   if (is_loop_sound)
15473   {
15474     /* This assures that quieter loop sounds do not overwrite louder ones,
15475        while restarting sound volume comparison with each new game frame. */
15476
15477     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15478       return;
15479
15480     loop_sound_volume[nr] = volume;
15481     loop_sound_frame[nr] = FrameCounter;
15482   }
15483
15484   PlaySoundExt(nr, volume, stereo_position, type);
15485 }
15486
15487 static void PlayLevelSound(int x, int y, int nr)
15488 {
15489   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15490 }
15491
15492 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15493 {
15494   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15495                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15496                  y < LEVELY(BY1) ? LEVELY(BY1) :
15497                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15498                  sound_action);
15499 }
15500
15501 static void PlayLevelSoundAction(int x, int y, int action)
15502 {
15503   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15504 }
15505
15506 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15507 {
15508   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15509
15510   if (sound_effect != SND_UNDEFINED)
15511     PlayLevelSound(x, y, sound_effect);
15512 }
15513
15514 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15515                                               int action)
15516 {
15517   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15518
15519   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15520     PlayLevelSound(x, y, sound_effect);
15521 }
15522
15523 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15524 {
15525   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15526
15527   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15528     PlayLevelSound(x, y, sound_effect);
15529 }
15530
15531 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15532 {
15533   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15534
15535   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15536     StopSound(sound_effect);
15537 }
15538
15539 static int getLevelMusicNr(void)
15540 {
15541   int level_pos = level_nr - leveldir_current->first_level;
15542
15543   if (levelset.music[level_nr] != MUS_UNDEFINED)
15544     return levelset.music[level_nr];            // from config file
15545   else
15546     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15547 }
15548
15549 static void FadeLevelSounds(void)
15550 {
15551   FadeSounds();
15552 }
15553
15554 static void FadeLevelMusic(void)
15555 {
15556   int music_nr = getLevelMusicNr();
15557   char *curr_music = getCurrentlyPlayingMusicFilename();
15558   char *next_music = getMusicInfoEntryFilename(music_nr);
15559
15560   if (!strEqual(curr_music, next_music))
15561     FadeMusic();
15562 }
15563
15564 void FadeLevelSoundsAndMusic(void)
15565 {
15566   FadeLevelSounds();
15567   FadeLevelMusic();
15568 }
15569
15570 static void PlayLevelMusic(void)
15571 {
15572   int music_nr = getLevelMusicNr();
15573   char *curr_music = getCurrentlyPlayingMusicFilename();
15574   char *next_music = getMusicInfoEntryFilename(music_nr);
15575
15576   if (!strEqual(curr_music, next_music))
15577     PlayMusicLoop(music_nr);
15578 }
15579
15580 static int getSoundAction_BD(int sample)
15581 {
15582   switch (sample)
15583   {
15584     case GD_S_STONE_PUSHING:
15585     case GD_S_MEGA_STONE_PUSHING:
15586     case GD_S_FLYING_STONE_PUSHING:
15587     case GD_S_WAITING_STONE_PUSHING:
15588     case GD_S_CHASING_STONE_PUSHING:
15589     case GD_S_NUT_PUSHING:
15590     case GD_S_NITRO_PACK_PUSHING:
15591     case GD_S_BLADDER_PUSHING:
15592     case GD_S_BOX_PUSHING:
15593       return ACTION_PUSHING;
15594
15595     case GD_S_STONE_FALLING:
15596     case GD_S_MEGA_STONE_FALLING:
15597     case GD_S_FLYING_STONE_FALLING:
15598     case GD_S_NUT_FALLING:
15599     case GD_S_DIRT_BALL_FALLING:
15600     case GD_S_DIRT_LOOSE_FALLING:
15601     case GD_S_NITRO_PACK_FALLING:
15602     case GD_S_FALLING_WALL_FALLING:
15603       return ACTION_FALLING;
15604
15605     case GD_S_STONE_IMPACT:
15606     case GD_S_MEGA_STONE_IMPACT:
15607     case GD_S_FLYING_STONE_IMPACT:
15608     case GD_S_NUT_IMPACT:
15609     case GD_S_DIRT_BALL_IMPACT:
15610     case GD_S_DIRT_LOOSE_IMPACT:
15611     case GD_S_NITRO_PACK_IMPACT:
15612     case GD_S_FALLING_WALL_IMPACT:
15613       return ACTION_IMPACT;
15614
15615     case GD_S_NUT_CRACKING:
15616       return ACTION_BREAKING;
15617
15618     case GD_S_EXPANDING_WALL:
15619     case GD_S_WALL_REAPPEARING:
15620     case GD_S_SLIME:
15621     case GD_S_LAVA:
15622     case GD_S_ACID_SPREADING:
15623       return ACTION_GROWING;
15624
15625     case GD_S_DIAMOND_COLLECTING:
15626     case GD_S_FLYING_DIAMOND_COLLECTING:
15627     case GD_S_SKELETON_COLLECTING:
15628     case GD_S_PNEUMATIC_COLLECTING:
15629     case GD_S_BOMB_COLLECTING:
15630     case GD_S_CLOCK_COLLECTING:
15631     case GD_S_SWEET_COLLECTING:
15632     case GD_S_KEY_COLLECTING:
15633     case GD_S_DIAMOND_KEY_COLLECTING:
15634       return ACTION_COLLECTING;
15635
15636     case GD_S_BOMB_PLACING:
15637     case GD_S_REPLICATOR:
15638       return ACTION_DROPPING;
15639
15640     case GD_S_BLADDER_MOVING:
15641       return ACTION_MOVING;
15642
15643     case GD_S_BLADDER_SPENDER:
15644     case GD_S_BLADDER_CONVERTING:
15645     case GD_S_GRAVITY_CHANGING:
15646       return ACTION_CHANGING;
15647
15648     case GD_S_BITER_EATING:
15649       return ACTION_EATING;
15650
15651     case GD_S_DOOR_OPENING:
15652     case GD_S_CRACKING:
15653       return ACTION_OPENING;
15654
15655     case GD_S_DIRT_WALKING:
15656       return ACTION_DIGGING;
15657
15658     case GD_S_EMPTY_WALKING:
15659       return ACTION_WALKING;
15660
15661     case GD_S_SWITCH_BITER:
15662     case GD_S_SWITCH_CREATURES:
15663     case GD_S_SWITCH_GRAVITY:
15664     case GD_S_SWITCH_EXPANDING:
15665     case GD_S_SWITCH_CONVEYOR:
15666     case GD_S_SWITCH_REPLICATOR:
15667     case GD_S_STIRRING:
15668       return ACTION_ACTIVATING;
15669
15670     case GD_S_TELEPORTER:
15671       return ACTION_PASSING;
15672
15673     case GD_S_EXPLODING:
15674     case GD_S_BOMB_EXPLODING:
15675     case GD_S_GHOST_EXPLODING:
15676     case GD_S_VOODOO_EXPLODING:
15677     case GD_S_NITRO_PACK_EXPLODING:
15678       return ACTION_EXPLODING;
15679
15680     case GD_S_COVERING:
15681     case GD_S_AMOEBA:
15682     case GD_S_MAGIC_WALL:
15683     case GD_S_PNEUMATIC_HAMMER:
15684     case GD_S_WATER:
15685       return ACTION_ACTIVE;
15686
15687     case GD_S_DIAMOND_FALLING_RANDOM:
15688     case GD_S_DIAMOND_FALLING_1:
15689     case GD_S_DIAMOND_FALLING_2:
15690     case GD_S_DIAMOND_FALLING_3:
15691     case GD_S_DIAMOND_FALLING_4:
15692     case GD_S_DIAMOND_FALLING_5:
15693     case GD_S_DIAMOND_FALLING_6:
15694     case GD_S_DIAMOND_FALLING_7:
15695     case GD_S_DIAMOND_FALLING_8:
15696     case GD_S_DIAMOND_IMPACT_RANDOM:
15697     case GD_S_DIAMOND_IMPACT_1:
15698     case GD_S_DIAMOND_IMPACT_2:
15699     case GD_S_DIAMOND_IMPACT_3:
15700     case GD_S_DIAMOND_IMPACT_4:
15701     case GD_S_DIAMOND_IMPACT_5:
15702     case GD_S_DIAMOND_IMPACT_6:
15703     case GD_S_DIAMOND_IMPACT_7:
15704     case GD_S_DIAMOND_IMPACT_8:
15705     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15706     case GD_S_FLYING_DIAMOND_FALLING_1:
15707     case GD_S_FLYING_DIAMOND_FALLING_2:
15708     case GD_S_FLYING_DIAMOND_FALLING_3:
15709     case GD_S_FLYING_DIAMOND_FALLING_4:
15710     case GD_S_FLYING_DIAMOND_FALLING_5:
15711     case GD_S_FLYING_DIAMOND_FALLING_6:
15712     case GD_S_FLYING_DIAMOND_FALLING_7:
15713     case GD_S_FLYING_DIAMOND_FALLING_8:
15714     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15715     case GD_S_FLYING_DIAMOND_IMPACT_1:
15716     case GD_S_FLYING_DIAMOND_IMPACT_2:
15717     case GD_S_FLYING_DIAMOND_IMPACT_3:
15718     case GD_S_FLYING_DIAMOND_IMPACT_4:
15719     case GD_S_FLYING_DIAMOND_IMPACT_5:
15720     case GD_S_FLYING_DIAMOND_IMPACT_6:
15721     case GD_S_FLYING_DIAMOND_IMPACT_7:
15722     case GD_S_FLYING_DIAMOND_IMPACT_8:
15723     case GD_S_TIMEOUT_0:
15724     case GD_S_TIMEOUT_1:
15725     case GD_S_TIMEOUT_2:
15726     case GD_S_TIMEOUT_3:
15727     case GD_S_TIMEOUT_4:
15728     case GD_S_TIMEOUT_5:
15729     case GD_S_TIMEOUT_6:
15730     case GD_S_TIMEOUT_7:
15731     case GD_S_TIMEOUT_8:
15732     case GD_S_TIMEOUT_9:
15733     case GD_S_TIMEOUT_10:
15734     case GD_S_BONUS_LIFE:
15735       // trigger special post-processing (and force sound to be non-looping)
15736       return ACTION_OTHER;
15737
15738     case GD_S_AMOEBA_MAGIC:
15739     case GD_S_FINISHED:
15740       // trigger special post-processing (and force sound to be looping)
15741       return ACTION_DEFAULT;
15742
15743     default:
15744       return ACTION_DEFAULT;
15745   }
15746 }
15747
15748 static int getSoundEffect_BD(int element_bd, int sample)
15749 {
15750   int sound_action = getSoundAction_BD(sample);
15751   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15752   int nr;
15753
15754   // standard sounds
15755   if (sound_action != ACTION_OTHER &&
15756       sound_action != ACTION_DEFAULT)
15757     return sound_effect;
15758
15759   // special post-processing for some sounds
15760   switch (sample)
15761   {
15762     case GD_S_DIAMOND_FALLING_RANDOM:
15763     case GD_S_DIAMOND_FALLING_1:
15764     case GD_S_DIAMOND_FALLING_2:
15765     case GD_S_DIAMOND_FALLING_3:
15766     case GD_S_DIAMOND_FALLING_4:
15767     case GD_S_DIAMOND_FALLING_5:
15768     case GD_S_DIAMOND_FALLING_6:
15769     case GD_S_DIAMOND_FALLING_7:
15770     case GD_S_DIAMOND_FALLING_8:
15771       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15772             sample - GD_S_DIAMOND_FALLING_1);
15773       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15774
15775       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15776         sound_effect = SND_BD_DIAMOND_FALLING;
15777       break;
15778
15779     case GD_S_DIAMOND_IMPACT_RANDOM:
15780     case GD_S_DIAMOND_IMPACT_1:
15781     case GD_S_DIAMOND_IMPACT_2:
15782     case GD_S_DIAMOND_IMPACT_3:
15783     case GD_S_DIAMOND_IMPACT_4:
15784     case GD_S_DIAMOND_IMPACT_5:
15785     case GD_S_DIAMOND_IMPACT_6:
15786     case GD_S_DIAMOND_IMPACT_7:
15787     case GD_S_DIAMOND_IMPACT_8:
15788       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15789             sample - GD_S_DIAMOND_IMPACT_1);
15790       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15791
15792       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15793         sound_effect = SND_BD_DIAMOND_IMPACT;
15794       break;
15795
15796     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15797     case GD_S_FLYING_DIAMOND_FALLING_1:
15798     case GD_S_FLYING_DIAMOND_FALLING_2:
15799     case GD_S_FLYING_DIAMOND_FALLING_3:
15800     case GD_S_FLYING_DIAMOND_FALLING_4:
15801     case GD_S_FLYING_DIAMOND_FALLING_5:
15802     case GD_S_FLYING_DIAMOND_FALLING_6:
15803     case GD_S_FLYING_DIAMOND_FALLING_7:
15804     case GD_S_FLYING_DIAMOND_FALLING_8:
15805       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15806             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15807       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15808
15809       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15810         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15811       break;
15812
15813     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15814     case GD_S_FLYING_DIAMOND_IMPACT_1:
15815     case GD_S_FLYING_DIAMOND_IMPACT_2:
15816     case GD_S_FLYING_DIAMOND_IMPACT_3:
15817     case GD_S_FLYING_DIAMOND_IMPACT_4:
15818     case GD_S_FLYING_DIAMOND_IMPACT_5:
15819     case GD_S_FLYING_DIAMOND_IMPACT_6:
15820     case GD_S_FLYING_DIAMOND_IMPACT_7:
15821     case GD_S_FLYING_DIAMOND_IMPACT_8:
15822       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15823             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15824       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15825
15826       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15827         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15828       break;
15829
15830     case GD_S_TIMEOUT_0:
15831     case GD_S_TIMEOUT_1:
15832     case GD_S_TIMEOUT_2:
15833     case GD_S_TIMEOUT_3:
15834     case GD_S_TIMEOUT_4:
15835     case GD_S_TIMEOUT_5:
15836     case GD_S_TIMEOUT_6:
15837     case GD_S_TIMEOUT_7:
15838     case GD_S_TIMEOUT_8:
15839     case GD_S_TIMEOUT_9:
15840     case GD_S_TIMEOUT_10:
15841       nr = sample - GD_S_TIMEOUT_0;
15842       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15843
15844       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15845         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15846       break;
15847
15848     case GD_S_BONUS_LIFE:
15849       sound_effect = SND_GAME_HEALTH_BONUS;
15850       break;
15851
15852     case GD_S_AMOEBA_MAGIC:
15853       sound_effect = SND_BD_AMOEBA_OTHER;
15854       break;
15855
15856     case GD_S_FINISHED:
15857       sound_effect = SND_GAME_LEVELTIME_BONUS;
15858       break;
15859
15860     default:
15861       sound_effect = SND_UNDEFINED;
15862       break;
15863   }
15864
15865   return sound_effect;
15866 }
15867
15868 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15869 {
15870   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15871   int sound_effect = getSoundEffect_BD(element, sample);
15872   int sound_action = getSoundAction_BD(sample);
15873   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15874   int offset = 0;
15875   int x = xx - offset;
15876   int y = yy - offset;
15877
15878   // some sound actions are always looping in native BD game engine
15879   if (sound_action == ACTION_DEFAULT)
15880     is_loop_sound = TRUE;
15881
15882   // some sound actions are always non-looping in native BD game engine
15883   if (sound_action == ACTION_FALLING ||
15884       sound_action == ACTION_MOVING ||
15885       sound_action == ACTION_OTHER)
15886     is_loop_sound = FALSE;
15887
15888   if (sound_effect != SND_UNDEFINED)
15889     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15890 }
15891
15892 void StopSound_BD(int element_bd, int sample)
15893 {
15894   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15895   int sound_effect = getSoundEffect_BD(element, sample);
15896
15897   if (sound_effect != SND_UNDEFINED)
15898     StopSound(sound_effect);
15899 }
15900
15901 boolean isSoundPlaying_BD(int element_bd, int sample)
15902 {
15903   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15904   int sound_effect = getSoundEffect_BD(element, sample);
15905
15906   if (sound_effect != SND_UNDEFINED)
15907     return isSoundPlaying(sound_effect);
15908
15909   return FALSE;
15910 }
15911
15912 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15913 {
15914   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15915   int offset = 0;
15916   int x = xx - offset;
15917   int y = yy - offset;
15918
15919   switch (sample)
15920   {
15921     case SOUND_blank:
15922       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15923       break;
15924
15925     case SOUND_roll:
15926       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15927       break;
15928
15929     case SOUND_stone:
15930       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15931       break;
15932
15933     case SOUND_nut:
15934       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15935       break;
15936
15937     case SOUND_crack:
15938       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15939       break;
15940
15941     case SOUND_bug:
15942       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15943       break;
15944
15945     case SOUND_tank:
15946       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15947       break;
15948
15949     case SOUND_android_clone:
15950       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15951       break;
15952
15953     case SOUND_android_move:
15954       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15955       break;
15956
15957     case SOUND_spring:
15958       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15959       break;
15960
15961     case SOUND_slurp:
15962       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15963       break;
15964
15965     case SOUND_eater:
15966       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15967       break;
15968
15969     case SOUND_eater_eat:
15970       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15971       break;
15972
15973     case SOUND_alien:
15974       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15975       break;
15976
15977     case SOUND_collect:
15978       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15979       break;
15980
15981     case SOUND_diamond:
15982       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15983       break;
15984
15985     case SOUND_squash:
15986       // !!! CHECK THIS !!!
15987 #if 1
15988       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15989 #else
15990       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15991 #endif
15992       break;
15993
15994     case SOUND_wonderfall:
15995       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15996       break;
15997
15998     case SOUND_drip:
15999       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
16000       break;
16001
16002     case SOUND_push:
16003       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
16004       break;
16005
16006     case SOUND_dirt:
16007       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16008       break;
16009
16010     case SOUND_acid:
16011       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16012       break;
16013
16014     case SOUND_ball:
16015       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16016       break;
16017
16018     case SOUND_slide:
16019       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16020       break;
16021
16022     case SOUND_wonder:
16023       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16024       break;
16025
16026     case SOUND_door:
16027       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16028       break;
16029
16030     case SOUND_exit_open:
16031       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16032       break;
16033
16034     case SOUND_exit_leave:
16035       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16036       break;
16037
16038     case SOUND_dynamite:
16039       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16040       break;
16041
16042     case SOUND_tick:
16043       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16044       break;
16045
16046     case SOUND_press:
16047       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16048       break;
16049
16050     case SOUND_wheel:
16051       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16052       break;
16053
16054     case SOUND_boom:
16055       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16056       break;
16057
16058     case SOUND_die:
16059       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16060       break;
16061
16062     case SOUND_time:
16063       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16064       break;
16065
16066     default:
16067       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16068       break;
16069   }
16070 }
16071
16072 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16073 {
16074   int element = map_element_SP_to_RND(element_sp);
16075   int action = map_action_SP_to_RND(action_sp);
16076   int offset = (setup.sp_show_border_elements ? 0 : 1);
16077   int x = xx - offset;
16078   int y = yy - offset;
16079
16080   PlayLevelSoundElementAction(x, y, element, action);
16081 }
16082
16083 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16084 {
16085   int element = map_element_MM_to_RND(element_mm);
16086   int action = map_action_MM_to_RND(action_mm);
16087   int offset = 0;
16088   int x = xx - offset;
16089   int y = yy - offset;
16090
16091   if (!IS_MM_ELEMENT(element))
16092     element = EL_MM_DEFAULT;
16093
16094   PlayLevelSoundElementAction(x, y, element, action);
16095 }
16096
16097 void PlaySound_MM(int sound_mm)
16098 {
16099   int sound = map_sound_MM_to_RND(sound_mm);
16100
16101   if (sound == SND_UNDEFINED)
16102     return;
16103
16104   PlaySound(sound);
16105 }
16106
16107 void PlaySoundLoop_MM(int sound_mm)
16108 {
16109   int sound = map_sound_MM_to_RND(sound_mm);
16110
16111   if (sound == SND_UNDEFINED)
16112     return;
16113
16114   PlaySoundLoop(sound);
16115 }
16116
16117 void StopSound_MM(int sound_mm)
16118 {
16119   int sound = map_sound_MM_to_RND(sound_mm);
16120
16121   if (sound == SND_UNDEFINED)
16122     return;
16123
16124   StopSound(sound);
16125 }
16126
16127 void RaiseScore(int value)
16128 {
16129   game.score += value;
16130
16131   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16132
16133   DisplayGameControlValues();
16134 }
16135
16136 void RaiseScoreElement(int element)
16137 {
16138   switch (element)
16139   {
16140     case EL_EMERALD:
16141     case EL_BD_DIAMOND:
16142     case EL_EMERALD_YELLOW:
16143     case EL_EMERALD_RED:
16144     case EL_EMERALD_PURPLE:
16145     case EL_SP_INFOTRON:
16146       RaiseScore(level.score[SC_EMERALD]);
16147       break;
16148     case EL_DIAMOND:
16149       RaiseScore(level.score[SC_DIAMOND]);
16150       break;
16151     case EL_CRYSTAL:
16152       RaiseScore(level.score[SC_CRYSTAL]);
16153       break;
16154     case EL_PEARL:
16155       RaiseScore(level.score[SC_PEARL]);
16156       break;
16157     case EL_BUG:
16158     case EL_BD_BUTTERFLY:
16159     case EL_SP_ELECTRON:
16160       RaiseScore(level.score[SC_BUG]);
16161       break;
16162     case EL_SPACESHIP:
16163     case EL_BD_FIREFLY:
16164     case EL_SP_SNIKSNAK:
16165       RaiseScore(level.score[SC_SPACESHIP]);
16166       break;
16167     case EL_YAMYAM:
16168     case EL_DARK_YAMYAM:
16169       RaiseScore(level.score[SC_YAMYAM]);
16170       break;
16171     case EL_ROBOT:
16172       RaiseScore(level.score[SC_ROBOT]);
16173       break;
16174     case EL_PACMAN:
16175       RaiseScore(level.score[SC_PACMAN]);
16176       break;
16177     case EL_NUT:
16178       RaiseScore(level.score[SC_NUT]);
16179       break;
16180     case EL_DYNAMITE:
16181     case EL_EM_DYNAMITE:
16182     case EL_SP_DISK_RED:
16183     case EL_DYNABOMB_INCREASE_NUMBER:
16184     case EL_DYNABOMB_INCREASE_SIZE:
16185     case EL_DYNABOMB_INCREASE_POWER:
16186       RaiseScore(level.score[SC_DYNAMITE]);
16187       break;
16188     case EL_SHIELD_NORMAL:
16189     case EL_SHIELD_DEADLY:
16190       RaiseScore(level.score[SC_SHIELD]);
16191       break;
16192     case EL_EXTRA_TIME:
16193       RaiseScore(level.extra_time_score);
16194       break;
16195     case EL_KEY_1:
16196     case EL_KEY_2:
16197     case EL_KEY_3:
16198     case EL_KEY_4:
16199     case EL_EM_KEY_1:
16200     case EL_EM_KEY_2:
16201     case EL_EM_KEY_3:
16202     case EL_EM_KEY_4:
16203     case EL_EMC_KEY_5:
16204     case EL_EMC_KEY_6:
16205     case EL_EMC_KEY_7:
16206     case EL_EMC_KEY_8:
16207     case EL_DC_KEY_WHITE:
16208       RaiseScore(level.score[SC_KEY]);
16209       break;
16210     default:
16211       RaiseScore(element_info[element].collect_score);
16212       break;
16213   }
16214 }
16215
16216 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16217 {
16218   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16219   {
16220     if (!quick_quit)
16221     {
16222       // prevent short reactivation of overlay buttons while closing door
16223       SetOverlayActive(FALSE);
16224       UnmapGameButtons();
16225
16226       // door may still be open due to skipped or envelope style request
16227       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16228     }
16229
16230     if (network.enabled)
16231     {
16232       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16233     }
16234     else
16235     {
16236       // when using BD game engine, cover screen before fading out
16237       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16238         game_bd.cover_screen = TRUE;
16239
16240       if (quick_quit)
16241         FadeSkipNextFadeIn();
16242
16243       SetGameStatus(GAME_MODE_MAIN);
16244
16245       DrawMainMenu();
16246     }
16247   }
16248   else          // continue playing the game
16249   {
16250     if (tape.playing && tape.deactivate_display)
16251       TapeDeactivateDisplayOff(TRUE);
16252
16253     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16254
16255     if (tape.playing && tape.deactivate_display)
16256       TapeDeactivateDisplayOn();
16257   }
16258 }
16259
16260 void RequestQuitGame(boolean escape_key_pressed)
16261 {
16262   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16263   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16264                         level_editor_test_game);
16265   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16266                           quick_quit || score_info_tape_play);
16267
16268   RequestQuitGameExt(skip_request, quick_quit,
16269                      "Do you really want to quit the game?");
16270 }
16271
16272 static char *getRestartGameMessage(void)
16273 {
16274   boolean play_again = hasStartedNetworkGame();
16275   static char message[MAX_OUTPUT_LINESIZE];
16276   char *game_over_text = "Game over!";
16277   char *play_again_text = " Play it again?";
16278
16279   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16280       game_mm.game_over_message != NULL)
16281     game_over_text = game_mm.game_over_message;
16282
16283   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16284            (play_again ? play_again_text : ""));
16285
16286   return message;
16287 }
16288
16289 static void RequestRestartGame(void)
16290 {
16291   char *message = getRestartGameMessage();
16292   boolean has_started_game = hasStartedNetworkGame();
16293   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16294   int door_state = DOOR_CLOSE_1;
16295
16296   boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
16297
16298   // if no restart wanted, continue with next level for BD style intermission levels
16299   if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
16300   {
16301     boolean success = AdvanceToNextLevel();
16302
16303     restart_wanted = (success && setup.auto_play_next_level);
16304   }
16305
16306   if (restart_wanted)
16307   {
16308     CloseDoor(door_state);
16309
16310     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16311   }
16312   else
16313   {
16314     // if game was invoked from level editor, also close tape recorder door
16315     if (level_editor_test_game)
16316       door_state = DOOR_CLOSE_ALL;
16317
16318     CloseDoor(door_state);
16319
16320     SetGameStatus(GAME_MODE_MAIN);
16321
16322     DrawMainMenu();
16323   }
16324 }
16325
16326 boolean CheckRestartGame(void)
16327 {
16328   static int game_over_delay = 0;
16329   int game_over_delay_value = 50;
16330   boolean game_over = checkGameFailed();
16331
16332   if (!game_over)
16333   {
16334     game_over_delay = game_over_delay_value;
16335
16336     return FALSE;
16337   }
16338
16339   if (game_over_delay > 0)
16340   {
16341     if (game_over_delay == game_over_delay_value / 2)
16342       PlaySound(SND_GAME_LOSING);
16343
16344     game_over_delay--;
16345
16346     return FALSE;
16347   }
16348
16349   // do not ask to play again if request dialog is already active
16350   if (checkRequestActive())
16351     return FALSE;
16352
16353   // do not ask to play again if request dialog already handled
16354   if (game.RestartGameRequested)
16355     return FALSE;
16356
16357   // do not ask to play again if game was never actually played
16358   if (!game.GamePlayed)
16359     return FALSE;
16360
16361   // do not ask to play again if this was disabled in setup menu
16362   if (!setup.ask_on_game_over)
16363     return FALSE;
16364
16365   game.RestartGameRequested = TRUE;
16366
16367   RequestRestartGame();
16368
16369   return TRUE;
16370 }
16371
16372 boolean checkGameRunning(void)
16373 {
16374   if (game_status != GAME_MODE_PLAYING)
16375     return FALSE;
16376
16377   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16378     return FALSE;
16379
16380   return TRUE;
16381 }
16382
16383 boolean checkGamePlaying(void)
16384 {
16385   if (game_status != GAME_MODE_PLAYING)
16386     return FALSE;
16387
16388   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16389     return FALSE;
16390
16391   return TRUE;
16392 }
16393
16394 boolean checkGameSolved(void)
16395 {
16396   // set for all game engines if level was solved
16397   return game.LevelSolved_GameEnd;
16398 }
16399
16400 boolean checkGameFailed(void)
16401 {
16402   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16403     return (game_bd.game_over && !game_bd.level_solved);
16404   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16405     return (game_em.game_over && !game_em.level_solved);
16406   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16407     return (game_sp.game_over && !game_sp.level_solved);
16408   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16409     return (game_mm.game_over && !game_mm.level_solved);
16410   else                          // GAME_ENGINE_TYPE_RND
16411     return (game.GameOver && !game.LevelSolved);
16412 }
16413
16414 boolean checkGameEnded(void)
16415 {
16416   return (checkGameSolved() || checkGameFailed());
16417 }
16418
16419 boolean checkRequestActive(void)
16420 {
16421   return (game.request_active || game.envelope_active || game.any_door_active);
16422 }
16423
16424
16425 // ----------------------------------------------------------------------------
16426 // random generator functions
16427 // ----------------------------------------------------------------------------
16428
16429 unsigned int InitEngineRandom_RND(int seed)
16430 {
16431   game.num_random_calls = 0;
16432
16433   return InitEngineRandom(seed);
16434 }
16435
16436 unsigned int RND(int max)
16437 {
16438   if (max > 0)
16439   {
16440     game.num_random_calls++;
16441
16442     return GetEngineRandom(max);
16443   }
16444
16445   return 0;
16446 }
16447
16448
16449 // ----------------------------------------------------------------------------
16450 // game engine snapshot handling functions
16451 // ----------------------------------------------------------------------------
16452
16453 struct EngineSnapshotInfo
16454 {
16455   // runtime values for custom element collect score
16456   int collect_score[NUM_CUSTOM_ELEMENTS];
16457
16458   // runtime values for group element choice position
16459   int choice_pos[NUM_GROUP_ELEMENTS];
16460
16461   // runtime values for belt position animations
16462   int belt_graphic[4][NUM_BELT_PARTS];
16463   int belt_anim_mode[4][NUM_BELT_PARTS];
16464 };
16465
16466 static struct EngineSnapshotInfo engine_snapshot_rnd;
16467 static char *snapshot_level_identifier = NULL;
16468 static int snapshot_level_nr = -1;
16469
16470 static void SaveEngineSnapshotValues_RND(void)
16471 {
16472   static int belt_base_active_element[4] =
16473   {
16474     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16475     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16476     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16477     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16478   };
16479   int i, j;
16480
16481   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16482   {
16483     int element = EL_CUSTOM_START + i;
16484
16485     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16486   }
16487
16488   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16489   {
16490     int element = EL_GROUP_START + i;
16491
16492     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16493   }
16494
16495   for (i = 0; i < 4; i++)
16496   {
16497     for (j = 0; j < NUM_BELT_PARTS; j++)
16498     {
16499       int element = belt_base_active_element[i] + j;
16500       int graphic = el2img(element);
16501       int anim_mode = graphic_info[graphic].anim_mode;
16502
16503       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16504       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16505     }
16506   }
16507 }
16508
16509 static void LoadEngineSnapshotValues_RND(void)
16510 {
16511   unsigned int num_random_calls = game.num_random_calls;
16512   int i, j;
16513
16514   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16515   {
16516     int element = EL_CUSTOM_START + i;
16517
16518     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16519   }
16520
16521   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16522   {
16523     int element = EL_GROUP_START + i;
16524
16525     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16526   }
16527
16528   for (i = 0; i < 4; i++)
16529   {
16530     for (j = 0; j < NUM_BELT_PARTS; j++)
16531     {
16532       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16533       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16534
16535       graphic_info[graphic].anim_mode = anim_mode;
16536     }
16537   }
16538
16539   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16540   {
16541     InitRND(tape.random_seed);
16542     for (i = 0; i < num_random_calls; i++)
16543       RND(1);
16544   }
16545
16546   if (game.num_random_calls != num_random_calls)
16547   {
16548     Error("number of random calls out of sync");
16549     Error("number of random calls should be %d", num_random_calls);
16550     Error("number of random calls is %d", game.num_random_calls);
16551
16552     Fail("this should not happen -- please debug");
16553   }
16554 }
16555
16556 void FreeEngineSnapshotSingle(void)
16557 {
16558   FreeSnapshotSingle();
16559
16560   setString(&snapshot_level_identifier, NULL);
16561   snapshot_level_nr = -1;
16562 }
16563
16564 void FreeEngineSnapshotList(void)
16565 {
16566   FreeSnapshotList();
16567 }
16568
16569 static ListNode *SaveEngineSnapshotBuffers(void)
16570 {
16571   ListNode *buffers = NULL;
16572
16573   // copy some special values to a structure better suited for the snapshot
16574
16575   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16576     SaveEngineSnapshotValues_RND();
16577   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16578     SaveEngineSnapshotValues_EM();
16579   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16580     SaveEngineSnapshotValues_SP(&buffers);
16581   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16582     SaveEngineSnapshotValues_MM();
16583
16584   // save values stored in special snapshot structure
16585
16586   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16587     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16588   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16589     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16590   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16591     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16592   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16593     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16594
16595   // save further RND engine values
16596
16597   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16600
16601   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16603   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16606   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16607
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16611
16612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16613
16614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16616
16617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16624   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16625   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16627   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16628   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16629   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16630   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16631   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16632   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16633   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16634   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16635
16636   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16638
16639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16642
16643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16645
16646   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16647   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16648   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16652
16653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16655
16656 #if 0
16657   ListNode *node = engine_snapshot_list_rnd;
16658   int num_bytes = 0;
16659
16660   while (node != NULL)
16661   {
16662     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16663
16664     node = node->next;
16665   }
16666
16667   Debug("game:playing:SaveEngineSnapshotBuffers",
16668         "size of engine snapshot: %d bytes", num_bytes);
16669 #endif
16670
16671   return buffers;
16672 }
16673
16674 void SaveEngineSnapshotSingle(void)
16675 {
16676   ListNode *buffers = SaveEngineSnapshotBuffers();
16677
16678   // finally save all snapshot buffers to single snapshot
16679   SaveSnapshotSingle(buffers);
16680
16681   // save level identification information
16682   setString(&snapshot_level_identifier, leveldir_current->identifier);
16683   snapshot_level_nr = level_nr;
16684 }
16685
16686 boolean CheckSaveEngineSnapshotToList(void)
16687 {
16688   boolean save_snapshot =
16689     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16690      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16691       game.snapshot.changed_action) ||
16692      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16693       game.snapshot.collected_item));
16694
16695   game.snapshot.changed_action = FALSE;
16696   game.snapshot.collected_item = FALSE;
16697   game.snapshot.save_snapshot = save_snapshot;
16698
16699   return save_snapshot;
16700 }
16701
16702 void SaveEngineSnapshotToList(void)
16703 {
16704   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16705       tape.quick_resume)
16706     return;
16707
16708   ListNode *buffers = SaveEngineSnapshotBuffers();
16709
16710   // finally save all snapshot buffers to snapshot list
16711   SaveSnapshotToList(buffers);
16712 }
16713
16714 void SaveEngineSnapshotToListInitial(void)
16715 {
16716   FreeEngineSnapshotList();
16717
16718   SaveEngineSnapshotToList();
16719 }
16720
16721 static void LoadEngineSnapshotValues(void)
16722 {
16723   // restore special values from snapshot structure
16724
16725   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16726     LoadEngineSnapshotValues_RND();
16727   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16728     LoadEngineSnapshotValues_EM();
16729   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16730     LoadEngineSnapshotValues_SP();
16731   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16732     LoadEngineSnapshotValues_MM();
16733 }
16734
16735 void LoadEngineSnapshotSingle(void)
16736 {
16737   LoadSnapshotSingle();
16738
16739   LoadEngineSnapshotValues();
16740 }
16741
16742 static void LoadEngineSnapshot_Undo(int steps)
16743 {
16744   LoadSnapshotFromList_Older(steps);
16745
16746   LoadEngineSnapshotValues();
16747 }
16748
16749 static void LoadEngineSnapshot_Redo(int steps)
16750 {
16751   LoadSnapshotFromList_Newer(steps);
16752
16753   LoadEngineSnapshotValues();
16754 }
16755
16756 boolean CheckEngineSnapshotSingle(void)
16757 {
16758   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16759           snapshot_level_nr == level_nr);
16760 }
16761
16762 boolean CheckEngineSnapshotList(void)
16763 {
16764   return CheckSnapshotList();
16765 }
16766
16767
16768 // ---------- new game button stuff -------------------------------------------
16769
16770 static struct
16771 {
16772   int graphic;
16773   struct XY *pos;
16774   int gadget_id;
16775   boolean *setup_value;
16776   boolean allowed_on_tape;
16777   boolean is_touch_button;
16778   char *infotext;
16779 } gamebutton_info[NUM_GAME_BUTTONS] =
16780 {
16781   {
16782     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16783     GAME_CTRL_ID_STOP,                          NULL,
16784     TRUE, FALSE,                                "stop game"
16785   },
16786   {
16787     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16788     GAME_CTRL_ID_PAUSE,                         NULL,
16789     TRUE, FALSE,                                "pause game"
16790   },
16791   {
16792     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16793     GAME_CTRL_ID_PLAY,                          NULL,
16794     TRUE, FALSE,                                "play game"
16795   },
16796   {
16797     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16798     GAME_CTRL_ID_UNDO,                          NULL,
16799     TRUE, FALSE,                                "undo step"
16800   },
16801   {
16802     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16803     GAME_CTRL_ID_REDO,                          NULL,
16804     TRUE, FALSE,                                "redo step"
16805   },
16806   {
16807     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16808     GAME_CTRL_ID_SAVE,                          NULL,
16809     TRUE, FALSE,                                "save game"
16810   },
16811   {
16812     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16813     GAME_CTRL_ID_PAUSE2,                        NULL,
16814     TRUE, FALSE,                                "pause game"
16815   },
16816   {
16817     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16818     GAME_CTRL_ID_LOAD,                          NULL,
16819     TRUE, FALSE,                                "load game"
16820   },
16821   {
16822     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16823     GAME_CTRL_ID_RESTART,                       NULL,
16824     TRUE, FALSE,                                "restart game"
16825   },
16826   {
16827     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16828     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16829     FALSE, FALSE,                               "stop game"
16830   },
16831   {
16832     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16833     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16834     FALSE, FALSE,                               "pause game"
16835   },
16836   {
16837     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16838     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16839     FALSE, FALSE,                               "play game"
16840   },
16841   {
16842     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16843     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16844     FALSE, FALSE,                               "restart game"
16845   },
16846   {
16847     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16848     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16849     FALSE, TRUE,                                "stop game"
16850   },
16851   {
16852     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16853     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16854     FALSE, TRUE,                                "pause game"
16855   },
16856   {
16857     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16858     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16859     FALSE, TRUE,                                "restart game"
16860   },
16861   {
16862     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16863     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16864     TRUE, FALSE,                                "background music on/off"
16865   },
16866   {
16867     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16868     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16869     TRUE, FALSE,                                "sound loops on/off"
16870   },
16871   {
16872     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16873     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16874     TRUE, FALSE,                                "normal sounds on/off"
16875   },
16876   {
16877     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16878     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16879     FALSE, FALSE,                               "background music on/off"
16880   },
16881   {
16882     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16883     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16884     FALSE, FALSE,                               "sound loops on/off"
16885   },
16886   {
16887     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16888     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16889     FALSE, FALSE,                               "normal sounds on/off"
16890   }
16891 };
16892
16893 void CreateGameButtons(void)
16894 {
16895   int i;
16896
16897   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16898   {
16899     int graphic = gamebutton_info[i].graphic;
16900     struct GraphicInfo *gfx = &graphic_info[graphic];
16901     struct XY *pos = gamebutton_info[i].pos;
16902     struct GadgetInfo *gi;
16903     int button_type;
16904     boolean checked;
16905     unsigned int event_mask;
16906     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16907     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16908     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16909     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16910     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16911     int gd_x   = gfx->src_x;
16912     int gd_y   = gfx->src_y;
16913     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16914     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16915     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16916     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16917     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16918     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16919     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16920     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16921     int id = i;
16922
16923     // do not use touch buttons if overlay touch buttons are disabled
16924     if (is_touch_button && !setup.touch.overlay_buttons)
16925       continue;
16926
16927     if (gfx->bitmap == NULL)
16928     {
16929       game_gadget[id] = NULL;
16930
16931       continue;
16932     }
16933
16934     if (id == GAME_CTRL_ID_STOP ||
16935         id == GAME_CTRL_ID_PANEL_STOP ||
16936         id == GAME_CTRL_ID_TOUCH_STOP ||
16937         id == GAME_CTRL_ID_PLAY ||
16938         id == GAME_CTRL_ID_PANEL_PLAY ||
16939         id == GAME_CTRL_ID_SAVE ||
16940         id == GAME_CTRL_ID_LOAD ||
16941         id == GAME_CTRL_ID_RESTART ||
16942         id == GAME_CTRL_ID_PANEL_RESTART ||
16943         id == GAME_CTRL_ID_TOUCH_RESTART)
16944     {
16945       button_type = GD_TYPE_NORMAL_BUTTON;
16946       checked = FALSE;
16947       event_mask = GD_EVENT_RELEASED;
16948     }
16949     else if (id == GAME_CTRL_ID_UNDO ||
16950              id == GAME_CTRL_ID_REDO)
16951     {
16952       button_type = GD_TYPE_NORMAL_BUTTON;
16953       checked = FALSE;
16954       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16955     }
16956     else
16957     {
16958       button_type = GD_TYPE_CHECK_BUTTON;
16959       checked = (gamebutton_info[i].setup_value != NULL ?
16960                  *gamebutton_info[i].setup_value : FALSE);
16961       event_mask = GD_EVENT_PRESSED;
16962     }
16963
16964     gi = CreateGadget(GDI_CUSTOM_ID, id,
16965                       GDI_IMAGE_ID, graphic,
16966                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16967                       GDI_X, base_x + x,
16968                       GDI_Y, base_y + y,
16969                       GDI_WIDTH, gfx->width,
16970                       GDI_HEIGHT, gfx->height,
16971                       GDI_TYPE, button_type,
16972                       GDI_STATE, GD_BUTTON_UNPRESSED,
16973                       GDI_CHECKED, checked,
16974                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16975                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16976                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16977                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16978                       GDI_DIRECT_DRAW, FALSE,
16979                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16980                       GDI_EVENT_MASK, event_mask,
16981                       GDI_CALLBACK_ACTION, HandleGameButtons,
16982                       GDI_END);
16983
16984     if (gi == NULL)
16985       Fail("cannot create gadget");
16986
16987     game_gadget[id] = gi;
16988   }
16989 }
16990
16991 void FreeGameButtons(void)
16992 {
16993   int i;
16994
16995   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16996     FreeGadget(game_gadget[i]);
16997 }
16998
16999 static void UnmapGameButtonsAtSamePosition(int id)
17000 {
17001   int i;
17002
17003   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17004     if (i != id &&
17005         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
17006         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
17007       UnmapGadget(game_gadget[i]);
17008 }
17009
17010 static void UnmapGameButtonsAtSamePosition_All(void)
17011 {
17012   if (setup.show_load_save_buttons)
17013   {
17014     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17015     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17016     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17017   }
17018   else if (setup.show_undo_redo_buttons)
17019   {
17020     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17021     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17022     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17023   }
17024   else
17025   {
17026     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
17027     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17028     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17029
17030     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17031     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17032     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17033   }
17034 }
17035
17036 void MapLoadSaveButtons(void)
17037 {
17038   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17039   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17040
17041   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17042   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17043 }
17044
17045 void MapUndoRedoButtons(void)
17046 {
17047   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17048   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17049
17050   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17051   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17052 }
17053
17054 void ModifyPauseButtons(void)
17055 {
17056   static int ids[] =
17057   {
17058     GAME_CTRL_ID_PAUSE,
17059     GAME_CTRL_ID_PAUSE2,
17060     GAME_CTRL_ID_PANEL_PAUSE,
17061     GAME_CTRL_ID_TOUCH_PAUSE,
17062     -1
17063   };
17064   int i;
17065
17066   // do not redraw pause button on closed door (may happen when restarting game)
17067   if (!(GetDoorState() & DOOR_OPEN_1))
17068     return;
17069
17070   for (i = 0; ids[i] > -1; i++)
17071     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17072 }
17073
17074 static void MapGameButtonsExt(boolean on_tape)
17075 {
17076   int i;
17077
17078   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17079   {
17080     if ((i == GAME_CTRL_ID_UNDO ||
17081          i == GAME_CTRL_ID_REDO) &&
17082         game_status != GAME_MODE_PLAYING)
17083       continue;
17084
17085     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17086       MapGadget(game_gadget[i]);
17087   }
17088
17089   UnmapGameButtonsAtSamePosition_All();
17090
17091   RedrawGameButtons();
17092 }
17093
17094 static void UnmapGameButtonsExt(boolean on_tape)
17095 {
17096   int i;
17097
17098   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17099     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17100       UnmapGadget(game_gadget[i]);
17101 }
17102
17103 static void RedrawGameButtonsExt(boolean on_tape)
17104 {
17105   int i;
17106
17107   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17108     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17109       RedrawGadget(game_gadget[i]);
17110 }
17111
17112 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17113 {
17114   if (gi == NULL)
17115     return;
17116
17117   gi->checked = state;
17118 }
17119
17120 static void RedrawSoundButtonGadget(int id)
17121 {
17122   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17123              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17124              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17125              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17126              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17127              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17128              id);
17129
17130   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17131   RedrawGadget(game_gadget[id2]);
17132 }
17133
17134 void MapGameButtons(void)
17135 {
17136   MapGameButtonsExt(FALSE);
17137 }
17138
17139 void UnmapGameButtons(void)
17140 {
17141   UnmapGameButtonsExt(FALSE);
17142 }
17143
17144 void RedrawGameButtons(void)
17145 {
17146   RedrawGameButtonsExt(FALSE);
17147 }
17148
17149 void MapGameButtonsOnTape(void)
17150 {
17151   MapGameButtonsExt(TRUE);
17152 }
17153
17154 void UnmapGameButtonsOnTape(void)
17155 {
17156   UnmapGameButtonsExt(TRUE);
17157 }
17158
17159 void RedrawGameButtonsOnTape(void)
17160 {
17161   RedrawGameButtonsExt(TRUE);
17162 }
17163
17164 static void GameUndoRedoExt(void)
17165 {
17166   ClearPlayerAction();
17167
17168   tape.pausing = TRUE;
17169
17170   RedrawPlayfield();
17171   UpdateAndDisplayGameControlValues();
17172
17173   DrawCompleteVideoDisplay();
17174   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17175   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17176   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17177
17178   ModifyPauseButtons();
17179
17180   BackToFront();
17181 }
17182
17183 static void GameUndo(int steps)
17184 {
17185   if (!CheckEngineSnapshotList())
17186     return;
17187
17188   int tape_property_bits = tape.property_bits;
17189
17190   LoadEngineSnapshot_Undo(steps);
17191
17192   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17193
17194   GameUndoRedoExt();
17195 }
17196
17197 static void GameRedo(int steps)
17198 {
17199   if (!CheckEngineSnapshotList())
17200     return;
17201
17202   int tape_property_bits = tape.property_bits;
17203
17204   LoadEngineSnapshot_Redo(steps);
17205
17206   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17207
17208   GameUndoRedoExt();
17209 }
17210
17211 static void HandleGameButtonsExt(int id, int button)
17212 {
17213   static boolean game_undo_executed = FALSE;
17214   int steps = BUTTON_STEPSIZE(button);
17215   boolean handle_game_buttons =
17216     (game_status == GAME_MODE_PLAYING ||
17217      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17218
17219   if (!handle_game_buttons)
17220     return;
17221
17222   switch (id)
17223   {
17224     case GAME_CTRL_ID_STOP:
17225     case GAME_CTRL_ID_PANEL_STOP:
17226     case GAME_CTRL_ID_TOUCH_STOP:
17227       TapeStopGame();
17228
17229       break;
17230
17231     case GAME_CTRL_ID_PAUSE:
17232     case GAME_CTRL_ID_PAUSE2:
17233     case GAME_CTRL_ID_PANEL_PAUSE:
17234     case GAME_CTRL_ID_TOUCH_PAUSE:
17235       if (network.enabled && game_status == GAME_MODE_PLAYING)
17236       {
17237         if (tape.pausing)
17238           SendToServer_ContinuePlaying();
17239         else
17240           SendToServer_PausePlaying();
17241       }
17242       else
17243         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17244
17245       game_undo_executed = FALSE;
17246
17247       break;
17248
17249     case GAME_CTRL_ID_PLAY:
17250     case GAME_CTRL_ID_PANEL_PLAY:
17251       if (game_status == GAME_MODE_MAIN)
17252       {
17253         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17254       }
17255       else if (tape.pausing)
17256       {
17257         if (network.enabled)
17258           SendToServer_ContinuePlaying();
17259         else
17260           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17261       }
17262       break;
17263
17264     case GAME_CTRL_ID_UNDO:
17265       // Important: When using "save snapshot when collecting an item" mode,
17266       // load last (current) snapshot for first "undo" after pressing "pause"
17267       // (else the last-but-one snapshot would be loaded, because the snapshot
17268       // pointer already points to the last snapshot when pressing "pause",
17269       // which is fine for "every step/move" mode, but not for "every collect")
17270       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17271           !game_undo_executed)
17272         steps--;
17273
17274       game_undo_executed = TRUE;
17275
17276       GameUndo(steps);
17277       break;
17278
17279     case GAME_CTRL_ID_REDO:
17280       GameRedo(steps);
17281       break;
17282
17283     case GAME_CTRL_ID_SAVE:
17284       TapeQuickSave();
17285       break;
17286
17287     case GAME_CTRL_ID_LOAD:
17288       TapeQuickLoad();
17289       break;
17290
17291     case GAME_CTRL_ID_RESTART:
17292     case GAME_CTRL_ID_PANEL_RESTART:
17293     case GAME_CTRL_ID_TOUCH_RESTART:
17294       TapeRestartGame();
17295
17296       break;
17297
17298     case SOUND_CTRL_ID_MUSIC:
17299     case SOUND_CTRL_ID_PANEL_MUSIC:
17300       if (setup.sound_music)
17301       { 
17302         setup.sound_music = FALSE;
17303
17304         FadeMusic();
17305       }
17306       else if (audio.music_available)
17307       { 
17308         setup.sound = setup.sound_music = TRUE;
17309
17310         SetAudioMode(setup.sound);
17311
17312         if (game_status == GAME_MODE_PLAYING)
17313           PlayLevelMusic();
17314       }
17315
17316       RedrawSoundButtonGadget(id);
17317
17318       break;
17319
17320     case SOUND_CTRL_ID_LOOPS:
17321     case SOUND_CTRL_ID_PANEL_LOOPS:
17322       if (setup.sound_loops)
17323         setup.sound_loops = FALSE;
17324       else if (audio.loops_available)
17325       {
17326         setup.sound = setup.sound_loops = TRUE;
17327
17328         SetAudioMode(setup.sound);
17329       }
17330
17331       RedrawSoundButtonGadget(id);
17332
17333       break;
17334
17335     case SOUND_CTRL_ID_SIMPLE:
17336     case SOUND_CTRL_ID_PANEL_SIMPLE:
17337       if (setup.sound_simple)
17338         setup.sound_simple = FALSE;
17339       else if (audio.sound_available)
17340       {
17341         setup.sound = setup.sound_simple = TRUE;
17342
17343         SetAudioMode(setup.sound);
17344       }
17345
17346       RedrawSoundButtonGadget(id);
17347
17348       break;
17349
17350     default:
17351       break;
17352   }
17353 }
17354
17355 static void HandleGameButtons(struct GadgetInfo *gi)
17356 {
17357   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17358 }
17359
17360 void HandleSoundButtonKeys(Key key)
17361 {
17362   if (key == setup.shortcut.sound_simple)
17363     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17364   else if (key == setup.shortcut.sound_loops)
17365     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17366   else if (key == setup.shortcut.sound_music)
17367     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17368 }