added config parameters for native BD graphics/sound engine (not used yet)
[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_TOTAL                   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_TOTAL,
250     &game.panel.gems_total,
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 InitField(int x, int y, boolean init_game)
1839 {
1840   int element = Tile[x][y];
1841
1842   switch (element)
1843   {
1844     case EL_SP_MURPHY:
1845     case EL_PLAYER_1:
1846     case EL_PLAYER_2:
1847     case EL_PLAYER_3:
1848     case EL_PLAYER_4:
1849       InitPlayerField(x, y, element, init_game);
1850       break;
1851
1852     case EL_SOKOBAN_FIELD_PLAYER:
1853       element = Tile[x][y] = EL_PLAYER_1;
1854       InitField(x, y, init_game);
1855
1856       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1857       InitField(x, y, init_game);
1858       break;
1859
1860     case EL_SOKOBAN_FIELD_EMPTY:
1861       IncrementSokobanFieldsNeeded();
1862       break;
1863
1864     case EL_SOKOBAN_OBJECT:
1865       IncrementSokobanObjectsNeeded();
1866       break;
1867
1868     case EL_STONEBLOCK:
1869       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1870         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1871       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1872         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1873       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1874         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1875       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1876         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1877       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1878         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1879       break;
1880
1881     case EL_BUG:
1882     case EL_BUG_RIGHT:
1883     case EL_BUG_UP:
1884     case EL_BUG_LEFT:
1885     case EL_BUG_DOWN:
1886     case EL_SPACESHIP:
1887     case EL_SPACESHIP_RIGHT:
1888     case EL_SPACESHIP_UP:
1889     case EL_SPACESHIP_LEFT:
1890     case EL_SPACESHIP_DOWN:
1891     case EL_BD_BUTTERFLY:
1892     case EL_BD_BUTTERFLY_RIGHT:
1893     case EL_BD_BUTTERFLY_UP:
1894     case EL_BD_BUTTERFLY_LEFT:
1895     case EL_BD_BUTTERFLY_DOWN:
1896     case EL_BD_FIREFLY:
1897     case EL_BD_FIREFLY_RIGHT:
1898     case EL_BD_FIREFLY_UP:
1899     case EL_BD_FIREFLY_LEFT:
1900     case EL_BD_FIREFLY_DOWN:
1901     case EL_PACMAN_RIGHT:
1902     case EL_PACMAN_UP:
1903     case EL_PACMAN_LEFT:
1904     case EL_PACMAN_DOWN:
1905     case EL_YAMYAM:
1906     case EL_YAMYAM_LEFT:
1907     case EL_YAMYAM_RIGHT:
1908     case EL_YAMYAM_UP:
1909     case EL_YAMYAM_DOWN:
1910     case EL_DARK_YAMYAM:
1911     case EL_ROBOT:
1912     case EL_PACMAN:
1913     case EL_SP_SNIKSNAK:
1914     case EL_SP_ELECTRON:
1915     case EL_MOLE:
1916     case EL_MOLE_LEFT:
1917     case EL_MOLE_RIGHT:
1918     case EL_MOLE_UP:
1919     case EL_MOLE_DOWN:
1920     case EL_SPRING_LEFT:
1921     case EL_SPRING_RIGHT:
1922       InitMovDir(x, y);
1923       break;
1924
1925     case EL_AMOEBA_FULL:
1926     case EL_BD_AMOEBA:
1927       InitAmoebaNr(x, y);
1928       break;
1929
1930     case EL_AMOEBA_DROP:
1931       if (y == lev_fieldy - 1)
1932       {
1933         Tile[x][y] = EL_AMOEBA_GROWING;
1934         Store[x][y] = EL_AMOEBA_WET;
1935       }
1936       break;
1937
1938     case EL_DYNAMITE_ACTIVE:
1939     case EL_SP_DISK_RED_ACTIVE:
1940     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1941     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1942     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1943     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1944       MovDelay[x][y] = 96;
1945       break;
1946
1947     case EL_EM_DYNAMITE_ACTIVE:
1948       MovDelay[x][y] = 32;
1949       break;
1950
1951     case EL_LAMP:
1952       game.lights_still_needed++;
1953       break;
1954
1955     case EL_PENGUIN:
1956       game.friends_still_needed++;
1957       break;
1958
1959     case EL_PIG:
1960     case EL_DRAGON:
1961       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1962       break;
1963
1964     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1965     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1966     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1967     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1968     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1969     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1970     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1971     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1972     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1973     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1974     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1975     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1976       if (init_game)
1977       {
1978         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1979         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1980         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1981
1982         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1983         {
1984           game.belt_dir[belt_nr] = belt_dir;
1985           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1986         }
1987         else    // more than one switch -- set it like the first switch
1988         {
1989           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1990         }
1991       }
1992       break;
1993
1994     case EL_LIGHT_SWITCH_ACTIVE:
1995       if (init_game)
1996         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1997       break;
1998
1999     case EL_INVISIBLE_STEELWALL:
2000     case EL_INVISIBLE_WALL:
2001     case EL_INVISIBLE_SAND:
2002       if (game.light_time_left > 0 ||
2003           game.lenses_time_left > 0)
2004         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2005       break;
2006
2007     case EL_EMC_MAGIC_BALL:
2008       if (game.ball_active)
2009         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2010       break;
2011
2012     case EL_EMC_MAGIC_BALL_SWITCH:
2013       if (game.ball_active)
2014         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2015       break;
2016
2017     case EL_TRIGGER_PLAYER:
2018     case EL_TRIGGER_ELEMENT:
2019     case EL_TRIGGER_CE_VALUE:
2020     case EL_TRIGGER_CE_SCORE:
2021     case EL_SELF:
2022     case EL_ANY_ELEMENT:
2023     case EL_CURRENT_CE_VALUE:
2024     case EL_CURRENT_CE_SCORE:
2025     case EL_PREV_CE_1:
2026     case EL_PREV_CE_2:
2027     case EL_PREV_CE_3:
2028     case EL_PREV_CE_4:
2029     case EL_PREV_CE_5:
2030     case EL_PREV_CE_6:
2031     case EL_PREV_CE_7:
2032     case EL_PREV_CE_8:
2033     case EL_NEXT_CE_1:
2034     case EL_NEXT_CE_2:
2035     case EL_NEXT_CE_3:
2036     case EL_NEXT_CE_4:
2037     case EL_NEXT_CE_5:
2038     case EL_NEXT_CE_6:
2039     case EL_NEXT_CE_7:
2040     case EL_NEXT_CE_8:
2041       // reference elements should not be used on the playfield
2042       Tile[x][y] = EL_EMPTY;
2043       break;
2044
2045     default:
2046       if (IS_CUSTOM_ELEMENT(element))
2047       {
2048         if (CAN_MOVE(element))
2049           InitMovDir(x, y);
2050
2051         if (!element_info[element].use_last_ce_value || init_game)
2052           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2053       }
2054       else if (IS_GROUP_ELEMENT(element))
2055       {
2056         Tile[x][y] = GetElementFromGroupElement(element);
2057
2058         InitField(x, y, init_game);
2059       }
2060       else if (IS_EMPTY_ELEMENT(element))
2061       {
2062         GfxElementEmpty[x][y] = element;
2063         Tile[x][y] = EL_EMPTY;
2064
2065         if (element_info[element].use_gfx_element)
2066           game.use_masked_elements = TRUE;
2067       }
2068
2069       break;
2070   }
2071
2072   if (!init_game)
2073     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2074 }
2075
2076 static void InitField_WithBug1(int x, int y, boolean init_game)
2077 {
2078   InitField(x, y, init_game);
2079
2080   // not needed to call InitMovDir() -- already done by InitField()!
2081   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2082       CAN_MOVE(Tile[x][y]))
2083     InitMovDir(x, y);
2084 }
2085
2086 static void InitField_WithBug2(int x, int y, boolean init_game)
2087 {
2088   int old_element = Tile[x][y];
2089
2090   InitField(x, y, init_game);
2091
2092   // not needed to call InitMovDir() -- already done by InitField()!
2093   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2094       CAN_MOVE(old_element) &&
2095       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2096     InitMovDir(x, y);
2097
2098   /* this case is in fact a combination of not less than three bugs:
2099      first, it calls InitMovDir() for elements that can move, although this is
2100      already done by InitField(); then, it checks the element that was at this
2101      field _before_ the call to InitField() (which can change it); lastly, it
2102      was not called for "mole with direction" elements, which were treated as
2103      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2104   */
2105 }
2106
2107 static int get_key_element_from_nr(int key_nr)
2108 {
2109   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2110                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2111                           EL_EM_KEY_1 : EL_KEY_1);
2112
2113   return key_base_element + key_nr;
2114 }
2115
2116 static int get_next_dropped_element(struct PlayerInfo *player)
2117 {
2118   return (player->inventory_size > 0 ?
2119           player->inventory_element[player->inventory_size - 1] :
2120           player->inventory_infinite_element != EL_UNDEFINED ?
2121           player->inventory_infinite_element :
2122           player->dynabombs_left > 0 ?
2123           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2124           EL_UNDEFINED);
2125 }
2126
2127 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2128 {
2129   // pos >= 0: get element from bottom of the stack;
2130   // pos <  0: get element from top of the stack
2131
2132   if (pos < 0)
2133   {
2134     int min_inventory_size = -pos;
2135     int inventory_pos = player->inventory_size - min_inventory_size;
2136     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2137
2138     return (player->inventory_size >= min_inventory_size ?
2139             player->inventory_element[inventory_pos] :
2140             player->inventory_infinite_element != EL_UNDEFINED ?
2141             player->inventory_infinite_element :
2142             player->dynabombs_left >= min_dynabombs_left ?
2143             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2144             EL_UNDEFINED);
2145   }
2146   else
2147   {
2148     int min_dynabombs_left = pos + 1;
2149     int min_inventory_size = pos + 1 - player->dynabombs_left;
2150     int inventory_pos = pos - player->dynabombs_left;
2151
2152     return (player->inventory_infinite_element != EL_UNDEFINED ?
2153             player->inventory_infinite_element :
2154             player->dynabombs_left >= min_dynabombs_left ?
2155             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2156             player->inventory_size >= min_inventory_size ?
2157             player->inventory_element[inventory_pos] :
2158             EL_UNDEFINED);
2159   }
2160 }
2161
2162 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2163 {
2164   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2165   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2166   int compare_result;
2167
2168   if (gpo1->sort_priority != gpo2->sort_priority)
2169     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2170   else
2171     compare_result = gpo1->nr - gpo2->nr;
2172
2173   return compare_result;
2174 }
2175
2176 int getPlayerInventorySize(int player_nr)
2177 {
2178   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179     return game_em.ply[player_nr]->dynamite;
2180   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2181     return game_sp.red_disk_count;
2182   else
2183     return stored_player[player_nr].inventory_size;
2184 }
2185
2186 static void InitGameControlValues(void)
2187 {
2188   int i;
2189
2190   for (i = 0; game_panel_controls[i].nr != -1; i++)
2191   {
2192     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2193     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2194     struct TextPosInfo *pos = gpc->pos;
2195     int nr = gpc->nr;
2196     int type = gpc->type;
2197
2198     if (nr != i)
2199     {
2200       Error("'game_panel_controls' structure corrupted at %d", i);
2201
2202       Fail("this should not happen -- please debug");
2203     }
2204
2205     // force update of game controls after initialization
2206     gpc->value = gpc->last_value = -1;
2207     gpc->frame = gpc->last_frame = -1;
2208     gpc->gfx_frame = -1;
2209
2210     // determine panel value width for later calculation of alignment
2211     if (type == TYPE_INTEGER || type == TYPE_STRING)
2212     {
2213       pos->width = pos->size * getFontWidth(pos->font);
2214       pos->height = getFontHeight(pos->font);
2215     }
2216     else if (type == TYPE_ELEMENT)
2217     {
2218       pos->width = pos->size;
2219       pos->height = pos->size;
2220     }
2221
2222     // fill structure for game panel draw order
2223     gpo->nr = gpc->nr;
2224     gpo->sort_priority = pos->sort_priority;
2225   }
2226
2227   // sort game panel controls according to sort_priority and control number
2228   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2229         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2230 }
2231
2232 static void UpdatePlayfieldElementCount(void)
2233 {
2234   boolean use_element_count = FALSE;
2235   int i, j, x, y;
2236
2237   // first check if it is needed at all to calculate playfield element count
2238   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2239     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2240       use_element_count = TRUE;
2241
2242   if (!use_element_count)
2243     return;
2244
2245   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2246     element_info[i].element_count = 0;
2247
2248   SCAN_PLAYFIELD(x, y)
2249   {
2250     element_info[Tile[x][y]].element_count++;
2251   }
2252
2253   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2254     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2255       if (IS_IN_GROUP(j, i))
2256         element_info[EL_GROUP_START + i].element_count +=
2257           element_info[j].element_count;
2258 }
2259
2260 static void UpdateGameControlValues(void)
2261 {
2262   int i, k;
2263   int time = (game.LevelSolved ?
2264               game.LevelSolved_CountingTime :
2265               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2266               game_em.lev->time :
2267               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2268               game_sp.time_played :
2269               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2270               game_mm.energy_left :
2271               game.no_level_time_limit ? TimePlayed : TimeLeft);
2272   int score = (game.LevelSolved ?
2273                game.LevelSolved_CountingScore :
2274                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2275                game_em.lev->score :
2276                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2277                game_sp.score :
2278                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2279                game_mm.score :
2280                game.score);
2281   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2282               game_em.lev->gems_needed :
2283               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2284               game_sp.infotrons_still_needed :
2285               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2286               game_mm.kettles_still_needed :
2287               game.gems_still_needed);
2288   int gems_total = level.gems_needed;
2289   int gems_collected = gems_total - gems;
2290   int gems_score = level.score[SC_EMERALD];
2291   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2292                      game_em.lev->gems_needed > 0 :
2293                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2294                      game_sp.infotrons_still_needed > 0 :
2295                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2296                      game_mm.kettles_still_needed > 0 ||
2297                      game_mm.lights_still_needed > 0 :
2298                      game.gems_still_needed > 0 ||
2299                      game.sokoban_fields_still_needed > 0 ||
2300                      game.sokoban_objects_still_needed > 0 ||
2301                      game.lights_still_needed > 0);
2302   int health = (game.LevelSolved ?
2303                 game.LevelSolved_CountingHealth :
2304                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2305                 MM_HEALTH(game_mm.laser_overload_value) :
2306                 game.health);
2307   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2308
2309   UpdatePlayfieldElementCount();
2310
2311   // update game panel control values
2312
2313   // used instead of "level_nr" (for network games)
2314   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2315   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2316   game_panel_controls[GAME_PANEL_GEMS_TOTAL].value = gems_total;
2317   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2318   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2319
2320   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2321   for (i = 0; i < MAX_NUM_KEYS; i++)
2322     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2323   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2324   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2325
2326   if (game.centered_player_nr == -1)
2327   {
2328     for (i = 0; i < MAX_PLAYERS; i++)
2329     {
2330       // only one player in Supaplex game engine
2331       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2332         break;
2333
2334       for (k = 0; k < MAX_NUM_KEYS; k++)
2335       {
2336         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2337         {
2338           if (game_em.ply[i]->keys & (1 << k))
2339             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2340               get_key_element_from_nr(k);
2341         }
2342         else if (stored_player[i].key[k])
2343           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2344             get_key_element_from_nr(k);
2345       }
2346
2347       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2348         getPlayerInventorySize(i);
2349
2350       if (stored_player[i].num_white_keys > 0)
2351         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2352           EL_DC_KEY_WHITE;
2353
2354       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355         stored_player[i].num_white_keys;
2356     }
2357   }
2358   else
2359   {
2360     int player_nr = game.centered_player_nr;
2361
2362     for (k = 0; k < MAX_NUM_KEYS; k++)
2363     {
2364       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2365       {
2366         if (game_em.ply[player_nr]->keys & (1 << k))
2367           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2368             get_key_element_from_nr(k);
2369       }
2370       else if (stored_player[player_nr].key[k])
2371         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2372           get_key_element_from_nr(k);
2373     }
2374
2375     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2376       getPlayerInventorySize(player_nr);
2377
2378     if (stored_player[player_nr].num_white_keys > 0)
2379       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2380
2381     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2382       stored_player[player_nr].num_white_keys;
2383   }
2384
2385   // re-arrange keys on game panel, if needed or if defined by style settings
2386   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2387   {
2388     int nr = GAME_PANEL_KEY_1 + i;
2389     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2390     struct TextPosInfo *pos = gpc->pos;
2391
2392     // skip check if key is not in the player's inventory
2393     if (gpc->value == EL_EMPTY)
2394       continue;
2395
2396     // check if keys should be arranged on panel from left to right
2397     if (pos->style == STYLE_LEFTMOST_POSITION)
2398     {
2399       // check previous key positions (left from current key)
2400       for (k = 0; k < i; k++)
2401       {
2402         int nr_new = GAME_PANEL_KEY_1 + k;
2403
2404         if (game_panel_controls[nr_new].value == EL_EMPTY)
2405         {
2406           game_panel_controls[nr_new].value = gpc->value;
2407           gpc->value = EL_EMPTY;
2408
2409           break;
2410         }
2411       }
2412     }
2413
2414     // check if "undefined" keys can be placed at some other position
2415     if (pos->x == -1 && pos->y == -1)
2416     {
2417       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2418
2419       // 1st try: display key at the same position as normal or EM keys
2420       if (game_panel_controls[nr_new].value == EL_EMPTY)
2421       {
2422         game_panel_controls[nr_new].value = gpc->value;
2423       }
2424       else
2425       {
2426         // 2nd try: display key at the next free position in the key panel
2427         for (k = 0; k < STD_NUM_KEYS; k++)
2428         {
2429           nr_new = GAME_PANEL_KEY_1 + k;
2430
2431           if (game_panel_controls[nr_new].value == EL_EMPTY)
2432           {
2433             game_panel_controls[nr_new].value = gpc->value;
2434
2435             break;
2436           }
2437         }
2438       }
2439     }
2440   }
2441
2442   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2443   {
2444     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2445       get_inventory_element_from_pos(local_player, i);
2446     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2447       get_inventory_element_from_pos(local_player, -i - 1);
2448   }
2449
2450   game_panel_controls[GAME_PANEL_SCORE].value = score;
2451   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2452
2453   game_panel_controls[GAME_PANEL_TIME].value = time;
2454
2455   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2456   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2457   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2458
2459   if (level.time == 0)
2460     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2461   else
2462     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2463
2464   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2465   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2466
2467   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2468
2469   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2470     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2471      EL_EMPTY);
2472   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2473     local_player->shield_normal_time_left;
2474   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2475     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2476      EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2478     local_player->shield_deadly_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EXIT].value =
2481     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2482
2483   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2484     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2485   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2486     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2487      EL_EMC_MAGIC_BALL_SWITCH);
2488
2489   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2490     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2491   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2492     game.light_time_left;
2493
2494   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2495     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2496   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2497     game.timegate_time_left;
2498
2499   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2500     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2501
2502   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2503     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2504   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2505     game.lenses_time_left;
2506
2507   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2508     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2509   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2510     game.magnify_time_left;
2511
2512   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2513     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2514      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2515      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2516      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2517      EL_BALLOON_SWITCH_NONE);
2518
2519   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2520     local_player->dynabomb_count;
2521   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2522     local_player->dynabomb_size;
2523   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2524     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2525
2526   game_panel_controls[GAME_PANEL_PENGUINS].value =
2527     game.friends_still_needed;
2528
2529   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2530     game.sokoban_objects_still_needed;
2531   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2532     game.sokoban_fields_still_needed;
2533
2534   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2535     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2536
2537   for (i = 0; i < NUM_BELTS; i++)
2538   {
2539     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2540       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2541        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2542     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2543       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2544   }
2545
2546   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2547     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2548   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2549     game.magic_wall_time_left;
2550
2551   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2552     local_player->gravity;
2553
2554   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2555     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2556
2557   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2558     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2559       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2560        game.panel.element[i].id : EL_UNDEFINED);
2561
2562   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2563     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2564       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2565        element_info[game.panel.element_count[i].id].element_count : 0);
2566
2567   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2568     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2569       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2570        element_info[game.panel.ce_score[i].id].collect_score : 0);
2571
2572   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2573     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2574       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2575        element_info[game.panel.ce_score_element[i].id].collect_score :
2576        EL_UNDEFINED);
2577
2578   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2579   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2580   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2581
2582   // update game panel control frames
2583
2584   for (i = 0; game_panel_controls[i].nr != -1; i++)
2585   {
2586     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2587
2588     if (gpc->type == TYPE_ELEMENT)
2589     {
2590       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2591       {
2592         int last_anim_random_frame = gfx.anim_random_frame;
2593         int element = gpc->value;
2594         int graphic = el2panelimg(element);
2595         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2596                                sync_random_frame :
2597                                graphic_info[graphic].anim_global_anim_sync ?
2598                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2599
2600         if (gpc->value != gpc->last_value)
2601         {
2602           gpc->gfx_frame = 0;
2603           gpc->gfx_random = init_gfx_random;
2604         }
2605         else
2606         {
2607           gpc->gfx_frame++;
2608
2609           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2610               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2611             gpc->gfx_random = init_gfx_random;
2612         }
2613
2614         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2615           gfx.anim_random_frame = gpc->gfx_random;
2616
2617         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2618           gpc->gfx_frame = element_info[element].collect_score;
2619
2620         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2621
2622         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2623           gfx.anim_random_frame = last_anim_random_frame;
2624       }
2625     }
2626     else if (gpc->type == TYPE_GRAPHIC)
2627     {
2628       if (gpc->graphic != IMG_UNDEFINED)
2629       {
2630         int last_anim_random_frame = gfx.anim_random_frame;
2631         int graphic = gpc->graphic;
2632         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2633                                sync_random_frame :
2634                                graphic_info[graphic].anim_global_anim_sync ?
2635                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2636
2637         if (gpc->value != gpc->last_value)
2638         {
2639           gpc->gfx_frame = 0;
2640           gpc->gfx_random = init_gfx_random;
2641         }
2642         else
2643         {
2644           gpc->gfx_frame++;
2645
2646           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2647               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2648             gpc->gfx_random = init_gfx_random;
2649         }
2650
2651         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2652           gfx.anim_random_frame = gpc->gfx_random;
2653
2654         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2655
2656         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2657           gfx.anim_random_frame = last_anim_random_frame;
2658       }
2659     }
2660   }
2661 }
2662
2663 static void DisplayGameControlValues(void)
2664 {
2665   boolean redraw_panel = FALSE;
2666   int i;
2667
2668   for (i = 0; game_panel_controls[i].nr != -1; i++)
2669   {
2670     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2671
2672     if (PANEL_DEACTIVATED(gpc->pos))
2673       continue;
2674
2675     if (gpc->value == gpc->last_value &&
2676         gpc->frame == gpc->last_frame)
2677       continue;
2678
2679     redraw_panel = TRUE;
2680   }
2681
2682   if (!redraw_panel)
2683     return;
2684
2685   // copy default game door content to main double buffer
2686
2687   // !!! CHECK AGAIN !!!
2688   SetPanelBackground();
2689   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2690   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2691
2692   // redraw game control buttons
2693   RedrawGameButtons();
2694
2695   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2696
2697   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2698   {
2699     int nr = game_panel_order[i].nr;
2700     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2701     struct TextPosInfo *pos = gpc->pos;
2702     int type = gpc->type;
2703     int value = gpc->value;
2704     int frame = gpc->frame;
2705     int size = pos->size;
2706     int font = pos->font;
2707     boolean draw_masked = pos->draw_masked;
2708     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2709
2710     if (PANEL_DEACTIVATED(pos))
2711       continue;
2712
2713     if (pos->class == get_hash_from_key("extra_panel_items") &&
2714         !setup.prefer_extra_panel_items)
2715       continue;
2716
2717     gpc->last_value = value;
2718     gpc->last_frame = frame;
2719
2720     if (type == TYPE_INTEGER)
2721     {
2722       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2723           nr == GAME_PANEL_INVENTORY_COUNT ||
2724           nr == GAME_PANEL_SCORE ||
2725           nr == GAME_PANEL_HIGHSCORE ||
2726           nr == GAME_PANEL_TIME)
2727       {
2728         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2729
2730         if (use_dynamic_size)           // use dynamic number of digits
2731         {
2732           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2733                               nr == GAME_PANEL_INVENTORY_COUNT ||
2734                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2735           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2736                           nr == GAME_PANEL_INVENTORY_COUNT ||
2737                           nr == GAME_PANEL_TIME ? 1 : 2);
2738           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2739                        nr == GAME_PANEL_INVENTORY_COUNT ||
2740                        nr == GAME_PANEL_TIME ? 3 : 5);
2741           int size2 = size1 + size_add;
2742           int font1 = pos->font;
2743           int font2 = pos->font_alt;
2744
2745           size = (value < value_change ? size1 : size2);
2746           font = (value < value_change ? font1 : font2);
2747         }
2748       }
2749
2750       // correct text size if "digits" is zero or less
2751       if (size <= 0)
2752         size = strlen(int2str(value, size));
2753
2754       // dynamically correct text alignment
2755       pos->width = size * getFontWidth(font);
2756
2757       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2758                   int2str(value, size), font, mask_mode);
2759     }
2760     else if (type == TYPE_ELEMENT)
2761     {
2762       int element, graphic;
2763       Bitmap *src_bitmap;
2764       int src_x, src_y;
2765       int width, height;
2766       int dst_x = PANEL_XPOS(pos);
2767       int dst_y = PANEL_YPOS(pos);
2768
2769       if (value != EL_UNDEFINED && value != EL_EMPTY)
2770       {
2771         element = value;
2772         graphic = el2panelimg(value);
2773
2774 #if 0
2775         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2776               element, EL_NAME(element), size);
2777 #endif
2778
2779         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2780           size = TILESIZE;
2781
2782         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2783                               &src_x, &src_y);
2784
2785         width  = graphic_info[graphic].width  * size / TILESIZE;
2786         height = graphic_info[graphic].height * size / TILESIZE;
2787
2788         if (draw_masked)
2789           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2790                            dst_x, dst_y);
2791         else
2792           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2793                      dst_x, dst_y);
2794       }
2795     }
2796     else if (type == TYPE_GRAPHIC)
2797     {
2798       int graphic        = gpc->graphic;
2799       int graphic_active = gpc->graphic_active;
2800       Bitmap *src_bitmap;
2801       int src_x, src_y;
2802       int width, height;
2803       int dst_x = PANEL_XPOS(pos);
2804       int dst_y = PANEL_YPOS(pos);
2805       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2806                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2807
2808       if (graphic != IMG_UNDEFINED && !skip)
2809       {
2810         if (pos->style == STYLE_REVERSE)
2811           value = 100 - value;
2812
2813         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2814
2815         if (pos->direction & MV_HORIZONTAL)
2816         {
2817           width  = graphic_info[graphic_active].width * value / 100;
2818           height = graphic_info[graphic_active].height;
2819
2820           if (pos->direction == MV_LEFT)
2821           {
2822             src_x += graphic_info[graphic_active].width - width;
2823             dst_x += graphic_info[graphic_active].width - width;
2824           }
2825         }
2826         else
2827         {
2828           width  = graphic_info[graphic_active].width;
2829           height = graphic_info[graphic_active].height * value / 100;
2830
2831           if (pos->direction == MV_UP)
2832           {
2833             src_y += graphic_info[graphic_active].height - height;
2834             dst_y += graphic_info[graphic_active].height - height;
2835           }
2836         }
2837
2838         if (draw_masked)
2839           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2840                            dst_x, dst_y);
2841         else
2842           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2843                      dst_x, dst_y);
2844
2845         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2846
2847         if (pos->direction & MV_HORIZONTAL)
2848         {
2849           if (pos->direction == MV_RIGHT)
2850           {
2851             src_x += width;
2852             dst_x += width;
2853           }
2854           else
2855           {
2856             dst_x = PANEL_XPOS(pos);
2857           }
2858
2859           width = graphic_info[graphic].width - width;
2860         }
2861         else
2862         {
2863           if (pos->direction == MV_DOWN)
2864           {
2865             src_y += height;
2866             dst_y += height;
2867           }
2868           else
2869           {
2870             dst_y = PANEL_YPOS(pos);
2871           }
2872
2873           height = graphic_info[graphic].height - height;
2874         }
2875
2876         if (draw_masked)
2877           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2878                            dst_x, dst_y);
2879         else
2880           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2881                      dst_x, dst_y);
2882       }
2883     }
2884     else if (type == TYPE_STRING)
2885     {
2886       boolean active = (value != 0);
2887       char *state_normal = "off";
2888       char *state_active = "on";
2889       char *state = (active ? state_active : state_normal);
2890       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2891                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2892                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2893                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2894
2895       if (nr == GAME_PANEL_GRAVITY_STATE)
2896       {
2897         int font1 = pos->font;          // (used for normal state)
2898         int font2 = pos->font_alt;      // (used for active state)
2899
2900         font = (active ? font2 : font1);
2901       }
2902
2903       if (s != NULL)
2904       {
2905         char *s_cut;
2906
2907         if (size <= 0)
2908         {
2909           // don't truncate output if "chars" is zero or less
2910           size = strlen(s);
2911
2912           // dynamically correct text alignment
2913           pos->width = size * getFontWidth(font);
2914         }
2915
2916         s_cut = getStringCopyN(s, size);
2917
2918         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2919                     s_cut, font, mask_mode);
2920
2921         free(s_cut);
2922       }
2923     }
2924
2925     redraw_mask |= REDRAW_DOOR_1;
2926   }
2927
2928   SetGameStatus(GAME_MODE_PLAYING);
2929 }
2930
2931 void UpdateAndDisplayGameControlValues(void)
2932 {
2933   if (tape.deactivate_display)
2934     return;
2935
2936   UpdateGameControlValues();
2937   DisplayGameControlValues();
2938 }
2939
2940 void UpdateGameDoorValues(void)
2941 {
2942   UpdateGameControlValues();
2943 }
2944
2945 void DrawGameDoorValues(void)
2946 {
2947   DisplayGameControlValues();
2948 }
2949
2950
2951 // ============================================================================
2952 // InitGameEngine()
2953 // ----------------------------------------------------------------------------
2954 // initialize game engine due to level / tape version number
2955 // ============================================================================
2956
2957 static void InitGameEngine(void)
2958 {
2959   int i, j, k, l, x, y;
2960
2961   // set game engine from tape file when re-playing, else from level file
2962   game.engine_version = (tape.playing ? tape.engine_version :
2963                          level.game_version);
2964
2965   // set single or multi-player game mode (needed for re-playing tapes)
2966   game.team_mode = setup.team_mode;
2967
2968   if (tape.playing)
2969   {
2970     int num_players = 0;
2971
2972     for (i = 0; i < MAX_PLAYERS; i++)
2973       if (tape.player_participates[i])
2974         num_players++;
2975
2976     // multi-player tapes contain input data for more than one player
2977     game.team_mode = (num_players > 1);
2978   }
2979
2980 #if 0
2981   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2982         level.game_version);
2983   Debug("game:init:level", "          tape.file_version   == %06d",
2984         tape.file_version);
2985   Debug("game:init:level", "          tape.game_version   == %06d",
2986         tape.game_version);
2987   Debug("game:init:level", "          tape.engine_version == %06d",
2988         tape.engine_version);
2989   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2990         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2991 #endif
2992
2993   // --------------------------------------------------------------------------
2994   // set flags for bugs and changes according to active game engine version
2995   // --------------------------------------------------------------------------
2996
2997   /*
2998     Summary of bugfix:
2999     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3000
3001     Bug was introduced in version:
3002     2.0.1
3003
3004     Bug was fixed in version:
3005     4.2.0.0
3006
3007     Description:
3008     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3009     but the property "can fall" was missing, which caused some levels to be
3010     unsolvable. This was fixed in version 4.2.0.0.
3011
3012     Affected levels/tapes:
3013     An example for a tape that was fixed by this bugfix is tape 029 from the
3014     level set "rnd_sam_bateman".
3015     The wrong behaviour will still be used for all levels or tapes that were
3016     created/recorded with it. An example for this is tape 023 from the level
3017     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3018   */
3019
3020   boolean use_amoeba_dropping_cannot_fall_bug =
3021     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3022       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3023      (tape.playing &&
3024       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed move speed of elements entering or leaving magic wall.
3030
3031     Fixed/changed in version:
3032     2.0.1
3033
3034     Description:
3035     Before 2.0.1, move speed of elements entering or leaving magic wall was
3036     twice as fast as it is now.
3037     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3038
3039     Affected levels/tapes:
3040     The first condition is generally needed for all levels/tapes before version
3041     2.0.1, which might use the old behaviour before it was changed; known tapes
3042     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3043     The second condition is an exception from the above case and is needed for
3044     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3045     above, but before it was known that this change would break tapes like the
3046     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3047     although the engine version while recording maybe was before 2.0.1. There
3048     are a lot of tapes that are affected by this exception, like tape 006 from
3049     the level set "rnd_conor_mancone".
3050   */
3051
3052   boolean use_old_move_stepsize_for_magic_wall =
3053     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3054      !(tape.playing &&
3055        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3056        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3057
3058   /*
3059     Summary of bugfix/change:
3060     Fixed handling for custom elements that change when pushed by the player.
3061
3062     Fixed/changed in version:
3063     3.1.0
3064
3065     Description:
3066     Before 3.1.0, custom elements that "change when pushing" changed directly
3067     after the player started pushing them (until then handled in "DigField()").
3068     Since 3.1.0, these custom elements are not changed until the "pushing"
3069     move of the element is finished (now handled in "ContinueMoving()").
3070
3071     Affected levels/tapes:
3072     The first condition is generally needed for all levels/tapes before version
3073     3.1.0, which might use the old behaviour before it was changed; known tapes
3074     that are affected are some tapes from the level set "Walpurgis Gardens" by
3075     Jamie Cullen.
3076     The second condition is an exception from the above case and is needed for
3077     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3078     above (including some development versions of 3.1.0), but before it was
3079     known that this change would break tapes like the above and was fixed in
3080     3.1.1, so that the changed behaviour was active although the engine version
3081     while recording maybe was before 3.1.0. There is at least one tape that is
3082     affected by this exception, which is the tape for the one-level set "Bug
3083     Machine" by Juergen Bonhagen.
3084   */
3085
3086   game.use_change_when_pushing_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3088      !(tape.playing &&
3089        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3090        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3091
3092   /*
3093     Summary of bugfix/change:
3094     Fixed handling for blocking the field the player leaves when moving.
3095
3096     Fixed/changed in version:
3097     3.1.1
3098
3099     Description:
3100     Before 3.1.1, when "block last field when moving" was enabled, the field
3101     the player is leaving when moving was blocked for the time of the move,
3102     and was directly unblocked afterwards. This resulted in the last field
3103     being blocked for exactly one less than the number of frames of one player
3104     move. Additionally, even when blocking was disabled, the last field was
3105     blocked for exactly one frame.
3106     Since 3.1.1, due to changes in player movement handling, the last field
3107     is not blocked at all when blocking is disabled. When blocking is enabled,
3108     the last field is blocked for exactly the number of frames of one player
3109     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3110     last field is blocked for exactly one more than the number of frames of
3111     one player move.
3112
3113     Affected levels/tapes:
3114     (!!! yet to be determined -- probably many !!!)
3115   */
3116
3117   game.use_block_last_field_bug =
3118     (game.engine_version < VERSION_IDENT(3,1,1,0));
3119
3120   /* various special flags and settings for native Emerald Mine game engine */
3121
3122   game_em.use_single_button =
3123     (game.engine_version > VERSION_IDENT(4,0,0,2));
3124
3125   game_em.use_push_delay =
3126     (game.engine_version > VERSION_IDENT(4,3,7,1));
3127
3128   game_em.use_snap_key_bug =
3129     (game.engine_version < VERSION_IDENT(4,0,1,0));
3130
3131   game_em.use_random_bug =
3132     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3133
3134   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3135
3136   game_em.use_old_explosions            = use_old_em_engine;
3137   game_em.use_old_android               = use_old_em_engine;
3138   game_em.use_old_push_elements         = use_old_em_engine;
3139   game_em.use_old_push_into_acid        = use_old_em_engine;
3140
3141   game_em.use_wrap_around               = !use_old_em_engine;
3142
3143   // --------------------------------------------------------------------------
3144
3145   // set maximal allowed number of custom element changes per game frame
3146   game.max_num_changes_per_frame = 1;
3147
3148   // default scan direction: scan playfield from top/left to bottom/right
3149   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3150
3151   // dynamically adjust element properties according to game engine version
3152   InitElementPropertiesEngine(game.engine_version);
3153
3154   // ---------- initialize special element properties -------------------------
3155
3156   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3157   if (use_amoeba_dropping_cannot_fall_bug)
3158     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3159
3160   // ---------- initialize player's initial move delay ------------------------
3161
3162   // dynamically adjust player properties according to level information
3163   for (i = 0; i < MAX_PLAYERS; i++)
3164     game.initial_move_delay_value[i] =
3165       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3166
3167   // dynamically adjust player properties according to game engine version
3168   for (i = 0; i < MAX_PLAYERS; i++)
3169     game.initial_move_delay[i] =
3170       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3171        game.initial_move_delay_value[i] : 0);
3172
3173   // ---------- initialize player's initial push delay ------------------------
3174
3175   // dynamically adjust player properties according to game engine version
3176   game.initial_push_delay_value =
3177     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3178
3179   // ---------- initialize changing elements ----------------------------------
3180
3181   // initialize changing elements information
3182   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3183   {
3184     struct ElementInfo *ei = &element_info[i];
3185
3186     // this pointer might have been changed in the level editor
3187     ei->change = &ei->change_page[0];
3188
3189     if (!IS_CUSTOM_ELEMENT(i))
3190     {
3191       ei->change->target_element = EL_EMPTY_SPACE;
3192       ei->change->delay_fixed = 0;
3193       ei->change->delay_random = 0;
3194       ei->change->delay_frames = 1;
3195     }
3196
3197     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3198     {
3199       ei->has_change_event[j] = FALSE;
3200
3201       ei->event_page_nr[j] = 0;
3202       ei->event_page[j] = &ei->change_page[0];
3203     }
3204   }
3205
3206   // add changing elements from pre-defined list
3207   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3208   {
3209     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3210     struct ElementInfo *ei = &element_info[ch_delay->element];
3211
3212     ei->change->target_element       = ch_delay->target_element;
3213     ei->change->delay_fixed          = ch_delay->change_delay;
3214
3215     ei->change->pre_change_function  = ch_delay->pre_change_function;
3216     ei->change->change_function      = ch_delay->change_function;
3217     ei->change->post_change_function = ch_delay->post_change_function;
3218
3219     ei->change->can_change = TRUE;
3220     ei->change->can_change_or_has_action = TRUE;
3221
3222     ei->has_change_event[CE_DELAY] = TRUE;
3223
3224     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3225     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3226   }
3227
3228   // ---------- initialize if element can trigger global animations -----------
3229
3230   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3231   {
3232     struct ElementInfo *ei = &element_info[i];
3233
3234     ei->has_anim_event = FALSE;
3235   }
3236
3237   InitGlobalAnimEventsForCustomElements();
3238
3239   // ---------- initialize internal run-time variables ------------------------
3240
3241   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3242   {
3243     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3244
3245     for (j = 0; j < ei->num_change_pages; j++)
3246     {
3247       ei->change_page[j].can_change_or_has_action =
3248         (ei->change_page[j].can_change |
3249          ei->change_page[j].has_action);
3250     }
3251   }
3252
3253   // add change events from custom element configuration
3254   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3255   {
3256     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3257
3258     for (j = 0; j < ei->num_change_pages; j++)
3259     {
3260       if (!ei->change_page[j].can_change_or_has_action)
3261         continue;
3262
3263       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3264       {
3265         // only add event page for the first page found with this event
3266         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3267         {
3268           ei->has_change_event[k] = TRUE;
3269
3270           ei->event_page_nr[k] = j;
3271           ei->event_page[k] = &ei->change_page[j];
3272         }
3273       }
3274     }
3275   }
3276
3277   // ---------- initialize reference elements in change conditions ------------
3278
3279   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3280   {
3281     int element = EL_CUSTOM_START + i;
3282     struct ElementInfo *ei = &element_info[element];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       int trigger_element = ei->change_page[j].initial_trigger_element;
3287
3288       if (trigger_element >= EL_PREV_CE_8 &&
3289           trigger_element <= EL_NEXT_CE_8)
3290         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3291
3292       ei->change_page[j].trigger_element = trigger_element;
3293     }
3294   }
3295
3296   // ---------- initialize run-time trigger player and element ----------------
3297
3298   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3299   {
3300     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3301
3302     for (j = 0; j < ei->num_change_pages; j++)
3303     {
3304       struct ElementChangeInfo *change = &ei->change_page[j];
3305
3306       change->actual_trigger_element = EL_EMPTY;
3307       change->actual_trigger_player = EL_EMPTY;
3308       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3309       change->actual_trigger_side = CH_SIDE_NONE;
3310       change->actual_trigger_ce_value = 0;
3311       change->actual_trigger_ce_score = 0;
3312       change->actual_trigger_x = -1;
3313       change->actual_trigger_y = -1;
3314     }
3315   }
3316
3317   // ---------- initialize trigger events -------------------------------------
3318
3319   // initialize trigger events information
3320   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3321     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3322       trigger_events[i][j] = FALSE;
3323
3324   // add trigger events from element change event properties
3325   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3326   {
3327     struct ElementInfo *ei = &element_info[i];
3328
3329     for (j = 0; j < ei->num_change_pages; j++)
3330     {
3331       struct ElementChangeInfo *change = &ei->change_page[j];
3332
3333       if (!change->can_change_or_has_action)
3334         continue;
3335
3336       if (change->has_event[CE_BY_OTHER_ACTION])
3337       {
3338         int trigger_element = change->trigger_element;
3339
3340         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3341         {
3342           if (change->has_event[k])
3343           {
3344             if (IS_GROUP_ELEMENT(trigger_element))
3345             {
3346               struct ElementGroupInfo *group =
3347                 element_info[trigger_element].group;
3348
3349               for (l = 0; l < group->num_elements_resolved; l++)
3350                 trigger_events[group->element_resolved[l]][k] = TRUE;
3351             }
3352             else if (trigger_element == EL_ANY_ELEMENT)
3353               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3354                 trigger_events[l][k] = TRUE;
3355             else
3356               trigger_events[trigger_element][k] = TRUE;
3357           }
3358         }
3359       }
3360     }
3361   }
3362
3363   // ---------- initialize push delay -----------------------------------------
3364
3365   // initialize push delay values to default
3366   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3367   {
3368     if (!IS_CUSTOM_ELEMENT(i))
3369     {
3370       // set default push delay values (corrected since version 3.0.7-1)
3371       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3372       {
3373         element_info[i].push_delay_fixed = 2;
3374         element_info[i].push_delay_random = 8;
3375       }
3376       else
3377       {
3378         element_info[i].push_delay_fixed = 8;
3379         element_info[i].push_delay_random = 8;
3380       }
3381     }
3382   }
3383
3384   // set push delay value for certain elements from pre-defined list
3385   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3386   {
3387     int e = push_delay_list[i].element;
3388
3389     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3390     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3391   }
3392
3393   // set push delay value for Supaplex elements for newer engine versions
3394   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3395   {
3396     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3397     {
3398       if (IS_SP_ELEMENT(i))
3399       {
3400         // set SP push delay to just enough to push under a falling zonk
3401         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3402
3403         element_info[i].push_delay_fixed  = delay;
3404         element_info[i].push_delay_random = 0;
3405       }
3406     }
3407   }
3408
3409   // ---------- initialize move stepsize --------------------------------------
3410
3411   // initialize move stepsize values to default
3412   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3413     if (!IS_CUSTOM_ELEMENT(i))
3414       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3415
3416   // set move stepsize value for certain elements from pre-defined list
3417   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3418   {
3419     int e = move_stepsize_list[i].element;
3420
3421     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3422
3423     // set move stepsize value for certain elements for older engine versions
3424     if (use_old_move_stepsize_for_magic_wall)
3425     {
3426       if (e == EL_MAGIC_WALL_FILLING ||
3427           e == EL_MAGIC_WALL_EMPTYING ||
3428           e == EL_BD_MAGIC_WALL_FILLING ||
3429           e == EL_BD_MAGIC_WALL_EMPTYING)
3430         element_info[e].move_stepsize *= 2;
3431     }
3432   }
3433
3434   // ---------- initialize collect score --------------------------------------
3435
3436   // initialize collect score values for custom elements from initial value
3437   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3438     if (IS_CUSTOM_ELEMENT(i))
3439       element_info[i].collect_score = element_info[i].collect_score_initial;
3440
3441   // ---------- initialize collect count --------------------------------------
3442
3443   // initialize collect count values for non-custom elements
3444   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3445     if (!IS_CUSTOM_ELEMENT(i))
3446       element_info[i].collect_count_initial = 0;
3447
3448   // add collect count values for all elements from pre-defined list
3449   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3450     element_info[collect_count_list[i].element].collect_count_initial =
3451       collect_count_list[i].count;
3452
3453   // ---------- initialize access direction -----------------------------------
3454
3455   // initialize access direction values to default (access from every side)
3456   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3457     if (!IS_CUSTOM_ELEMENT(i))
3458       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3459
3460   // set access direction value for certain elements from pre-defined list
3461   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3462     element_info[access_direction_list[i].element].access_direction =
3463       access_direction_list[i].direction;
3464
3465   // ---------- initialize explosion content ----------------------------------
3466   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3467   {
3468     if (IS_CUSTOM_ELEMENT(i))
3469       continue;
3470
3471     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3472     {
3473       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3474
3475       element_info[i].content.e[x][y] =
3476         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3477          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3478          i == EL_PLAYER_3 ? EL_EMERALD :
3479          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3480          i == EL_MOLE ? EL_EMERALD_RED :
3481          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3482          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3483          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3484          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3485          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3486          i == EL_WALL_EMERALD ? EL_EMERALD :
3487          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3488          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3489          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3490          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3491          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3492          i == EL_WALL_PEARL ? EL_PEARL :
3493          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3494          EL_EMPTY);
3495     }
3496   }
3497
3498   // ---------- initialize recursion detection --------------------------------
3499   recursion_loop_depth = 0;
3500   recursion_loop_detected = FALSE;
3501   recursion_loop_element = EL_UNDEFINED;
3502
3503   // ---------- initialize graphics engine ------------------------------------
3504   game.scroll_delay_value =
3505     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3506      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3507      !setup.forced_scroll_delay           ? 0 :
3508      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3509   if (game.forced_scroll_delay_value == -1)
3510     game.scroll_delay_value =
3511       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3512
3513   // ---------- initialize game engine snapshots ------------------------------
3514   for (i = 0; i < MAX_PLAYERS; i++)
3515     game.snapshot.last_action[i] = 0;
3516   game.snapshot.changed_action = FALSE;
3517   game.snapshot.collected_item = FALSE;
3518   game.snapshot.mode =
3519     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3520      SNAPSHOT_MODE_EVERY_STEP :
3521      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3522      SNAPSHOT_MODE_EVERY_MOVE :
3523      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3524      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3525   game.snapshot.save_snapshot = FALSE;
3526
3527   // ---------- initialize level time for Supaplex engine ---------------------
3528   // Supaplex levels with time limit currently unsupported -- should be added
3529   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3530     level.time = 0;
3531
3532   // ---------- initialize flags for handling game actions --------------------
3533
3534   // set flags for game actions to default values
3535   game.use_key_actions = TRUE;
3536   game.use_mouse_actions = FALSE;
3537
3538   // when using Mirror Magic game engine, handle mouse events only
3539   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3540   {
3541     game.use_key_actions = FALSE;
3542     game.use_mouse_actions = TRUE;
3543   }
3544
3545   // check for custom elements with mouse click events
3546   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3547   {
3548     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3549     {
3550       int element = EL_CUSTOM_START + i;
3551
3552       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3553           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3554           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3555           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3556         game.use_mouse_actions = TRUE;
3557     }
3558   }
3559 }
3560
3561 static int get_num_special_action(int element, int action_first,
3562                                   int action_last)
3563 {
3564   int num_special_action = 0;
3565   int i, j;
3566
3567   for (i = action_first; i <= action_last; i++)
3568   {
3569     boolean found = FALSE;
3570
3571     for (j = 0; j < NUM_DIRECTIONS; j++)
3572       if (el_act_dir2img(element, i, j) !=
3573           el_act_dir2img(element, ACTION_DEFAULT, j))
3574         found = TRUE;
3575
3576     if (found)
3577       num_special_action++;
3578     else
3579       break;
3580   }
3581
3582   return num_special_action;
3583 }
3584
3585
3586 // ============================================================================
3587 // InitGame()
3588 // ----------------------------------------------------------------------------
3589 // initialize and start new game
3590 // ============================================================================
3591
3592 #if DEBUG_INIT_PLAYER
3593 static void DebugPrintPlayerStatus(char *message)
3594 {
3595   int i;
3596
3597   if (!options.debug)
3598     return;
3599
3600   Debug("game:init:player", "%s:", message);
3601
3602   for (i = 0; i < MAX_PLAYERS; i++)
3603   {
3604     struct PlayerInfo *player = &stored_player[i];
3605
3606     Debug("game:init:player",
3607           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3608           i + 1,
3609           player->present,
3610           player->connected,
3611           player->connected_locally,
3612           player->connected_network,
3613           player->active,
3614           (local_player == player ? " (local player)" : ""));
3615   }
3616 }
3617 #endif
3618
3619 void InitGame(void)
3620 {
3621   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3622   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3623   int fade_mask = REDRAW_FIELD;
3624   boolean restarting = (game_status == GAME_MODE_PLAYING);
3625   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3626   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3627   int initial_move_dir = MV_DOWN;
3628   int i, j, x, y;
3629
3630   // required here to update video display before fading (FIX THIS)
3631   DrawMaskedBorder(REDRAW_DOOR_2);
3632
3633   if (!game.restart_level)
3634     CloseDoor(DOOR_CLOSE_1);
3635
3636   if (restarting)
3637   {
3638     // force fading out global animations displayed during game play
3639     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3640   }
3641   else
3642   {
3643     SetGameStatus(GAME_MODE_PLAYING);
3644   }
3645
3646   if (level_editor_test_game)
3647     FadeSkipNextFadeOut();
3648   else
3649     FadeSetEnterScreen();
3650
3651   if (CheckFadeAll())
3652     fade_mask = REDRAW_ALL;
3653
3654   FadeLevelSoundsAndMusic();
3655
3656   ExpireSoundLoops(TRUE);
3657
3658   FadeOut(fade_mask);
3659
3660   if (restarting)
3661   {
3662     // force restarting global animations displayed during game play
3663     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3664
3665     // this is required for "transforming" fade modes like cross-fading
3666     // (else global animations will be stopped, but not restarted here)
3667     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3668
3669     SetGameStatus(GAME_MODE_PLAYING);
3670   }
3671
3672   if (level_editor_test_game)
3673     FadeSkipNextFadeIn();
3674
3675   // needed if different viewport properties defined for playing
3676   ChangeViewportPropertiesIfNeeded();
3677
3678   ClearField();
3679
3680   DrawCompleteVideoDisplay();
3681
3682   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3683
3684   InitGameEngine();
3685   InitGameControlValues();
3686
3687   if (tape.recording)
3688   {
3689     // initialize tape actions from game when recording tape
3690     tape.use_key_actions   = game.use_key_actions;
3691     tape.use_mouse_actions = game.use_mouse_actions;
3692
3693     // initialize visible playfield size when recording tape (for team mode)
3694     tape.scr_fieldx = SCR_FIELDX;
3695     tape.scr_fieldy = SCR_FIELDY;
3696   }
3697
3698   // don't play tapes over network
3699   network_playing = (network.enabled && !tape.playing);
3700
3701   for (i = 0; i < MAX_PLAYERS; i++)
3702   {
3703     struct PlayerInfo *player = &stored_player[i];
3704
3705     player->index_nr = i;
3706     player->index_bit = (1 << i);
3707     player->element_nr = EL_PLAYER_1 + i;
3708
3709     player->present = FALSE;
3710     player->active = FALSE;
3711     player->mapped = FALSE;
3712
3713     player->killed = FALSE;
3714     player->reanimated = FALSE;
3715     player->buried = FALSE;
3716
3717     player->action = 0;
3718     player->effective_action = 0;
3719     player->programmed_action = 0;
3720     player->snap_action = 0;
3721
3722     player->mouse_action.lx = 0;
3723     player->mouse_action.ly = 0;
3724     player->mouse_action.button = 0;
3725     player->mouse_action.button_hint = 0;
3726
3727     player->effective_mouse_action.lx = 0;
3728     player->effective_mouse_action.ly = 0;
3729     player->effective_mouse_action.button = 0;
3730     player->effective_mouse_action.button_hint = 0;
3731
3732     for (j = 0; j < MAX_NUM_KEYS; j++)
3733       player->key[j] = FALSE;
3734
3735     player->num_white_keys = 0;
3736
3737     player->dynabomb_count = 0;
3738     player->dynabomb_size = 1;
3739     player->dynabombs_left = 0;
3740     player->dynabomb_xl = FALSE;
3741
3742     player->MovDir = initial_move_dir;
3743     player->MovPos = 0;
3744     player->GfxPos = 0;
3745     player->GfxDir = initial_move_dir;
3746     player->GfxAction = ACTION_DEFAULT;
3747     player->Frame = 0;
3748     player->StepFrame = 0;
3749
3750     player->initial_element = player->element_nr;
3751     player->artwork_element =
3752       (level.use_artwork_element[i] ? level.artwork_element[i] :
3753        player->element_nr);
3754     player->use_murphy = FALSE;
3755
3756     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3757     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3758
3759     player->gravity = level.initial_player_gravity[i];
3760
3761     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3762
3763     player->actual_frame_counter.count = 0;
3764     player->actual_frame_counter.value = 1;
3765
3766     player->step_counter = 0;
3767
3768     player->last_move_dir = initial_move_dir;
3769
3770     player->is_active = FALSE;
3771
3772     player->is_waiting = FALSE;
3773     player->is_moving = FALSE;
3774     player->is_auto_moving = FALSE;
3775     player->is_digging = FALSE;
3776     player->is_snapping = FALSE;
3777     player->is_collecting = FALSE;
3778     player->is_pushing = FALSE;
3779     player->is_switching = FALSE;
3780     player->is_dropping = FALSE;
3781     player->is_dropping_pressed = FALSE;
3782
3783     player->is_bored = FALSE;
3784     player->is_sleeping = FALSE;
3785
3786     player->was_waiting = TRUE;
3787     player->was_moving = FALSE;
3788     player->was_snapping = FALSE;
3789     player->was_dropping = FALSE;
3790
3791     player->force_dropping = FALSE;
3792
3793     player->frame_counter_bored = -1;
3794     player->frame_counter_sleeping = -1;
3795
3796     player->anim_delay_counter = 0;
3797     player->post_delay_counter = 0;
3798
3799     player->dir_waiting = initial_move_dir;
3800     player->action_waiting = ACTION_DEFAULT;
3801     player->last_action_waiting = ACTION_DEFAULT;
3802     player->special_action_bored = ACTION_DEFAULT;
3803     player->special_action_sleeping = ACTION_DEFAULT;
3804
3805     player->switch_x = -1;
3806     player->switch_y = -1;
3807
3808     player->drop_x = -1;
3809     player->drop_y = -1;
3810
3811     player->show_envelope = 0;
3812
3813     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3814
3815     player->push_delay       = -1;      // initialized when pushing starts
3816     player->push_delay_value = game.initial_push_delay_value;
3817
3818     player->drop_delay = 0;
3819     player->drop_pressed_delay = 0;
3820
3821     player->last_jx = -1;
3822     player->last_jy = -1;
3823     player->jx = -1;
3824     player->jy = -1;
3825
3826     player->shield_normal_time_left = 0;
3827     player->shield_deadly_time_left = 0;
3828
3829     player->last_removed_element = EL_UNDEFINED;
3830
3831     player->inventory_infinite_element = EL_UNDEFINED;
3832     player->inventory_size = 0;
3833
3834     if (level.use_initial_inventory[i])
3835     {
3836       for (j = 0; j < level.initial_inventory_size[i]; j++)
3837       {
3838         int element = level.initial_inventory_content[i][j];
3839         int collect_count = element_info[element].collect_count_initial;
3840         int k;
3841
3842         if (!IS_CUSTOM_ELEMENT(element))
3843           collect_count = 1;
3844
3845         if (collect_count == 0)
3846           player->inventory_infinite_element = element;
3847         else
3848           for (k = 0; k < collect_count; k++)
3849             if (player->inventory_size < MAX_INVENTORY_SIZE)
3850               player->inventory_element[player->inventory_size++] = element;
3851       }
3852     }
3853
3854     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3855     SnapField(player, 0, 0);
3856
3857     map_player_action[i] = i;
3858   }
3859
3860   network_player_action_received = FALSE;
3861
3862   // initial null action
3863   if (network_playing)
3864     SendToServer_MovePlayer(MV_NONE);
3865
3866   FrameCounter = 0;
3867   TimeFrames = 0;
3868   TimePlayed = 0;
3869   TimeLeft = level.time;
3870
3871   TapeTimeFrames = 0;
3872   TapeTime = 0;
3873
3874   ScreenMovDir = MV_NONE;
3875   ScreenMovPos = 0;
3876   ScreenGfxPos = 0;
3877
3878   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3879
3880   game.robot_wheel_x = -1;
3881   game.robot_wheel_y = -1;
3882
3883   game.exit_x = -1;
3884   game.exit_y = -1;
3885
3886   game.all_players_gone = FALSE;
3887
3888   game.LevelSolved = FALSE;
3889   game.GameOver = FALSE;
3890
3891   game.GamePlayed = !tape.playing;
3892
3893   game.LevelSolved_GameWon = FALSE;
3894   game.LevelSolved_GameEnd = FALSE;
3895   game.LevelSolved_SaveTape = FALSE;
3896   game.LevelSolved_SaveScore = FALSE;
3897
3898   game.LevelSolved_CountingTime = 0;
3899   game.LevelSolved_CountingScore = 0;
3900   game.LevelSolved_CountingHealth = 0;
3901
3902   game.RestartGameRequested = FALSE;
3903
3904   game.panel.active = TRUE;
3905
3906   game.no_level_time_limit = (level.time == 0);
3907   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3908
3909   game.yamyam_content_nr = 0;
3910   game.robot_wheel_active = FALSE;
3911   game.magic_wall_active = FALSE;
3912   game.magic_wall_time_left = 0;
3913   game.light_time_left = 0;
3914   game.timegate_time_left = 0;
3915   game.switchgate_pos = 0;
3916   game.wind_direction = level.wind_direction_initial;
3917
3918   game.time_final = 0;
3919   game.score_time_final = 0;
3920
3921   game.score = 0;
3922   game.score_final = 0;
3923
3924   game.health = MAX_HEALTH;
3925   game.health_final = MAX_HEALTH;
3926
3927   game.gems_still_needed = level.gems_needed;
3928   game.sokoban_fields_still_needed = 0;
3929   game.sokoban_objects_still_needed = 0;
3930   game.lights_still_needed = 0;
3931   game.players_still_needed = 0;
3932   game.friends_still_needed = 0;
3933
3934   game.lenses_time_left = 0;
3935   game.magnify_time_left = 0;
3936
3937   game.ball_active = level.ball_active_initial;
3938   game.ball_content_nr = 0;
3939
3940   game.explosions_delayed = TRUE;
3941
3942   game.envelope_active = FALSE;
3943
3944   // special case: set custom artwork setting to initial value
3945   game.use_masked_elements = game.use_masked_elements_initial;
3946
3947   for (i = 0; i < NUM_BELTS; i++)
3948   {
3949     game.belt_dir[i] = MV_NONE;
3950     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3951   }
3952
3953   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3954     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3955
3956 #if DEBUG_INIT_PLAYER
3957   DebugPrintPlayerStatus("Player status at level initialization");
3958 #endif
3959
3960   SCAN_PLAYFIELD(x, y)
3961   {
3962     Tile[x][y] = Last[x][y] = level.field[x][y];
3963     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3964     ChangeDelay[x][y] = 0;
3965     ChangePage[x][y] = -1;
3966     CustomValue[x][y] = 0;              // initialized in InitField()
3967     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3968     AmoebaNr[x][y] = 0;
3969     WasJustMoving[x][y] = 0;
3970     WasJustFalling[x][y] = 0;
3971     CheckCollision[x][y] = 0;
3972     CheckImpact[x][y] = 0;
3973     Stop[x][y] = FALSE;
3974     Pushed[x][y] = FALSE;
3975
3976     ChangeCount[x][y] = 0;
3977     ChangeEvent[x][y] = -1;
3978
3979     ExplodePhase[x][y] = 0;
3980     ExplodeDelay[x][y] = 0;
3981     ExplodeField[x][y] = EX_TYPE_NONE;
3982
3983     RunnerVisit[x][y] = 0;
3984     PlayerVisit[x][y] = 0;
3985
3986     GfxFrame[x][y] = 0;
3987     GfxRandom[x][y] = INIT_GFX_RANDOM();
3988     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3989     GfxElement[x][y] = EL_UNDEFINED;
3990     GfxElementEmpty[x][y] = EL_EMPTY;
3991     GfxAction[x][y] = ACTION_DEFAULT;
3992     GfxDir[x][y] = MV_NONE;
3993     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3994   }
3995
3996   SCAN_PLAYFIELD(x, y)
3997   {
3998     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3999       emulate_bd = FALSE;
4000     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4001       emulate_sp = FALSE;
4002
4003     InitField(x, y, TRUE);
4004
4005     ResetGfxAnimation(x, y);
4006   }
4007
4008   InitBeltMovement();
4009
4010   // required if level does not contain any "empty space" element
4011   if (element_info[EL_EMPTY].use_gfx_element)
4012     game.use_masked_elements = TRUE;
4013
4014   for (i = 0; i < MAX_PLAYERS; i++)
4015   {
4016     struct PlayerInfo *player = &stored_player[i];
4017
4018     // set number of special actions for bored and sleeping animation
4019     player->num_special_action_bored =
4020       get_num_special_action(player->artwork_element,
4021                              ACTION_BORING_1, ACTION_BORING_LAST);
4022     player->num_special_action_sleeping =
4023       get_num_special_action(player->artwork_element,
4024                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4025   }
4026
4027   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4028                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4029
4030   // initialize type of slippery elements
4031   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4032   {
4033     if (!IS_CUSTOM_ELEMENT(i))
4034     {
4035       // default: elements slip down either to the left or right randomly
4036       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4037
4038       // SP style elements prefer to slip down on the left side
4039       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4040         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4041
4042       // BD style elements prefer to slip down on the left side
4043       if (game.emulation == EMU_BOULDERDASH)
4044         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4045     }
4046   }
4047
4048   // initialize explosion and ignition delay
4049   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4050   {
4051     if (!IS_CUSTOM_ELEMENT(i))
4052     {
4053       int num_phase = 8;
4054       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4055                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4056                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4057       int last_phase = (num_phase + 1) * delay;
4058       int half_phase = (num_phase / 2) * delay;
4059
4060       element_info[i].explosion_delay = last_phase - 1;
4061       element_info[i].ignition_delay = half_phase;
4062
4063       if (i == EL_BLACK_ORB)
4064         element_info[i].ignition_delay = 1;
4065     }
4066   }
4067
4068   // correct non-moving belts to start moving left
4069   for (i = 0; i < NUM_BELTS; i++)
4070     if (game.belt_dir[i] == MV_NONE)
4071       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4072
4073 #if USE_NEW_PLAYER_ASSIGNMENTS
4074   // use preferred player also in local single-player mode
4075   if (!network.enabled && !game.team_mode)
4076   {
4077     int new_index_nr = setup.network_player_nr;
4078
4079     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4080     {
4081       for (i = 0; i < MAX_PLAYERS; i++)
4082         stored_player[i].connected_locally = FALSE;
4083
4084       stored_player[new_index_nr].connected_locally = TRUE;
4085     }
4086   }
4087
4088   for (i = 0; i < MAX_PLAYERS; i++)
4089   {
4090     stored_player[i].connected = FALSE;
4091
4092     // in network game mode, the local player might not be the first player
4093     if (stored_player[i].connected_locally)
4094       local_player = &stored_player[i];
4095   }
4096
4097   if (!network.enabled)
4098     local_player->connected = TRUE;
4099
4100   if (tape.playing)
4101   {
4102     for (i = 0; i < MAX_PLAYERS; i++)
4103       stored_player[i].connected = tape.player_participates[i];
4104   }
4105   else if (network.enabled)
4106   {
4107     // add team mode players connected over the network (needed for correct
4108     // assignment of player figures from level to locally playing players)
4109
4110     for (i = 0; i < MAX_PLAYERS; i++)
4111       if (stored_player[i].connected_network)
4112         stored_player[i].connected = TRUE;
4113   }
4114   else if (game.team_mode)
4115   {
4116     // try to guess locally connected team mode players (needed for correct
4117     // assignment of player figures from level to locally playing players)
4118
4119     for (i = 0; i < MAX_PLAYERS; i++)
4120       if (setup.input[i].use_joystick ||
4121           setup.input[i].key.left != KSYM_UNDEFINED)
4122         stored_player[i].connected = TRUE;
4123   }
4124
4125 #if DEBUG_INIT_PLAYER
4126   DebugPrintPlayerStatus("Player status after level initialization");
4127 #endif
4128
4129 #if DEBUG_INIT_PLAYER
4130   Debug("game:init:player", "Reassigning players ...");
4131 #endif
4132
4133   // check if any connected player was not found in playfield
4134   for (i = 0; i < MAX_PLAYERS; i++)
4135   {
4136     struct PlayerInfo *player = &stored_player[i];
4137
4138     if (player->connected && !player->present)
4139     {
4140       struct PlayerInfo *field_player = NULL;
4141
4142 #if DEBUG_INIT_PLAYER
4143       Debug("game:init:player",
4144             "- looking for field player for player %d ...", i + 1);
4145 #endif
4146
4147       // assign first free player found that is present in the playfield
4148
4149       // first try: look for unmapped playfield player that is not connected
4150       for (j = 0; j < MAX_PLAYERS; j++)
4151         if (field_player == NULL &&
4152             stored_player[j].present &&
4153             !stored_player[j].mapped &&
4154             !stored_player[j].connected)
4155           field_player = &stored_player[j];
4156
4157       // second try: look for *any* unmapped playfield player
4158       for (j = 0; j < MAX_PLAYERS; j++)
4159         if (field_player == NULL &&
4160             stored_player[j].present &&
4161             !stored_player[j].mapped)
4162           field_player = &stored_player[j];
4163
4164       if (field_player != NULL)
4165       {
4166         int jx = field_player->jx, jy = field_player->jy;
4167
4168 #if DEBUG_INIT_PLAYER
4169         Debug("game:init:player", "- found player %d",
4170               field_player->index_nr + 1);
4171 #endif
4172
4173         player->present = FALSE;
4174         player->active = FALSE;
4175
4176         field_player->present = TRUE;
4177         field_player->active = TRUE;
4178
4179         /*
4180         player->initial_element = field_player->initial_element;
4181         player->artwork_element = field_player->artwork_element;
4182
4183         player->block_last_field       = field_player->block_last_field;
4184         player->block_delay_adjustment = field_player->block_delay_adjustment;
4185         */
4186
4187         StorePlayer[jx][jy] = field_player->element_nr;
4188
4189         field_player->jx = field_player->last_jx = jx;
4190         field_player->jy = field_player->last_jy = jy;
4191
4192         if (local_player == player)
4193           local_player = field_player;
4194
4195         map_player_action[field_player->index_nr] = i;
4196
4197         field_player->mapped = TRUE;
4198
4199 #if DEBUG_INIT_PLAYER
4200         Debug("game:init:player", "- map_player_action[%d] == %d",
4201               field_player->index_nr + 1, i + 1);
4202 #endif
4203       }
4204     }
4205
4206     if (player->connected && player->present)
4207       player->mapped = TRUE;
4208   }
4209
4210 #if DEBUG_INIT_PLAYER
4211   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4212 #endif
4213
4214 #else
4215
4216   // check if any connected player was not found in playfield
4217   for (i = 0; i < MAX_PLAYERS; i++)
4218   {
4219     struct PlayerInfo *player = &stored_player[i];
4220
4221     if (player->connected && !player->present)
4222     {
4223       for (j = 0; j < MAX_PLAYERS; j++)
4224       {
4225         struct PlayerInfo *field_player = &stored_player[j];
4226         int jx = field_player->jx, jy = field_player->jy;
4227
4228         // assign first free player found that is present in the playfield
4229         if (field_player->present && !field_player->connected)
4230         {
4231           player->present = TRUE;
4232           player->active = TRUE;
4233
4234           field_player->present = FALSE;
4235           field_player->active = FALSE;
4236
4237           player->initial_element = field_player->initial_element;
4238           player->artwork_element = field_player->artwork_element;
4239
4240           player->block_last_field       = field_player->block_last_field;
4241           player->block_delay_adjustment = field_player->block_delay_adjustment;
4242
4243           StorePlayer[jx][jy] = player->element_nr;
4244
4245           player->jx = player->last_jx = jx;
4246           player->jy = player->last_jy = jy;
4247
4248           break;
4249         }
4250       }
4251     }
4252   }
4253 #endif
4254
4255 #if 0
4256   Debug("game:init:player", "local_player->present == %d",
4257         local_player->present);
4258 #endif
4259
4260   // set focus to local player for network games, else to all players
4261   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4262   game.centered_player_nr_next = game.centered_player_nr;
4263   game.set_centered_player = FALSE;
4264   game.set_centered_player_wrap = FALSE;
4265
4266   if (network_playing && tape.recording)
4267   {
4268     // store client dependent player focus when recording network games
4269     tape.centered_player_nr_next = game.centered_player_nr_next;
4270     tape.set_centered_player = TRUE;
4271   }
4272
4273   if (tape.playing)
4274   {
4275     // when playing a tape, eliminate all players who do not participate
4276
4277 #if USE_NEW_PLAYER_ASSIGNMENTS
4278
4279     if (!game.team_mode)
4280     {
4281       for (i = 0; i < MAX_PLAYERS; i++)
4282       {
4283         if (stored_player[i].active &&
4284             !tape.player_participates[map_player_action[i]])
4285         {
4286           struct PlayerInfo *player = &stored_player[i];
4287           int jx = player->jx, jy = player->jy;
4288
4289 #if DEBUG_INIT_PLAYER
4290           Debug("game:init:player", "Removing player %d at (%d, %d)",
4291                 i + 1, jx, jy);
4292 #endif
4293
4294           player->active = FALSE;
4295           StorePlayer[jx][jy] = 0;
4296           Tile[jx][jy] = EL_EMPTY;
4297         }
4298       }
4299     }
4300
4301 #else
4302
4303     for (i = 0; i < MAX_PLAYERS; i++)
4304     {
4305       if (stored_player[i].active &&
4306           !tape.player_participates[i])
4307       {
4308         struct PlayerInfo *player = &stored_player[i];
4309         int jx = player->jx, jy = player->jy;
4310
4311         player->active = FALSE;
4312         StorePlayer[jx][jy] = 0;
4313         Tile[jx][jy] = EL_EMPTY;
4314       }
4315     }
4316 #endif
4317   }
4318   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4319   {
4320     // when in single player mode, eliminate all but the local player
4321
4322     for (i = 0; i < MAX_PLAYERS; i++)
4323     {
4324       struct PlayerInfo *player = &stored_player[i];
4325
4326       if (player->active && player != local_player)
4327       {
4328         int jx = player->jx, jy = player->jy;
4329
4330         player->active = FALSE;
4331         player->present = FALSE;
4332
4333         StorePlayer[jx][jy] = 0;
4334         Tile[jx][jy] = EL_EMPTY;
4335       }
4336     }
4337   }
4338
4339   for (i = 0; i < MAX_PLAYERS; i++)
4340     if (stored_player[i].active)
4341       game.players_still_needed++;
4342
4343   if (level.solved_by_one_player)
4344     game.players_still_needed = 1;
4345
4346   // when recording the game, store which players take part in the game
4347   if (tape.recording)
4348   {
4349 #if USE_NEW_PLAYER_ASSIGNMENTS
4350     for (i = 0; i < MAX_PLAYERS; i++)
4351       if (stored_player[i].connected)
4352         tape.player_participates[i] = TRUE;
4353 #else
4354     for (i = 0; i < MAX_PLAYERS; i++)
4355       if (stored_player[i].active)
4356         tape.player_participates[i] = TRUE;
4357 #endif
4358   }
4359
4360 #if DEBUG_INIT_PLAYER
4361   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4362 #endif
4363
4364   if (BorderElement == EL_EMPTY)
4365   {
4366     SBX_Left = 0;
4367     SBX_Right = lev_fieldx - SCR_FIELDX;
4368     SBY_Upper = 0;
4369     SBY_Lower = lev_fieldy - SCR_FIELDY;
4370   }
4371   else
4372   {
4373     SBX_Left = -1;
4374     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4375     SBY_Upper = -1;
4376     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4377   }
4378
4379   if (full_lev_fieldx <= SCR_FIELDX)
4380     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4381   if (full_lev_fieldy <= SCR_FIELDY)
4382     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4383
4384   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4385     SBX_Left--;
4386   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4387     SBY_Upper--;
4388
4389   // if local player not found, look for custom element that might create
4390   // the player (make some assumptions about the right custom element)
4391   if (!local_player->present)
4392   {
4393     int start_x = 0, start_y = 0;
4394     int found_rating = 0;
4395     int found_element = EL_UNDEFINED;
4396     int player_nr = local_player->index_nr;
4397
4398     SCAN_PLAYFIELD(x, y)
4399     {
4400       int element = Tile[x][y];
4401       int content;
4402       int xx, yy;
4403       boolean is_player;
4404
4405       if (level.use_start_element[player_nr] &&
4406           level.start_element[player_nr] == element &&
4407           found_rating < 4)
4408       {
4409         start_x = x;
4410         start_y = y;
4411
4412         found_rating = 4;
4413         found_element = element;
4414       }
4415
4416       if (!IS_CUSTOM_ELEMENT(element))
4417         continue;
4418
4419       if (CAN_CHANGE(element))
4420       {
4421         for (i = 0; i < element_info[element].num_change_pages; i++)
4422         {
4423           // check for player created from custom element as single target
4424           content = element_info[element].change_page[i].target_element;
4425           is_player = IS_PLAYER_ELEMENT(content);
4426
4427           if (is_player && (found_rating < 3 ||
4428                             (found_rating == 3 && element < found_element)))
4429           {
4430             start_x = x;
4431             start_y = y;
4432
4433             found_rating = 3;
4434             found_element = element;
4435           }
4436         }
4437       }
4438
4439       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4440       {
4441         // check for player created from custom element as explosion content
4442         content = element_info[element].content.e[xx][yy];
4443         is_player = IS_PLAYER_ELEMENT(content);
4444
4445         if (is_player && (found_rating < 2 ||
4446                           (found_rating == 2 && element < found_element)))
4447         {
4448           start_x = x + xx - 1;
4449           start_y = y + yy - 1;
4450
4451           found_rating = 2;
4452           found_element = element;
4453         }
4454
4455         if (!CAN_CHANGE(element))
4456           continue;
4457
4458         for (i = 0; i < element_info[element].num_change_pages; i++)
4459         {
4460           // check for player created from custom element as extended target
4461           content =
4462             element_info[element].change_page[i].target_content.e[xx][yy];
4463
4464           is_player = IS_PLAYER_ELEMENT(content);
4465
4466           if (is_player && (found_rating < 1 ||
4467                             (found_rating == 1 && element < found_element)))
4468           {
4469             start_x = x + xx - 1;
4470             start_y = y + yy - 1;
4471
4472             found_rating = 1;
4473             found_element = element;
4474           }
4475         }
4476       }
4477     }
4478
4479     scroll_x = SCROLL_POSITION_X(start_x);
4480     scroll_y = SCROLL_POSITION_Y(start_y);
4481   }
4482   else
4483   {
4484     scroll_x = SCROLL_POSITION_X(local_player->jx);
4485     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4486   }
4487
4488   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4489     scroll_x = game.forced_scroll_x;
4490   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4491     scroll_y = game.forced_scroll_y;
4492
4493   // !!! FIX THIS (START) !!!
4494   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4495   {
4496     InitGameEngine_EM();
4497   }
4498   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4499   {
4500     InitGameEngine_SP();
4501   }
4502   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4503   {
4504     InitGameEngine_MM();
4505   }
4506   else
4507   {
4508     DrawLevel(REDRAW_FIELD);
4509     DrawAllPlayers();
4510
4511     // after drawing the level, correct some elements
4512     if (game.timegate_time_left == 0)
4513       CloseAllOpenTimegates();
4514   }
4515
4516   // blit playfield from scroll buffer to normal back buffer for fading in
4517   BlitScreenToBitmap(backbuffer);
4518   // !!! FIX THIS (END) !!!
4519
4520   DrawMaskedBorder(fade_mask);
4521
4522   FadeIn(fade_mask);
4523
4524 #if 1
4525   // full screen redraw is required at this point in the following cases:
4526   // - special editor door undrawn when game was started from level editor
4527   // - drawing area (playfield) was changed and has to be removed completely
4528   redraw_mask = REDRAW_ALL;
4529   BackToFront();
4530 #endif
4531
4532   if (!game.restart_level)
4533   {
4534     // copy default game door content to main double buffer
4535
4536     // !!! CHECK AGAIN !!!
4537     SetPanelBackground();
4538     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4539     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4540   }
4541
4542   SetPanelBackground();
4543   SetDrawBackgroundMask(REDRAW_DOOR_1);
4544
4545   UpdateAndDisplayGameControlValues();
4546
4547   if (!game.restart_level)
4548   {
4549     UnmapGameButtons();
4550     UnmapTapeButtons();
4551
4552     FreeGameButtons();
4553     CreateGameButtons();
4554
4555     MapGameButtons();
4556     MapTapeButtons();
4557
4558     // copy actual game door content to door double buffer for OpenDoor()
4559     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4560
4561     OpenDoor(DOOR_OPEN_ALL);
4562
4563     KeyboardAutoRepeatOffUnlessAutoplay();
4564
4565 #if DEBUG_INIT_PLAYER
4566     DebugPrintPlayerStatus("Player status (final)");
4567 #endif
4568   }
4569
4570   UnmapAllGadgets();
4571
4572   MapGameButtons();
4573   MapTapeButtons();
4574
4575   if (!game.restart_level && !tape.playing)
4576   {
4577     LevelStats_incPlayed(level_nr);
4578
4579     SaveLevelSetup_SeriesInfo();
4580   }
4581
4582   game.restart_level = FALSE;
4583   game.request_active = FALSE;
4584
4585   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4586     InitGameActions_MM();
4587
4588   SaveEngineSnapshotToListInitial();
4589
4590   if (!game.restart_level)
4591   {
4592     PlaySound(SND_GAME_STARTING);
4593
4594     if (setup.sound_music)
4595       PlayLevelMusic();
4596   }
4597
4598   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4599 }
4600
4601 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4602                         int actual_player_x, int actual_player_y)
4603 {
4604   // this is used for non-R'n'D game engines to update certain engine values
4605
4606   // needed to determine if sounds are played within the visible screen area
4607   scroll_x = actual_scroll_x;
4608   scroll_y = actual_scroll_y;
4609
4610   // needed to get player position for "follow finger" playing input method
4611   local_player->jx = actual_player_x;
4612   local_player->jy = actual_player_y;
4613 }
4614
4615 void InitMovDir(int x, int y)
4616 {
4617   int i, element = Tile[x][y];
4618   static int xy[4][2] =
4619   {
4620     {  0, +1 },
4621     { +1,  0 },
4622     {  0, -1 },
4623     { -1,  0 }
4624   };
4625   static int direction[3][4] =
4626   {
4627     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4628     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4629     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4630   };
4631
4632   switch (element)
4633   {
4634     case EL_BUG_RIGHT:
4635     case EL_BUG_UP:
4636     case EL_BUG_LEFT:
4637     case EL_BUG_DOWN:
4638       Tile[x][y] = EL_BUG;
4639       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4640       break;
4641
4642     case EL_SPACESHIP_RIGHT:
4643     case EL_SPACESHIP_UP:
4644     case EL_SPACESHIP_LEFT:
4645     case EL_SPACESHIP_DOWN:
4646       Tile[x][y] = EL_SPACESHIP;
4647       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4648       break;
4649
4650     case EL_BD_BUTTERFLY_RIGHT:
4651     case EL_BD_BUTTERFLY_UP:
4652     case EL_BD_BUTTERFLY_LEFT:
4653     case EL_BD_BUTTERFLY_DOWN:
4654       Tile[x][y] = EL_BD_BUTTERFLY;
4655       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4656       break;
4657
4658     case EL_BD_FIREFLY_RIGHT:
4659     case EL_BD_FIREFLY_UP:
4660     case EL_BD_FIREFLY_LEFT:
4661     case EL_BD_FIREFLY_DOWN:
4662       Tile[x][y] = EL_BD_FIREFLY;
4663       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4664       break;
4665
4666     case EL_PACMAN_RIGHT:
4667     case EL_PACMAN_UP:
4668     case EL_PACMAN_LEFT:
4669     case EL_PACMAN_DOWN:
4670       Tile[x][y] = EL_PACMAN;
4671       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4672       break;
4673
4674     case EL_YAMYAM_LEFT:
4675     case EL_YAMYAM_RIGHT:
4676     case EL_YAMYAM_UP:
4677     case EL_YAMYAM_DOWN:
4678       Tile[x][y] = EL_YAMYAM;
4679       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4680       break;
4681
4682     case EL_SP_SNIKSNAK:
4683       MovDir[x][y] = MV_UP;
4684       break;
4685
4686     case EL_SP_ELECTRON:
4687       MovDir[x][y] = MV_LEFT;
4688       break;
4689
4690     case EL_MOLE_LEFT:
4691     case EL_MOLE_RIGHT:
4692     case EL_MOLE_UP:
4693     case EL_MOLE_DOWN:
4694       Tile[x][y] = EL_MOLE;
4695       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4696       break;
4697
4698     case EL_SPRING_LEFT:
4699     case EL_SPRING_RIGHT:
4700       Tile[x][y] = EL_SPRING;
4701       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4702       break;
4703
4704     default:
4705       if (IS_CUSTOM_ELEMENT(element))
4706       {
4707         struct ElementInfo *ei = &element_info[element];
4708         int move_direction_initial = ei->move_direction_initial;
4709         int move_pattern = ei->move_pattern;
4710
4711         if (move_direction_initial == MV_START_PREVIOUS)
4712         {
4713           if (MovDir[x][y] != MV_NONE)
4714             return;
4715
4716           move_direction_initial = MV_START_AUTOMATIC;
4717         }
4718
4719         if (move_direction_initial == MV_START_RANDOM)
4720           MovDir[x][y] = 1 << RND(4);
4721         else if (move_direction_initial & MV_ANY_DIRECTION)
4722           MovDir[x][y] = move_direction_initial;
4723         else if (move_pattern == MV_ALL_DIRECTIONS ||
4724                  move_pattern == MV_TURNING_LEFT ||
4725                  move_pattern == MV_TURNING_RIGHT ||
4726                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4727                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4728                  move_pattern == MV_TURNING_RANDOM)
4729           MovDir[x][y] = 1 << RND(4);
4730         else if (move_pattern == MV_HORIZONTAL)
4731           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4732         else if (move_pattern == MV_VERTICAL)
4733           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4734         else if (move_pattern & MV_ANY_DIRECTION)
4735           MovDir[x][y] = element_info[element].move_pattern;
4736         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4737                  move_pattern == MV_ALONG_RIGHT_SIDE)
4738         {
4739           // use random direction as default start direction
4740           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4741             MovDir[x][y] = 1 << RND(4);
4742
4743           for (i = 0; i < NUM_DIRECTIONS; i++)
4744           {
4745             int x1 = x + xy[i][0];
4746             int y1 = y + xy[i][1];
4747
4748             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4749             {
4750               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4751                 MovDir[x][y] = direction[0][i];
4752               else
4753                 MovDir[x][y] = direction[1][i];
4754
4755               break;
4756             }
4757           }
4758         }                
4759       }
4760       else
4761       {
4762         MovDir[x][y] = 1 << RND(4);
4763
4764         if (element != EL_BUG &&
4765             element != EL_SPACESHIP &&
4766             element != EL_BD_BUTTERFLY &&
4767             element != EL_BD_FIREFLY)
4768           break;
4769
4770         for (i = 0; i < NUM_DIRECTIONS; i++)
4771         {
4772           int x1 = x + xy[i][0];
4773           int y1 = y + xy[i][1];
4774
4775           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4776           {
4777             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4778             {
4779               MovDir[x][y] = direction[0][i];
4780               break;
4781             }
4782             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4783                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4784             {
4785               MovDir[x][y] = direction[1][i];
4786               break;
4787             }
4788           }
4789         }
4790       }
4791       break;
4792   }
4793
4794   GfxDir[x][y] = MovDir[x][y];
4795 }
4796
4797 void InitAmoebaNr(int x, int y)
4798 {
4799   int i;
4800   int group_nr = AmoebaNeighbourNr(x, y);
4801
4802   if (group_nr == 0)
4803   {
4804     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4805     {
4806       if (AmoebaCnt[i] == 0)
4807       {
4808         group_nr = i;
4809         break;
4810       }
4811     }
4812   }
4813
4814   AmoebaNr[x][y] = group_nr;
4815   AmoebaCnt[group_nr]++;
4816   AmoebaCnt2[group_nr]++;
4817 }
4818
4819 static void LevelSolved_SetFinalGameValues(void)
4820 {
4821   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4822                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4823   game.score_time_final = (level.use_step_counter ? TimePlayed :
4824                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4825
4826   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4827                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4828                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4829                       game.score);
4830
4831   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4832                        MM_HEALTH(game_mm.laser_overload_value) :
4833                        game.health);
4834
4835   game.LevelSolved_CountingTime = game.time_final;
4836   game.LevelSolved_CountingScore = game.score_final;
4837   game.LevelSolved_CountingHealth = game.health_final;
4838 }
4839
4840 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4841 {
4842   game.LevelSolved_CountingTime = time;
4843   game.LevelSolved_CountingScore = score;
4844   game.LevelSolved_CountingHealth = health;
4845
4846   game_panel_controls[GAME_PANEL_TIME].value = time;
4847   game_panel_controls[GAME_PANEL_SCORE].value = score;
4848   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4849
4850   DisplayGameControlValues();
4851 }
4852
4853 static void LevelSolved(void)
4854 {
4855   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4856       game.players_still_needed > 0)
4857     return;
4858
4859   game.LevelSolved = TRUE;
4860   game.GameOver = TRUE;
4861
4862   tape.solved = TRUE;
4863
4864   // needed here to display correct panel values while player walks into exit
4865   LevelSolved_SetFinalGameValues();
4866 }
4867
4868 void GameWon(void)
4869 {
4870   static int time_count_steps;
4871   static int time, time_final;
4872   static float score, score_final; // needed for time score < 10 for 10 seconds
4873   static int health, health_final;
4874   static int game_over_delay_1 = 0;
4875   static int game_over_delay_2 = 0;
4876   static int game_over_delay_3 = 0;
4877   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4878   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4879
4880   if (!game.LevelSolved_GameWon)
4881   {
4882     int i;
4883
4884     // do not start end game actions before the player stops moving (to exit)
4885     if (local_player->active && local_player->MovPos)
4886       return;
4887
4888     // calculate final game values after player finished walking into exit
4889     LevelSolved_SetFinalGameValues();
4890
4891     game.LevelSolved_GameWon = TRUE;
4892     game.LevelSolved_SaveTape = tape.recording;
4893     game.LevelSolved_SaveScore = !tape.playing;
4894
4895     if (!tape.playing)
4896     {
4897       LevelStats_incSolved(level_nr);
4898
4899       SaveLevelSetup_SeriesInfo();
4900     }
4901
4902     if (tape.auto_play)         // tape might already be stopped here
4903       tape.auto_play_level_solved = TRUE;
4904
4905     TapeStop();
4906
4907     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4908     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4909     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4910
4911     time = time_final = game.time_final;
4912     score = score_final = game.score_final;
4913     health = health_final = game.health_final;
4914
4915     // update game panel values before (delayed) counting of score (if any)
4916     LevelSolved_DisplayFinalGameValues(time, score, health);
4917
4918     // if level has time score defined, calculate new final game values
4919     if (time_score > 0)
4920     {
4921       int time_final_max = 999;
4922       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4923       int time_frames = 0;
4924       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4925       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4926
4927       if (TimeLeft > 0)
4928       {
4929         time_final = 0;
4930         time_frames = time_frames_left;
4931       }
4932       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4933       {
4934         time_final = time_final_max;
4935         time_frames = time_frames_final_max - time_frames_played;
4936       }
4937
4938       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4939
4940       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4941
4942       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4943       {
4944         // keep previous values (final values already processed here)
4945         time_final = time;
4946         score_final = score;
4947       }
4948       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4949       {
4950         health_final = 0;
4951         score_final += health * time_score;
4952       }
4953
4954       game.score_final = score_final;
4955       game.health_final = health_final;
4956     }
4957
4958     // if not counting score after game, immediately update game panel values
4959     if (level_editor_test_game || !setup.count_score_after_game)
4960     {
4961       time = time_final;
4962       score = score_final;
4963
4964       LevelSolved_DisplayFinalGameValues(time, score, health);
4965     }
4966
4967     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4968     {
4969       // check if last player has left the level
4970       if (game.exit_x >= 0 &&
4971           game.exit_y >= 0)
4972       {
4973         int x = game.exit_x;
4974         int y = game.exit_y;
4975         int element = Tile[x][y];
4976
4977         // close exit door after last player
4978         if ((game.all_players_gone &&
4979              (element == EL_EXIT_OPEN ||
4980               element == EL_SP_EXIT_OPEN ||
4981               element == EL_STEEL_EXIT_OPEN)) ||
4982             element == EL_EM_EXIT_OPEN ||
4983             element == EL_EM_STEEL_EXIT_OPEN)
4984         {
4985
4986           Tile[x][y] =
4987             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4988              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4989              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4990              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4991              EL_EM_STEEL_EXIT_CLOSING);
4992
4993           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4994         }
4995
4996         // player disappears
4997         DrawLevelField(x, y);
4998       }
4999
5000       for (i = 0; i < MAX_PLAYERS; i++)
5001       {
5002         struct PlayerInfo *player = &stored_player[i];
5003
5004         if (player->present)
5005         {
5006           RemovePlayer(player);
5007
5008           // player disappears
5009           DrawLevelField(player->jx, player->jy);
5010         }
5011       }
5012     }
5013
5014     PlaySound(SND_GAME_WINNING);
5015   }
5016
5017   if (setup.count_score_after_game)
5018   {
5019     if (time != time_final)
5020     {
5021       if (game_over_delay_1 > 0)
5022       {
5023         game_over_delay_1--;
5024
5025         return;
5026       }
5027
5028       int time_to_go = ABS(time_final - time);
5029       int time_count_dir = (time < time_final ? +1 : -1);
5030
5031       if (time_to_go < time_count_steps)
5032         time_count_steps = 1;
5033
5034       time  += time_count_steps * time_count_dir;
5035       score += time_count_steps * time_score;
5036
5037       // set final score to correct rounding differences after counting score
5038       if (time == time_final)
5039         score = score_final;
5040
5041       LevelSolved_DisplayFinalGameValues(time, score, health);
5042
5043       if (time == time_final)
5044         StopSound(SND_GAME_LEVELTIME_BONUS);
5045       else if (setup.sound_loops)
5046         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5047       else
5048         PlaySound(SND_GAME_LEVELTIME_BONUS);
5049
5050       return;
5051     }
5052
5053     if (health != health_final)
5054     {
5055       if (game_over_delay_2 > 0)
5056       {
5057         game_over_delay_2--;
5058
5059         return;
5060       }
5061
5062       int health_count_dir = (health < health_final ? +1 : -1);
5063
5064       health += health_count_dir;
5065       score  += time_score;
5066
5067       LevelSolved_DisplayFinalGameValues(time, score, health);
5068
5069       if (health == health_final)
5070         StopSound(SND_GAME_LEVELTIME_BONUS);
5071       else if (setup.sound_loops)
5072         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5073       else
5074         PlaySound(SND_GAME_LEVELTIME_BONUS);
5075
5076       return;
5077     }
5078   }
5079
5080   game.panel.active = FALSE;
5081
5082   if (game_over_delay_3 > 0)
5083   {
5084     game_over_delay_3--;
5085
5086     return;
5087   }
5088
5089   GameEnd();
5090 }
5091
5092 void GameEnd(void)
5093 {
5094   // used instead of "level_nr" (needed for network games)
5095   int last_level_nr = levelset.level_nr;
5096   boolean tape_saved = FALSE;
5097
5098   game.LevelSolved_GameEnd = TRUE;
5099
5100   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5101   {
5102     // make sure that request dialog to save tape does not open door again
5103     if (!global.use_envelope_request)
5104       CloseDoor(DOOR_CLOSE_1);
5105
5106     // ask to save tape
5107     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5108
5109     // set unique basename for score tape (also saved in high score table)
5110     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5111   }
5112
5113   // if no tape is to be saved, close both doors simultaneously
5114   CloseDoor(DOOR_CLOSE_ALL);
5115
5116   if (level_editor_test_game || score_info_tape_play)
5117   {
5118     SetGameStatus(GAME_MODE_MAIN);
5119
5120     DrawMainMenu();
5121
5122     return;
5123   }
5124
5125   if (!game.LevelSolved_SaveScore)
5126   {
5127     SetGameStatus(GAME_MODE_MAIN);
5128
5129     DrawMainMenu();
5130
5131     return;
5132   }
5133
5134   if (level_nr == leveldir_current->handicap_level)
5135   {
5136     leveldir_current->handicap_level++;
5137
5138     SaveLevelSetup_SeriesInfo();
5139   }
5140
5141   // save score and score tape before potentially erasing tape below
5142   NewHighScore(last_level_nr, tape_saved);
5143
5144   if (setup.increment_levels &&
5145       level_nr < leveldir_current->last_level &&
5146       !network_playing)
5147   {
5148     level_nr++;         // advance to next level
5149     TapeErase();        // start with empty tape
5150
5151     if (setup.auto_play_next_level)
5152     {
5153       scores.continue_playing = TRUE;
5154       scores.next_level_nr = level_nr;
5155
5156       LoadLevel(level_nr);
5157
5158       SaveLevelSetup_SeriesInfo();
5159     }
5160   }
5161
5162   if (scores.last_added >= 0 && setup.show_scores_after_game)
5163   {
5164     SetGameStatus(GAME_MODE_SCORES);
5165
5166     DrawHallOfFame(last_level_nr);
5167   }
5168   else if (scores.continue_playing)
5169   {
5170     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5171   }
5172   else
5173   {
5174     SetGameStatus(GAME_MODE_MAIN);
5175
5176     DrawMainMenu();
5177   }
5178 }
5179
5180 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5181                          boolean one_score_entry_per_name)
5182 {
5183   int i;
5184
5185   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5186     return -1;
5187
5188   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5189   {
5190     struct ScoreEntry *entry = &list->entry[i];
5191     boolean score_is_better = (new_entry->score >  entry->score);
5192     boolean score_is_equal  = (new_entry->score == entry->score);
5193     boolean time_is_better  = (new_entry->time  <  entry->time);
5194     boolean time_is_equal   = (new_entry->time  == entry->time);
5195     boolean better_by_score = (score_is_better ||
5196                                (score_is_equal && time_is_better));
5197     boolean better_by_time  = (time_is_better ||
5198                                (time_is_equal && score_is_better));
5199     boolean is_better = (level.rate_time_over_score ? better_by_time :
5200                          better_by_score);
5201     boolean entry_is_empty = (entry->score == 0 &&
5202                               entry->time == 0);
5203
5204     // prevent adding server score entries if also existing in local score file
5205     // (special case: historic score entries have an empty tape basename entry)
5206     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5207         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5208     {
5209       // add fields from server score entry not stored in local score entry
5210       // (currently, this means setting platform, version and country fields;
5211       // in rare cases, this may also correct an invalid score value, as
5212       // historic scores might have been truncated to 16-bit values locally)
5213       *entry = *new_entry;
5214
5215       return -1;
5216     }
5217
5218     if (is_better || entry_is_empty)
5219     {
5220       // player has made it to the hall of fame
5221
5222       if (i < MAX_SCORE_ENTRIES - 1)
5223       {
5224         int m = MAX_SCORE_ENTRIES - 1;
5225         int l;
5226
5227         if (one_score_entry_per_name)
5228         {
5229           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5230             if (strEqual(list->entry[l].name, new_entry->name))
5231               m = l;
5232
5233           if (m == i)   // player's new highscore overwrites his old one
5234             goto put_into_list;
5235         }
5236
5237         for (l = m; l > i; l--)
5238           list->entry[l] = list->entry[l - 1];
5239       }
5240
5241       put_into_list:
5242
5243       *entry = *new_entry;
5244
5245       return i;
5246     }
5247     else if (one_score_entry_per_name &&
5248              strEqual(entry->name, new_entry->name))
5249     {
5250       // player already in high score list with better score or time
5251
5252       return -1;
5253     }
5254   }
5255
5256   // special case: new score is beyond the last high score list position
5257   return MAX_SCORE_ENTRIES;
5258 }
5259
5260 void NewHighScore(int level_nr, boolean tape_saved)
5261 {
5262   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5263   boolean one_per_name = FALSE;
5264
5265   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5266   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5267
5268   new_entry.score = game.score_final;
5269   new_entry.time = game.score_time_final;
5270
5271   LoadScore(level_nr);
5272
5273   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5274
5275   if (scores.last_added >= MAX_SCORE_ENTRIES)
5276   {
5277     scores.last_added = MAX_SCORE_ENTRIES - 1;
5278     scores.force_last_added = TRUE;
5279
5280     scores.entry[scores.last_added] = new_entry;
5281
5282     // store last added local score entry (before merging server scores)
5283     scores.last_added_local = scores.last_added;
5284
5285     return;
5286   }
5287
5288   if (scores.last_added < 0)
5289     return;
5290
5291   SaveScore(level_nr);
5292
5293   // store last added local score entry (before merging server scores)
5294   scores.last_added_local = scores.last_added;
5295
5296   if (!game.LevelSolved_SaveTape)
5297     return;
5298
5299   SaveScoreTape(level_nr);
5300
5301   if (setup.ask_for_using_api_server)
5302   {
5303     setup.use_api_server =
5304       Request("Upload your score and tape to the high score server?", REQ_ASK);
5305
5306     if (!setup.use_api_server)
5307       Request("Not using high score server! Use setup menu to enable again!",
5308               REQ_CONFIRM);
5309
5310     runtime.use_api_server = setup.use_api_server;
5311
5312     // after asking for using API server once, do not ask again
5313     setup.ask_for_using_api_server = FALSE;
5314
5315     SaveSetup_ServerSetup();
5316   }
5317
5318   SaveServerScore(level_nr, tape_saved);
5319 }
5320
5321 void MergeServerScore(void)
5322 {
5323   struct ScoreEntry last_added_entry;
5324   boolean one_per_name = FALSE;
5325   int i;
5326
5327   if (scores.last_added >= 0)
5328     last_added_entry = scores.entry[scores.last_added];
5329
5330   for (i = 0; i < server_scores.num_entries; i++)
5331   {
5332     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5333
5334     if (pos >= 0 && pos <= scores.last_added)
5335       scores.last_added++;
5336   }
5337
5338   if (scores.last_added >= MAX_SCORE_ENTRIES)
5339   {
5340     scores.last_added = MAX_SCORE_ENTRIES - 1;
5341     scores.force_last_added = TRUE;
5342
5343     scores.entry[scores.last_added] = last_added_entry;
5344   }
5345 }
5346
5347 static int getElementMoveStepsizeExt(int x, int y, int direction)
5348 {
5349   int element = Tile[x][y];
5350   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5351   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5352   int horiz_move = (dx != 0);
5353   int sign = (horiz_move ? dx : dy);
5354   int step = sign * element_info[element].move_stepsize;
5355
5356   // special values for move stepsize for spring and things on conveyor belt
5357   if (horiz_move)
5358   {
5359     if (CAN_FALL(element) &&
5360         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5361       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5362     else if (element == EL_SPRING)
5363       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5364   }
5365
5366   return step;
5367 }
5368
5369 static int getElementMoveStepsize(int x, int y)
5370 {
5371   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5372 }
5373
5374 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5375 {
5376   if (player->GfxAction != action || player->GfxDir != dir)
5377   {
5378     player->GfxAction = action;
5379     player->GfxDir = dir;
5380     player->Frame = 0;
5381     player->StepFrame = 0;
5382   }
5383 }
5384
5385 static void ResetGfxFrame(int x, int y)
5386 {
5387   // profiling showed that "autotest" spends 10~20% of its time in this function
5388   if (DrawingDeactivatedField())
5389     return;
5390
5391   int element = Tile[x][y];
5392   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5393
5394   if (graphic_info[graphic].anim_global_sync)
5395     GfxFrame[x][y] = FrameCounter;
5396   else if (graphic_info[graphic].anim_global_anim_sync)
5397     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5398   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5399     GfxFrame[x][y] = CustomValue[x][y];
5400   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5401     GfxFrame[x][y] = element_info[element].collect_score;
5402   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5403     GfxFrame[x][y] = ChangeDelay[x][y];
5404 }
5405
5406 static void ResetGfxAnimation(int x, int y)
5407 {
5408   GfxAction[x][y] = ACTION_DEFAULT;
5409   GfxDir[x][y] = MovDir[x][y];
5410   GfxFrame[x][y] = 0;
5411
5412   ResetGfxFrame(x, y);
5413 }
5414
5415 static void ResetRandomAnimationValue(int x, int y)
5416 {
5417   GfxRandom[x][y] = INIT_GFX_RANDOM();
5418 }
5419
5420 static void InitMovingField(int x, int y, int direction)
5421 {
5422   int element = Tile[x][y];
5423   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5424   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5425   int newx = x + dx;
5426   int newy = y + dy;
5427   boolean is_moving_before, is_moving_after;
5428
5429   // check if element was/is moving or being moved before/after mode change
5430   is_moving_before = (WasJustMoving[x][y] != 0);
5431   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5432
5433   // reset animation only for moving elements which change direction of moving
5434   // or which just started or stopped moving
5435   // (else CEs with property "can move" / "not moving" are reset each frame)
5436   if (is_moving_before != is_moving_after ||
5437       direction != MovDir[x][y])
5438     ResetGfxAnimation(x, y);
5439
5440   MovDir[x][y] = direction;
5441   GfxDir[x][y] = direction;
5442
5443   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5444                      direction == MV_DOWN && CAN_FALL(element) ?
5445                      ACTION_FALLING : ACTION_MOVING);
5446
5447   // this is needed for CEs with property "can move" / "not moving"
5448
5449   if (is_moving_after)
5450   {
5451     if (Tile[newx][newy] == EL_EMPTY)
5452       Tile[newx][newy] = EL_BLOCKED;
5453
5454     MovDir[newx][newy] = MovDir[x][y];
5455
5456     CustomValue[newx][newy] = CustomValue[x][y];
5457
5458     GfxFrame[newx][newy] = GfxFrame[x][y];
5459     GfxRandom[newx][newy] = GfxRandom[x][y];
5460     GfxAction[newx][newy] = GfxAction[x][y];
5461     GfxDir[newx][newy] = GfxDir[x][y];
5462   }
5463 }
5464
5465 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5466 {
5467   int direction = MovDir[x][y];
5468   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5469   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5470
5471   *goes_to_x = newx;
5472   *goes_to_y = newy;
5473 }
5474
5475 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5476 {
5477   int direction = MovDir[x][y];
5478   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5479   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5480
5481   *comes_from_x = oldx;
5482   *comes_from_y = oldy;
5483 }
5484
5485 static int MovingOrBlocked2Element(int x, int y)
5486 {
5487   int element = Tile[x][y];
5488
5489   if (element == EL_BLOCKED)
5490   {
5491     int oldx, oldy;
5492
5493     Blocked2Moving(x, y, &oldx, &oldy);
5494
5495     return Tile[oldx][oldy];
5496   }
5497
5498   return element;
5499 }
5500
5501 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5502 {
5503   // like MovingOrBlocked2Element(), but if element is moving
5504   // and (x, y) is the field the moving element is just leaving,
5505   // return EL_BLOCKED instead of the element value
5506   int element = Tile[x][y];
5507
5508   if (IS_MOVING(x, y))
5509   {
5510     if (element == EL_BLOCKED)
5511     {
5512       int oldx, oldy;
5513
5514       Blocked2Moving(x, y, &oldx, &oldy);
5515       return Tile[oldx][oldy];
5516     }
5517     else
5518       return EL_BLOCKED;
5519   }
5520   else
5521     return element;
5522 }
5523
5524 static void RemoveField(int x, int y)
5525 {
5526   Tile[x][y] = EL_EMPTY;
5527
5528   MovPos[x][y] = 0;
5529   MovDir[x][y] = 0;
5530   MovDelay[x][y] = 0;
5531
5532   CustomValue[x][y] = 0;
5533
5534   AmoebaNr[x][y] = 0;
5535   ChangeDelay[x][y] = 0;
5536   ChangePage[x][y] = -1;
5537   Pushed[x][y] = FALSE;
5538
5539   GfxElement[x][y] = EL_UNDEFINED;
5540   GfxAction[x][y] = ACTION_DEFAULT;
5541   GfxDir[x][y] = MV_NONE;
5542 }
5543
5544 static void RemoveMovingField(int x, int y)
5545 {
5546   int oldx = x, oldy = y, newx = x, newy = y;
5547   int element = Tile[x][y];
5548   int next_element = EL_UNDEFINED;
5549
5550   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5551     return;
5552
5553   if (IS_MOVING(x, y))
5554   {
5555     Moving2Blocked(x, y, &newx, &newy);
5556
5557     if (Tile[newx][newy] != EL_BLOCKED)
5558     {
5559       // element is moving, but target field is not free (blocked), but
5560       // already occupied by something different (example: acid pool);
5561       // in this case, only remove the moving field, but not the target
5562
5563       RemoveField(oldx, oldy);
5564
5565       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5566
5567       TEST_DrawLevelField(oldx, oldy);
5568
5569       return;
5570     }
5571   }
5572   else if (element == EL_BLOCKED)
5573   {
5574     Blocked2Moving(x, y, &oldx, &oldy);
5575     if (!IS_MOVING(oldx, oldy))
5576       return;
5577   }
5578
5579   if (element == EL_BLOCKED &&
5580       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5581        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5582        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5583        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5584        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5585        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5586     next_element = get_next_element(Tile[oldx][oldy]);
5587
5588   RemoveField(oldx, oldy);
5589   RemoveField(newx, newy);
5590
5591   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5592
5593   if (next_element != EL_UNDEFINED)
5594     Tile[oldx][oldy] = next_element;
5595
5596   TEST_DrawLevelField(oldx, oldy);
5597   TEST_DrawLevelField(newx, newy);
5598 }
5599
5600 void DrawDynamite(int x, int y)
5601 {
5602   int sx = SCREENX(x), sy = SCREENY(y);
5603   int graphic = el2img(Tile[x][y]);
5604   int frame;
5605
5606   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5607     return;
5608
5609   if (IS_WALKABLE_INSIDE(Back[x][y]))
5610     return;
5611
5612   if (Back[x][y])
5613     DrawLevelElement(x, y, Back[x][y]);
5614   else if (Store[x][y])
5615     DrawLevelElement(x, y, Store[x][y]);
5616   else if (game.use_masked_elements)
5617     DrawLevelElement(x, y, EL_EMPTY);
5618
5619   frame = getGraphicAnimationFrameXY(graphic, x, y);
5620
5621   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5622     DrawGraphicThruMask(sx, sy, graphic, frame);
5623   else
5624     DrawGraphic(sx, sy, graphic, frame);
5625 }
5626
5627 static void CheckDynamite(int x, int y)
5628 {
5629   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5630   {
5631     MovDelay[x][y]--;
5632
5633     if (MovDelay[x][y] != 0)
5634     {
5635       DrawDynamite(x, y);
5636       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5637
5638       return;
5639     }
5640   }
5641
5642   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5643
5644   Bang(x, y);
5645 }
5646
5647 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5648 {
5649   boolean num_checked_players = 0;
5650   int i;
5651
5652   for (i = 0; i < MAX_PLAYERS; i++)
5653   {
5654     if (stored_player[i].active)
5655     {
5656       int sx = stored_player[i].jx;
5657       int sy = stored_player[i].jy;
5658
5659       if (num_checked_players == 0)
5660       {
5661         *sx1 = *sx2 = sx;
5662         *sy1 = *sy2 = sy;
5663       }
5664       else
5665       {
5666         *sx1 = MIN(*sx1, sx);
5667         *sy1 = MIN(*sy1, sy);
5668         *sx2 = MAX(*sx2, sx);
5669         *sy2 = MAX(*sy2, sy);
5670       }
5671
5672       num_checked_players++;
5673     }
5674   }
5675 }
5676
5677 static boolean checkIfAllPlayersFitToScreen_RND(void)
5678 {
5679   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5680
5681   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5682
5683   return (sx2 - sx1 < SCR_FIELDX &&
5684           sy2 - sy1 < SCR_FIELDY);
5685 }
5686
5687 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5688 {
5689   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5690
5691   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5692
5693   *sx = (sx1 + sx2) / 2;
5694   *sy = (sy1 + sy2) / 2;
5695 }
5696
5697 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5698                                boolean center_screen, boolean quick_relocation)
5699 {
5700   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5701   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5702   boolean no_delay = (tape.warp_forward);
5703   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5704   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5705   int new_scroll_x, new_scroll_y;
5706
5707   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5708   {
5709     // case 1: quick relocation inside visible screen (without scrolling)
5710
5711     RedrawPlayfield();
5712
5713     return;
5714   }
5715
5716   if (!level.shifted_relocation || center_screen)
5717   {
5718     // relocation _with_ centering of screen
5719
5720     new_scroll_x = SCROLL_POSITION_X(x);
5721     new_scroll_y = SCROLL_POSITION_Y(y);
5722   }
5723   else
5724   {
5725     // relocation _without_ centering of screen
5726
5727     // apply distance between old and new player position to scroll position
5728     int shifted_scroll_x = scroll_x + (x - old_x);
5729     int shifted_scroll_y = scroll_y + (y - old_y);
5730
5731     // make sure that shifted scroll position does not scroll beyond screen
5732     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5733     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5734
5735     // special case for teleporting from one end of the playfield to the other
5736     // (this kludge prevents the destination area to be shifted by half a tile
5737     // against the source destination for even screen width or screen height;
5738     // probably most useful when used with high "game.forced_scroll_delay_value"
5739     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5740     if (quick_relocation)
5741     {
5742       if (EVEN(SCR_FIELDX))
5743       {
5744         // relocate (teleport) between left and right border (half or full)
5745         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5746           new_scroll_x = SBX_Right;
5747         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5748           new_scroll_x = SBX_Right - 1;
5749         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5750           new_scroll_x = SBX_Left;
5751         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5752           new_scroll_x = SBX_Left + 1;
5753       }
5754
5755       if (EVEN(SCR_FIELDY))
5756       {
5757         // relocate (teleport) between top and bottom border (half or full)
5758         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5759           new_scroll_y = SBY_Lower;
5760         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5761           new_scroll_y = SBY_Lower - 1;
5762         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5763           new_scroll_y = SBY_Upper;
5764         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5765           new_scroll_y = SBY_Upper + 1;
5766       }
5767     }
5768   }
5769
5770   if (quick_relocation)
5771   {
5772     // case 2: quick relocation (redraw without visible scrolling)
5773
5774     scroll_x = new_scroll_x;
5775     scroll_y = new_scroll_y;
5776
5777     RedrawPlayfield();
5778
5779     return;
5780   }
5781
5782   // case 3: visible relocation (with scrolling to new position)
5783
5784   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5785
5786   SetVideoFrameDelay(wait_delay_value);
5787
5788   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5789   {
5790     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5791     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5792
5793     if (dx == 0 && dy == 0)             // no scrolling needed at all
5794       break;
5795
5796     scroll_x -= dx;
5797     scroll_y -= dy;
5798
5799     // set values for horizontal/vertical screen scrolling (half tile size)
5800     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5801     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5802     int pos_x = dx * TILEX / 2;
5803     int pos_y = dy * TILEY / 2;
5804     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5805     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5806
5807     ScrollLevel(dx, dy);
5808     DrawAllPlayers();
5809
5810     // scroll in two steps of half tile size to make things smoother
5811     BlitScreenToBitmapExt_RND(window, fx, fy);
5812
5813     // scroll second step to align at full tile size
5814     BlitScreenToBitmap(window);
5815   }
5816
5817   DrawAllPlayers();
5818   BackToFront();
5819
5820   SetVideoFrameDelay(frame_delay_value_old);
5821 }
5822
5823 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5824 {
5825   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5826   int player_nr = GET_PLAYER_NR(el_player);
5827   struct PlayerInfo *player = &stored_player[player_nr];
5828   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5829   boolean no_delay = (tape.warp_forward);
5830   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5831   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5832   int old_jx = player->jx;
5833   int old_jy = player->jy;
5834   int old_element = Tile[old_jx][old_jy];
5835   int element = Tile[jx][jy];
5836   boolean player_relocated = (old_jx != jx || old_jy != jy);
5837
5838   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5839   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5840   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5841   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5842   int leave_side_horiz = move_dir_horiz;
5843   int leave_side_vert  = move_dir_vert;
5844   int enter_side = enter_side_horiz | enter_side_vert;
5845   int leave_side = leave_side_horiz | leave_side_vert;
5846
5847   if (player->buried)           // do not reanimate dead player
5848     return;
5849
5850   if (!player_relocated)        // no need to relocate the player
5851     return;
5852
5853   if (IS_PLAYER(jx, jy))        // player already placed at new position
5854   {
5855     RemoveField(jx, jy);        // temporarily remove newly placed player
5856     DrawLevelField(jx, jy);
5857   }
5858
5859   if (player->present)
5860   {
5861     while (player->MovPos)
5862     {
5863       ScrollPlayer(player, SCROLL_GO_ON);
5864       ScrollScreen(NULL, SCROLL_GO_ON);
5865
5866       AdvanceFrameAndPlayerCounters(player->index_nr);
5867
5868       DrawPlayer(player);
5869
5870       BackToFront_WithFrameDelay(wait_delay_value);
5871     }
5872
5873     DrawPlayer(player);         // needed here only to cleanup last field
5874     DrawLevelField(player->jx, player->jy);     // remove player graphic
5875
5876     player->is_moving = FALSE;
5877   }
5878
5879   if (IS_CUSTOM_ELEMENT(old_element))
5880     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5881                                CE_LEFT_BY_PLAYER,
5882                                player->index_bit, leave_side);
5883
5884   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5885                                       CE_PLAYER_LEAVES_X,
5886                                       player->index_bit, leave_side);
5887
5888   Tile[jx][jy] = el_player;
5889   InitPlayerField(jx, jy, el_player, TRUE);
5890
5891   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5892      possible that the relocation target field did not contain a player element,
5893      but a walkable element, to which the new player was relocated -- in this
5894      case, restore that (already initialized!) element on the player field */
5895   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5896   {
5897     Tile[jx][jy] = element;     // restore previously existing element
5898   }
5899
5900   // only visually relocate centered player
5901   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5902                      FALSE, level.instant_relocation);
5903
5904   TestIfPlayerTouchesBadThing(jx, jy);
5905   TestIfPlayerTouchesCustomElement(jx, jy);
5906
5907   if (IS_CUSTOM_ELEMENT(element))
5908     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5909                                player->index_bit, enter_side);
5910
5911   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5912                                       player->index_bit, enter_side);
5913
5914   if (player->is_switching)
5915   {
5916     /* ensure that relocation while still switching an element does not cause
5917        a new element to be treated as also switched directly after relocation
5918        (this is important for teleporter switches that teleport the player to
5919        a place where another teleporter switch is in the same direction, which
5920        would then incorrectly be treated as immediately switched before the
5921        direction key that caused the switch was released) */
5922
5923     player->switch_x += jx - old_jx;
5924     player->switch_y += jy - old_jy;
5925   }
5926 }
5927
5928 static void Explode(int ex, int ey, int phase, int mode)
5929 {
5930   int x, y;
5931   int last_phase;
5932   int border_element;
5933
5934   if (game.explosions_delayed)
5935   {
5936     ExplodeField[ex][ey] = mode;
5937     return;
5938   }
5939
5940   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5941   {
5942     int center_element = Tile[ex][ey];
5943     int ce_value = CustomValue[ex][ey];
5944     int ce_score = element_info[center_element].collect_score;
5945     int artwork_element, explosion_element;     // set these values later
5946
5947     // remove things displayed in background while burning dynamite
5948     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5949       Back[ex][ey] = 0;
5950
5951     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5952     {
5953       // put moving element to center field (and let it explode there)
5954       center_element = MovingOrBlocked2Element(ex, ey);
5955       RemoveMovingField(ex, ey);
5956       Tile[ex][ey] = center_element;
5957     }
5958
5959     // now "center_element" is finally determined -- set related values now
5960     artwork_element = center_element;           // for custom player artwork
5961     explosion_element = center_element;         // for custom player artwork
5962
5963     if (IS_PLAYER(ex, ey))
5964     {
5965       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5966
5967       artwork_element = stored_player[player_nr].artwork_element;
5968
5969       if (level.use_explosion_element[player_nr])
5970       {
5971         explosion_element = level.explosion_element[player_nr];
5972         artwork_element = explosion_element;
5973       }
5974     }
5975
5976     if (mode == EX_TYPE_NORMAL ||
5977         mode == EX_TYPE_CENTER ||
5978         mode == EX_TYPE_CROSS)
5979       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5980
5981     last_phase = element_info[explosion_element].explosion_delay + 1;
5982
5983     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5984     {
5985       int xx = x - ex + 1;
5986       int yy = y - ey + 1;
5987       int element;
5988
5989       if (!IN_LEV_FIELD(x, y) ||
5990           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5991           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5992         continue;
5993
5994       element = Tile[x][y];
5995
5996       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5997       {
5998         element = MovingOrBlocked2Element(x, y);
5999
6000         if (!IS_EXPLOSION_PROOF(element))
6001           RemoveMovingField(x, y);
6002       }
6003
6004       // indestructible elements can only explode in center (but not flames)
6005       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6006                                            mode == EX_TYPE_BORDER)) ||
6007           element == EL_FLAMES)
6008         continue;
6009
6010       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6011          behaviour, for example when touching a yamyam that explodes to rocks
6012          with active deadly shield, a rock is created under the player !!! */
6013       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6014 #if 0
6015       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6016           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6017            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6018 #else
6019       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6020 #endif
6021       {
6022         if (IS_ACTIVE_BOMB(element))
6023         {
6024           // re-activate things under the bomb like gate or penguin
6025           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6026           Back[x][y] = 0;
6027         }
6028
6029         continue;
6030       }
6031
6032       // save walkable background elements while explosion on same tile
6033       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6034           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6035         Back[x][y] = element;
6036
6037       // ignite explodable elements reached by other explosion
6038       if (element == EL_EXPLOSION)
6039         element = Store2[x][y];
6040
6041       if (AmoebaNr[x][y] &&
6042           (element == EL_AMOEBA_FULL ||
6043            element == EL_BD_AMOEBA ||
6044            element == EL_AMOEBA_GROWING))
6045       {
6046         AmoebaCnt[AmoebaNr[x][y]]--;
6047         AmoebaCnt2[AmoebaNr[x][y]]--;
6048       }
6049
6050       RemoveField(x, y);
6051
6052       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6053       {
6054         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6055
6056         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6057
6058         if (PLAYERINFO(ex, ey)->use_murphy)
6059           Store[x][y] = EL_EMPTY;
6060       }
6061
6062       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6063       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6064       else if (IS_PLAYER_ELEMENT(center_element))
6065         Store[x][y] = EL_EMPTY;
6066       else if (center_element == EL_YAMYAM)
6067         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6068       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6069         Store[x][y] = element_info[center_element].content.e[xx][yy];
6070 #if 1
6071       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6072       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6073       // otherwise) -- FIX THIS !!!
6074       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6075         Store[x][y] = element_info[element].content.e[1][1];
6076 #else
6077       else if (!CAN_EXPLODE(element))
6078         Store[x][y] = element_info[element].content.e[1][1];
6079 #endif
6080       else
6081         Store[x][y] = EL_EMPTY;
6082
6083       if (IS_CUSTOM_ELEMENT(center_element))
6084         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6085                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6086                        Store[x][y] >= EL_PREV_CE_8 &&
6087                        Store[x][y] <= EL_NEXT_CE_8 ?
6088                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6089                        Store[x][y]);
6090
6091       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6092           center_element == EL_AMOEBA_TO_DIAMOND)
6093         Store2[x][y] = element;
6094
6095       Tile[x][y] = EL_EXPLOSION;
6096       GfxElement[x][y] = artwork_element;
6097
6098       ExplodePhase[x][y] = 1;
6099       ExplodeDelay[x][y] = last_phase;
6100
6101       Stop[x][y] = TRUE;
6102     }
6103
6104     if (center_element == EL_YAMYAM)
6105       game.yamyam_content_nr =
6106         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6107
6108     return;
6109   }
6110
6111   if (Stop[ex][ey])
6112     return;
6113
6114   x = ex;
6115   y = ey;
6116
6117   if (phase == 1)
6118     GfxFrame[x][y] = 0;         // restart explosion animation
6119
6120   last_phase = ExplodeDelay[x][y];
6121
6122   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6123
6124   // this can happen if the player leaves an explosion just in time
6125   if (GfxElement[x][y] == EL_UNDEFINED)
6126     GfxElement[x][y] = EL_EMPTY;
6127
6128   border_element = Store2[x][y];
6129   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6130     border_element = StorePlayer[x][y];
6131
6132   if (phase == element_info[border_element].ignition_delay ||
6133       phase == last_phase)
6134   {
6135     boolean border_explosion = FALSE;
6136
6137     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6138         !PLAYER_EXPLOSION_PROTECTED(x, y))
6139     {
6140       KillPlayerUnlessExplosionProtected(x, y);
6141       border_explosion = TRUE;
6142     }
6143     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6144     {
6145       Tile[x][y] = Store2[x][y];
6146       Store2[x][y] = 0;
6147       Bang(x, y);
6148       border_explosion = TRUE;
6149     }
6150     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6151     {
6152       AmoebaToDiamond(x, y);
6153       Store2[x][y] = 0;
6154       border_explosion = TRUE;
6155     }
6156
6157     // if an element just explodes due to another explosion (chain-reaction),
6158     // do not immediately end the new explosion when it was the last frame of
6159     // the explosion (as it would be done in the following "if"-statement!)
6160     if (border_explosion && phase == last_phase)
6161       return;
6162   }
6163
6164   // this can happen if the player was just killed by an explosion
6165   if (GfxElement[x][y] == EL_UNDEFINED)
6166     GfxElement[x][y] = EL_EMPTY;
6167
6168   if (phase == last_phase)
6169   {
6170     int element;
6171
6172     element = Tile[x][y] = Store[x][y];
6173     Store[x][y] = Store2[x][y] = 0;
6174     GfxElement[x][y] = EL_UNDEFINED;
6175
6176     // player can escape from explosions and might therefore be still alive
6177     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6178         element <= EL_PLAYER_IS_EXPLODING_4)
6179     {
6180       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6181       int explosion_element = EL_PLAYER_1 + player_nr;
6182       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6183       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6184
6185       if (level.use_explosion_element[player_nr])
6186         explosion_element = level.explosion_element[player_nr];
6187
6188       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6189                     element_info[explosion_element].content.e[xx][yy]);
6190     }
6191
6192     // restore probably existing indestructible background element
6193     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6194       element = Tile[x][y] = Back[x][y];
6195     Back[x][y] = 0;
6196
6197     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6198     GfxDir[x][y] = MV_NONE;
6199     ChangeDelay[x][y] = 0;
6200     ChangePage[x][y] = -1;
6201
6202     CustomValue[x][y] = 0;
6203
6204     InitField_WithBug2(x, y, FALSE);
6205
6206     TEST_DrawLevelField(x, y);
6207
6208     TestIfElementTouchesCustomElement(x, y);
6209
6210     if (GFX_CRUMBLED(element))
6211       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6212
6213     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6214       StorePlayer[x][y] = 0;
6215
6216     if (IS_PLAYER_ELEMENT(element))
6217       RelocatePlayer(x, y, element);
6218   }
6219   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6220   {
6221     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6222     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6223
6224     if (phase == 1)
6225       TEST_DrawLevelFieldCrumbled(x, y);
6226
6227     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6228     {
6229       DrawLevelElement(x, y, Back[x][y]);
6230       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6231     }
6232     else if (IS_WALKABLE_UNDER(Back[x][y]))
6233     {
6234       DrawLevelGraphic(x, y, graphic, frame);
6235       DrawLevelElementThruMask(x, y, Back[x][y]);
6236     }
6237     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6238       DrawLevelGraphic(x, y, graphic, frame);
6239   }
6240 }
6241
6242 static void DynaExplode(int ex, int ey)
6243 {
6244   int i, j;
6245   int dynabomb_element = Tile[ex][ey];
6246   int dynabomb_size = 1;
6247   boolean dynabomb_xl = FALSE;
6248   struct PlayerInfo *player;
6249   struct XY *xy = xy_topdown;
6250
6251   if (IS_ACTIVE_BOMB(dynabomb_element))
6252   {
6253     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6254     dynabomb_size = player->dynabomb_size;
6255     dynabomb_xl = player->dynabomb_xl;
6256     player->dynabombs_left++;
6257   }
6258
6259   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6260
6261   for (i = 0; i < NUM_DIRECTIONS; i++)
6262   {
6263     for (j = 1; j <= dynabomb_size; j++)
6264     {
6265       int x = ex + j * xy[i].x;
6266       int y = ey + j * xy[i].y;
6267       int element;
6268
6269       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6270         break;
6271
6272       element = Tile[x][y];
6273
6274       // do not restart explosions of fields with active bombs
6275       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6276         continue;
6277
6278       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6279
6280       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6281           !IS_DIGGABLE(element) && !dynabomb_xl)
6282         break;
6283     }
6284   }
6285 }
6286
6287 void Bang(int x, int y)
6288 {
6289   int element = MovingOrBlocked2Element(x, y);
6290   int explosion_type = EX_TYPE_NORMAL;
6291
6292   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6293   {
6294     struct PlayerInfo *player = PLAYERINFO(x, y);
6295
6296     element = Tile[x][y] = player->initial_element;
6297
6298     if (level.use_explosion_element[player->index_nr])
6299     {
6300       int explosion_element = level.explosion_element[player->index_nr];
6301
6302       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6303         explosion_type = EX_TYPE_CROSS;
6304       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6305         explosion_type = EX_TYPE_CENTER;
6306     }
6307   }
6308
6309   switch (element)
6310   {
6311     case EL_BUG:
6312     case EL_SPACESHIP:
6313     case EL_BD_BUTTERFLY:
6314     case EL_BD_FIREFLY:
6315     case EL_YAMYAM:
6316     case EL_DARK_YAMYAM:
6317     case EL_ROBOT:
6318     case EL_PACMAN:
6319     case EL_MOLE:
6320       RaiseScoreElement(element);
6321       break;
6322
6323     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6324     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6325     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6326     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6327     case EL_DYNABOMB_INCREASE_NUMBER:
6328     case EL_DYNABOMB_INCREASE_SIZE:
6329     case EL_DYNABOMB_INCREASE_POWER:
6330       explosion_type = EX_TYPE_DYNA;
6331       break;
6332
6333     case EL_DC_LANDMINE:
6334       explosion_type = EX_TYPE_CENTER;
6335       break;
6336
6337     case EL_PENGUIN:
6338     case EL_LAMP:
6339     case EL_LAMP_ACTIVE:
6340     case EL_AMOEBA_TO_DIAMOND:
6341       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6342         explosion_type = EX_TYPE_CENTER;
6343       break;
6344
6345     default:
6346       if (element_info[element].explosion_type == EXPLODES_CROSS)
6347         explosion_type = EX_TYPE_CROSS;
6348       else if (element_info[element].explosion_type == EXPLODES_1X1)
6349         explosion_type = EX_TYPE_CENTER;
6350       break;
6351   }
6352
6353   if (explosion_type == EX_TYPE_DYNA)
6354     DynaExplode(x, y);
6355   else
6356     Explode(x, y, EX_PHASE_START, explosion_type);
6357
6358   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6359 }
6360
6361 static void SplashAcid(int x, int y)
6362 {
6363   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6364       (!IN_LEV_FIELD(x - 1, y - 2) ||
6365        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6366     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6367
6368   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6369       (!IN_LEV_FIELD(x + 1, y - 2) ||
6370        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6371     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6372
6373   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6374 }
6375
6376 static void InitBeltMovement(void)
6377 {
6378   static int belt_base_element[4] =
6379   {
6380     EL_CONVEYOR_BELT_1_LEFT,
6381     EL_CONVEYOR_BELT_2_LEFT,
6382     EL_CONVEYOR_BELT_3_LEFT,
6383     EL_CONVEYOR_BELT_4_LEFT
6384   };
6385   static int belt_base_active_element[4] =
6386   {
6387     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6388     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6389     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6390     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6391   };
6392
6393   int x, y, i, j;
6394
6395   // set frame order for belt animation graphic according to belt direction
6396   for (i = 0; i < NUM_BELTS; i++)
6397   {
6398     int belt_nr = i;
6399
6400     for (j = 0; j < NUM_BELT_PARTS; j++)
6401     {
6402       int element = belt_base_active_element[belt_nr] + j;
6403       int graphic_1 = el2img(element);
6404       int graphic_2 = el2panelimg(element);
6405
6406       if (game.belt_dir[i] == MV_LEFT)
6407       {
6408         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6409         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6410       }
6411       else
6412       {
6413         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6414         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6415       }
6416     }
6417   }
6418
6419   SCAN_PLAYFIELD(x, y)
6420   {
6421     int element = Tile[x][y];
6422
6423     for (i = 0; i < NUM_BELTS; i++)
6424     {
6425       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6426       {
6427         int e_belt_nr = getBeltNrFromBeltElement(element);
6428         int belt_nr = i;
6429
6430         if (e_belt_nr == belt_nr)
6431         {
6432           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6433
6434           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6435         }
6436       }
6437     }
6438   }
6439 }
6440
6441 static void ToggleBeltSwitch(int x, int y)
6442 {
6443   static int belt_base_element[4] =
6444   {
6445     EL_CONVEYOR_BELT_1_LEFT,
6446     EL_CONVEYOR_BELT_2_LEFT,
6447     EL_CONVEYOR_BELT_3_LEFT,
6448     EL_CONVEYOR_BELT_4_LEFT
6449   };
6450   static int belt_base_active_element[4] =
6451   {
6452     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6453     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6454     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6455     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6456   };
6457   static int belt_base_switch_element[4] =
6458   {
6459     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6460     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6461     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6462     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6463   };
6464   static int belt_move_dir[4] =
6465   {
6466     MV_LEFT,
6467     MV_NONE,
6468     MV_RIGHT,
6469     MV_NONE,
6470   };
6471
6472   int element = Tile[x][y];
6473   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6474   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6475   int belt_dir = belt_move_dir[belt_dir_nr];
6476   int xx, yy, i;
6477
6478   if (!IS_BELT_SWITCH(element))
6479     return;
6480
6481   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6482   game.belt_dir[belt_nr] = belt_dir;
6483
6484   if (belt_dir_nr == 3)
6485     belt_dir_nr = 1;
6486
6487   // set frame order for belt animation graphic according to belt direction
6488   for (i = 0; i < NUM_BELT_PARTS; i++)
6489   {
6490     int element = belt_base_active_element[belt_nr] + i;
6491     int graphic_1 = el2img(element);
6492     int graphic_2 = el2panelimg(element);
6493
6494     if (belt_dir == MV_LEFT)
6495     {
6496       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6497       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6498     }
6499     else
6500     {
6501       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6502       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6503     }
6504   }
6505
6506   SCAN_PLAYFIELD(xx, yy)
6507   {
6508     int element = Tile[xx][yy];
6509
6510     if (IS_BELT_SWITCH(element))
6511     {
6512       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6513
6514       if (e_belt_nr == belt_nr)
6515       {
6516         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6517         TEST_DrawLevelField(xx, yy);
6518       }
6519     }
6520     else if (IS_BELT(element) && belt_dir != MV_NONE)
6521     {
6522       int e_belt_nr = getBeltNrFromBeltElement(element);
6523
6524       if (e_belt_nr == belt_nr)
6525       {
6526         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6527
6528         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6529         TEST_DrawLevelField(xx, yy);
6530       }
6531     }
6532     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6533     {
6534       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6535
6536       if (e_belt_nr == belt_nr)
6537       {
6538         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6539
6540         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6541         TEST_DrawLevelField(xx, yy);
6542       }
6543     }
6544   }
6545 }
6546
6547 static void ToggleSwitchgateSwitch(void)
6548 {
6549   int xx, yy;
6550
6551   game.switchgate_pos = !game.switchgate_pos;
6552
6553   SCAN_PLAYFIELD(xx, yy)
6554   {
6555     int element = Tile[xx][yy];
6556
6557     if (element == EL_SWITCHGATE_SWITCH_UP)
6558     {
6559       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6560       TEST_DrawLevelField(xx, yy);
6561     }
6562     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6563     {
6564       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6565       TEST_DrawLevelField(xx, yy);
6566     }
6567     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6568     {
6569       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6570       TEST_DrawLevelField(xx, yy);
6571     }
6572     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6573     {
6574       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6575       TEST_DrawLevelField(xx, yy);
6576     }
6577     else if (element == EL_SWITCHGATE_OPEN ||
6578              element == EL_SWITCHGATE_OPENING)
6579     {
6580       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6581
6582       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6583     }
6584     else if (element == EL_SWITCHGATE_CLOSED ||
6585              element == EL_SWITCHGATE_CLOSING)
6586     {
6587       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6588
6589       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6590     }
6591   }
6592 }
6593
6594 static int getInvisibleActiveFromInvisibleElement(int element)
6595 {
6596   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6597           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6598           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6599           element);
6600 }
6601
6602 static int getInvisibleFromInvisibleActiveElement(int element)
6603 {
6604   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6605           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6606           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6607           element);
6608 }
6609
6610 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6611 {
6612   int x, y;
6613
6614   SCAN_PLAYFIELD(x, y)
6615   {
6616     int element = Tile[x][y];
6617
6618     if (element == EL_LIGHT_SWITCH &&
6619         game.light_time_left > 0)
6620     {
6621       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6622       TEST_DrawLevelField(x, y);
6623     }
6624     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6625              game.light_time_left == 0)
6626     {
6627       Tile[x][y] = EL_LIGHT_SWITCH;
6628       TEST_DrawLevelField(x, y);
6629     }
6630     else if (element == EL_EMC_DRIPPER &&
6631              game.light_time_left > 0)
6632     {
6633       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6634       TEST_DrawLevelField(x, y);
6635     }
6636     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6637              game.light_time_left == 0)
6638     {
6639       Tile[x][y] = EL_EMC_DRIPPER;
6640       TEST_DrawLevelField(x, y);
6641     }
6642     else if (element == EL_INVISIBLE_STEELWALL ||
6643              element == EL_INVISIBLE_WALL ||
6644              element == EL_INVISIBLE_SAND)
6645     {
6646       if (game.light_time_left > 0)
6647         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6648
6649       TEST_DrawLevelField(x, y);
6650
6651       // uncrumble neighbour fields, if needed
6652       if (element == EL_INVISIBLE_SAND)
6653         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6654     }
6655     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6656              element == EL_INVISIBLE_WALL_ACTIVE ||
6657              element == EL_INVISIBLE_SAND_ACTIVE)
6658     {
6659       if (game.light_time_left == 0)
6660         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6661
6662       TEST_DrawLevelField(x, y);
6663
6664       // re-crumble neighbour fields, if needed
6665       if (element == EL_INVISIBLE_SAND)
6666         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6667     }
6668   }
6669 }
6670
6671 static void RedrawAllInvisibleElementsForLenses(void)
6672 {
6673   int x, y;
6674
6675   SCAN_PLAYFIELD(x, y)
6676   {
6677     int element = Tile[x][y];
6678
6679     if (element == EL_EMC_DRIPPER &&
6680         game.lenses_time_left > 0)
6681     {
6682       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6683       TEST_DrawLevelField(x, y);
6684     }
6685     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6686              game.lenses_time_left == 0)
6687     {
6688       Tile[x][y] = EL_EMC_DRIPPER;
6689       TEST_DrawLevelField(x, y);
6690     }
6691     else if (element == EL_INVISIBLE_STEELWALL ||
6692              element == EL_INVISIBLE_WALL ||
6693              element == EL_INVISIBLE_SAND)
6694     {
6695       if (game.lenses_time_left > 0)
6696         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6697
6698       TEST_DrawLevelField(x, y);
6699
6700       // uncrumble neighbour fields, if needed
6701       if (element == EL_INVISIBLE_SAND)
6702         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6703     }
6704     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6705              element == EL_INVISIBLE_WALL_ACTIVE ||
6706              element == EL_INVISIBLE_SAND_ACTIVE)
6707     {
6708       if (game.lenses_time_left == 0)
6709         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6710
6711       TEST_DrawLevelField(x, y);
6712
6713       // re-crumble neighbour fields, if needed
6714       if (element == EL_INVISIBLE_SAND)
6715         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6716     }
6717   }
6718 }
6719
6720 static void RedrawAllInvisibleElementsForMagnifier(void)
6721 {
6722   int x, y;
6723
6724   SCAN_PLAYFIELD(x, y)
6725   {
6726     int element = Tile[x][y];
6727
6728     if (element == EL_EMC_FAKE_GRASS &&
6729         game.magnify_time_left > 0)
6730     {
6731       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6732       TEST_DrawLevelField(x, y);
6733     }
6734     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6735              game.magnify_time_left == 0)
6736     {
6737       Tile[x][y] = EL_EMC_FAKE_GRASS;
6738       TEST_DrawLevelField(x, y);
6739     }
6740     else if (IS_GATE_GRAY(element) &&
6741              game.magnify_time_left > 0)
6742     {
6743       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6744                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6745                     IS_EM_GATE_GRAY(element) ?
6746                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6747                     IS_EMC_GATE_GRAY(element) ?
6748                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6749                     IS_DC_GATE_GRAY(element) ?
6750                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6751                     element);
6752       TEST_DrawLevelField(x, y);
6753     }
6754     else if (IS_GATE_GRAY_ACTIVE(element) &&
6755              game.magnify_time_left == 0)
6756     {
6757       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6758                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6759                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6760                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6761                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6762                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6763                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6764                     EL_DC_GATE_WHITE_GRAY :
6765                     element);
6766       TEST_DrawLevelField(x, y);
6767     }
6768   }
6769 }
6770
6771 static void ToggleLightSwitch(int x, int y)
6772 {
6773   int element = Tile[x][y];
6774
6775   game.light_time_left =
6776     (element == EL_LIGHT_SWITCH ?
6777      level.time_light * FRAMES_PER_SECOND : 0);
6778
6779   RedrawAllLightSwitchesAndInvisibleElements();
6780 }
6781
6782 static void ActivateTimegateSwitch(int x, int y)
6783 {
6784   int xx, yy;
6785
6786   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6787
6788   SCAN_PLAYFIELD(xx, yy)
6789   {
6790     int element = Tile[xx][yy];
6791
6792     if (element == EL_TIMEGATE_CLOSED ||
6793         element == EL_TIMEGATE_CLOSING)
6794     {
6795       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6796       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6797     }
6798
6799     /*
6800     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6801     {
6802       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6803       TEST_DrawLevelField(xx, yy);
6804     }
6805     */
6806
6807   }
6808
6809   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6810                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6811 }
6812
6813 static void Impact(int x, int y)
6814 {
6815   boolean last_line = (y == lev_fieldy - 1);
6816   boolean object_hit = FALSE;
6817   boolean impact = (last_line || object_hit);
6818   int element = Tile[x][y];
6819   int smashed = EL_STEELWALL;
6820
6821   if (!last_line)       // check if element below was hit
6822   {
6823     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6824       return;
6825
6826     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6827                                          MovDir[x][y + 1] != MV_DOWN ||
6828                                          MovPos[x][y + 1] <= TILEY / 2));
6829
6830     // do not smash moving elements that left the smashed field in time
6831     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6832         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6833       object_hit = FALSE;
6834
6835 #if USE_QUICKSAND_IMPACT_BUGFIX
6836     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6837     {
6838       RemoveMovingField(x, y + 1);
6839       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6840       Tile[x][y + 2] = EL_ROCK;
6841       TEST_DrawLevelField(x, y + 2);
6842
6843       object_hit = TRUE;
6844     }
6845
6846     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6847     {
6848       RemoveMovingField(x, y + 1);
6849       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6850       Tile[x][y + 2] = EL_ROCK;
6851       TEST_DrawLevelField(x, y + 2);
6852
6853       object_hit = TRUE;
6854     }
6855 #endif
6856
6857     if (object_hit)
6858       smashed = MovingOrBlocked2Element(x, y + 1);
6859
6860     impact = (last_line || object_hit);
6861   }
6862
6863   if (!last_line && smashed == EL_ACID) // element falls into acid
6864   {
6865     SplashAcid(x, y + 1);
6866     return;
6867   }
6868
6869   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6870   // only reset graphic animation if graphic really changes after impact
6871   if (impact &&
6872       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6873   {
6874     ResetGfxAnimation(x, y);
6875     TEST_DrawLevelField(x, y);
6876   }
6877
6878   if (impact && CAN_EXPLODE_IMPACT(element))
6879   {
6880     Bang(x, y);
6881     return;
6882   }
6883   else if (impact && element == EL_PEARL &&
6884            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6885   {
6886     ResetGfxAnimation(x, y);
6887
6888     Tile[x][y] = EL_PEARL_BREAKING;
6889     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6890     return;
6891   }
6892   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6893   {
6894     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6895
6896     return;
6897   }
6898
6899   if (impact && element == EL_AMOEBA_DROP)
6900   {
6901     if (object_hit && IS_PLAYER(x, y + 1))
6902       KillPlayerUnlessEnemyProtected(x, y + 1);
6903     else if (object_hit && smashed == EL_PENGUIN)
6904       Bang(x, y + 1);
6905     else
6906     {
6907       Tile[x][y] = EL_AMOEBA_GROWING;
6908       Store[x][y] = EL_AMOEBA_WET;
6909
6910       ResetRandomAnimationValue(x, y);
6911     }
6912     return;
6913   }
6914
6915   if (object_hit)               // check which object was hit
6916   {
6917     if ((CAN_PASS_MAGIC_WALL(element) && 
6918          (smashed == EL_MAGIC_WALL ||
6919           smashed == EL_BD_MAGIC_WALL)) ||
6920         (CAN_PASS_DC_MAGIC_WALL(element) &&
6921          smashed == EL_DC_MAGIC_WALL))
6922     {
6923       int xx, yy;
6924       int activated_magic_wall =
6925         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6926          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6927          EL_DC_MAGIC_WALL_ACTIVE);
6928
6929       // activate magic wall / mill
6930       SCAN_PLAYFIELD(xx, yy)
6931       {
6932         if (Tile[xx][yy] == smashed)
6933           Tile[xx][yy] = activated_magic_wall;
6934       }
6935
6936       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6937       game.magic_wall_active = TRUE;
6938
6939       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6940                             SND_MAGIC_WALL_ACTIVATING :
6941                             smashed == EL_BD_MAGIC_WALL ?
6942                             SND_BD_MAGIC_WALL_ACTIVATING :
6943                             SND_DC_MAGIC_WALL_ACTIVATING));
6944     }
6945
6946     if (IS_PLAYER(x, y + 1))
6947     {
6948       if (CAN_SMASH_PLAYER(element))
6949       {
6950         KillPlayerUnlessEnemyProtected(x, y + 1);
6951         return;
6952       }
6953     }
6954     else if (smashed == EL_PENGUIN)
6955     {
6956       if (CAN_SMASH_PLAYER(element))
6957       {
6958         Bang(x, y + 1);
6959         return;
6960       }
6961     }
6962     else if (element == EL_BD_DIAMOND)
6963     {
6964       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6965       {
6966         Bang(x, y + 1);
6967         return;
6968       }
6969     }
6970     else if (((element == EL_SP_INFOTRON ||
6971                element == EL_SP_ZONK) &&
6972               (smashed == EL_SP_SNIKSNAK ||
6973                smashed == EL_SP_ELECTRON ||
6974                smashed == EL_SP_DISK_ORANGE)) ||
6975              (element == EL_SP_INFOTRON &&
6976               smashed == EL_SP_DISK_YELLOW))
6977     {
6978       Bang(x, y + 1);
6979       return;
6980     }
6981     else if (CAN_SMASH_EVERYTHING(element))
6982     {
6983       if (IS_CLASSIC_ENEMY(smashed) ||
6984           CAN_EXPLODE_SMASHED(smashed))
6985       {
6986         Bang(x, y + 1);
6987         return;
6988       }
6989       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6990       {
6991         if (smashed == EL_LAMP ||
6992             smashed == EL_LAMP_ACTIVE)
6993         {
6994           Bang(x, y + 1);
6995           return;
6996         }
6997         else if (smashed == EL_NUT)
6998         {
6999           Tile[x][y + 1] = EL_NUT_BREAKING;
7000           PlayLevelSound(x, y, SND_NUT_BREAKING);
7001           RaiseScoreElement(EL_NUT);
7002           return;
7003         }
7004         else if (smashed == EL_PEARL)
7005         {
7006           ResetGfxAnimation(x, y);
7007
7008           Tile[x][y + 1] = EL_PEARL_BREAKING;
7009           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7010           return;
7011         }
7012         else if (smashed == EL_DIAMOND)
7013         {
7014           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7015           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7016           return;
7017         }
7018         else if (IS_BELT_SWITCH(smashed))
7019         {
7020           ToggleBeltSwitch(x, y + 1);
7021         }
7022         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7023                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7024                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7025                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7026         {
7027           ToggleSwitchgateSwitch();
7028         }
7029         else if (smashed == EL_LIGHT_SWITCH ||
7030                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7031         {
7032           ToggleLightSwitch(x, y + 1);
7033         }
7034         else
7035         {
7036           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7037
7038           CheckElementChangeBySide(x, y + 1, smashed, element,
7039                                    CE_SWITCHED, CH_SIDE_TOP);
7040           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7041                                             CH_SIDE_TOP);
7042         }
7043       }
7044       else
7045       {
7046         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7047       }
7048     }
7049   }
7050
7051   // play sound of magic wall / mill
7052   if (!last_line &&
7053       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7054        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7055        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7056   {
7057     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7058       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7059     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7060       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7061     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7062       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7063
7064     return;
7065   }
7066
7067   // play sound of object that hits the ground
7068   if (last_line || object_hit)
7069     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7070 }
7071
7072 static void TurnRoundExt(int x, int y)
7073 {
7074   static struct
7075   {
7076     int dx, dy;
7077   } move_xy[] =
7078   {
7079     {  0,  0 },
7080     { -1,  0 },
7081     { +1,  0 },
7082     {  0,  0 },
7083     {  0, -1 },
7084     {  0,  0 }, { 0, 0 }, { 0, 0 },
7085     {  0, +1 }
7086   };
7087   static struct
7088   {
7089     int left, right, back;
7090   } turn[] =
7091   {
7092     { 0,        0,              0        },
7093     { MV_DOWN,  MV_UP,          MV_RIGHT },
7094     { MV_UP,    MV_DOWN,        MV_LEFT  },
7095     { 0,        0,              0        },
7096     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7097     { 0,        0,              0        },
7098     { 0,        0,              0        },
7099     { 0,        0,              0        },
7100     { MV_RIGHT, MV_LEFT,        MV_UP    }
7101   };
7102
7103   int element = Tile[x][y];
7104   int move_pattern = element_info[element].move_pattern;
7105
7106   int old_move_dir = MovDir[x][y];
7107   int left_dir  = turn[old_move_dir].left;
7108   int right_dir = turn[old_move_dir].right;
7109   int back_dir  = turn[old_move_dir].back;
7110
7111   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7112   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7113   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7114   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7115
7116   int left_x  = x + left_dx,  left_y  = y + left_dy;
7117   int right_x = x + right_dx, right_y = y + right_dy;
7118   int move_x  = x + move_dx,  move_y  = y + move_dy;
7119
7120   int xx, yy;
7121
7122   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7123   {
7124     TestIfBadThingTouchesOtherBadThing(x, y);
7125
7126     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7127       MovDir[x][y] = right_dir;
7128     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7129       MovDir[x][y] = left_dir;
7130
7131     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7132       MovDelay[x][y] = 9;
7133     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7134       MovDelay[x][y] = 1;
7135   }
7136   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7137   {
7138     TestIfBadThingTouchesOtherBadThing(x, y);
7139
7140     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7141       MovDir[x][y] = left_dir;
7142     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7143       MovDir[x][y] = right_dir;
7144
7145     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7146       MovDelay[x][y] = 9;
7147     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7148       MovDelay[x][y] = 1;
7149   }
7150   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7151   {
7152     TestIfBadThingTouchesOtherBadThing(x, y);
7153
7154     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7155       MovDir[x][y] = left_dir;
7156     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7157       MovDir[x][y] = right_dir;
7158
7159     if (MovDir[x][y] != old_move_dir)
7160       MovDelay[x][y] = 9;
7161   }
7162   else if (element == EL_YAMYAM)
7163   {
7164     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7165     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7166
7167     if (can_turn_left && can_turn_right)
7168       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7169     else if (can_turn_left)
7170       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7171     else if (can_turn_right)
7172       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7173     else
7174       MovDir[x][y] = back_dir;
7175
7176     MovDelay[x][y] = 16 + 16 * RND(3);
7177   }
7178   else if (element == EL_DARK_YAMYAM)
7179   {
7180     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7181                                                          left_x, left_y);
7182     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7183                                                          right_x, right_y);
7184
7185     if (can_turn_left && can_turn_right)
7186       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7187     else if (can_turn_left)
7188       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7189     else if (can_turn_right)
7190       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7191     else
7192       MovDir[x][y] = back_dir;
7193
7194     MovDelay[x][y] = 16 + 16 * RND(3);
7195   }
7196   else if (element == EL_PACMAN)
7197   {
7198     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7199     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7200
7201     if (can_turn_left && can_turn_right)
7202       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7203     else if (can_turn_left)
7204       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7205     else if (can_turn_right)
7206       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7207     else
7208       MovDir[x][y] = back_dir;
7209
7210     MovDelay[x][y] = 6 + RND(40);
7211   }
7212   else if (element == EL_PIG)
7213   {
7214     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7215     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7216     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7217     boolean should_turn_left, should_turn_right, should_move_on;
7218     int rnd_value = 24;
7219     int rnd = RND(rnd_value);
7220
7221     should_turn_left = (can_turn_left &&
7222                         (!can_move_on ||
7223                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7224                                                    y + back_dy + left_dy)));
7225     should_turn_right = (can_turn_right &&
7226                          (!can_move_on ||
7227                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7228                                                     y + back_dy + right_dy)));
7229     should_move_on = (can_move_on &&
7230                       (!can_turn_left ||
7231                        !can_turn_right ||
7232                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7233                                                  y + move_dy + left_dy) ||
7234                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7235                                                  y + move_dy + right_dy)));
7236
7237     if (should_turn_left || should_turn_right || should_move_on)
7238     {
7239       if (should_turn_left && should_turn_right && should_move_on)
7240         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7241                         rnd < 2 * rnd_value / 3 ? right_dir :
7242                         old_move_dir);
7243       else if (should_turn_left && should_turn_right)
7244         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7245       else if (should_turn_left && should_move_on)
7246         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7247       else if (should_turn_right && should_move_on)
7248         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7249       else if (should_turn_left)
7250         MovDir[x][y] = left_dir;
7251       else if (should_turn_right)
7252         MovDir[x][y] = right_dir;
7253       else if (should_move_on)
7254         MovDir[x][y] = old_move_dir;
7255     }
7256     else if (can_move_on && rnd > rnd_value / 8)
7257       MovDir[x][y] = old_move_dir;
7258     else if (can_turn_left && can_turn_right)
7259       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7260     else if (can_turn_left && rnd > rnd_value / 8)
7261       MovDir[x][y] = left_dir;
7262     else if (can_turn_right && rnd > rnd_value/8)
7263       MovDir[x][y] = right_dir;
7264     else
7265       MovDir[x][y] = back_dir;
7266
7267     xx = x + move_xy[MovDir[x][y]].dx;
7268     yy = y + move_xy[MovDir[x][y]].dy;
7269
7270     if (!IN_LEV_FIELD(xx, yy) ||
7271         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7272       MovDir[x][y] = old_move_dir;
7273
7274     MovDelay[x][y] = 0;
7275   }
7276   else if (element == EL_DRAGON)
7277   {
7278     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7279     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7280     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7281     int rnd_value = 24;
7282     int rnd = RND(rnd_value);
7283
7284     if (can_move_on && rnd > rnd_value / 8)
7285       MovDir[x][y] = old_move_dir;
7286     else if (can_turn_left && can_turn_right)
7287       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7288     else if (can_turn_left && rnd > rnd_value / 8)
7289       MovDir[x][y] = left_dir;
7290     else if (can_turn_right && rnd > rnd_value / 8)
7291       MovDir[x][y] = right_dir;
7292     else
7293       MovDir[x][y] = back_dir;
7294
7295     xx = x + move_xy[MovDir[x][y]].dx;
7296     yy = y + move_xy[MovDir[x][y]].dy;
7297
7298     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7299       MovDir[x][y] = old_move_dir;
7300
7301     MovDelay[x][y] = 0;
7302   }
7303   else if (element == EL_MOLE)
7304   {
7305     boolean can_move_on =
7306       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7307                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7308                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7309     if (!can_move_on)
7310     {
7311       boolean can_turn_left =
7312         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7313                               IS_AMOEBOID(Tile[left_x][left_y])));
7314
7315       boolean can_turn_right =
7316         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7317                               IS_AMOEBOID(Tile[right_x][right_y])));
7318
7319       if (can_turn_left && can_turn_right)
7320         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7321       else if (can_turn_left)
7322         MovDir[x][y] = left_dir;
7323       else
7324         MovDir[x][y] = right_dir;
7325     }
7326
7327     if (MovDir[x][y] != old_move_dir)
7328       MovDelay[x][y] = 9;
7329   }
7330   else if (element == EL_BALLOON)
7331   {
7332     MovDir[x][y] = game.wind_direction;
7333     MovDelay[x][y] = 0;
7334   }
7335   else if (element == EL_SPRING)
7336   {
7337     if (MovDir[x][y] & MV_HORIZONTAL)
7338     {
7339       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7340           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7341       {
7342         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7343         ResetGfxAnimation(move_x, move_y);
7344         TEST_DrawLevelField(move_x, move_y);
7345
7346         MovDir[x][y] = back_dir;
7347       }
7348       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7349                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7350         MovDir[x][y] = MV_NONE;
7351     }
7352
7353     MovDelay[x][y] = 0;
7354   }
7355   else if (element == EL_ROBOT ||
7356            element == EL_SATELLITE ||
7357            element == EL_PENGUIN ||
7358            element == EL_EMC_ANDROID)
7359   {
7360     int attr_x = -1, attr_y = -1;
7361
7362     if (game.all_players_gone)
7363     {
7364       attr_x = game.exit_x;
7365       attr_y = game.exit_y;
7366     }
7367     else
7368     {
7369       int i;
7370
7371       for (i = 0; i < MAX_PLAYERS; i++)
7372       {
7373         struct PlayerInfo *player = &stored_player[i];
7374         int jx = player->jx, jy = player->jy;
7375
7376         if (!player->active)
7377           continue;
7378
7379         if (attr_x == -1 ||
7380             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7381         {
7382           attr_x = jx;
7383           attr_y = jy;
7384         }
7385       }
7386     }
7387
7388     if (element == EL_ROBOT &&
7389         game.robot_wheel_x >= 0 &&
7390         game.robot_wheel_y >= 0 &&
7391         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7392          game.engine_version < VERSION_IDENT(3,1,0,0)))
7393     {
7394       attr_x = game.robot_wheel_x;
7395       attr_y = game.robot_wheel_y;
7396     }
7397
7398     if (element == EL_PENGUIN)
7399     {
7400       int i;
7401       struct XY *xy = xy_topdown;
7402
7403       for (i = 0; i < NUM_DIRECTIONS; i++)
7404       {
7405         int ex = x + xy[i].x;
7406         int ey = y + xy[i].y;
7407
7408         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7409                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7410                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7411                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7412         {
7413           attr_x = ex;
7414           attr_y = ey;
7415           break;
7416         }
7417       }
7418     }
7419
7420     MovDir[x][y] = MV_NONE;
7421     if (attr_x < x)
7422       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7423     else if (attr_x > x)
7424       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7425     if (attr_y < y)
7426       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7427     else if (attr_y > y)
7428       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7429
7430     if (element == EL_ROBOT)
7431     {
7432       int newx, newy;
7433
7434       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7435         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7436       Moving2Blocked(x, y, &newx, &newy);
7437
7438       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7439         MovDelay[x][y] = 8 + 8 * !RND(3);
7440       else
7441         MovDelay[x][y] = 16;
7442     }
7443     else if (element == EL_PENGUIN)
7444     {
7445       int newx, newy;
7446
7447       MovDelay[x][y] = 1;
7448
7449       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7450       {
7451         boolean first_horiz = RND(2);
7452         int new_move_dir = MovDir[x][y];
7453
7454         MovDir[x][y] =
7455           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7456         Moving2Blocked(x, y, &newx, &newy);
7457
7458         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7459           return;
7460
7461         MovDir[x][y] =
7462           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7463         Moving2Blocked(x, y, &newx, &newy);
7464
7465         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7466           return;
7467
7468         MovDir[x][y] = old_move_dir;
7469         return;
7470       }
7471     }
7472     else if (element == EL_SATELLITE)
7473     {
7474       int newx, newy;
7475
7476       MovDelay[x][y] = 1;
7477
7478       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7479       {
7480         boolean first_horiz = RND(2);
7481         int new_move_dir = MovDir[x][y];
7482
7483         MovDir[x][y] =
7484           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485         Moving2Blocked(x, y, &newx, &newy);
7486
7487         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7488           return;
7489
7490         MovDir[x][y] =
7491           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7492         Moving2Blocked(x, y, &newx, &newy);
7493
7494         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7495           return;
7496
7497         MovDir[x][y] = old_move_dir;
7498         return;
7499       }
7500     }
7501     else if (element == EL_EMC_ANDROID)
7502     {
7503       static int check_pos[16] =
7504       {
7505         -1,             //  0 => (invalid)
7506         7,              //  1 => MV_LEFT
7507         3,              //  2 => MV_RIGHT
7508         -1,             //  3 => (invalid)
7509         1,              //  4 =>            MV_UP
7510         0,              //  5 => MV_LEFT  | MV_UP
7511         2,              //  6 => MV_RIGHT | MV_UP
7512         -1,             //  7 => (invalid)
7513         5,              //  8 =>            MV_DOWN
7514         6,              //  9 => MV_LEFT  | MV_DOWN
7515         4,              // 10 => MV_RIGHT | MV_DOWN
7516         -1,             // 11 => (invalid)
7517         -1,             // 12 => (invalid)
7518         -1,             // 13 => (invalid)
7519         -1,             // 14 => (invalid)
7520         -1,             // 15 => (invalid)
7521       };
7522       static struct
7523       {
7524         int dx, dy;
7525         int dir;
7526       } check_xy[8] =
7527       {
7528         { -1, -1,       MV_LEFT  | MV_UP   },
7529         {  0, -1,                  MV_UP   },
7530         { +1, -1,       MV_RIGHT | MV_UP   },
7531         { +1,  0,       MV_RIGHT           },
7532         { +1, +1,       MV_RIGHT | MV_DOWN },
7533         {  0, +1,                  MV_DOWN },
7534         { -1, +1,       MV_LEFT  | MV_DOWN },
7535         { -1,  0,       MV_LEFT            },
7536       };
7537       int start_pos, check_order;
7538       boolean can_clone = FALSE;
7539       int i;
7540
7541       // check if there is any free field around current position
7542       for (i = 0; i < 8; i++)
7543       {
7544         int newx = x + check_xy[i].dx;
7545         int newy = y + check_xy[i].dy;
7546
7547         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7548         {
7549           can_clone = TRUE;
7550
7551           break;
7552         }
7553       }
7554
7555       if (can_clone)            // randomly find an element to clone
7556       {
7557         can_clone = FALSE;
7558
7559         start_pos = check_pos[RND(8)];
7560         check_order = (RND(2) ? -1 : +1);
7561
7562         for (i = 0; i < 8; i++)
7563         {
7564           int pos_raw = start_pos + i * check_order;
7565           int pos = (pos_raw + 8) % 8;
7566           int newx = x + check_xy[pos].dx;
7567           int newy = y + check_xy[pos].dy;
7568
7569           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7570           {
7571             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7572             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7573
7574             Store[x][y] = Tile[newx][newy];
7575
7576             can_clone = TRUE;
7577
7578             break;
7579           }
7580         }
7581       }
7582
7583       if (can_clone)            // randomly find a direction to move
7584       {
7585         can_clone = FALSE;
7586
7587         start_pos = check_pos[RND(8)];
7588         check_order = (RND(2) ? -1 : +1);
7589
7590         for (i = 0; i < 8; i++)
7591         {
7592           int pos_raw = start_pos + i * check_order;
7593           int pos = (pos_raw + 8) % 8;
7594           int newx = x + check_xy[pos].dx;
7595           int newy = y + check_xy[pos].dy;
7596           int new_move_dir = check_xy[pos].dir;
7597
7598           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7599           {
7600             MovDir[x][y] = new_move_dir;
7601             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7602
7603             can_clone = TRUE;
7604
7605             break;
7606           }
7607         }
7608       }
7609
7610       if (can_clone)            // cloning and moving successful
7611         return;
7612
7613       // cannot clone -- try to move towards player
7614
7615       start_pos = check_pos[MovDir[x][y] & 0x0f];
7616       check_order = (RND(2) ? -1 : +1);
7617
7618       for (i = 0; i < 3; i++)
7619       {
7620         // first check start_pos, then previous/next or (next/previous) pos
7621         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7622         int pos = (pos_raw + 8) % 8;
7623         int newx = x + check_xy[pos].dx;
7624         int newy = y + check_xy[pos].dy;
7625         int new_move_dir = check_xy[pos].dir;
7626
7627         if (IS_PLAYER(newx, newy))
7628           break;
7629
7630         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7631         {
7632           MovDir[x][y] = new_move_dir;
7633           MovDelay[x][y] = level.android_move_time * 8 + 1;
7634
7635           break;
7636         }
7637       }
7638     }
7639   }
7640   else if (move_pattern == MV_TURNING_LEFT ||
7641            move_pattern == MV_TURNING_RIGHT ||
7642            move_pattern == MV_TURNING_LEFT_RIGHT ||
7643            move_pattern == MV_TURNING_RIGHT_LEFT ||
7644            move_pattern == MV_TURNING_RANDOM ||
7645            move_pattern == MV_ALL_DIRECTIONS)
7646   {
7647     boolean can_turn_left =
7648       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7649     boolean can_turn_right =
7650       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7651
7652     if (element_info[element].move_stepsize == 0)       // "not moving"
7653       return;
7654
7655     if (move_pattern == MV_TURNING_LEFT)
7656       MovDir[x][y] = left_dir;
7657     else if (move_pattern == MV_TURNING_RIGHT)
7658       MovDir[x][y] = right_dir;
7659     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7660       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7661     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7662       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7663     else if (move_pattern == MV_TURNING_RANDOM)
7664       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7665                       can_turn_right && !can_turn_left ? right_dir :
7666                       RND(2) ? left_dir : right_dir);
7667     else if (can_turn_left && can_turn_right)
7668       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7669     else if (can_turn_left)
7670       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7671     else if (can_turn_right)
7672       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7673     else
7674       MovDir[x][y] = back_dir;
7675
7676     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7677   }
7678   else if (move_pattern == MV_HORIZONTAL ||
7679            move_pattern == MV_VERTICAL)
7680   {
7681     if (move_pattern & old_move_dir)
7682       MovDir[x][y] = back_dir;
7683     else if (move_pattern == MV_HORIZONTAL)
7684       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7685     else if (move_pattern == MV_VERTICAL)
7686       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7687
7688     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7689   }
7690   else if (move_pattern & MV_ANY_DIRECTION)
7691   {
7692     MovDir[x][y] = move_pattern;
7693     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7694   }
7695   else if (move_pattern & MV_WIND_DIRECTION)
7696   {
7697     MovDir[x][y] = game.wind_direction;
7698     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7699   }
7700   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7701   {
7702     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7703       MovDir[x][y] = left_dir;
7704     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7705       MovDir[x][y] = right_dir;
7706
7707     if (MovDir[x][y] != old_move_dir)
7708       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7709   }
7710   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7711   {
7712     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7713       MovDir[x][y] = right_dir;
7714     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7715       MovDir[x][y] = left_dir;
7716
7717     if (MovDir[x][y] != old_move_dir)
7718       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7719   }
7720   else if (move_pattern == MV_TOWARDS_PLAYER ||
7721            move_pattern == MV_AWAY_FROM_PLAYER)
7722   {
7723     int attr_x = -1, attr_y = -1;
7724     int newx, newy;
7725     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7726
7727     if (game.all_players_gone)
7728     {
7729       attr_x = game.exit_x;
7730       attr_y = game.exit_y;
7731     }
7732     else
7733     {
7734       int i;
7735
7736       for (i = 0; i < MAX_PLAYERS; i++)
7737       {
7738         struct PlayerInfo *player = &stored_player[i];
7739         int jx = player->jx, jy = player->jy;
7740
7741         if (!player->active)
7742           continue;
7743
7744         if (attr_x == -1 ||
7745             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7746         {
7747           attr_x = jx;
7748           attr_y = jy;
7749         }
7750       }
7751     }
7752
7753     MovDir[x][y] = MV_NONE;
7754     if (attr_x < x)
7755       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7756     else if (attr_x > x)
7757       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7758     if (attr_y < y)
7759       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7760     else if (attr_y > y)
7761       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7762
7763     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7764
7765     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7766     {
7767       boolean first_horiz = RND(2);
7768       int new_move_dir = MovDir[x][y];
7769
7770       if (element_info[element].move_stepsize == 0)     // "not moving"
7771       {
7772         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7773         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7774
7775         return;
7776       }
7777
7778       MovDir[x][y] =
7779         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7780       Moving2Blocked(x, y, &newx, &newy);
7781
7782       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7783         return;
7784
7785       MovDir[x][y] =
7786         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7787       Moving2Blocked(x, y, &newx, &newy);
7788
7789       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7790         return;
7791
7792       MovDir[x][y] = old_move_dir;
7793     }
7794   }
7795   else if (move_pattern == MV_WHEN_PUSHED ||
7796            move_pattern == MV_WHEN_DROPPED)
7797   {
7798     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7799       MovDir[x][y] = MV_NONE;
7800
7801     MovDelay[x][y] = 0;
7802   }
7803   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7804   {
7805     struct XY *test_xy = xy_topdown;
7806     static int test_dir[4] =
7807     {
7808       MV_UP,
7809       MV_LEFT,
7810       MV_RIGHT,
7811       MV_DOWN
7812     };
7813     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7814     int move_preference = -1000000;     // start with very low preference
7815     int new_move_dir = MV_NONE;
7816     int start_test = RND(4);
7817     int i;
7818
7819     for (i = 0; i < NUM_DIRECTIONS; i++)
7820     {
7821       int j = (start_test + i) % 4;
7822       int move_dir = test_dir[j];
7823       int move_dir_preference;
7824
7825       xx = x + test_xy[j].x;
7826       yy = y + test_xy[j].y;
7827
7828       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7829           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7830       {
7831         new_move_dir = move_dir;
7832
7833         break;
7834       }
7835
7836       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7837         continue;
7838
7839       move_dir_preference = -1 * RunnerVisit[xx][yy];
7840       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7841         move_dir_preference = PlayerVisit[xx][yy];
7842
7843       if (move_dir_preference > move_preference)
7844       {
7845         // prefer field that has not been visited for the longest time
7846         move_preference = move_dir_preference;
7847         new_move_dir = move_dir;
7848       }
7849       else if (move_dir_preference == move_preference &&
7850                move_dir == old_move_dir)
7851       {
7852         // prefer last direction when all directions are preferred equally
7853         move_preference = move_dir_preference;
7854         new_move_dir = move_dir;
7855       }
7856     }
7857
7858     MovDir[x][y] = new_move_dir;
7859     if (old_move_dir != new_move_dir)
7860       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7861   }
7862 }
7863
7864 static void TurnRound(int x, int y)
7865 {
7866   int direction = MovDir[x][y];
7867
7868   TurnRoundExt(x, y);
7869
7870   GfxDir[x][y] = MovDir[x][y];
7871
7872   if (direction != MovDir[x][y])
7873     GfxFrame[x][y] = 0;
7874
7875   if (MovDelay[x][y])
7876     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7877
7878   ResetGfxFrame(x, y);
7879 }
7880
7881 static boolean JustBeingPushed(int x, int y)
7882 {
7883   int i;
7884
7885   for (i = 0; i < MAX_PLAYERS; i++)
7886   {
7887     struct PlayerInfo *player = &stored_player[i];
7888
7889     if (player->active && player->is_pushing && player->MovPos)
7890     {
7891       int next_jx = player->jx + (player->jx - player->last_jx);
7892       int next_jy = player->jy + (player->jy - player->last_jy);
7893
7894       if (x == next_jx && y == next_jy)
7895         return TRUE;
7896     }
7897   }
7898
7899   return FALSE;
7900 }
7901
7902 static void StartMoving(int x, int y)
7903 {
7904   boolean started_moving = FALSE;       // some elements can fall _and_ move
7905   int element = Tile[x][y];
7906
7907   if (Stop[x][y])
7908     return;
7909
7910   if (MovDelay[x][y] == 0)
7911     GfxAction[x][y] = ACTION_DEFAULT;
7912
7913   if (CAN_FALL(element) && y < lev_fieldy - 1)
7914   {
7915     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7916         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7917       if (JustBeingPushed(x, y))
7918         return;
7919
7920     if (element == EL_QUICKSAND_FULL)
7921     {
7922       if (IS_FREE(x, y + 1))
7923       {
7924         InitMovingField(x, y, MV_DOWN);
7925         started_moving = TRUE;
7926
7927         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7928 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7929         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7930           Store[x][y] = EL_ROCK;
7931 #else
7932         Store[x][y] = EL_ROCK;
7933 #endif
7934
7935         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7936       }
7937       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7938       {
7939         if (!MovDelay[x][y])
7940         {
7941           MovDelay[x][y] = TILEY + 1;
7942
7943           ResetGfxAnimation(x, y);
7944           ResetGfxAnimation(x, y + 1);
7945         }
7946
7947         if (MovDelay[x][y])
7948         {
7949           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7950           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7951
7952           MovDelay[x][y]--;
7953           if (MovDelay[x][y])
7954             return;
7955         }
7956
7957         Tile[x][y] = EL_QUICKSAND_EMPTY;
7958         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7959         Store[x][y + 1] = Store[x][y];
7960         Store[x][y] = 0;
7961
7962         PlayLevelSoundAction(x, y, ACTION_FILLING);
7963       }
7964       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7965       {
7966         if (!MovDelay[x][y])
7967         {
7968           MovDelay[x][y] = TILEY + 1;
7969
7970           ResetGfxAnimation(x, y);
7971           ResetGfxAnimation(x, y + 1);
7972         }
7973
7974         if (MovDelay[x][y])
7975         {
7976           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7977           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7978
7979           MovDelay[x][y]--;
7980           if (MovDelay[x][y])
7981             return;
7982         }
7983
7984         Tile[x][y] = EL_QUICKSAND_EMPTY;
7985         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7986         Store[x][y + 1] = Store[x][y];
7987         Store[x][y] = 0;
7988
7989         PlayLevelSoundAction(x, y, ACTION_FILLING);
7990       }
7991     }
7992     else if (element == EL_QUICKSAND_FAST_FULL)
7993     {
7994       if (IS_FREE(x, y + 1))
7995       {
7996         InitMovingField(x, y, MV_DOWN);
7997         started_moving = TRUE;
7998
7999         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8000 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8001         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8002           Store[x][y] = EL_ROCK;
8003 #else
8004         Store[x][y] = EL_ROCK;
8005 #endif
8006
8007         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8008       }
8009       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8010       {
8011         if (!MovDelay[x][y])
8012         {
8013           MovDelay[x][y] = TILEY + 1;
8014
8015           ResetGfxAnimation(x, y);
8016           ResetGfxAnimation(x, y + 1);
8017         }
8018
8019         if (MovDelay[x][y])
8020         {
8021           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8022           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8023
8024           MovDelay[x][y]--;
8025           if (MovDelay[x][y])
8026             return;
8027         }
8028
8029         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8030         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8031         Store[x][y + 1] = Store[x][y];
8032         Store[x][y] = 0;
8033
8034         PlayLevelSoundAction(x, y, ACTION_FILLING);
8035       }
8036       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8037       {
8038         if (!MovDelay[x][y])
8039         {
8040           MovDelay[x][y] = TILEY + 1;
8041
8042           ResetGfxAnimation(x, y);
8043           ResetGfxAnimation(x, y + 1);
8044         }
8045
8046         if (MovDelay[x][y])
8047         {
8048           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8049           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8050
8051           MovDelay[x][y]--;
8052           if (MovDelay[x][y])
8053             return;
8054         }
8055
8056         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8057         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8058         Store[x][y + 1] = Store[x][y];
8059         Store[x][y] = 0;
8060
8061         PlayLevelSoundAction(x, y, ACTION_FILLING);
8062       }
8063     }
8064     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8065              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8066     {
8067       InitMovingField(x, y, MV_DOWN);
8068       started_moving = TRUE;
8069
8070       Tile[x][y] = EL_QUICKSAND_FILLING;
8071       Store[x][y] = element;
8072
8073       PlayLevelSoundAction(x, y, ACTION_FILLING);
8074     }
8075     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8076              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8077     {
8078       InitMovingField(x, y, MV_DOWN);
8079       started_moving = TRUE;
8080
8081       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8082       Store[x][y] = element;
8083
8084       PlayLevelSoundAction(x, y, ACTION_FILLING);
8085     }
8086     else if (element == EL_MAGIC_WALL_FULL)
8087     {
8088       if (IS_FREE(x, y + 1))
8089       {
8090         InitMovingField(x, y, MV_DOWN);
8091         started_moving = TRUE;
8092
8093         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
8094         Store[x][y] = EL_CHANGED(Store[x][y]);
8095       }
8096       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
8097       {
8098         if (!MovDelay[x][y])
8099           MovDelay[x][y] = TILEY / 4 + 1;
8100
8101         if (MovDelay[x][y])
8102         {
8103           MovDelay[x][y]--;
8104           if (MovDelay[x][y])
8105             return;
8106         }
8107
8108         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
8109         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8110         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8111         Store[x][y] = 0;
8112       }
8113     }
8114     else if (element == EL_BD_MAGIC_WALL_FULL)
8115     {
8116       if (IS_FREE(x, y + 1))
8117       {
8118         InitMovingField(x, y, MV_DOWN);
8119         started_moving = TRUE;
8120
8121         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8122         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8123       }
8124       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8125       {
8126         if (!MovDelay[x][y])
8127           MovDelay[x][y] = TILEY / 4 + 1;
8128
8129         if (MovDelay[x][y])
8130         {
8131           MovDelay[x][y]--;
8132           if (MovDelay[x][y])
8133             return;
8134         }
8135
8136         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8137         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8138         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8139         Store[x][y] = 0;
8140       }
8141     }
8142     else if (element == EL_DC_MAGIC_WALL_FULL)
8143     {
8144       if (IS_FREE(x, y + 1))
8145       {
8146         InitMovingField(x, y, MV_DOWN);
8147         started_moving = TRUE;
8148
8149         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8150         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8151       }
8152       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8153       {
8154         if (!MovDelay[x][y])
8155           MovDelay[x][y] = TILEY / 4 + 1;
8156
8157         if (MovDelay[x][y])
8158         {
8159           MovDelay[x][y]--;
8160           if (MovDelay[x][y])
8161             return;
8162         }
8163
8164         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8165         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8166         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8167         Store[x][y] = 0;
8168       }
8169     }
8170     else if ((CAN_PASS_MAGIC_WALL(element) &&
8171               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8172                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8173              (CAN_PASS_DC_MAGIC_WALL(element) &&
8174               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8175
8176     {
8177       InitMovingField(x, y, MV_DOWN);
8178       started_moving = TRUE;
8179
8180       Tile[x][y] =
8181         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8182          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8183          EL_DC_MAGIC_WALL_FILLING);
8184       Store[x][y] = element;
8185     }
8186     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8187     {
8188       SplashAcid(x, y + 1);
8189
8190       InitMovingField(x, y, MV_DOWN);
8191       started_moving = TRUE;
8192
8193       Store[x][y] = EL_ACID;
8194     }
8195     else if (
8196              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8197               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8198              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8199               CAN_FALL(element) && WasJustFalling[x][y] &&
8200               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8201
8202              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8203               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8204               (Tile[x][y + 1] == EL_BLOCKED)))
8205     {
8206       /* this is needed for a special case not covered by calling "Impact()"
8207          from "ContinueMoving()": if an element moves to a tile directly below
8208          another element which was just falling on that tile (which was empty
8209          in the previous frame), the falling element above would just stop
8210          instead of smashing the element below (in previous version, the above
8211          element was just checked for "moving" instead of "falling", resulting
8212          in incorrect smashes caused by horizontal movement of the above
8213          element; also, the case of the player being the element to smash was
8214          simply not covered here... :-/ ) */
8215
8216       CheckCollision[x][y] = 0;
8217       CheckImpact[x][y] = 0;
8218
8219       Impact(x, y);
8220     }
8221     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8222     {
8223       if (MovDir[x][y] == MV_NONE)
8224       {
8225         InitMovingField(x, y, MV_DOWN);
8226         started_moving = TRUE;
8227       }
8228     }
8229     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8230     {
8231       if (WasJustFalling[x][y]) // prevent animation from being restarted
8232         MovDir[x][y] = MV_DOWN;
8233
8234       InitMovingField(x, y, MV_DOWN);
8235       started_moving = TRUE;
8236     }
8237     else if (element == EL_AMOEBA_DROP)
8238     {
8239       Tile[x][y] = EL_AMOEBA_GROWING;
8240       Store[x][y] = EL_AMOEBA_WET;
8241     }
8242     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8243               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8244              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8245              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8246     {
8247       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8248                                 (IS_FREE(x - 1, y + 1) ||
8249                                  Tile[x - 1][y + 1] == EL_ACID));
8250       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8251                                 (IS_FREE(x + 1, y + 1) ||
8252                                  Tile[x + 1][y + 1] == EL_ACID));
8253       boolean can_fall_any  = (can_fall_left || can_fall_right);
8254       boolean can_fall_both = (can_fall_left && can_fall_right);
8255       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8256
8257       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8258       {
8259         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8260           can_fall_right = FALSE;
8261         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8262           can_fall_left = FALSE;
8263         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8264           can_fall_right = FALSE;
8265         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8266           can_fall_left = FALSE;
8267
8268         can_fall_any  = (can_fall_left || can_fall_right);
8269         can_fall_both = FALSE;
8270       }
8271
8272       if (can_fall_both)
8273       {
8274         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8275           can_fall_right = FALSE;       // slip down on left side
8276         else
8277           can_fall_left = !(can_fall_right = RND(2));
8278
8279         can_fall_both = FALSE;
8280       }
8281
8282       if (can_fall_any)
8283       {
8284         // if not determined otherwise, prefer left side for slipping down
8285         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8286         started_moving = TRUE;
8287       }
8288     }
8289     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8290     {
8291       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8292       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8293       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8294       int belt_dir = game.belt_dir[belt_nr];
8295
8296       if ((belt_dir == MV_LEFT  && left_is_free) ||
8297           (belt_dir == MV_RIGHT && right_is_free))
8298       {
8299         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8300
8301         InitMovingField(x, y, belt_dir);
8302         started_moving = TRUE;
8303
8304         Pushed[x][y] = TRUE;
8305         Pushed[nextx][y] = TRUE;
8306
8307         GfxAction[x][y] = ACTION_DEFAULT;
8308       }
8309       else
8310       {
8311         MovDir[x][y] = 0;       // if element was moving, stop it
8312       }
8313     }
8314   }
8315
8316   // not "else if" because of elements that can fall and move (EL_SPRING)
8317   if (CAN_MOVE(element) && !started_moving)
8318   {
8319     int move_pattern = element_info[element].move_pattern;
8320     int newx, newy;
8321
8322     Moving2Blocked(x, y, &newx, &newy);
8323
8324     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8325       return;
8326
8327     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8328         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8329     {
8330       WasJustMoving[x][y] = 0;
8331       CheckCollision[x][y] = 0;
8332
8333       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8334
8335       if (Tile[x][y] != element)        // element has changed
8336         return;
8337     }
8338
8339     if (!MovDelay[x][y])        // start new movement phase
8340     {
8341       // all objects that can change their move direction after each step
8342       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8343
8344       if (element != EL_YAMYAM &&
8345           element != EL_DARK_YAMYAM &&
8346           element != EL_PACMAN &&
8347           !(move_pattern & MV_ANY_DIRECTION) &&
8348           move_pattern != MV_TURNING_LEFT &&
8349           move_pattern != MV_TURNING_RIGHT &&
8350           move_pattern != MV_TURNING_LEFT_RIGHT &&
8351           move_pattern != MV_TURNING_RIGHT_LEFT &&
8352           move_pattern != MV_TURNING_RANDOM)
8353       {
8354         TurnRound(x, y);
8355
8356         if (MovDelay[x][y] && (element == EL_BUG ||
8357                                element == EL_SPACESHIP ||
8358                                element == EL_SP_SNIKSNAK ||
8359                                element == EL_SP_ELECTRON ||
8360                                element == EL_MOLE))
8361           TEST_DrawLevelField(x, y);
8362       }
8363     }
8364
8365     if (MovDelay[x][y])         // wait some time before next movement
8366     {
8367       MovDelay[x][y]--;
8368
8369       if (element == EL_ROBOT ||
8370           element == EL_YAMYAM ||
8371           element == EL_DARK_YAMYAM)
8372       {
8373         DrawLevelElementAnimationIfNeeded(x, y, element);
8374         PlayLevelSoundAction(x, y, ACTION_WAITING);
8375       }
8376       else if (element == EL_SP_ELECTRON)
8377         DrawLevelElementAnimationIfNeeded(x, y, element);
8378       else if (element == EL_DRAGON)
8379       {
8380         int i;
8381         int dir = MovDir[x][y];
8382         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8383         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8384         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8385                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8386                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8387                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8388         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8389
8390         GfxAction[x][y] = ACTION_ATTACKING;
8391
8392         if (IS_PLAYER(x, y))
8393           DrawPlayerField(x, y);
8394         else
8395           TEST_DrawLevelField(x, y);
8396
8397         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8398
8399         for (i = 1; i <= 3; i++)
8400         {
8401           int xx = x + i * dx;
8402           int yy = y + i * dy;
8403           int sx = SCREENX(xx);
8404           int sy = SCREENY(yy);
8405           int flame_graphic = graphic + (i - 1);
8406
8407           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8408             break;
8409
8410           if (MovDelay[x][y])
8411           {
8412             int flamed = MovingOrBlocked2Element(xx, yy);
8413
8414             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8415               Bang(xx, yy);
8416             else
8417               RemoveMovingField(xx, yy);
8418
8419             ChangeDelay[xx][yy] = 0;
8420
8421             Tile[xx][yy] = EL_FLAMES;
8422
8423             if (IN_SCR_FIELD(sx, sy))
8424             {
8425               TEST_DrawLevelFieldCrumbled(xx, yy);
8426               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8427             }
8428           }
8429           else
8430           {
8431             if (Tile[xx][yy] == EL_FLAMES)
8432               Tile[xx][yy] = EL_EMPTY;
8433             TEST_DrawLevelField(xx, yy);
8434           }
8435         }
8436       }
8437
8438       if (MovDelay[x][y])       // element still has to wait some time
8439       {
8440         PlayLevelSoundAction(x, y, ACTION_WAITING);
8441
8442         return;
8443       }
8444     }
8445
8446     // now make next step
8447
8448     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8449
8450     if (DONT_COLLIDE_WITH(element) &&
8451         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8452         !PLAYER_ENEMY_PROTECTED(newx, newy))
8453     {
8454       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8455
8456       return;
8457     }
8458
8459     else if (CAN_MOVE_INTO_ACID(element) &&
8460              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8461              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8462              (MovDir[x][y] == MV_DOWN ||
8463               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8464     {
8465       SplashAcid(newx, newy);
8466       Store[x][y] = EL_ACID;
8467     }
8468     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8469     {
8470       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8471           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8472           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8473           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8474       {
8475         RemoveField(x, y);
8476         TEST_DrawLevelField(x, y);
8477
8478         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8479         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8480           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8481
8482         game.friends_still_needed--;
8483         if (!game.friends_still_needed &&
8484             !game.GameOver &&
8485             game.all_players_gone)
8486           LevelSolved();
8487
8488         return;
8489       }
8490       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8491       {
8492         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8493           TEST_DrawLevelField(newx, newy);
8494         else
8495           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8496       }
8497       else if (!IS_FREE(newx, newy))
8498       {
8499         GfxAction[x][y] = ACTION_WAITING;
8500
8501         if (IS_PLAYER(x, y))
8502           DrawPlayerField(x, y);
8503         else
8504           TEST_DrawLevelField(x, y);
8505
8506         return;
8507       }
8508     }
8509     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8510     {
8511       if (IS_FOOD_PIG(Tile[newx][newy]))
8512       {
8513         if (IS_MOVING(newx, newy))
8514           RemoveMovingField(newx, newy);
8515         else
8516         {
8517           Tile[newx][newy] = EL_EMPTY;
8518           TEST_DrawLevelField(newx, newy);
8519         }
8520
8521         PlayLevelSound(x, y, SND_PIG_DIGGING);
8522       }
8523       else if (!IS_FREE(newx, newy))
8524       {
8525         if (IS_PLAYER(x, y))
8526           DrawPlayerField(x, y);
8527         else
8528           TEST_DrawLevelField(x, y);
8529
8530         return;
8531       }
8532     }
8533     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8534     {
8535       if (Store[x][y] != EL_EMPTY)
8536       {
8537         boolean can_clone = FALSE;
8538         int xx, yy;
8539
8540         // check if element to clone is still there
8541         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8542         {
8543           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8544           {
8545             can_clone = TRUE;
8546
8547             break;
8548           }
8549         }
8550
8551         // cannot clone or target field not free anymore -- do not clone
8552         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8553           Store[x][y] = EL_EMPTY;
8554       }
8555
8556       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8557       {
8558         if (IS_MV_DIAGONAL(MovDir[x][y]))
8559         {
8560           int diagonal_move_dir = MovDir[x][y];
8561           int stored = Store[x][y];
8562           int change_delay = 8;
8563           int graphic;
8564
8565           // android is moving diagonally
8566
8567           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8568
8569           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8570           GfxElement[x][y] = EL_EMC_ANDROID;
8571           GfxAction[x][y] = ACTION_SHRINKING;
8572           GfxDir[x][y] = diagonal_move_dir;
8573           ChangeDelay[x][y] = change_delay;
8574
8575           if (Store[x][y] == EL_EMPTY)
8576             Store[x][y] = GfxElementEmpty[x][y];
8577
8578           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8579                                    GfxDir[x][y]);
8580
8581           DrawLevelGraphicAnimation(x, y, graphic);
8582           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8583
8584           if (Tile[newx][newy] == EL_ACID)
8585           {
8586             SplashAcid(newx, newy);
8587
8588             return;
8589           }
8590
8591           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8592
8593           Store[newx][newy] = EL_EMC_ANDROID;
8594           GfxElement[newx][newy] = EL_EMC_ANDROID;
8595           GfxAction[newx][newy] = ACTION_GROWING;
8596           GfxDir[newx][newy] = diagonal_move_dir;
8597           ChangeDelay[newx][newy] = change_delay;
8598
8599           graphic = el_act_dir2img(GfxElement[newx][newy],
8600                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8601
8602           DrawLevelGraphicAnimation(newx, newy, graphic);
8603           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8604
8605           return;
8606         }
8607         else
8608         {
8609           Tile[newx][newy] = EL_EMPTY;
8610           TEST_DrawLevelField(newx, newy);
8611
8612           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8613         }
8614       }
8615       else if (!IS_FREE(newx, newy))
8616       {
8617         return;
8618       }
8619     }
8620     else if (IS_CUSTOM_ELEMENT(element) &&
8621              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8622     {
8623       if (!DigFieldByCE(newx, newy, element))
8624         return;
8625
8626       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8627       {
8628         RunnerVisit[x][y] = FrameCounter;
8629         PlayerVisit[x][y] /= 8;         // expire player visit path
8630       }
8631     }
8632     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8633     {
8634       if (!IS_FREE(newx, newy))
8635       {
8636         if (IS_PLAYER(x, y))
8637           DrawPlayerField(x, y);
8638         else
8639           TEST_DrawLevelField(x, y);
8640
8641         return;
8642       }
8643       else
8644       {
8645         boolean wanna_flame = !RND(10);
8646         int dx = newx - x, dy = newy - y;
8647         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8648         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8649         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8650                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8651         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8652                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8653
8654         if ((wanna_flame ||
8655              IS_CLASSIC_ENEMY(element1) ||
8656              IS_CLASSIC_ENEMY(element2)) &&
8657             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8658             element1 != EL_FLAMES && element2 != EL_FLAMES)
8659         {
8660           ResetGfxAnimation(x, y);
8661           GfxAction[x][y] = ACTION_ATTACKING;
8662
8663           if (IS_PLAYER(x, y))
8664             DrawPlayerField(x, y);
8665           else
8666             TEST_DrawLevelField(x, y);
8667
8668           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8669
8670           MovDelay[x][y] = 50;
8671
8672           Tile[newx][newy] = EL_FLAMES;
8673           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8674             Tile[newx1][newy1] = EL_FLAMES;
8675           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8676             Tile[newx2][newy2] = EL_FLAMES;
8677
8678           return;
8679         }
8680       }
8681     }
8682     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8683              Tile[newx][newy] == EL_DIAMOND)
8684     {
8685       if (IS_MOVING(newx, newy))
8686         RemoveMovingField(newx, newy);
8687       else
8688       {
8689         Tile[newx][newy] = EL_EMPTY;
8690         TEST_DrawLevelField(newx, newy);
8691       }
8692
8693       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8694     }
8695     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8696              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8697     {
8698       if (AmoebaNr[newx][newy])
8699       {
8700         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8701         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8702             Tile[newx][newy] == EL_BD_AMOEBA)
8703           AmoebaCnt[AmoebaNr[newx][newy]]--;
8704       }
8705
8706       if (IS_MOVING(newx, newy))
8707       {
8708         RemoveMovingField(newx, newy);
8709       }
8710       else
8711       {
8712         Tile[newx][newy] = EL_EMPTY;
8713         TEST_DrawLevelField(newx, newy);
8714       }
8715
8716       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8717     }
8718     else if ((element == EL_PACMAN || element == EL_MOLE)
8719              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8720     {
8721       if (AmoebaNr[newx][newy])
8722       {
8723         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8724         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8725             Tile[newx][newy] == EL_BD_AMOEBA)
8726           AmoebaCnt[AmoebaNr[newx][newy]]--;
8727       }
8728
8729       if (element == EL_MOLE)
8730       {
8731         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8732         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8733
8734         ResetGfxAnimation(x, y);
8735         GfxAction[x][y] = ACTION_DIGGING;
8736         TEST_DrawLevelField(x, y);
8737
8738         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8739
8740         return;                         // wait for shrinking amoeba
8741       }
8742       else      // element == EL_PACMAN
8743       {
8744         Tile[newx][newy] = EL_EMPTY;
8745         TEST_DrawLevelField(newx, newy);
8746         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8747       }
8748     }
8749     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8750              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8751               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8752     {
8753       // wait for shrinking amoeba to completely disappear
8754       return;
8755     }
8756     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8757     {
8758       // object was running against a wall
8759
8760       TurnRound(x, y);
8761
8762       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8763         DrawLevelElementAnimation(x, y, element);
8764
8765       if (DONT_TOUCH(element))
8766         TestIfBadThingTouchesPlayer(x, y);
8767
8768       return;
8769     }
8770
8771     InitMovingField(x, y, MovDir[x][y]);
8772
8773     PlayLevelSoundAction(x, y, ACTION_MOVING);
8774   }
8775
8776   if (MovDir[x][y])
8777     ContinueMoving(x, y);
8778 }
8779
8780 void ContinueMoving(int x, int y)
8781 {
8782   int element = Tile[x][y];
8783   struct ElementInfo *ei = &element_info[element];
8784   int direction = MovDir[x][y];
8785   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8786   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8787   int newx = x + dx, newy = y + dy;
8788   int stored = Store[x][y];
8789   int stored_new = Store[newx][newy];
8790   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8791   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8792   boolean last_line = (newy == lev_fieldy - 1);
8793   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8794
8795   if (pushed_by_player)         // special case: moving object pushed by player
8796   {
8797     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8798   }
8799   else if (use_step_delay)      // special case: moving object has step delay
8800   {
8801     if (!MovDelay[x][y])
8802       MovPos[x][y] += getElementMoveStepsize(x, y);
8803
8804     if (MovDelay[x][y])
8805       MovDelay[x][y]--;
8806     else
8807       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8808
8809     if (MovDelay[x][y])
8810     {
8811       TEST_DrawLevelField(x, y);
8812
8813       return;   // element is still waiting
8814     }
8815   }
8816   else                          // normal case: generically moving object
8817   {
8818     MovPos[x][y] += getElementMoveStepsize(x, y);
8819   }
8820
8821   if (ABS(MovPos[x][y]) < TILEX)
8822   {
8823     TEST_DrawLevelField(x, y);
8824
8825     return;     // element is still moving
8826   }
8827
8828   // element reached destination field
8829
8830   Tile[x][y] = EL_EMPTY;
8831   Tile[newx][newy] = element;
8832   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8833
8834   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8835   {
8836     element = Tile[newx][newy] = EL_ACID;
8837   }
8838   else if (element == EL_MOLE)
8839   {
8840     Tile[x][y] = EL_SAND;
8841
8842     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8843   }
8844   else if (element == EL_QUICKSAND_FILLING)
8845   {
8846     element = Tile[newx][newy] = get_next_element(element);
8847     Store[newx][newy] = Store[x][y];
8848   }
8849   else if (element == EL_QUICKSAND_EMPTYING)
8850   {
8851     Tile[x][y] = get_next_element(element);
8852     element = Tile[newx][newy] = Store[x][y];
8853   }
8854   else if (element == EL_QUICKSAND_FAST_FILLING)
8855   {
8856     element = Tile[newx][newy] = get_next_element(element);
8857     Store[newx][newy] = Store[x][y];
8858   }
8859   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8860   {
8861     Tile[x][y] = get_next_element(element);
8862     element = Tile[newx][newy] = Store[x][y];
8863   }
8864   else if (element == EL_MAGIC_WALL_FILLING)
8865   {
8866     element = Tile[newx][newy] = get_next_element(element);
8867     if (!game.magic_wall_active)
8868       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8869     Store[newx][newy] = Store[x][y];
8870   }
8871   else if (element == EL_MAGIC_WALL_EMPTYING)
8872   {
8873     Tile[x][y] = get_next_element(element);
8874     if (!game.magic_wall_active)
8875       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8876     element = Tile[newx][newy] = Store[x][y];
8877
8878     InitField(newx, newy, FALSE);
8879   }
8880   else if (element == EL_BD_MAGIC_WALL_FILLING)
8881   {
8882     element = Tile[newx][newy] = get_next_element(element);
8883     if (!game.magic_wall_active)
8884       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8885     Store[newx][newy] = Store[x][y];
8886   }
8887   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8888   {
8889     Tile[x][y] = get_next_element(element);
8890     if (!game.magic_wall_active)
8891       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8892     element = Tile[newx][newy] = Store[x][y];
8893
8894     InitField(newx, newy, FALSE);
8895   }
8896   else if (element == EL_DC_MAGIC_WALL_FILLING)
8897   {
8898     element = Tile[newx][newy] = get_next_element(element);
8899     if (!game.magic_wall_active)
8900       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8901     Store[newx][newy] = Store[x][y];
8902   }
8903   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8904   {
8905     Tile[x][y] = get_next_element(element);
8906     if (!game.magic_wall_active)
8907       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8908     element = Tile[newx][newy] = Store[x][y];
8909
8910     InitField(newx, newy, FALSE);
8911   }
8912   else if (element == EL_AMOEBA_DROPPING)
8913   {
8914     Tile[x][y] = get_next_element(element);
8915     element = Tile[newx][newy] = Store[x][y];
8916   }
8917   else if (element == EL_SOKOBAN_OBJECT)
8918   {
8919     if (Back[x][y])
8920       Tile[x][y] = Back[x][y];
8921
8922     if (Back[newx][newy])
8923       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8924
8925     Back[x][y] = Back[newx][newy] = 0;
8926   }
8927
8928   Store[x][y] = EL_EMPTY;
8929   MovPos[x][y] = 0;
8930   MovDir[x][y] = 0;
8931   MovDelay[x][y] = 0;
8932
8933   MovDelay[newx][newy] = 0;
8934
8935   if (CAN_CHANGE_OR_HAS_ACTION(element))
8936   {
8937     // copy element change control values to new field
8938     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8939     ChangePage[newx][newy]  = ChangePage[x][y];
8940     ChangeCount[newx][newy] = ChangeCount[x][y];
8941     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8942   }
8943
8944   CustomValue[newx][newy] = CustomValue[x][y];
8945
8946   ChangeDelay[x][y] = 0;
8947   ChangePage[x][y] = -1;
8948   ChangeCount[x][y] = 0;
8949   ChangeEvent[x][y] = -1;
8950
8951   CustomValue[x][y] = 0;
8952
8953   // copy animation control values to new field
8954   GfxFrame[newx][newy]  = GfxFrame[x][y];
8955   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8956   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8957   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8958
8959   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8960
8961   // some elements can leave other elements behind after moving
8962   if (ei->move_leave_element != EL_EMPTY &&
8963       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8964       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8965   {
8966     int move_leave_element = ei->move_leave_element;
8967
8968     // this makes it possible to leave the removed element again
8969     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8970       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8971
8972     Tile[x][y] = move_leave_element;
8973
8974     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8975       MovDir[x][y] = direction;
8976
8977     InitField(x, y, FALSE);
8978
8979     if (GFX_CRUMBLED(Tile[x][y]))
8980       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8981
8982     if (IS_PLAYER_ELEMENT(move_leave_element))
8983       RelocatePlayer(x, y, move_leave_element);
8984   }
8985
8986   // do this after checking for left-behind element
8987   ResetGfxAnimation(x, y);      // reset animation values for old field
8988
8989   if (!CAN_MOVE(element) ||
8990       (CAN_FALL(element) && direction == MV_DOWN &&
8991        (element == EL_SPRING ||
8992         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8993         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8994     GfxDir[x][y] = MovDir[newx][newy] = 0;
8995
8996   TEST_DrawLevelField(x, y);
8997   TEST_DrawLevelField(newx, newy);
8998
8999   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9000
9001   // prevent pushed element from moving on in pushed direction
9002   if (pushed_by_player && CAN_MOVE(element) &&
9003       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9004       !(element_info[element].move_pattern & direction))
9005     TurnRound(newx, newy);
9006
9007   // prevent elements on conveyor belt from moving on in last direction
9008   if (pushed_by_conveyor && CAN_FALL(element) &&
9009       direction & MV_HORIZONTAL)
9010     MovDir[newx][newy] = 0;
9011
9012   if (!pushed_by_player)
9013   {
9014     int nextx = newx + dx, nexty = newy + dy;
9015     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9016
9017     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9018
9019     if (CAN_FALL(element) && direction == MV_DOWN)
9020       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9021
9022     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9023       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9024
9025     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9026       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9027   }
9028
9029   if (DONT_TOUCH(element))      // object may be nasty to player or others
9030   {
9031     TestIfBadThingTouchesPlayer(newx, newy);
9032     TestIfBadThingTouchesFriend(newx, newy);
9033
9034     if (!IS_CUSTOM_ELEMENT(element))
9035       TestIfBadThingTouchesOtherBadThing(newx, newy);
9036   }
9037   else if (element == EL_PENGUIN)
9038     TestIfFriendTouchesBadThing(newx, newy);
9039
9040   if (DONT_GET_HIT_BY(element))
9041   {
9042     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9043   }
9044
9045   // give the player one last chance (one more frame) to move away
9046   if (CAN_FALL(element) && direction == MV_DOWN &&
9047       (last_line || (!IS_FREE(x, newy + 1) &&
9048                      (!IS_PLAYER(x, newy + 1) ||
9049                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9050     Impact(x, newy);
9051
9052   if (pushed_by_player && !game.use_change_when_pushing_bug)
9053   {
9054     int push_side = MV_DIR_OPPOSITE(direction);
9055     struct PlayerInfo *player = PLAYERINFO(x, y);
9056
9057     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9058                                player->index_bit, push_side);
9059     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9060                                         player->index_bit, push_side);
9061   }
9062
9063   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9064     MovDelay[newx][newy] = 1;
9065
9066   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9067
9068   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9069   TestIfElementHitsCustomElement(newx, newy, direction);
9070   TestIfPlayerTouchesCustomElement(newx, newy);
9071   TestIfElementTouchesCustomElement(newx, newy);
9072
9073   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9074       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9075     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9076                              MV_DIR_OPPOSITE(direction));
9077 }
9078
9079 int AmoebaNeighbourNr(int ax, int ay)
9080 {
9081   int i;
9082   int element = Tile[ax][ay];
9083   int group_nr = 0;
9084   struct XY *xy = xy_topdown;
9085
9086   for (i = 0; i < NUM_DIRECTIONS; i++)
9087   {
9088     int x = ax + xy[i].x;
9089     int y = ay + xy[i].y;
9090
9091     if (!IN_LEV_FIELD(x, y))
9092       continue;
9093
9094     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9095       group_nr = AmoebaNr[x][y];
9096   }
9097
9098   return group_nr;
9099 }
9100
9101 static void AmoebaMerge(int ax, int ay)
9102 {
9103   int i, x, y, xx, yy;
9104   int new_group_nr = AmoebaNr[ax][ay];
9105   struct XY *xy = xy_topdown;
9106
9107   if (new_group_nr == 0)
9108     return;
9109
9110   for (i = 0; i < NUM_DIRECTIONS; i++)
9111   {
9112     x = ax + xy[i].x;
9113     y = ay + xy[i].y;
9114
9115     if (!IN_LEV_FIELD(x, y))
9116       continue;
9117
9118     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9119          Tile[x][y] == EL_BD_AMOEBA ||
9120          Tile[x][y] == EL_AMOEBA_DEAD) &&
9121         AmoebaNr[x][y] != new_group_nr)
9122     {
9123       int old_group_nr = AmoebaNr[x][y];
9124
9125       if (old_group_nr == 0)
9126         return;
9127
9128       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9129       AmoebaCnt[old_group_nr] = 0;
9130       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9131       AmoebaCnt2[old_group_nr] = 0;
9132
9133       SCAN_PLAYFIELD(xx, yy)
9134       {
9135         if (AmoebaNr[xx][yy] == old_group_nr)
9136           AmoebaNr[xx][yy] = new_group_nr;
9137       }
9138     }
9139   }
9140 }
9141
9142 void AmoebaToDiamond(int ax, int ay)
9143 {
9144   int i, x, y;
9145
9146   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9147   {
9148     int group_nr = AmoebaNr[ax][ay];
9149
9150 #ifdef DEBUG
9151     if (group_nr == 0)
9152     {
9153       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9154       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9155
9156       return;
9157     }
9158 #endif
9159
9160     SCAN_PLAYFIELD(x, y)
9161     {
9162       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9163       {
9164         AmoebaNr[x][y] = 0;
9165         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9166       }
9167     }
9168
9169     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9170                             SND_AMOEBA_TURNING_TO_GEM :
9171                             SND_AMOEBA_TURNING_TO_ROCK));
9172     Bang(ax, ay);
9173   }
9174   else
9175   {
9176     struct XY *xy = xy_topdown;
9177
9178     for (i = 0; i < NUM_DIRECTIONS; i++)
9179     {
9180       x = ax + xy[i].x;
9181       y = ay + xy[i].y;
9182
9183       if (!IN_LEV_FIELD(x, y))
9184         continue;
9185
9186       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9187       {
9188         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9189                               SND_AMOEBA_TURNING_TO_GEM :
9190                               SND_AMOEBA_TURNING_TO_ROCK));
9191         Bang(x, y);
9192       }
9193     }
9194   }
9195 }
9196
9197 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9198 {
9199   int x, y;
9200   int group_nr = AmoebaNr[ax][ay];
9201   boolean done = FALSE;
9202
9203 #ifdef DEBUG
9204   if (group_nr == 0)
9205   {
9206     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9207     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9208
9209     return;
9210   }
9211 #endif
9212
9213   SCAN_PLAYFIELD(x, y)
9214   {
9215     if (AmoebaNr[x][y] == group_nr &&
9216         (Tile[x][y] == EL_AMOEBA_DEAD ||
9217          Tile[x][y] == EL_BD_AMOEBA ||
9218          Tile[x][y] == EL_AMOEBA_GROWING))
9219     {
9220       AmoebaNr[x][y] = 0;
9221       Tile[x][y] = new_element;
9222       InitField(x, y, FALSE);
9223       TEST_DrawLevelField(x, y);
9224       done = TRUE;
9225     }
9226   }
9227
9228   if (done)
9229     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9230                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9231                             SND_BD_AMOEBA_TURNING_TO_GEM));
9232 }
9233
9234 static void AmoebaGrowing(int x, int y)
9235 {
9236   static DelayCounter sound_delay = { 0 };
9237
9238   if (!MovDelay[x][y])          // start new growing cycle
9239   {
9240     MovDelay[x][y] = 7;
9241
9242     if (DelayReached(&sound_delay))
9243     {
9244       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9245       sound_delay.value = 30;
9246     }
9247   }
9248
9249   if (MovDelay[x][y])           // wait some time before growing bigger
9250   {
9251     MovDelay[x][y]--;
9252     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9253     {
9254       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9255                                            6 - MovDelay[x][y]);
9256
9257       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9258     }
9259
9260     if (!MovDelay[x][y])
9261     {
9262       Tile[x][y] = Store[x][y];
9263       Store[x][y] = 0;
9264       TEST_DrawLevelField(x, y);
9265     }
9266   }
9267 }
9268
9269 static void AmoebaShrinking(int x, int y)
9270 {
9271   static DelayCounter sound_delay = { 0 };
9272
9273   if (!MovDelay[x][y])          // start new shrinking cycle
9274   {
9275     MovDelay[x][y] = 7;
9276
9277     if (DelayReached(&sound_delay))
9278       sound_delay.value = 30;
9279   }
9280
9281   if (MovDelay[x][y])           // wait some time before shrinking
9282   {
9283     MovDelay[x][y]--;
9284     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9285     {
9286       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9287                                            6 - MovDelay[x][y]);
9288
9289       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9290     }
9291
9292     if (!MovDelay[x][y])
9293     {
9294       Tile[x][y] = EL_EMPTY;
9295       TEST_DrawLevelField(x, y);
9296
9297       // don't let mole enter this field in this cycle;
9298       // (give priority to objects falling to this field from above)
9299       Stop[x][y] = TRUE;
9300     }
9301   }
9302 }
9303
9304 static void AmoebaReproduce(int ax, int ay)
9305 {
9306   int i;
9307   int element = Tile[ax][ay];
9308   int graphic = el2img(element);
9309   int newax = ax, neway = ay;
9310   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9311   struct XY *xy = xy_topdown;
9312
9313   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9314   {
9315     Tile[ax][ay] = EL_AMOEBA_DEAD;
9316     TEST_DrawLevelField(ax, ay);
9317     return;
9318   }
9319
9320   if (IS_ANIMATED(graphic))
9321     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9322
9323   if (!MovDelay[ax][ay])        // start making new amoeba field
9324     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9325
9326   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9327   {
9328     MovDelay[ax][ay]--;
9329     if (MovDelay[ax][ay])
9330       return;
9331   }
9332
9333   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9334   {
9335     int start = RND(4);
9336     int x = ax + xy[start].x;
9337     int y = ay + xy[start].y;
9338
9339     if (!IN_LEV_FIELD(x, y))
9340       return;
9341
9342     if (IS_FREE(x, y) ||
9343         CAN_GROW_INTO(Tile[x][y]) ||
9344         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9345         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9346     {
9347       newax = x;
9348       neway = y;
9349     }
9350
9351     if (newax == ax && neway == ay)
9352       return;
9353   }
9354   else                          // normal or "filled" (BD style) amoeba
9355   {
9356     int start = RND(4);
9357     boolean waiting_for_player = FALSE;
9358
9359     for (i = 0; i < NUM_DIRECTIONS; i++)
9360     {
9361       int j = (start + i) % 4;
9362       int x = ax + xy[j].x;
9363       int y = ay + xy[j].y;
9364
9365       if (!IN_LEV_FIELD(x, y))
9366         continue;
9367
9368       if (IS_FREE(x, y) ||
9369           CAN_GROW_INTO(Tile[x][y]) ||
9370           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9371           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9372       {
9373         newax = x;
9374         neway = y;
9375         break;
9376       }
9377       else if (IS_PLAYER(x, y))
9378         waiting_for_player = TRUE;
9379     }
9380
9381     if (newax == ax && neway == ay)             // amoeba cannot grow
9382     {
9383       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9384       {
9385         Tile[ax][ay] = EL_AMOEBA_DEAD;
9386         TEST_DrawLevelField(ax, ay);
9387         AmoebaCnt[AmoebaNr[ax][ay]]--;
9388
9389         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9390         {
9391           if (element == EL_AMOEBA_FULL)
9392             AmoebaToDiamond(ax, ay);
9393           else if (element == EL_BD_AMOEBA)
9394             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9395         }
9396       }
9397       return;
9398     }
9399     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9400     {
9401       // amoeba gets larger by growing in some direction
9402
9403       int new_group_nr = AmoebaNr[ax][ay];
9404
9405 #ifdef DEBUG
9406   if (new_group_nr == 0)
9407   {
9408     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9409           newax, neway);
9410     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9411
9412     return;
9413   }
9414 #endif
9415
9416       AmoebaNr[newax][neway] = new_group_nr;
9417       AmoebaCnt[new_group_nr]++;
9418       AmoebaCnt2[new_group_nr]++;
9419
9420       // if amoeba touches other amoeba(s) after growing, unify them
9421       AmoebaMerge(newax, neway);
9422
9423       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9424       {
9425         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9426         return;
9427       }
9428     }
9429   }
9430
9431   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9432       (neway == lev_fieldy - 1 && newax != ax))
9433   {
9434     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9435     Store[newax][neway] = element;
9436   }
9437   else if (neway == ay || element == EL_EMC_DRIPPER)
9438   {
9439     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9440
9441     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9442   }
9443   else
9444   {
9445     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9446     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9447     Store[ax][ay] = EL_AMOEBA_DROP;
9448     ContinueMoving(ax, ay);
9449     return;
9450   }
9451
9452   TEST_DrawLevelField(newax, neway);
9453 }
9454
9455 static void Life(int ax, int ay)
9456 {
9457   int x1, y1, x2, y2;
9458   int life_time = 40;
9459   int element = Tile[ax][ay];
9460   int graphic = el2img(element);
9461   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9462                          level.biomaze);
9463   boolean changed = FALSE;
9464
9465   if (IS_ANIMATED(graphic))
9466     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9467
9468   if (Stop[ax][ay])
9469     return;
9470
9471   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9472     MovDelay[ax][ay] = life_time;
9473
9474   if (MovDelay[ax][ay])         // wait some time before next cycle
9475   {
9476     MovDelay[ax][ay]--;
9477     if (MovDelay[ax][ay])
9478       return;
9479   }
9480
9481   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9482   {
9483     int xx = ax + x1, yy = ay + y1;
9484     int old_element = Tile[xx][yy];
9485     int num_neighbours = 0;
9486
9487     if (!IN_LEV_FIELD(xx, yy))
9488       continue;
9489
9490     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9491     {
9492       int x = xx + x2, y = yy + y2;
9493
9494       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9495         continue;
9496
9497       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9498       boolean is_neighbour = FALSE;
9499
9500       if (level.use_life_bugs)
9501         is_neighbour =
9502           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9503            (IS_FREE(x, y)                             &&  Stop[x][y]));
9504       else
9505         is_neighbour =
9506           (Last[x][y] == element || is_player_cell);
9507
9508       if (is_neighbour)
9509         num_neighbours++;
9510     }
9511
9512     boolean is_free = FALSE;
9513
9514     if (level.use_life_bugs)
9515       is_free = (IS_FREE(xx, yy));
9516     else
9517       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9518
9519     if (xx == ax && yy == ay)           // field in the middle
9520     {
9521       if (num_neighbours < life_parameter[0] ||
9522           num_neighbours > life_parameter[1])
9523       {
9524         Tile[xx][yy] = EL_EMPTY;
9525         if (Tile[xx][yy] != old_element)
9526           TEST_DrawLevelField(xx, yy);
9527         Stop[xx][yy] = TRUE;
9528         changed = TRUE;
9529       }
9530     }
9531     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9532     {                                   // free border field
9533       if (num_neighbours >= life_parameter[2] &&
9534           num_neighbours <= life_parameter[3])
9535       {
9536         Tile[xx][yy] = element;
9537         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9538         if (Tile[xx][yy] != old_element)
9539           TEST_DrawLevelField(xx, yy);
9540         Stop[xx][yy] = TRUE;
9541         changed = TRUE;
9542       }
9543     }
9544   }
9545
9546   if (changed)
9547     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9548                    SND_GAME_OF_LIFE_GROWING);
9549 }
9550
9551 static void InitRobotWheel(int x, int y)
9552 {
9553   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9554 }
9555
9556 static void RunRobotWheel(int x, int y)
9557 {
9558   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9559 }
9560
9561 static void StopRobotWheel(int x, int y)
9562 {
9563   if (game.robot_wheel_x == x &&
9564       game.robot_wheel_y == y)
9565   {
9566     game.robot_wheel_x = -1;
9567     game.robot_wheel_y = -1;
9568     game.robot_wheel_active = FALSE;
9569   }
9570 }
9571
9572 static void InitTimegateWheel(int x, int y)
9573 {
9574   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9575 }
9576
9577 static void RunTimegateWheel(int x, int y)
9578 {
9579   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9580 }
9581
9582 static void InitMagicBallDelay(int x, int y)
9583 {
9584   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9585 }
9586
9587 static void ActivateMagicBall(int bx, int by)
9588 {
9589   int x, y;
9590
9591   if (level.ball_random)
9592   {
9593     int pos_border = RND(8);    // select one of the eight border elements
9594     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9595     int xx = pos_content % 3;
9596     int yy = pos_content / 3;
9597
9598     x = bx - 1 + xx;
9599     y = by - 1 + yy;
9600
9601     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9602       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9603   }
9604   else
9605   {
9606     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9607     {
9608       int xx = x - bx + 1;
9609       int yy = y - by + 1;
9610
9611       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9612         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9613     }
9614   }
9615
9616   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9617 }
9618
9619 static void CheckExit(int x, int y)
9620 {
9621   if (game.gems_still_needed > 0 ||
9622       game.sokoban_fields_still_needed > 0 ||
9623       game.sokoban_objects_still_needed > 0 ||
9624       game.lights_still_needed > 0)
9625   {
9626     int element = Tile[x][y];
9627     int graphic = el2img(element);
9628
9629     if (IS_ANIMATED(graphic))
9630       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9631
9632     return;
9633   }
9634
9635   // do not re-open exit door closed after last player
9636   if (game.all_players_gone)
9637     return;
9638
9639   Tile[x][y] = EL_EXIT_OPENING;
9640
9641   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9642 }
9643
9644 static void CheckExitEM(int x, int y)
9645 {
9646   if (game.gems_still_needed > 0 ||
9647       game.sokoban_fields_still_needed > 0 ||
9648       game.sokoban_objects_still_needed > 0 ||
9649       game.lights_still_needed > 0)
9650   {
9651     int element = Tile[x][y];
9652     int graphic = el2img(element);
9653
9654     if (IS_ANIMATED(graphic))
9655       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9656
9657     return;
9658   }
9659
9660   // do not re-open exit door closed after last player
9661   if (game.all_players_gone)
9662     return;
9663
9664   Tile[x][y] = EL_EM_EXIT_OPENING;
9665
9666   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9667 }
9668
9669 static void CheckExitSteel(int x, int y)
9670 {
9671   if (game.gems_still_needed > 0 ||
9672       game.sokoban_fields_still_needed > 0 ||
9673       game.sokoban_objects_still_needed > 0 ||
9674       game.lights_still_needed > 0)
9675   {
9676     int element = Tile[x][y];
9677     int graphic = el2img(element);
9678
9679     if (IS_ANIMATED(graphic))
9680       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9681
9682     return;
9683   }
9684
9685   // do not re-open exit door closed after last player
9686   if (game.all_players_gone)
9687     return;
9688
9689   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9690
9691   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9692 }
9693
9694 static void CheckExitSteelEM(int x, int y)
9695 {
9696   if (game.gems_still_needed > 0 ||
9697       game.sokoban_fields_still_needed > 0 ||
9698       game.sokoban_objects_still_needed > 0 ||
9699       game.lights_still_needed > 0)
9700   {
9701     int element = Tile[x][y];
9702     int graphic = el2img(element);
9703
9704     if (IS_ANIMATED(graphic))
9705       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9706
9707     return;
9708   }
9709
9710   // do not re-open exit door closed after last player
9711   if (game.all_players_gone)
9712     return;
9713
9714   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9715
9716   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9717 }
9718
9719 static void CheckExitSP(int x, int y)
9720 {
9721   if (game.gems_still_needed > 0)
9722   {
9723     int element = Tile[x][y];
9724     int graphic = el2img(element);
9725
9726     if (IS_ANIMATED(graphic))
9727       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9728
9729     return;
9730   }
9731
9732   // do not re-open exit door closed after last player
9733   if (game.all_players_gone)
9734     return;
9735
9736   Tile[x][y] = EL_SP_EXIT_OPENING;
9737
9738   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9739 }
9740
9741 static void CloseAllOpenTimegates(void)
9742 {
9743   int x, y;
9744
9745   SCAN_PLAYFIELD(x, y)
9746   {
9747     int element = Tile[x][y];
9748
9749     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9750     {
9751       Tile[x][y] = EL_TIMEGATE_CLOSING;
9752
9753       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9754     }
9755   }
9756 }
9757
9758 static void DrawTwinkleOnField(int x, int y)
9759 {
9760   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9761     return;
9762
9763   if (Tile[x][y] == EL_BD_DIAMOND)
9764     return;
9765
9766   if (MovDelay[x][y] == 0)      // next animation frame
9767     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9768
9769   if (MovDelay[x][y] != 0)      // wait some time before next frame
9770   {
9771     MovDelay[x][y]--;
9772
9773     DrawLevelElementAnimation(x, y, Tile[x][y]);
9774
9775     if (MovDelay[x][y] != 0)
9776     {
9777       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9778                                            10 - MovDelay[x][y]);
9779
9780       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9781     }
9782   }
9783 }
9784
9785 static void WallGrowing(int x, int y)
9786 {
9787   int delay = 6;
9788
9789   if (!MovDelay[x][y])          // next animation frame
9790     MovDelay[x][y] = 3 * delay;
9791
9792   if (MovDelay[x][y])           // wait some time before next frame
9793   {
9794     MovDelay[x][y]--;
9795
9796     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9797     {
9798       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9799       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9800
9801       DrawLevelGraphic(x, y, graphic, frame);
9802     }
9803
9804     if (!MovDelay[x][y])
9805     {
9806       if (MovDir[x][y] == MV_LEFT)
9807       {
9808         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9809           TEST_DrawLevelField(x - 1, y);
9810       }
9811       else if (MovDir[x][y] == MV_RIGHT)
9812       {
9813         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9814           TEST_DrawLevelField(x + 1, y);
9815       }
9816       else if (MovDir[x][y] == MV_UP)
9817       {
9818         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9819           TEST_DrawLevelField(x, y - 1);
9820       }
9821       else
9822       {
9823         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9824           TEST_DrawLevelField(x, y + 1);
9825       }
9826
9827       Tile[x][y] = Store[x][y];
9828       Store[x][y] = 0;
9829       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9830       TEST_DrawLevelField(x, y);
9831     }
9832   }
9833 }
9834
9835 static void CheckWallGrowing(int ax, int ay)
9836 {
9837   int element = Tile[ax][ay];
9838   int graphic = el2img(element);
9839   boolean free_top    = FALSE;
9840   boolean free_bottom = FALSE;
9841   boolean free_left   = FALSE;
9842   boolean free_right  = FALSE;
9843   boolean stop_top    = FALSE;
9844   boolean stop_bottom = FALSE;
9845   boolean stop_left   = FALSE;
9846   boolean stop_right  = FALSE;
9847   boolean new_wall    = FALSE;
9848
9849   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9850                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9851                            element == EL_EXPANDABLE_STEELWALL_ANY);
9852
9853   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9854                              element == EL_EXPANDABLE_WALL_ANY ||
9855                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9856                              element == EL_EXPANDABLE_STEELWALL_ANY);
9857
9858   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9859                              element == EL_EXPANDABLE_WALL_ANY ||
9860                              element == EL_EXPANDABLE_WALL ||
9861                              element == EL_BD_EXPANDABLE_WALL ||
9862                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9863                              element == EL_EXPANDABLE_STEELWALL_ANY);
9864
9865   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9866                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9867
9868   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9869                              element == EL_EXPANDABLE_WALL ||
9870                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9871
9872   int wall_growing = (is_steelwall ?
9873                       EL_EXPANDABLE_STEELWALL_GROWING :
9874                       EL_EXPANDABLE_WALL_GROWING);
9875
9876   int gfx_wall_growing_up    = (is_steelwall ?
9877                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9878                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9879   int gfx_wall_growing_down  = (is_steelwall ?
9880                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9881                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9882   int gfx_wall_growing_left  = (is_steelwall ?
9883                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9884                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9885   int gfx_wall_growing_right = (is_steelwall ?
9886                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9887                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9888
9889   if (IS_ANIMATED(graphic))
9890     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9891
9892   if (!MovDelay[ax][ay])        // start building new wall
9893     MovDelay[ax][ay] = 6;
9894
9895   if (MovDelay[ax][ay])         // wait some time before building new wall
9896   {
9897     MovDelay[ax][ay]--;
9898     if (MovDelay[ax][ay])
9899       return;
9900   }
9901
9902   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9903     free_top = TRUE;
9904   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9905     free_bottom = TRUE;
9906   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9907     free_left = TRUE;
9908   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9909     free_right = TRUE;
9910
9911   if (grow_vertical)
9912   {
9913     if (free_top)
9914     {
9915       Tile[ax][ay - 1] = wall_growing;
9916       Store[ax][ay - 1] = element;
9917       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9918
9919       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9920         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9921
9922       new_wall = TRUE;
9923     }
9924
9925     if (free_bottom)
9926     {
9927       Tile[ax][ay + 1] = wall_growing;
9928       Store[ax][ay + 1] = element;
9929       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9930
9931       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9932         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9933
9934       new_wall = TRUE;
9935     }
9936   }
9937
9938   if (grow_horizontal)
9939   {
9940     if (free_left)
9941     {
9942       Tile[ax - 1][ay] = wall_growing;
9943       Store[ax - 1][ay] = element;
9944       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9945
9946       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9947         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9948
9949       new_wall = TRUE;
9950     }
9951
9952     if (free_right)
9953     {
9954       Tile[ax + 1][ay] = wall_growing;
9955       Store[ax + 1][ay] = element;
9956       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9957
9958       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9959         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9960
9961       new_wall = TRUE;
9962     }
9963   }
9964
9965   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9966     TEST_DrawLevelField(ax, ay);
9967
9968   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9969     stop_top = TRUE;
9970   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9971     stop_bottom = TRUE;
9972   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9973     stop_left = TRUE;
9974   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9975     stop_right = TRUE;
9976
9977   if (((stop_top && stop_bottom) || stop_horizontal) &&
9978       ((stop_left && stop_right) || stop_vertical))
9979     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
9980
9981   if (new_wall)
9982     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9983 }
9984
9985 static void CheckForDragon(int x, int y)
9986 {
9987   int i, j;
9988   boolean dragon_found = FALSE;
9989   struct XY *xy = xy_topdown;
9990
9991   for (i = 0; i < NUM_DIRECTIONS; i++)
9992   {
9993     for (j = 0; j < 4; j++)
9994     {
9995       int xx = x + j * xy[i].x;
9996       int yy = y + j * xy[i].y;
9997
9998       if (IN_LEV_FIELD(xx, yy) &&
9999           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10000       {
10001         if (Tile[xx][yy] == EL_DRAGON)
10002           dragon_found = TRUE;
10003       }
10004       else
10005         break;
10006     }
10007   }
10008
10009   if (!dragon_found)
10010   {
10011     for (i = 0; i < NUM_DIRECTIONS; i++)
10012     {
10013       for (j = 0; j < 3; j++)
10014       {
10015         int xx = x + j * xy[i].x;
10016         int yy = y + j * xy[i].y;
10017
10018         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10019         {
10020           Tile[xx][yy] = EL_EMPTY;
10021           TEST_DrawLevelField(xx, yy);
10022         }
10023         else
10024           break;
10025       }
10026     }
10027   }
10028 }
10029
10030 static void InitBuggyBase(int x, int y)
10031 {
10032   int element = Tile[x][y];
10033   int activating_delay = FRAMES_PER_SECOND / 4;
10034
10035   ChangeDelay[x][y] =
10036     (element == EL_SP_BUGGY_BASE ?
10037      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10038      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10039      activating_delay :
10040      element == EL_SP_BUGGY_BASE_ACTIVE ?
10041      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10042 }
10043
10044 static void WarnBuggyBase(int x, int y)
10045 {
10046   int i;
10047   struct XY *xy = xy_topdown;
10048
10049   for (i = 0; i < NUM_DIRECTIONS; i++)
10050   {
10051     int xx = x + xy[i].x;
10052     int yy = y + xy[i].y;
10053
10054     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10055     {
10056       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10057
10058       break;
10059     }
10060   }
10061 }
10062
10063 static void InitTrap(int x, int y)
10064 {
10065   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10066 }
10067
10068 static void ActivateTrap(int x, int y)
10069 {
10070   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10071 }
10072
10073 static void ChangeActiveTrap(int x, int y)
10074 {
10075   int graphic = IMG_TRAP_ACTIVE;
10076
10077   // if new animation frame was drawn, correct crumbled sand border
10078   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10079     TEST_DrawLevelFieldCrumbled(x, y);
10080 }
10081
10082 static int getSpecialActionElement(int element, int number, int base_element)
10083 {
10084   return (element != EL_EMPTY ? element :
10085           number != -1 ? base_element + number - 1 :
10086           EL_EMPTY);
10087 }
10088
10089 static int getModifiedActionNumber(int value_old, int operator, int operand,
10090                                    int value_min, int value_max)
10091 {
10092   int value_new = (operator == CA_MODE_SET      ? operand :
10093                    operator == CA_MODE_ADD      ? value_old + operand :
10094                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10095                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10096                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10097                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10098                    value_old);
10099
10100   return (value_new < value_min ? value_min :
10101           value_new > value_max ? value_max :
10102           value_new);
10103 }
10104
10105 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10106 {
10107   struct ElementInfo *ei = &element_info[element];
10108   struct ElementChangeInfo *change = &ei->change_page[page];
10109   int target_element = change->target_element;
10110   int action_type = change->action_type;
10111   int action_mode = change->action_mode;
10112   int action_arg = change->action_arg;
10113   int action_element = change->action_element;
10114   int i;
10115
10116   if (!change->has_action)
10117     return;
10118
10119   // ---------- determine action paramater values -----------------------------
10120
10121   int level_time_value =
10122     (level.time > 0 ? TimeLeft :
10123      TimePlayed);
10124
10125   int action_arg_element_raw =
10126     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10127      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10128      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10129      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10130      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10131      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10132      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10133      EL_EMPTY);
10134   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10135
10136   int action_arg_direction =
10137     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10138      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10139      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10140      change->actual_trigger_side :
10141      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10142      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10143      MV_NONE);
10144
10145   int action_arg_number_min =
10146     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10147      CA_ARG_MIN);
10148
10149   int action_arg_number_max =
10150     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10151      action_type == CA_SET_LEVEL_GEMS ? 999 :
10152      action_type == CA_SET_LEVEL_TIME ? 9999 :
10153      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10154      action_type == CA_SET_CE_VALUE ? 9999 :
10155      action_type == CA_SET_CE_SCORE ? 9999 :
10156      CA_ARG_MAX);
10157
10158   int action_arg_number_reset =
10159     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10160      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10161      action_type == CA_SET_LEVEL_TIME ? level.time :
10162      action_type == CA_SET_LEVEL_SCORE ? 0 :
10163      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10164      action_type == CA_SET_CE_SCORE ? 0 :
10165      0);
10166
10167   int action_arg_number =
10168     (action_arg <= CA_ARG_MAX ? action_arg :
10169      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10170      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10171      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10172      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10173      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10174      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10175      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10176      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10177      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10178      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10179      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10180      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10181      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10182      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10183      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10184      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10185      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10186      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10187      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10188      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10189      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10190      -1);
10191
10192   int action_arg_number_old =
10193     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10194      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10195      action_type == CA_SET_LEVEL_SCORE ? game.score :
10196      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10197      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10198      0);
10199
10200   int action_arg_number_new =
10201     getModifiedActionNumber(action_arg_number_old,
10202                             action_mode, action_arg_number,
10203                             action_arg_number_min, action_arg_number_max);
10204
10205   int trigger_player_bits =
10206     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10207      change->actual_trigger_player_bits : change->trigger_player);
10208
10209   int action_arg_player_bits =
10210     (action_arg >= CA_ARG_PLAYER_1 &&
10211      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10212      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10213      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10214      PLAYER_BITS_ANY);
10215
10216   // ---------- execute action  -----------------------------------------------
10217
10218   switch (action_type)
10219   {
10220     case CA_NO_ACTION:
10221     {
10222       return;
10223     }
10224
10225     // ---------- level actions  ----------------------------------------------
10226
10227     case CA_RESTART_LEVEL:
10228     {
10229       game.restart_level = TRUE;
10230
10231       break;
10232     }
10233
10234     case CA_SHOW_ENVELOPE:
10235     {
10236       int element = getSpecialActionElement(action_arg_element,
10237                                             action_arg_number, EL_ENVELOPE_1);
10238
10239       if (IS_ENVELOPE(element))
10240         local_player->show_envelope = element;
10241
10242       break;
10243     }
10244
10245     case CA_SET_LEVEL_TIME:
10246     {
10247       if (level.time > 0)       // only modify limited time value
10248       {
10249         TimeLeft = action_arg_number_new;
10250
10251         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10252
10253         DisplayGameControlValues();
10254
10255         if (!TimeLeft && game.time_limit)
10256           for (i = 0; i < MAX_PLAYERS; i++)
10257             KillPlayer(&stored_player[i]);
10258       }
10259
10260       break;
10261     }
10262
10263     case CA_SET_LEVEL_SCORE:
10264     {
10265       game.score = action_arg_number_new;
10266
10267       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10268
10269       DisplayGameControlValues();
10270
10271       break;
10272     }
10273
10274     case CA_SET_LEVEL_GEMS:
10275     {
10276       game.gems_still_needed = action_arg_number_new;
10277
10278       game.snapshot.collected_item = TRUE;
10279
10280       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10281
10282       DisplayGameControlValues();
10283
10284       break;
10285     }
10286
10287     case CA_SET_LEVEL_WIND:
10288     {
10289       game.wind_direction = action_arg_direction;
10290
10291       break;
10292     }
10293
10294     case CA_SET_LEVEL_RANDOM_SEED:
10295     {
10296       // ensure that setting a new random seed while playing is predictable
10297       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10298
10299       break;
10300     }
10301
10302     // ---------- player actions  ---------------------------------------------
10303
10304     case CA_MOVE_PLAYER:
10305     case CA_MOVE_PLAYER_NEW:
10306     {
10307       // automatically move to the next field in specified direction
10308       for (i = 0; i < MAX_PLAYERS; i++)
10309         if (trigger_player_bits & (1 << i))
10310           if (action_type == CA_MOVE_PLAYER ||
10311               stored_player[i].MovPos == 0)
10312             stored_player[i].programmed_action = action_arg_direction;
10313
10314       break;
10315     }
10316
10317     case CA_EXIT_PLAYER:
10318     {
10319       for (i = 0; i < MAX_PLAYERS; i++)
10320         if (action_arg_player_bits & (1 << i))
10321           ExitPlayer(&stored_player[i]);
10322
10323       if (game.players_still_needed == 0)
10324         LevelSolved();
10325
10326       break;
10327     }
10328
10329     case CA_KILL_PLAYER:
10330     {
10331       for (i = 0; i < MAX_PLAYERS; i++)
10332         if (action_arg_player_bits & (1 << i))
10333           KillPlayer(&stored_player[i]);
10334
10335       break;
10336     }
10337
10338     case CA_SET_PLAYER_KEYS:
10339     {
10340       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10341       int element = getSpecialActionElement(action_arg_element,
10342                                             action_arg_number, EL_KEY_1);
10343
10344       if (IS_KEY(element))
10345       {
10346         for (i = 0; i < MAX_PLAYERS; i++)
10347         {
10348           if (trigger_player_bits & (1 << i))
10349           {
10350             stored_player[i].key[KEY_NR(element)] = key_state;
10351
10352             DrawGameDoorValues();
10353           }
10354         }
10355       }
10356
10357       break;
10358     }
10359
10360     case CA_SET_PLAYER_SPEED:
10361     {
10362       for (i = 0; i < MAX_PLAYERS; i++)
10363       {
10364         if (trigger_player_bits & (1 << i))
10365         {
10366           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10367
10368           if (action_arg == CA_ARG_SPEED_FASTER &&
10369               stored_player[i].cannot_move)
10370           {
10371             action_arg_number = STEPSIZE_VERY_SLOW;
10372           }
10373           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10374                    action_arg == CA_ARG_SPEED_FASTER)
10375           {
10376             action_arg_number = 2;
10377             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10378                            CA_MODE_MULTIPLY);
10379           }
10380           else if (action_arg == CA_ARG_NUMBER_RESET)
10381           {
10382             action_arg_number = level.initial_player_stepsize[i];
10383           }
10384
10385           move_stepsize =
10386             getModifiedActionNumber(move_stepsize,
10387                                     action_mode,
10388                                     action_arg_number,
10389                                     action_arg_number_min,
10390                                     action_arg_number_max);
10391
10392           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10393         }
10394       }
10395
10396       break;
10397     }
10398
10399     case CA_SET_PLAYER_SHIELD:
10400     {
10401       for (i = 0; i < MAX_PLAYERS; i++)
10402       {
10403         if (trigger_player_bits & (1 << i))
10404         {
10405           if (action_arg == CA_ARG_SHIELD_OFF)
10406           {
10407             stored_player[i].shield_normal_time_left = 0;
10408             stored_player[i].shield_deadly_time_left = 0;
10409           }
10410           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10411           {
10412             stored_player[i].shield_normal_time_left = 999999;
10413           }
10414           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10415           {
10416             stored_player[i].shield_normal_time_left = 999999;
10417             stored_player[i].shield_deadly_time_left = 999999;
10418           }
10419         }
10420       }
10421
10422       break;
10423     }
10424
10425     case CA_SET_PLAYER_GRAVITY:
10426     {
10427       for (i = 0; i < MAX_PLAYERS; i++)
10428       {
10429         if (trigger_player_bits & (1 << i))
10430         {
10431           stored_player[i].gravity =
10432             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10433              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10434              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10435              stored_player[i].gravity);
10436         }
10437       }
10438
10439       break;
10440     }
10441
10442     case CA_SET_PLAYER_ARTWORK:
10443     {
10444       for (i = 0; i < MAX_PLAYERS; i++)
10445       {
10446         if (trigger_player_bits & (1 << i))
10447         {
10448           int artwork_element = action_arg_element;
10449
10450           if (action_arg == CA_ARG_ELEMENT_RESET)
10451             artwork_element =
10452               (level.use_artwork_element[i] ? level.artwork_element[i] :
10453                stored_player[i].element_nr);
10454
10455           if (stored_player[i].artwork_element != artwork_element)
10456             stored_player[i].Frame = 0;
10457
10458           stored_player[i].artwork_element = artwork_element;
10459
10460           SetPlayerWaiting(&stored_player[i], FALSE);
10461
10462           // set number of special actions for bored and sleeping animation
10463           stored_player[i].num_special_action_bored =
10464             get_num_special_action(artwork_element,
10465                                    ACTION_BORING_1, ACTION_BORING_LAST);
10466           stored_player[i].num_special_action_sleeping =
10467             get_num_special_action(artwork_element,
10468                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10469         }
10470       }
10471
10472       break;
10473     }
10474
10475     case CA_SET_PLAYER_INVENTORY:
10476     {
10477       for (i = 0; i < MAX_PLAYERS; i++)
10478       {
10479         struct PlayerInfo *player = &stored_player[i];
10480         int j, k;
10481
10482         if (trigger_player_bits & (1 << i))
10483         {
10484           int inventory_element = action_arg_element;
10485
10486           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10487               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10488               action_arg == CA_ARG_ELEMENT_ACTION)
10489           {
10490             int element = inventory_element;
10491             int collect_count = element_info[element].collect_count_initial;
10492
10493             if (!IS_CUSTOM_ELEMENT(element))
10494               collect_count = 1;
10495
10496             if (collect_count == 0)
10497               player->inventory_infinite_element = element;
10498             else
10499               for (k = 0; k < collect_count; k++)
10500                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10501                   player->inventory_element[player->inventory_size++] =
10502                     element;
10503           }
10504           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10505                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10506                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10507           {
10508             if (player->inventory_infinite_element != EL_UNDEFINED &&
10509                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10510                                      action_arg_element_raw))
10511               player->inventory_infinite_element = EL_UNDEFINED;
10512
10513             for (k = 0, j = 0; j < player->inventory_size; j++)
10514             {
10515               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10516                                         action_arg_element_raw))
10517                 player->inventory_element[k++] = player->inventory_element[j];
10518             }
10519
10520             player->inventory_size = k;
10521           }
10522           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10523           {
10524             if (player->inventory_size > 0)
10525             {
10526               for (j = 0; j < player->inventory_size - 1; j++)
10527                 player->inventory_element[j] = player->inventory_element[j + 1];
10528
10529               player->inventory_size--;
10530             }
10531           }
10532           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10533           {
10534             if (player->inventory_size > 0)
10535               player->inventory_size--;
10536           }
10537           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10538           {
10539             player->inventory_infinite_element = EL_UNDEFINED;
10540             player->inventory_size = 0;
10541           }
10542           else if (action_arg == CA_ARG_INVENTORY_RESET)
10543           {
10544             player->inventory_infinite_element = EL_UNDEFINED;
10545             player->inventory_size = 0;
10546
10547             if (level.use_initial_inventory[i])
10548             {
10549               for (j = 0; j < level.initial_inventory_size[i]; j++)
10550               {
10551                 int element = level.initial_inventory_content[i][j];
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             }
10566           }
10567         }
10568       }
10569
10570       break;
10571     }
10572
10573     // ---------- CE actions  -------------------------------------------------
10574
10575     case CA_SET_CE_VALUE:
10576     {
10577       int last_ce_value = CustomValue[x][y];
10578
10579       CustomValue[x][y] = action_arg_number_new;
10580
10581       if (CustomValue[x][y] != last_ce_value)
10582       {
10583         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10584         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10585
10586         if (CustomValue[x][y] == 0)
10587         {
10588           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10589           ChangeCount[x][y] = 0;        // allow at least one more change
10590
10591           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10592           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10593         }
10594       }
10595
10596       break;
10597     }
10598
10599     case CA_SET_CE_SCORE:
10600     {
10601       int last_ce_score = ei->collect_score;
10602
10603       ei->collect_score = action_arg_number_new;
10604
10605       if (ei->collect_score != last_ce_score)
10606       {
10607         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10608         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10609
10610         if (ei->collect_score == 0)
10611         {
10612           int xx, yy;
10613
10614           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10615           ChangeCount[x][y] = 0;        // allow at least one more change
10616
10617           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10618           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10619
10620           /*
10621             This is a very special case that seems to be a mixture between
10622             CheckElementChange() and CheckTriggeredElementChange(): while
10623             the first one only affects single elements that are triggered
10624             directly, the second one affects multiple elements in the playfield
10625             that are triggered indirectly by another element. This is a third
10626             case: Changing the CE score always affects multiple identical CEs,
10627             so every affected CE must be checked, not only the single CE for
10628             which the CE score was changed in the first place (as every instance
10629             of that CE shares the same CE score, and therefore also can change)!
10630           */
10631           SCAN_PLAYFIELD(xx, yy)
10632           {
10633             if (Tile[xx][yy] == element)
10634               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10635                                  CE_SCORE_GETS_ZERO);
10636           }
10637         }
10638       }
10639
10640       break;
10641     }
10642
10643     case CA_SET_CE_ARTWORK:
10644     {
10645       int artwork_element = action_arg_element;
10646       boolean reset_frame = FALSE;
10647       int xx, yy;
10648
10649       if (action_arg == CA_ARG_ELEMENT_RESET)
10650         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10651                            element);
10652
10653       if (ei->gfx_element != artwork_element)
10654         reset_frame = TRUE;
10655
10656       ei->gfx_element = artwork_element;
10657
10658       SCAN_PLAYFIELD(xx, yy)
10659       {
10660         if (Tile[xx][yy] == element)
10661         {
10662           if (reset_frame)
10663           {
10664             ResetGfxAnimation(xx, yy);
10665             ResetRandomAnimationValue(xx, yy);
10666           }
10667
10668           TEST_DrawLevelField(xx, yy);
10669         }
10670       }
10671
10672       break;
10673     }
10674
10675     // ---------- engine actions  ---------------------------------------------
10676
10677     case CA_SET_ENGINE_SCAN_MODE:
10678     {
10679       InitPlayfieldScanMode(action_arg);
10680
10681       break;
10682     }
10683
10684     default:
10685       break;
10686   }
10687 }
10688
10689 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10690 {
10691   int old_element = Tile[x][y];
10692   int new_element = GetElementFromGroupElement(element);
10693   int previous_move_direction = MovDir[x][y];
10694   int last_ce_value = CustomValue[x][y];
10695   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10696   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10697   boolean add_player_onto_element = (new_element_is_player &&
10698                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10699                                      IS_WALKABLE(old_element));
10700
10701   if (!add_player_onto_element)
10702   {
10703     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10704       RemoveMovingField(x, y);
10705     else
10706       RemoveField(x, y);
10707
10708     Tile[x][y] = new_element;
10709
10710     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10711       MovDir[x][y] = previous_move_direction;
10712
10713     if (element_info[new_element].use_last_ce_value)
10714       CustomValue[x][y] = last_ce_value;
10715
10716     InitField_WithBug1(x, y, FALSE);
10717
10718     new_element = Tile[x][y];   // element may have changed
10719
10720     ResetGfxAnimation(x, y);
10721     ResetRandomAnimationValue(x, y);
10722
10723     TEST_DrawLevelField(x, y);
10724
10725     if (GFX_CRUMBLED(new_element))
10726       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10727
10728     if (old_element == EL_EXPLOSION)
10729     {
10730       Store[x][y] = Store2[x][y] = 0;
10731
10732       // check if new element replaces an exploding player, requiring cleanup
10733       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10734         StorePlayer[x][y] = 0;
10735     }
10736
10737     // check if element under the player changes from accessible to unaccessible
10738     // (needed for special case of dropping element which then changes)
10739     // (must be checked after creating new element for walkable group elements)
10740     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10741         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10742     {
10743       KillPlayer(PLAYERINFO(x, y));
10744
10745       return;
10746     }
10747   }
10748
10749   // "ChangeCount" not set yet to allow "entered by player" change one time
10750   if (new_element_is_player)
10751     RelocatePlayer(x, y, new_element);
10752
10753   if (is_change)
10754     ChangeCount[x][y]++;        // count number of changes in the same frame
10755
10756   TestIfBadThingTouchesPlayer(x, y);
10757   TestIfPlayerTouchesCustomElement(x, y);
10758   TestIfElementTouchesCustomElement(x, y);
10759 }
10760
10761 static void CreateField(int x, int y, int element)
10762 {
10763   CreateFieldExt(x, y, element, FALSE);
10764 }
10765
10766 static void CreateElementFromChange(int x, int y, int element)
10767 {
10768   element = GET_VALID_RUNTIME_ELEMENT(element);
10769
10770   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10771   {
10772     int old_element = Tile[x][y];
10773
10774     // prevent changed element from moving in same engine frame
10775     // unless both old and new element can either fall or move
10776     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10777         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10778       Stop[x][y] = TRUE;
10779   }
10780
10781   CreateFieldExt(x, y, element, TRUE);
10782 }
10783
10784 static boolean ChangeElement(int x, int y, int element, int page)
10785 {
10786   struct ElementInfo *ei = &element_info[element];
10787   struct ElementChangeInfo *change = &ei->change_page[page];
10788   int ce_value = CustomValue[x][y];
10789   int ce_score = ei->collect_score;
10790   int target_element;
10791   int old_element = Tile[x][y];
10792
10793   // always use default change event to prevent running into a loop
10794   if (ChangeEvent[x][y] == -1)
10795     ChangeEvent[x][y] = CE_DELAY;
10796
10797   if (ChangeEvent[x][y] == CE_DELAY)
10798   {
10799     // reset actual trigger element, trigger player and action element
10800     change->actual_trigger_element = EL_EMPTY;
10801     change->actual_trigger_player = EL_EMPTY;
10802     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10803     change->actual_trigger_side = CH_SIDE_NONE;
10804     change->actual_trigger_ce_value = 0;
10805     change->actual_trigger_ce_score = 0;
10806     change->actual_trigger_x = -1;
10807     change->actual_trigger_y = -1;
10808   }
10809
10810   // do not change elements more than a specified maximum number of changes
10811   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10812     return FALSE;
10813
10814   ChangeCount[x][y]++;          // count number of changes in the same frame
10815
10816   if (ei->has_anim_event)
10817     HandleGlobalAnimEventByElementChange(element, page, x, y,
10818                                          change->actual_trigger_x,
10819                                          change->actual_trigger_y);
10820
10821   if (change->explode)
10822   {
10823     Bang(x, y);
10824
10825     return TRUE;
10826   }
10827
10828   if (change->use_target_content)
10829   {
10830     boolean complete_replace = TRUE;
10831     boolean can_replace[3][3];
10832     int xx, yy;
10833
10834     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10835     {
10836       boolean is_empty;
10837       boolean is_walkable;
10838       boolean is_diggable;
10839       boolean is_collectible;
10840       boolean is_removable;
10841       boolean is_destructible;
10842       int ex = x + xx - 1;
10843       int ey = y + yy - 1;
10844       int content_element = change->target_content.e[xx][yy];
10845       int e;
10846
10847       can_replace[xx][yy] = TRUE;
10848
10849       if (ex == x && ey == y)   // do not check changing element itself
10850         continue;
10851
10852       if (content_element == EL_EMPTY_SPACE)
10853       {
10854         can_replace[xx][yy] = FALSE;    // do not replace border with space
10855
10856         continue;
10857       }
10858
10859       if (!IN_LEV_FIELD(ex, ey))
10860       {
10861         can_replace[xx][yy] = FALSE;
10862         complete_replace = FALSE;
10863
10864         continue;
10865       }
10866
10867       e = Tile[ex][ey];
10868
10869       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10870         e = MovingOrBlocked2Element(ex, ey);
10871
10872       is_empty = (IS_FREE(ex, ey) ||
10873                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10874
10875       is_walkable     = (is_empty || IS_WALKABLE(e));
10876       is_diggable     = (is_empty || IS_DIGGABLE(e));
10877       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10878       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10879       is_removable    = (is_diggable || is_collectible);
10880
10881       can_replace[xx][yy] =
10882         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10883           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10884           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10885           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10886           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10887           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10888          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10889
10890       if (!can_replace[xx][yy])
10891         complete_replace = FALSE;
10892     }
10893
10894     if (!change->only_if_complete || complete_replace)
10895     {
10896       boolean something_has_changed = FALSE;
10897
10898       if (change->only_if_complete && change->use_random_replace &&
10899           RND(100) < change->random_percentage)
10900         return FALSE;
10901
10902       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10903       {
10904         int ex = x + xx - 1;
10905         int ey = y + yy - 1;
10906         int content_element;
10907
10908         if (can_replace[xx][yy] && (!change->use_random_replace ||
10909                                     RND(100) < change->random_percentage))
10910         {
10911           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10912             RemoveMovingField(ex, ey);
10913
10914           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10915
10916           content_element = change->target_content.e[xx][yy];
10917           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10918                                               ce_value, ce_score);
10919
10920           CreateElementFromChange(ex, ey, target_element);
10921
10922           something_has_changed = TRUE;
10923
10924           // for symmetry reasons, freeze newly created border elements
10925           if (ex != x || ey != y)
10926             Stop[ex][ey] = TRUE;        // no more moving in this frame
10927         }
10928       }
10929
10930       if (something_has_changed)
10931       {
10932         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10933         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10934       }
10935     }
10936   }
10937   else
10938   {
10939     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10940                                         ce_value, ce_score);
10941
10942     if (element == EL_DIAGONAL_GROWING ||
10943         element == EL_DIAGONAL_SHRINKING)
10944     {
10945       target_element = Store[x][y];
10946
10947       Store[x][y] = EL_EMPTY;
10948     }
10949
10950     // special case: element changes to player (and may be kept if walkable)
10951     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10952       CreateElementFromChange(x, y, EL_EMPTY);
10953
10954     CreateElementFromChange(x, y, target_element);
10955
10956     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10957     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10958   }
10959
10960   // this uses direct change before indirect change
10961   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10962
10963   return TRUE;
10964 }
10965
10966 static void HandleElementChange(int x, int y, int page)
10967 {
10968   int element = MovingOrBlocked2Element(x, y);
10969   struct ElementInfo *ei = &element_info[element];
10970   struct ElementChangeInfo *change = &ei->change_page[page];
10971   boolean handle_action_before_change = FALSE;
10972
10973 #ifdef DEBUG
10974   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10975       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10976   {
10977     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10978           x, y, element, element_info[element].token_name);
10979     Debug("game:playing:HandleElementChange", "This should never happen!");
10980   }
10981 #endif
10982
10983   // this can happen with classic bombs on walkable, changing elements
10984   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10985   {
10986     return;
10987   }
10988
10989   if (ChangeDelay[x][y] == 0)           // initialize element change
10990   {
10991     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10992
10993     if (change->can_change)
10994     {
10995       // !!! not clear why graphic animation should be reset at all here !!!
10996       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10997       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10998
10999       /*
11000         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11001
11002         When using an animation frame delay of 1 (this only happens with
11003         "sp_zonk.moving.left/right" in the classic graphics), the default
11004         (non-moving) animation shows wrong animation frames (while the
11005         moving animation, like "sp_zonk.moving.left/right", is correct,
11006         so this graphical bug never shows up with the classic graphics).
11007         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11008         be drawn instead of the correct frames 0,1,2,3. This is caused by
11009         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11010         an element change: First when the change delay ("ChangeDelay[][]")
11011         counter has reached zero after decrementing, then a second time in
11012         the next frame (after "GfxFrame[][]" was already incremented) when
11013         "ChangeDelay[][]" is reset to the initial delay value again.
11014
11015         This causes frame 0 to be drawn twice, while the last frame won't
11016         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11017
11018         As some animations may already be cleverly designed around this bug
11019         (at least the "Snake Bite" snake tail animation does this), it cannot
11020         simply be fixed here without breaking such existing animations.
11021         Unfortunately, it cannot easily be detected if a graphics set was
11022         designed "before" or "after" the bug was fixed. As a workaround,
11023         a new graphics set option "game.graphics_engine_version" was added
11024         to be able to specify the game's major release version for which the
11025         graphics set was designed, which can then be used to decide if the
11026         bugfix should be used (version 4 and above) or not (version 3 or
11027         below, or if no version was specified at all, as with old sets).
11028
11029         (The wrong/fixed animation frames can be tested with the test level set
11030         "test_gfxframe" and level "000", which contains a specially prepared
11031         custom element at level position (x/y) == (11/9) which uses the zonk
11032         animation mentioned above. Using "game.graphics_engine_version: 4"
11033         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11034         This can also be seen from the debug output for this test element.)
11035       */
11036
11037       // when a custom element is about to change (for example by change delay),
11038       // do not reset graphic animation when the custom element is moving
11039       if (game.graphics_engine_version < 4 &&
11040           !IS_MOVING(x, y))
11041       {
11042         ResetGfxAnimation(x, y);
11043         ResetRandomAnimationValue(x, y);
11044       }
11045
11046       if (change->pre_change_function)
11047         change->pre_change_function(x, y);
11048     }
11049   }
11050
11051   ChangeDelay[x][y]--;
11052
11053   if (ChangeDelay[x][y] != 0)           // continue element change
11054   {
11055     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11056
11057     // also needed if CE can not change, but has CE delay with CE action
11058     if (IS_ANIMATED(graphic))
11059       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11060
11061     if (change->can_change)
11062     {
11063       if (change->change_function)
11064         change->change_function(x, y);
11065     }
11066   }
11067   else                                  // finish element change
11068   {
11069     if (ChangePage[x][y] != -1)         // remember page from delayed change
11070     {
11071       page = ChangePage[x][y];
11072       ChangePage[x][y] = -1;
11073
11074       change = &ei->change_page[page];
11075     }
11076
11077     if (IS_MOVING(x, y))                // never change a running system ;-)
11078     {
11079       ChangeDelay[x][y] = 1;            // try change after next move step
11080       ChangePage[x][y] = page;          // remember page to use for change
11081
11082       return;
11083     }
11084
11085     // special case: set new level random seed before changing element
11086     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11087       handle_action_before_change = TRUE;
11088
11089     if (change->has_action && handle_action_before_change)
11090       ExecuteCustomElementAction(x, y, element, page);
11091
11092     if (change->can_change)
11093     {
11094       if (ChangeElement(x, y, element, page))
11095       {
11096         if (change->post_change_function)
11097           change->post_change_function(x, y);
11098       }
11099     }
11100
11101     if (change->has_action && !handle_action_before_change)
11102       ExecuteCustomElementAction(x, y, element, page);
11103   }
11104 }
11105
11106 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11107                                               int trigger_element,
11108                                               int trigger_event,
11109                                               int trigger_player,
11110                                               int trigger_side,
11111                                               int trigger_page)
11112 {
11113   boolean change_done_any = FALSE;
11114   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11115   int i;
11116
11117   if (!(trigger_events[trigger_element][trigger_event]))
11118     return FALSE;
11119
11120   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11121
11122   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11123   {
11124     int element = EL_CUSTOM_START + i;
11125     boolean change_done = FALSE;
11126     int p;
11127
11128     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11129         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11130       continue;
11131
11132     for (p = 0; p < element_info[element].num_change_pages; p++)
11133     {
11134       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11135
11136       if (change->can_change_or_has_action &&
11137           change->has_event[trigger_event] &&
11138           change->trigger_side & trigger_side &&
11139           change->trigger_player & trigger_player &&
11140           change->trigger_page & trigger_page_bits &&
11141           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11142       {
11143         change->actual_trigger_element = trigger_element;
11144         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11145         change->actual_trigger_player_bits = trigger_player;
11146         change->actual_trigger_side = trigger_side;
11147         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11148         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11149         change->actual_trigger_x = trigger_x;
11150         change->actual_trigger_y = trigger_y;
11151
11152         if ((change->can_change && !change_done) || change->has_action)
11153         {
11154           int x, y;
11155
11156           SCAN_PLAYFIELD(x, y)
11157           {
11158             if (Tile[x][y] == element)
11159             {
11160               if (change->can_change && !change_done)
11161               {
11162                 // if element already changed in this frame, not only prevent
11163                 // another element change (checked in ChangeElement()), but
11164                 // also prevent additional element actions for this element
11165
11166                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11167                     !level.use_action_after_change_bug)
11168                   continue;
11169
11170                 ChangeDelay[x][y] = 1;
11171                 ChangeEvent[x][y] = trigger_event;
11172
11173                 HandleElementChange(x, y, p);
11174               }
11175               else if (change->has_action)
11176               {
11177                 // if element already changed in this frame, not only prevent
11178                 // another element change (checked in ChangeElement()), but
11179                 // also prevent additional element actions for this element
11180
11181                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11182                     !level.use_action_after_change_bug)
11183                   continue;
11184
11185                 ExecuteCustomElementAction(x, y, element, p);
11186                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11187               }
11188             }
11189           }
11190
11191           if (change->can_change)
11192           {
11193             change_done = TRUE;
11194             change_done_any = TRUE;
11195           }
11196         }
11197       }
11198     }
11199   }
11200
11201   RECURSION_LOOP_DETECTION_END();
11202
11203   return change_done_any;
11204 }
11205
11206 static boolean CheckElementChangeExt(int x, int y,
11207                                      int element,
11208                                      int trigger_element,
11209                                      int trigger_event,
11210                                      int trigger_player,
11211                                      int trigger_side)
11212 {
11213   boolean change_done = FALSE;
11214   int p;
11215
11216   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11217       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11218     return FALSE;
11219
11220   if (Tile[x][y] == EL_BLOCKED)
11221   {
11222     Blocked2Moving(x, y, &x, &y);
11223     element = Tile[x][y];
11224   }
11225
11226   // check if element has already changed or is about to change after moving
11227   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11228        Tile[x][y] != element) ||
11229
11230       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11231        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11232         ChangePage[x][y] != -1)))
11233     return FALSE;
11234
11235   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11236
11237   for (p = 0; p < element_info[element].num_change_pages; p++)
11238   {
11239     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11240
11241     /* check trigger element for all events where the element that is checked
11242        for changing interacts with a directly adjacent element -- this is
11243        different to element changes that affect other elements to change on the
11244        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11245     boolean check_trigger_element =
11246       (trigger_event == CE_NEXT_TO_X ||
11247        trigger_event == CE_TOUCHING_X ||
11248        trigger_event == CE_HITTING_X ||
11249        trigger_event == CE_HIT_BY_X ||
11250        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11251
11252     if (change->can_change_or_has_action &&
11253         change->has_event[trigger_event] &&
11254         change->trigger_side & trigger_side &&
11255         change->trigger_player & trigger_player &&
11256         (!check_trigger_element ||
11257          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11258     {
11259       change->actual_trigger_element = trigger_element;
11260       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11261       change->actual_trigger_player_bits = trigger_player;
11262       change->actual_trigger_side = trigger_side;
11263       change->actual_trigger_ce_value = CustomValue[x][y];
11264       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11265       change->actual_trigger_x = x;
11266       change->actual_trigger_y = y;
11267
11268       // special case: trigger element not at (x,y) position for some events
11269       if (check_trigger_element)
11270       {
11271         static struct
11272         {
11273           int dx, dy;
11274         } move_xy[] =
11275           {
11276             {  0,  0 },
11277             { -1,  0 },
11278             { +1,  0 },
11279             {  0,  0 },
11280             {  0, -1 },
11281             {  0,  0 }, { 0, 0 }, { 0, 0 },
11282             {  0, +1 }
11283           };
11284
11285         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11286         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11287
11288         change->actual_trigger_ce_value = CustomValue[xx][yy];
11289         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11290         change->actual_trigger_x = xx;
11291         change->actual_trigger_y = yy;
11292       }
11293
11294       if (change->can_change && !change_done)
11295       {
11296         ChangeDelay[x][y] = 1;
11297         ChangeEvent[x][y] = trigger_event;
11298
11299         HandleElementChange(x, y, p);
11300
11301         change_done = TRUE;
11302       }
11303       else if (change->has_action)
11304       {
11305         ExecuteCustomElementAction(x, y, element, p);
11306         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11307       }
11308     }
11309   }
11310
11311   RECURSION_LOOP_DETECTION_END();
11312
11313   return change_done;
11314 }
11315
11316 static void PlayPlayerSound(struct PlayerInfo *player)
11317 {
11318   int jx = player->jx, jy = player->jy;
11319   int sound_element = player->artwork_element;
11320   int last_action = player->last_action_waiting;
11321   int action = player->action_waiting;
11322
11323   if (player->is_waiting)
11324   {
11325     if (action != last_action)
11326       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11327     else
11328       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11329   }
11330   else
11331   {
11332     if (action != last_action)
11333       StopSound(element_info[sound_element].sound[last_action]);
11334
11335     if (last_action == ACTION_SLEEPING)
11336       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11337   }
11338 }
11339
11340 static void PlayAllPlayersSound(void)
11341 {
11342   int i;
11343
11344   for (i = 0; i < MAX_PLAYERS; i++)
11345     if (stored_player[i].active)
11346       PlayPlayerSound(&stored_player[i]);
11347 }
11348
11349 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11350 {
11351   boolean last_waiting = player->is_waiting;
11352   int move_dir = player->MovDir;
11353
11354   player->dir_waiting = move_dir;
11355   player->last_action_waiting = player->action_waiting;
11356
11357   if (is_waiting)
11358   {
11359     if (!last_waiting)          // not waiting -> waiting
11360     {
11361       player->is_waiting = TRUE;
11362
11363       player->frame_counter_bored =
11364         FrameCounter +
11365         game.player_boring_delay_fixed +
11366         GetSimpleRandom(game.player_boring_delay_random);
11367       player->frame_counter_sleeping =
11368         FrameCounter +
11369         game.player_sleeping_delay_fixed +
11370         GetSimpleRandom(game.player_sleeping_delay_random);
11371
11372       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11373     }
11374
11375     if (game.player_sleeping_delay_fixed +
11376         game.player_sleeping_delay_random > 0 &&
11377         player->anim_delay_counter == 0 &&
11378         player->post_delay_counter == 0 &&
11379         FrameCounter >= player->frame_counter_sleeping)
11380       player->is_sleeping = TRUE;
11381     else if (game.player_boring_delay_fixed +
11382              game.player_boring_delay_random > 0 &&
11383              FrameCounter >= player->frame_counter_bored)
11384       player->is_bored = TRUE;
11385
11386     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11387                               player->is_bored ? ACTION_BORING :
11388                               ACTION_WAITING);
11389
11390     if (player->is_sleeping && player->use_murphy)
11391     {
11392       // special case for sleeping Murphy when leaning against non-free tile
11393
11394       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11395           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11396            !IS_MOVING(player->jx - 1, player->jy)))
11397         move_dir = MV_LEFT;
11398       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11399                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11400                 !IS_MOVING(player->jx + 1, player->jy)))
11401         move_dir = MV_RIGHT;
11402       else
11403         player->is_sleeping = FALSE;
11404
11405       player->dir_waiting = move_dir;
11406     }
11407
11408     if (player->is_sleeping)
11409     {
11410       if (player->num_special_action_sleeping > 0)
11411       {
11412         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11413         {
11414           int last_special_action = player->special_action_sleeping;
11415           int num_special_action = player->num_special_action_sleeping;
11416           int special_action =
11417             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11418              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11419              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11420              last_special_action + 1 : ACTION_SLEEPING);
11421           int special_graphic =
11422             el_act_dir2img(player->artwork_element, special_action, move_dir);
11423
11424           player->anim_delay_counter =
11425             graphic_info[special_graphic].anim_delay_fixed +
11426             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11427           player->post_delay_counter =
11428             graphic_info[special_graphic].post_delay_fixed +
11429             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11430
11431           player->special_action_sleeping = special_action;
11432         }
11433
11434         if (player->anim_delay_counter > 0)
11435         {
11436           player->action_waiting = player->special_action_sleeping;
11437           player->anim_delay_counter--;
11438         }
11439         else if (player->post_delay_counter > 0)
11440         {
11441           player->post_delay_counter--;
11442         }
11443       }
11444     }
11445     else if (player->is_bored)
11446     {
11447       if (player->num_special_action_bored > 0)
11448       {
11449         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11450         {
11451           int special_action =
11452             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11453           int special_graphic =
11454             el_act_dir2img(player->artwork_element, special_action, move_dir);
11455
11456           player->anim_delay_counter =
11457             graphic_info[special_graphic].anim_delay_fixed +
11458             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11459           player->post_delay_counter =
11460             graphic_info[special_graphic].post_delay_fixed +
11461             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11462
11463           player->special_action_bored = special_action;
11464         }
11465
11466         if (player->anim_delay_counter > 0)
11467         {
11468           player->action_waiting = player->special_action_bored;
11469           player->anim_delay_counter--;
11470         }
11471         else if (player->post_delay_counter > 0)
11472         {
11473           player->post_delay_counter--;
11474         }
11475       }
11476     }
11477   }
11478   else if (last_waiting)        // waiting -> not waiting
11479   {
11480     player->is_waiting = FALSE;
11481     player->is_bored = FALSE;
11482     player->is_sleeping = FALSE;
11483
11484     player->frame_counter_bored = -1;
11485     player->frame_counter_sleeping = -1;
11486
11487     player->anim_delay_counter = 0;
11488     player->post_delay_counter = 0;
11489
11490     player->dir_waiting = player->MovDir;
11491     player->action_waiting = ACTION_DEFAULT;
11492
11493     player->special_action_bored = ACTION_DEFAULT;
11494     player->special_action_sleeping = ACTION_DEFAULT;
11495   }
11496 }
11497
11498 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11499 {
11500   if ((!player->is_moving  && player->was_moving) ||
11501       (player->MovPos == 0 && player->was_moving) ||
11502       (player->is_snapping && !player->was_snapping) ||
11503       (player->is_dropping && !player->was_dropping))
11504   {
11505     if (!CheckSaveEngineSnapshotToList())
11506       return;
11507
11508     player->was_moving = FALSE;
11509     player->was_snapping = TRUE;
11510     player->was_dropping = TRUE;
11511   }
11512   else
11513   {
11514     if (player->is_moving)
11515       player->was_moving = TRUE;
11516
11517     if (!player->is_snapping)
11518       player->was_snapping = FALSE;
11519
11520     if (!player->is_dropping)
11521       player->was_dropping = FALSE;
11522   }
11523
11524   static struct MouseActionInfo mouse_action_last = { 0 };
11525   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11526   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11527
11528   if (new_released)
11529     CheckSaveEngineSnapshotToList();
11530
11531   mouse_action_last = mouse_action;
11532 }
11533
11534 static void CheckSingleStepMode(struct PlayerInfo *player)
11535 {
11536   if (tape.single_step && tape.recording && !tape.pausing)
11537   {
11538     // as it is called "single step mode", just return to pause mode when the
11539     // player stopped moving after one tile (or never starts moving at all)
11540     // (reverse logic needed here in case single step mode used in team mode)
11541     if (player->is_moving ||
11542         player->is_pushing ||
11543         player->is_dropping_pressed ||
11544         player->effective_mouse_action.button)
11545       game.enter_single_step_mode = FALSE;
11546   }
11547
11548   CheckSaveEngineSnapshot(player);
11549 }
11550
11551 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11552 {
11553   int left      = player_action & JOY_LEFT;
11554   int right     = player_action & JOY_RIGHT;
11555   int up        = player_action & JOY_UP;
11556   int down      = player_action & JOY_DOWN;
11557   int button1   = player_action & JOY_BUTTON_1;
11558   int button2   = player_action & JOY_BUTTON_2;
11559   int dx        = (left ? -1 : right ? 1 : 0);
11560   int dy        = (up   ? -1 : down  ? 1 : 0);
11561
11562   if (!player->active || tape.pausing)
11563     return 0;
11564
11565   if (player_action)
11566   {
11567     if (button1)
11568       SnapField(player, dx, dy);
11569     else
11570     {
11571       if (button2)
11572         DropElement(player);
11573
11574       MovePlayer(player, dx, dy);
11575     }
11576
11577     CheckSingleStepMode(player);
11578
11579     SetPlayerWaiting(player, FALSE);
11580
11581     return player_action;
11582   }
11583   else
11584   {
11585     // no actions for this player (no input at player's configured device)
11586
11587     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11588     SnapField(player, 0, 0);
11589     CheckGravityMovementWhenNotMoving(player);
11590
11591     if (player->MovPos == 0)
11592       SetPlayerWaiting(player, TRUE);
11593
11594     if (player->MovPos == 0)    // needed for tape.playing
11595       player->is_moving = FALSE;
11596
11597     player->is_dropping = FALSE;
11598     player->is_dropping_pressed = FALSE;
11599     player->drop_pressed_delay = 0;
11600
11601     CheckSingleStepMode(player);
11602
11603     return 0;
11604   }
11605 }
11606
11607 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11608                                          byte *tape_action)
11609 {
11610   if (!tape.use_mouse_actions)
11611     return;
11612
11613   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11614   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11615   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11616 }
11617
11618 static void SetTapeActionFromMouseAction(byte *tape_action,
11619                                          struct MouseActionInfo *mouse_action)
11620 {
11621   if (!tape.use_mouse_actions)
11622     return;
11623
11624   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11625   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11626   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11627 }
11628
11629 static void CheckLevelSolved(void)
11630 {
11631   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11632   {
11633     if (game_bd.level_solved &&
11634         !game_bd.game_over)                             // game won
11635     {
11636       LevelSolved();
11637
11638       game_bd.game_over = TRUE;
11639
11640       game.all_players_gone = TRUE;
11641     }
11642
11643     if (game_bd.game_over)                              // game lost
11644       game.all_players_gone = TRUE;
11645   }
11646   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11647   {
11648     if (game_em.level_solved &&
11649         !game_em.game_over)                             // game won
11650     {
11651       LevelSolved();
11652
11653       game_em.game_over = TRUE;
11654
11655       game.all_players_gone = TRUE;
11656     }
11657
11658     if (game_em.game_over)                              // game lost
11659       game.all_players_gone = TRUE;
11660   }
11661   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11662   {
11663     if (game_sp.level_solved &&
11664         !game_sp.game_over)                             // game won
11665     {
11666       LevelSolved();
11667
11668       game_sp.game_over = TRUE;
11669
11670       game.all_players_gone = TRUE;
11671     }
11672
11673     if (game_sp.game_over)                              // game lost
11674       game.all_players_gone = TRUE;
11675   }
11676   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11677   {
11678     if (game_mm.level_solved &&
11679         !game_mm.game_over)                             // game won
11680     {
11681       LevelSolved();
11682
11683       game_mm.game_over = TRUE;
11684
11685       game.all_players_gone = TRUE;
11686     }
11687
11688     if (game_mm.game_over)                              // game lost
11689       game.all_players_gone = TRUE;
11690   }
11691 }
11692
11693 static void PlayTimeoutSound(int seconds_left)
11694 {
11695   // try to use individual "running out of time" sound for each second left
11696   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11697
11698   // if special sound per second not defined, use default sound
11699   if (getSoundInfoEntryFilename(sound) == NULL)
11700     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11701
11702   // if out of time, but player still alive, play special "timeout" sound, if defined
11703   if (seconds_left == 0 && !checkGameFailed())
11704     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11705       sound = SND_GAME_TIMEOUT;
11706
11707   PlaySound(sound);
11708 }
11709
11710 static void CheckLevelTime_StepCounter(void)
11711 {
11712   int i;
11713
11714   TimePlayed++;
11715
11716   if (TimeLeft > 0)
11717   {
11718     TimeLeft--;
11719
11720     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11721       PlayTimeoutSound(TimeLeft);
11722
11723     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11724
11725     DisplayGameControlValues();
11726
11727     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11728       for (i = 0; i < MAX_PLAYERS; i++)
11729         KillPlayer(&stored_player[i]);
11730   }
11731   else if (game.no_level_time_limit && !game.all_players_gone)
11732   {
11733     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11734
11735     DisplayGameControlValues();
11736   }
11737 }
11738
11739 static void CheckLevelTime(void)
11740 {
11741   int i;
11742
11743   if (TimeFrames >= FRAMES_PER_SECOND)
11744   {
11745     TimeFrames = 0;
11746
11747     for (i = 0; i < MAX_PLAYERS; i++)
11748     {
11749       struct PlayerInfo *player = &stored_player[i];
11750
11751       if (SHIELD_ON(player))
11752       {
11753         player->shield_normal_time_left--;
11754
11755         if (player->shield_deadly_time_left > 0)
11756           player->shield_deadly_time_left--;
11757       }
11758     }
11759
11760     if (!game.LevelSolved && !level.use_step_counter)
11761     {
11762       TimePlayed++;
11763
11764       if (TimeLeft > 0)
11765       {
11766         TimeLeft--;
11767
11768         if (TimeLeft <= 10 && game.time_limit)
11769           PlayTimeoutSound(TimeLeft);
11770
11771         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11772            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11773
11774         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11775
11776         if (!TimeLeft && game.time_limit)
11777         {
11778           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11779             game_em.lev->killed_out_of_time = TRUE;
11780           else
11781             for (i = 0; i < MAX_PLAYERS; i++)
11782               KillPlayer(&stored_player[i]);
11783         }
11784       }
11785       else if (game.no_level_time_limit && !game.all_players_gone)
11786       {
11787         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11788       }
11789
11790       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11791     }
11792   }
11793
11794   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11795   {
11796     TapeTimeFrames = 0;
11797     TapeTime++;
11798
11799     if (tape.recording || tape.playing)
11800       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11801   }
11802
11803   if (tape.recording || tape.playing)
11804     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11805
11806   UpdateAndDisplayGameControlValues();
11807 }
11808
11809 void AdvanceFrameAndPlayerCounters(int player_nr)
11810 {
11811   int i;
11812
11813   // advance frame counters (global frame counter and tape time frame counter)
11814   FrameCounter++;
11815   TapeTimeFrames++;
11816
11817   // advance time frame counter (used to control available time to solve level)
11818   TimeFrames++;
11819
11820   // advance player counters (counters for move delay, move animation etc.)
11821   for (i = 0; i < MAX_PLAYERS; i++)
11822   {
11823     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11824     int move_delay_value = stored_player[i].move_delay_value;
11825     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11826
11827     if (!advance_player_counters)       // not all players may be affected
11828       continue;
11829
11830     if (move_frames == 0)       // less than one move per game frame
11831     {
11832       int stepsize = TILEX / move_delay_value;
11833       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11834       int count = (stored_player[i].is_moving ?
11835                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11836
11837       if (count % delay == 0)
11838         move_frames = 1;
11839     }
11840
11841     stored_player[i].Frame += move_frames;
11842
11843     if (stored_player[i].MovPos != 0)
11844       stored_player[i].StepFrame += move_frames;
11845
11846     if (stored_player[i].move_delay > 0)
11847       stored_player[i].move_delay--;
11848
11849     // due to bugs in previous versions, counter must count up, not down
11850     if (stored_player[i].push_delay != -1)
11851       stored_player[i].push_delay++;
11852
11853     if (stored_player[i].drop_delay > 0)
11854       stored_player[i].drop_delay--;
11855
11856     if (stored_player[i].is_dropping_pressed)
11857       stored_player[i].drop_pressed_delay++;
11858   }
11859 }
11860
11861 void AdvanceFrameCounter(void)
11862 {
11863   FrameCounter++;
11864 }
11865
11866 void AdvanceGfxFrame(void)
11867 {
11868   int x, y;
11869
11870   SCAN_PLAYFIELD(x, y)
11871   {
11872     GfxFrame[x][y]++;
11873   }
11874 }
11875
11876 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11877                               struct MouseActionInfo *mouse_action_last)
11878 {
11879   if (mouse_action->button)
11880   {
11881     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11882     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11883     int x = mouse_action->lx;
11884     int y = mouse_action->ly;
11885     int element = Tile[x][y];
11886
11887     if (new_button)
11888     {
11889       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11890       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11891                                          ch_button);
11892     }
11893
11894     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11895     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11896                                        ch_button);
11897
11898     if (level.use_step_counter)
11899     {
11900       boolean counted_click = FALSE;
11901
11902       // element clicked that can change when clicked/pressed
11903       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11904           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11905            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
11906         counted_click = TRUE;
11907
11908       // element clicked that can trigger change when clicked/pressed
11909       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
11910           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
11911         counted_click = TRUE;
11912
11913       if (new_button && counted_click)
11914         CheckLevelTime_StepCounter();
11915     }
11916   }
11917 }
11918
11919 void StartGameActions(boolean init_network_game, boolean record_tape,
11920                       int random_seed)
11921 {
11922   unsigned int new_random_seed = InitRND(random_seed);
11923
11924   if (record_tape)
11925     TapeStartRecording(new_random_seed);
11926
11927   if (setup.auto_pause_on_start && !tape.pausing)
11928     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11929
11930   if (init_network_game)
11931   {
11932     SendToServer_LevelFile();
11933     SendToServer_StartPlaying();
11934
11935     return;
11936   }
11937
11938   InitGame();
11939 }
11940
11941 static void GameActionsExt(void)
11942 {
11943 #if 0
11944   static unsigned int game_frame_delay = 0;
11945 #endif
11946   unsigned int game_frame_delay_value;
11947   byte *recorded_player_action;
11948   byte summarized_player_action = 0;
11949   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11950   int i;
11951
11952   // detect endless loops, caused by custom element programming
11953   if (recursion_loop_detected && recursion_loop_depth == 0)
11954   {
11955     char *message = getStringCat3("Internal Error! Element ",
11956                                   EL_NAME(recursion_loop_element),
11957                                   " caused endless loop! Quit the game?");
11958
11959     Warn("element '%s' caused endless loop in game engine",
11960          EL_NAME(recursion_loop_element));
11961
11962     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11963
11964     recursion_loop_detected = FALSE;    // if game should be continued
11965
11966     free(message);
11967
11968     return;
11969   }
11970
11971   if (game.restart_level)
11972     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11973
11974   CheckLevelSolved();
11975
11976   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11977     GameWon();
11978
11979   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11980     TapeStop();
11981
11982   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11983     return;
11984
11985   game_frame_delay_value =
11986     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11987
11988   if (tape.playing && tape.warp_forward && !tape.pausing)
11989     game_frame_delay_value = 0;
11990
11991   SetVideoFrameDelay(game_frame_delay_value);
11992
11993   // (de)activate virtual buttons depending on current game status
11994   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11995   {
11996     if (game.all_players_gone)  // if no players there to be controlled anymore
11997       SetOverlayActive(FALSE);
11998     else if (!tape.playing)     // if game continues after tape stopped playing
11999       SetOverlayActive(TRUE);
12000   }
12001
12002 #if 0
12003 #if 0
12004   // ---------- main game synchronization point ----------
12005
12006   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12007
12008   Debug("game:playing:skip", "skip == %d", skip);
12009
12010 #else
12011   // ---------- main game synchronization point ----------
12012
12013   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12014 #endif
12015 #endif
12016
12017   if (network_playing && !network_player_action_received)
12018   {
12019     // try to get network player actions in time
12020
12021     // last chance to get network player actions without main loop delay
12022     HandleNetworking();
12023
12024     // game was quit by network peer
12025     if (game_status != GAME_MODE_PLAYING)
12026       return;
12027
12028     // check if network player actions still missing and game still running
12029     if (!network_player_action_received && !checkGameEnded())
12030       return;           // failed to get network player actions in time
12031
12032     // do not yet reset "network_player_action_received" (for tape.pausing)
12033   }
12034
12035   if (tape.pausing)
12036     return;
12037
12038   // at this point we know that we really continue executing the game
12039
12040   network_player_action_received = FALSE;
12041
12042   // when playing tape, read previously recorded player input from tape data
12043   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12044
12045   local_player->effective_mouse_action = local_player->mouse_action;
12046
12047   if (recorded_player_action != NULL)
12048     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12049                                  recorded_player_action);
12050
12051   // TapePlayAction() may return NULL when toggling to "pause before death"
12052   if (tape.pausing)
12053     return;
12054
12055   if (tape.set_centered_player)
12056   {
12057     game.centered_player_nr_next = tape.centered_player_nr_next;
12058     game.set_centered_player = TRUE;
12059   }
12060
12061   for (i = 0; i < MAX_PLAYERS; i++)
12062   {
12063     summarized_player_action |= stored_player[i].action;
12064
12065     if (!network_playing && (game.team_mode || tape.playing))
12066       stored_player[i].effective_action = stored_player[i].action;
12067   }
12068
12069   if (network_playing && !checkGameEnded())
12070     SendToServer_MovePlayer(summarized_player_action);
12071
12072   // summarize all actions at local players mapped input device position
12073   // (this allows using different input devices in single player mode)
12074   if (!network.enabled && !game.team_mode)
12075     stored_player[map_player_action[local_player->index_nr]].effective_action =
12076       summarized_player_action;
12077
12078   // summarize all actions at centered player in local team mode
12079   if (tape.recording &&
12080       setup.team_mode && !network.enabled &&
12081       setup.input_on_focus &&
12082       game.centered_player_nr != -1)
12083   {
12084     for (i = 0; i < MAX_PLAYERS; i++)
12085       stored_player[map_player_action[i]].effective_action =
12086         (i == game.centered_player_nr ? summarized_player_action : 0);
12087   }
12088
12089   if (recorded_player_action != NULL)
12090     for (i = 0; i < MAX_PLAYERS; i++)
12091       stored_player[i].effective_action = recorded_player_action[i];
12092
12093   for (i = 0; i < MAX_PLAYERS; i++)
12094   {
12095     tape_action[i] = stored_player[i].effective_action;
12096
12097     /* (this may happen in the RND game engine if a player was not present on
12098        the playfield on level start, but appeared later from a custom element */
12099     if (setup.team_mode &&
12100         tape.recording &&
12101         tape_action[i] &&
12102         !tape.player_participates[i])
12103       tape.player_participates[i] = TRUE;
12104   }
12105
12106   SetTapeActionFromMouseAction(tape_action,
12107                                &local_player->effective_mouse_action);
12108
12109   // only record actions from input devices, but not programmed actions
12110   if (tape.recording)
12111     TapeRecordAction(tape_action);
12112
12113   // remember if game was played (especially after tape stopped playing)
12114   if (!tape.playing && summarized_player_action && !checkGameFailed())
12115     game.GamePlayed = TRUE;
12116
12117 #if USE_NEW_PLAYER_ASSIGNMENTS
12118   // !!! also map player actions in single player mode !!!
12119   // if (game.team_mode)
12120   if (1)
12121   {
12122     byte mapped_action[MAX_PLAYERS];
12123
12124 #if DEBUG_PLAYER_ACTIONS
12125     for (i = 0; i < MAX_PLAYERS; i++)
12126       DebugContinued("", "%d, ", stored_player[i].effective_action);
12127 #endif
12128
12129     for (i = 0; i < MAX_PLAYERS; i++)
12130       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12131
12132     for (i = 0; i < MAX_PLAYERS; i++)
12133       stored_player[i].effective_action = mapped_action[i];
12134
12135 #if DEBUG_PLAYER_ACTIONS
12136     DebugContinued("", "=> ");
12137     for (i = 0; i < MAX_PLAYERS; i++)
12138       DebugContinued("", "%d, ", stored_player[i].effective_action);
12139     DebugContinued("game:playing:player", "\n");
12140 #endif
12141   }
12142 #if DEBUG_PLAYER_ACTIONS
12143   else
12144   {
12145     for (i = 0; i < MAX_PLAYERS; i++)
12146       DebugContinued("", "%d, ", stored_player[i].effective_action);
12147     DebugContinued("game:playing:player", "\n");
12148   }
12149 #endif
12150 #endif
12151
12152   for (i = 0; i < MAX_PLAYERS; i++)
12153   {
12154     // allow engine snapshot in case of changed movement attempt
12155     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12156         (stored_player[i].effective_action & KEY_MOTION))
12157       game.snapshot.changed_action = TRUE;
12158
12159     // allow engine snapshot in case of snapping/dropping attempt
12160     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12161         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12162       game.snapshot.changed_action = TRUE;
12163
12164     game.snapshot.last_action[i] = stored_player[i].effective_action;
12165   }
12166
12167   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12168   {
12169     GameActions_EM_Main();
12170   }
12171   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12172   {
12173     GameActions_SP_Main();
12174   }
12175   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12176   {
12177     GameActions_MM_Main();
12178   }
12179   else
12180   {
12181     GameActions_RND_Main();
12182   }
12183
12184   BlitScreenToBitmap(backbuffer);
12185
12186   CheckLevelSolved();
12187   CheckLevelTime();
12188
12189   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12190
12191   if (global.show_frames_per_second)
12192   {
12193     static unsigned int fps_counter = 0;
12194     static int fps_frames = 0;
12195     unsigned int fps_delay_ms = Counter() - fps_counter;
12196
12197     fps_frames++;
12198
12199     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12200     {
12201       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12202
12203       fps_frames = 0;
12204       fps_counter = Counter();
12205
12206       // always draw FPS to screen after FPS value was updated
12207       redraw_mask |= REDRAW_FPS;
12208     }
12209
12210     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12211     if (GetDrawDeactivationMask() == REDRAW_NONE)
12212       redraw_mask |= REDRAW_FPS;
12213   }
12214 }
12215
12216 static void GameActions_CheckSaveEngineSnapshot(void)
12217 {
12218   if (!game.snapshot.save_snapshot)
12219     return;
12220
12221   // clear flag for saving snapshot _before_ saving snapshot
12222   game.snapshot.save_snapshot = FALSE;
12223
12224   SaveEngineSnapshotToList();
12225 }
12226
12227 void GameActions(void)
12228 {
12229   GameActionsExt();
12230
12231   GameActions_CheckSaveEngineSnapshot();
12232 }
12233
12234 void GameActions_EM_Main(void)
12235 {
12236   byte effective_action[MAX_PLAYERS];
12237   int i;
12238
12239   for (i = 0; i < MAX_PLAYERS; i++)
12240     effective_action[i] = stored_player[i].effective_action;
12241
12242   GameActions_EM(effective_action);
12243 }
12244
12245 void GameActions_SP_Main(void)
12246 {
12247   byte effective_action[MAX_PLAYERS];
12248   int i;
12249
12250   for (i = 0; i < MAX_PLAYERS; i++)
12251     effective_action[i] = stored_player[i].effective_action;
12252
12253   GameActions_SP(effective_action);
12254
12255   for (i = 0; i < MAX_PLAYERS; i++)
12256   {
12257     if (stored_player[i].force_dropping)
12258       stored_player[i].action |= KEY_BUTTON_DROP;
12259
12260     stored_player[i].force_dropping = FALSE;
12261   }
12262 }
12263
12264 void GameActions_MM_Main(void)
12265 {
12266   AdvanceGfxFrame();
12267
12268   GameActions_MM(local_player->effective_mouse_action);
12269 }
12270
12271 void GameActions_RND_Main(void)
12272 {
12273   GameActions_RND();
12274 }
12275
12276 void GameActions_RND(void)
12277 {
12278   static struct MouseActionInfo mouse_action_last = { 0 };
12279   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12280   int magic_wall_x = 0, magic_wall_y = 0;
12281   int i, x, y, element, graphic, last_gfx_frame;
12282
12283   InitPlayfieldScanModeVars();
12284
12285   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12286   {
12287     SCAN_PLAYFIELD(x, y)
12288     {
12289       ChangeCount[x][y] = 0;
12290       ChangeEvent[x][y] = -1;
12291     }
12292   }
12293
12294   if (game.set_centered_player)
12295   {
12296     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12297
12298     // switching to "all players" only possible if all players fit to screen
12299     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12300     {
12301       game.centered_player_nr_next = game.centered_player_nr;
12302       game.set_centered_player = FALSE;
12303     }
12304
12305     // do not switch focus to non-existing (or non-active) player
12306     if (game.centered_player_nr_next >= 0 &&
12307         !stored_player[game.centered_player_nr_next].active)
12308     {
12309       game.centered_player_nr_next = game.centered_player_nr;
12310       game.set_centered_player = FALSE;
12311     }
12312   }
12313
12314   if (game.set_centered_player &&
12315       ScreenMovPos == 0)        // screen currently aligned at tile position
12316   {
12317     int sx, sy;
12318
12319     if (game.centered_player_nr_next == -1)
12320     {
12321       setScreenCenteredToAllPlayers(&sx, &sy);
12322     }
12323     else
12324     {
12325       sx = stored_player[game.centered_player_nr_next].jx;
12326       sy = stored_player[game.centered_player_nr_next].jy;
12327     }
12328
12329     game.centered_player_nr = game.centered_player_nr_next;
12330     game.set_centered_player = FALSE;
12331
12332     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12333     DrawGameDoorValues();
12334   }
12335
12336   // check single step mode (set flag and clear again if any player is active)
12337   game.enter_single_step_mode =
12338     (tape.single_step && tape.recording && !tape.pausing);
12339
12340   for (i = 0; i < MAX_PLAYERS; i++)
12341   {
12342     int actual_player_action = stored_player[i].effective_action;
12343
12344 #if 1
12345     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12346        - rnd_equinox_tetrachloride 048
12347        - rnd_equinox_tetrachloride_ii 096
12348        - rnd_emanuel_schmieg 002
12349        - doctor_sloan_ww 001, 020
12350     */
12351     if (stored_player[i].MovPos == 0)
12352       CheckGravityMovement(&stored_player[i]);
12353 #endif
12354
12355     // overwrite programmed action with tape action
12356     if (stored_player[i].programmed_action)
12357       actual_player_action = stored_player[i].programmed_action;
12358
12359     PlayerActions(&stored_player[i], actual_player_action);
12360
12361     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12362   }
12363
12364   // single step pause mode may already have been toggled by "ScrollPlayer()"
12365   if (game.enter_single_step_mode && !tape.pausing)
12366     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12367
12368   ScrollScreen(NULL, SCROLL_GO_ON);
12369
12370   /* for backwards compatibility, the following code emulates a fixed bug that
12371      occured when pushing elements (causing elements that just made their last
12372      pushing step to already (if possible) make their first falling step in the
12373      same game frame, which is bad); this code is also needed to use the famous
12374      "spring push bug" which is used in older levels and might be wanted to be
12375      used also in newer levels, but in this case the buggy pushing code is only
12376      affecting the "spring" element and no other elements */
12377
12378   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12379   {
12380     for (i = 0; i < MAX_PLAYERS; i++)
12381     {
12382       struct PlayerInfo *player = &stored_player[i];
12383       int x = player->jx;
12384       int y = player->jy;
12385
12386       if (player->active && player->is_pushing && player->is_moving &&
12387           IS_MOVING(x, y) &&
12388           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12389            Tile[x][y] == EL_SPRING))
12390       {
12391         ContinueMoving(x, y);
12392
12393         // continue moving after pushing (this is actually a bug)
12394         if (!IS_MOVING(x, y))
12395           Stop[x][y] = FALSE;
12396       }
12397     }
12398   }
12399
12400   SCAN_PLAYFIELD(x, y)
12401   {
12402     Last[x][y] = Tile[x][y];
12403
12404     ChangeCount[x][y] = 0;
12405     ChangeEvent[x][y] = -1;
12406
12407     // this must be handled before main playfield loop
12408     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12409     {
12410       MovDelay[x][y]--;
12411       if (MovDelay[x][y] <= 0)
12412         RemoveField(x, y);
12413     }
12414
12415     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12416     {
12417       MovDelay[x][y]--;
12418       if (MovDelay[x][y] <= 0)
12419       {
12420         int element = Store[x][y];
12421         int move_direction = MovDir[x][y];
12422         int player_index_bit = Store2[x][y];
12423
12424         Store[x][y] = 0;
12425         Store2[x][y] = 0;
12426
12427         RemoveField(x, y);
12428         TEST_DrawLevelField(x, y);
12429
12430         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12431
12432         if (IS_ENVELOPE(element))
12433           local_player->show_envelope = element;
12434       }
12435     }
12436
12437 #if DEBUG
12438     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12439     {
12440       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12441             x, y);
12442       Debug("game:playing:GameActions_RND", "This should never happen!");
12443
12444       ChangePage[x][y] = -1;
12445     }
12446 #endif
12447
12448     Stop[x][y] = FALSE;
12449     if (WasJustMoving[x][y] > 0)
12450       WasJustMoving[x][y]--;
12451     if (WasJustFalling[x][y] > 0)
12452       WasJustFalling[x][y]--;
12453     if (CheckCollision[x][y] > 0)
12454       CheckCollision[x][y]--;
12455     if (CheckImpact[x][y] > 0)
12456       CheckImpact[x][y]--;
12457
12458     GfxFrame[x][y]++;
12459
12460     /* reset finished pushing action (not done in ContinueMoving() to allow
12461        continuous pushing animation for elements with zero push delay) */
12462     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12463     {
12464       ResetGfxAnimation(x, y);
12465       TEST_DrawLevelField(x, y);
12466     }
12467
12468 #if DEBUG
12469     if (IS_BLOCKED(x, y))
12470     {
12471       int oldx, oldy;
12472
12473       Blocked2Moving(x, y, &oldx, &oldy);
12474       if (!IS_MOVING(oldx, oldy))
12475       {
12476         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12477         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12478         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12479         Debug("game:playing:GameActions_RND", "This should never happen!");
12480       }
12481     }
12482 #endif
12483   }
12484
12485   HandleMouseAction(&mouse_action, &mouse_action_last);
12486
12487   SCAN_PLAYFIELD(x, y)
12488   {
12489     element = Tile[x][y];
12490     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12491     last_gfx_frame = GfxFrame[x][y];
12492
12493     if (element == EL_EMPTY)
12494       graphic = el2img(GfxElementEmpty[x][y]);
12495
12496     ResetGfxFrame(x, y);
12497
12498     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12499       DrawLevelGraphicAnimation(x, y, graphic);
12500
12501     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12502         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12503       ResetRandomAnimationValue(x, y);
12504
12505     SetRandomAnimationValue(x, y);
12506
12507     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12508
12509     if (IS_INACTIVE(element))
12510     {
12511       if (IS_ANIMATED(graphic))
12512         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12513
12514       continue;
12515     }
12516
12517     // this may take place after moving, so 'element' may have changed
12518     if (IS_CHANGING(x, y) &&
12519         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12520     {
12521       int page = element_info[element].event_page_nr[CE_DELAY];
12522
12523       HandleElementChange(x, y, page);
12524
12525       element = Tile[x][y];
12526       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12527     }
12528
12529     CheckNextToConditions(x, y);
12530
12531     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12532     {
12533       StartMoving(x, y);
12534
12535       element = Tile[x][y];
12536       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12537
12538       if (IS_ANIMATED(graphic) &&
12539           !IS_MOVING(x, y) &&
12540           !Stop[x][y])
12541         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12542
12543       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12544         TEST_DrawTwinkleOnField(x, y);
12545     }
12546     else if (element == EL_ACID)
12547     {
12548       if (!Stop[x][y])
12549         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12550     }
12551     else if ((element == EL_EXIT_OPEN ||
12552               element == EL_EM_EXIT_OPEN ||
12553               element == EL_SP_EXIT_OPEN ||
12554               element == EL_STEEL_EXIT_OPEN ||
12555               element == EL_EM_STEEL_EXIT_OPEN ||
12556               element == EL_SP_TERMINAL ||
12557               element == EL_SP_TERMINAL_ACTIVE ||
12558               element == EL_EXTRA_TIME ||
12559               element == EL_SHIELD_NORMAL ||
12560               element == EL_SHIELD_DEADLY) &&
12561              IS_ANIMATED(graphic))
12562       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12563     else if (IS_MOVING(x, y))
12564       ContinueMoving(x, y);
12565     else if (IS_ACTIVE_BOMB(element))
12566       CheckDynamite(x, y);
12567     else if (element == EL_AMOEBA_GROWING)
12568       AmoebaGrowing(x, y);
12569     else if (element == EL_AMOEBA_SHRINKING)
12570       AmoebaShrinking(x, y);
12571
12572 #if !USE_NEW_AMOEBA_CODE
12573     else if (IS_AMOEBALIVE(element))
12574       AmoebaReproduce(x, y);
12575 #endif
12576
12577     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12578       Life(x, y);
12579     else if (element == EL_EXIT_CLOSED)
12580       CheckExit(x, y);
12581     else if (element == EL_EM_EXIT_CLOSED)
12582       CheckExitEM(x, y);
12583     else if (element == EL_STEEL_EXIT_CLOSED)
12584       CheckExitSteel(x, y);
12585     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12586       CheckExitSteelEM(x, y);
12587     else if (element == EL_SP_EXIT_CLOSED)
12588       CheckExitSP(x, y);
12589     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12590              element == EL_EXPANDABLE_STEELWALL_GROWING)
12591       WallGrowing(x, y);
12592     else if (element == EL_EXPANDABLE_WALL ||
12593              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12594              element == EL_EXPANDABLE_WALL_VERTICAL ||
12595              element == EL_EXPANDABLE_WALL_ANY ||
12596              element == EL_BD_EXPANDABLE_WALL ||
12597              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12598              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12599              element == EL_EXPANDABLE_STEELWALL_ANY)
12600       CheckWallGrowing(x, y);
12601     else if (element == EL_FLAMES)
12602       CheckForDragon(x, y);
12603     else if (element == EL_EXPLOSION)
12604       ; // drawing of correct explosion animation is handled separately
12605     else if (element == EL_ELEMENT_SNAPPING ||
12606              element == EL_DIAGONAL_SHRINKING ||
12607              element == EL_DIAGONAL_GROWING)
12608     {
12609       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12610
12611       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12612     }
12613     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12614       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12615
12616     if (IS_BELT_ACTIVE(element))
12617       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12618
12619     if (game.magic_wall_active)
12620     {
12621       int jx = local_player->jx, jy = local_player->jy;
12622
12623       // play the element sound at the position nearest to the player
12624       if ((element == EL_MAGIC_WALL_FULL ||
12625            element == EL_MAGIC_WALL_ACTIVE ||
12626            element == EL_MAGIC_WALL_EMPTYING ||
12627            element == EL_BD_MAGIC_WALL_FULL ||
12628            element == EL_BD_MAGIC_WALL_ACTIVE ||
12629            element == EL_BD_MAGIC_WALL_EMPTYING ||
12630            element == EL_DC_MAGIC_WALL_FULL ||
12631            element == EL_DC_MAGIC_WALL_ACTIVE ||
12632            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12633           ABS(x - jx) + ABS(y - jy) <
12634           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12635       {
12636         magic_wall_x = x;
12637         magic_wall_y = y;
12638       }
12639     }
12640   }
12641
12642 #if USE_NEW_AMOEBA_CODE
12643   // new experimental amoeba growth stuff
12644   if (!(FrameCounter % 8))
12645   {
12646     static unsigned int random = 1684108901;
12647
12648     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12649     {
12650       x = RND(lev_fieldx);
12651       y = RND(lev_fieldy);
12652       element = Tile[x][y];
12653
12654       if (!IS_PLAYER(x, y) &&
12655           (element == EL_EMPTY ||
12656            CAN_GROW_INTO(element) ||
12657            element == EL_QUICKSAND_EMPTY ||
12658            element == EL_QUICKSAND_FAST_EMPTY ||
12659            element == EL_ACID_SPLASH_LEFT ||
12660            element == EL_ACID_SPLASH_RIGHT))
12661       {
12662         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12663             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12664             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12665             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12666           Tile[x][y] = EL_AMOEBA_DROP;
12667       }
12668
12669       random = random * 129 + 1;
12670     }
12671   }
12672 #endif
12673
12674   game.explosions_delayed = FALSE;
12675
12676   SCAN_PLAYFIELD(x, y)
12677   {
12678     element = Tile[x][y];
12679
12680     if (ExplodeField[x][y])
12681       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12682     else if (element == EL_EXPLOSION)
12683       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12684
12685     ExplodeField[x][y] = EX_TYPE_NONE;
12686   }
12687
12688   game.explosions_delayed = TRUE;
12689
12690   if (game.magic_wall_active)
12691   {
12692     if (!(game.magic_wall_time_left % 4))
12693     {
12694       int element = Tile[magic_wall_x][magic_wall_y];
12695
12696       if (element == EL_BD_MAGIC_WALL_FULL ||
12697           element == EL_BD_MAGIC_WALL_ACTIVE ||
12698           element == EL_BD_MAGIC_WALL_EMPTYING)
12699         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12700       else if (element == EL_DC_MAGIC_WALL_FULL ||
12701                element == EL_DC_MAGIC_WALL_ACTIVE ||
12702                element == EL_DC_MAGIC_WALL_EMPTYING)
12703         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12704       else
12705         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12706     }
12707
12708     if (game.magic_wall_time_left > 0)
12709     {
12710       game.magic_wall_time_left--;
12711
12712       if (!game.magic_wall_time_left)
12713       {
12714         SCAN_PLAYFIELD(x, y)
12715         {
12716           element = Tile[x][y];
12717
12718           if (element == EL_MAGIC_WALL_ACTIVE ||
12719               element == EL_MAGIC_WALL_FULL)
12720           {
12721             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12722             TEST_DrawLevelField(x, y);
12723           }
12724           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12725                    element == EL_BD_MAGIC_WALL_FULL)
12726           {
12727             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12728             TEST_DrawLevelField(x, y);
12729           }
12730           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12731                    element == EL_DC_MAGIC_WALL_FULL)
12732           {
12733             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12734             TEST_DrawLevelField(x, y);
12735           }
12736         }
12737
12738         game.magic_wall_active = FALSE;
12739       }
12740     }
12741   }
12742
12743   if (game.light_time_left > 0)
12744   {
12745     game.light_time_left--;
12746
12747     if (game.light_time_left == 0)
12748       RedrawAllLightSwitchesAndInvisibleElements();
12749   }
12750
12751   if (game.timegate_time_left > 0)
12752   {
12753     game.timegate_time_left--;
12754
12755     if (game.timegate_time_left == 0)
12756       CloseAllOpenTimegates();
12757   }
12758
12759   if (game.lenses_time_left > 0)
12760   {
12761     game.lenses_time_left--;
12762
12763     if (game.lenses_time_left == 0)
12764       RedrawAllInvisibleElementsForLenses();
12765   }
12766
12767   if (game.magnify_time_left > 0)
12768   {
12769     game.magnify_time_left--;
12770
12771     if (game.magnify_time_left == 0)
12772       RedrawAllInvisibleElementsForMagnifier();
12773   }
12774
12775   for (i = 0; i < MAX_PLAYERS; i++)
12776   {
12777     struct PlayerInfo *player = &stored_player[i];
12778
12779     if (SHIELD_ON(player))
12780     {
12781       if (player->shield_deadly_time_left)
12782         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12783       else if (player->shield_normal_time_left)
12784         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12785     }
12786   }
12787
12788 #if USE_DELAYED_GFX_REDRAW
12789   SCAN_PLAYFIELD(x, y)
12790   {
12791     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12792     {
12793       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12794          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12795
12796       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12797         DrawLevelField(x, y);
12798
12799       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12800         DrawLevelFieldCrumbled(x, y);
12801
12802       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12803         DrawLevelFieldCrumbledNeighbours(x, y);
12804
12805       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12806         DrawTwinkleOnField(x, y);
12807     }
12808
12809     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12810   }
12811 #endif
12812
12813   DrawAllPlayers();
12814   PlayAllPlayersSound();
12815
12816   for (i = 0; i < MAX_PLAYERS; i++)
12817   {
12818     struct PlayerInfo *player = &stored_player[i];
12819
12820     if (player->show_envelope != 0 && (!player->active ||
12821                                        player->MovPos == 0))
12822     {
12823       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12824
12825       player->show_envelope = 0;
12826     }
12827   }
12828
12829   // use random number generator in every frame to make it less predictable
12830   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12831     RND(1);
12832
12833   mouse_action_last = mouse_action;
12834 }
12835
12836 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12837 {
12838   int min_x = x, min_y = y, max_x = x, max_y = y;
12839   int scr_fieldx = getScreenFieldSizeX();
12840   int scr_fieldy = getScreenFieldSizeY();
12841   int i;
12842
12843   for (i = 0; i < MAX_PLAYERS; i++)
12844   {
12845     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12846
12847     if (!stored_player[i].active || &stored_player[i] == player)
12848       continue;
12849
12850     min_x = MIN(min_x, jx);
12851     min_y = MIN(min_y, jy);
12852     max_x = MAX(max_x, jx);
12853     max_y = MAX(max_y, jy);
12854   }
12855
12856   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12857 }
12858
12859 static boolean AllPlayersInVisibleScreen(void)
12860 {
12861   int i;
12862
12863   for (i = 0; i < MAX_PLAYERS; i++)
12864   {
12865     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12866
12867     if (!stored_player[i].active)
12868       continue;
12869
12870     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12871       return FALSE;
12872   }
12873
12874   return TRUE;
12875 }
12876
12877 void ScrollLevel(int dx, int dy)
12878 {
12879   int scroll_offset = 2 * TILEX_VAR;
12880   int x, y;
12881
12882   BlitBitmap(drawto_field, drawto_field,
12883              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12884              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12885              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12886              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12887              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12888              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12889
12890   if (dx != 0)
12891   {
12892     x = (dx == 1 ? BX1 : BX2);
12893     for (y = BY1; y <= BY2; y++)
12894       DrawScreenField(x, y);
12895   }
12896
12897   if (dy != 0)
12898   {
12899     y = (dy == 1 ? BY1 : BY2);
12900     for (x = BX1; x <= BX2; x++)
12901       DrawScreenField(x, y);
12902   }
12903
12904   redraw_mask |= REDRAW_FIELD;
12905 }
12906
12907 static boolean canFallDown(struct PlayerInfo *player)
12908 {
12909   int jx = player->jx, jy = player->jy;
12910
12911   return (IN_LEV_FIELD(jx, jy + 1) &&
12912           (IS_FREE(jx, jy + 1) ||
12913            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12914           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12915           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12916 }
12917
12918 static boolean canPassField(int x, int y, int move_dir)
12919 {
12920   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12921   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12922   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12923   int nextx = x + dx;
12924   int nexty = y + dy;
12925   int element = Tile[x][y];
12926
12927   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12928           !CAN_MOVE(element) &&
12929           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12930           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12931           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12932 }
12933
12934 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12935 {
12936   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12937   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12938   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12939   int newx = x + dx;
12940   int newy = y + dy;
12941
12942   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12943           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12944           (IS_DIGGABLE(Tile[newx][newy]) ||
12945            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12946            canPassField(newx, newy, move_dir)));
12947 }
12948
12949 static void CheckGravityMovement(struct PlayerInfo *player)
12950 {
12951   if (player->gravity && !player->programmed_action)
12952   {
12953     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12954     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12955     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12956     int jx = player->jx, jy = player->jy;
12957     boolean player_is_moving_to_valid_field =
12958       (!player_is_snapping &&
12959        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12960         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12961     boolean player_can_fall_down = canFallDown(player);
12962
12963     if (player_can_fall_down &&
12964         !player_is_moving_to_valid_field)
12965       player->programmed_action = MV_DOWN;
12966   }
12967 }
12968
12969 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12970 {
12971   return CheckGravityMovement(player);
12972
12973   if (player->gravity && !player->programmed_action)
12974   {
12975     int jx = player->jx, jy = player->jy;
12976     boolean field_under_player_is_free =
12977       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12978     boolean player_is_standing_on_valid_field =
12979       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12980        (IS_WALKABLE(Tile[jx][jy]) &&
12981         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12982
12983     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12984       player->programmed_action = MV_DOWN;
12985   }
12986 }
12987
12988 /*
12989   MovePlayerOneStep()
12990   -----------------------------------------------------------------------------
12991   dx, dy:               direction (non-diagonal) to try to move the player to
12992   real_dx, real_dy:     direction as read from input device (can be diagonal)
12993 */
12994
12995 boolean MovePlayerOneStep(struct PlayerInfo *player,
12996                           int dx, int dy, int real_dx, int real_dy)
12997 {
12998   int jx = player->jx, jy = player->jy;
12999   int new_jx = jx + dx, new_jy = jy + dy;
13000   int can_move;
13001   boolean player_can_move = !player->cannot_move;
13002
13003   if (!player->active || (!dx && !dy))
13004     return MP_NO_ACTION;
13005
13006   player->MovDir = (dx < 0 ? MV_LEFT :
13007                     dx > 0 ? MV_RIGHT :
13008                     dy < 0 ? MV_UP :
13009                     dy > 0 ? MV_DOWN :  MV_NONE);
13010
13011   if (!IN_LEV_FIELD(new_jx, new_jy))
13012     return MP_NO_ACTION;
13013
13014   if (!player_can_move)
13015   {
13016     if (player->MovPos == 0)
13017     {
13018       player->is_moving = FALSE;
13019       player->is_digging = FALSE;
13020       player->is_collecting = FALSE;
13021       player->is_snapping = FALSE;
13022       player->is_pushing = FALSE;
13023     }
13024   }
13025
13026   if (!network.enabled && game.centered_player_nr == -1 &&
13027       !AllPlayersInSight(player, new_jx, new_jy))
13028     return MP_NO_ACTION;
13029
13030   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13031   if (can_move != MP_MOVING)
13032     return can_move;
13033
13034   // check if DigField() has caused relocation of the player
13035   if (player->jx != jx || player->jy != jy)
13036     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13037
13038   StorePlayer[jx][jy] = 0;
13039   player->last_jx = jx;
13040   player->last_jy = jy;
13041   player->jx = new_jx;
13042   player->jy = new_jy;
13043   StorePlayer[new_jx][new_jy] = player->element_nr;
13044
13045   if (player->move_delay_value_next != -1)
13046   {
13047     player->move_delay_value = player->move_delay_value_next;
13048     player->move_delay_value_next = -1;
13049   }
13050
13051   player->MovPos =
13052     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13053
13054   player->step_counter++;
13055
13056   PlayerVisit[jx][jy] = FrameCounter;
13057
13058   player->is_moving = TRUE;
13059
13060 #if 1
13061   // should better be called in MovePlayer(), but this breaks some tapes
13062   ScrollPlayer(player, SCROLL_INIT);
13063 #endif
13064
13065   return MP_MOVING;
13066 }
13067
13068 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13069 {
13070   int jx = player->jx, jy = player->jy;
13071   int old_jx = jx, old_jy = jy;
13072   int moved = MP_NO_ACTION;
13073
13074   if (!player->active)
13075     return FALSE;
13076
13077   if (!dx && !dy)
13078   {
13079     if (player->MovPos == 0)
13080     {
13081       player->is_moving = FALSE;
13082       player->is_digging = FALSE;
13083       player->is_collecting = FALSE;
13084       player->is_snapping = FALSE;
13085       player->is_pushing = FALSE;
13086     }
13087
13088     return FALSE;
13089   }
13090
13091   if (player->move_delay > 0)
13092     return FALSE;
13093
13094   player->move_delay = -1;              // set to "uninitialized" value
13095
13096   // store if player is automatically moved to next field
13097   player->is_auto_moving = (player->programmed_action != MV_NONE);
13098
13099   // remove the last programmed player action
13100   player->programmed_action = 0;
13101
13102   if (player->MovPos)
13103   {
13104     // should only happen if pre-1.2 tape recordings are played
13105     // this is only for backward compatibility
13106
13107     int original_move_delay_value = player->move_delay_value;
13108
13109 #if DEBUG
13110     Debug("game:playing:MovePlayer",
13111           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13112           tape.counter);
13113 #endif
13114
13115     // scroll remaining steps with finest movement resolution
13116     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13117
13118     while (player->MovPos)
13119     {
13120       ScrollPlayer(player, SCROLL_GO_ON);
13121       ScrollScreen(NULL, SCROLL_GO_ON);
13122
13123       AdvanceFrameAndPlayerCounters(player->index_nr);
13124
13125       DrawAllPlayers();
13126       BackToFront_WithFrameDelay(0);
13127     }
13128
13129     player->move_delay_value = original_move_delay_value;
13130   }
13131
13132   player->is_active = FALSE;
13133
13134   if (player->last_move_dir & MV_HORIZONTAL)
13135   {
13136     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13137       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13138   }
13139   else
13140   {
13141     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13142       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13143   }
13144
13145   if (!moved && !player->is_active)
13146   {
13147     player->is_moving = FALSE;
13148     player->is_digging = FALSE;
13149     player->is_collecting = FALSE;
13150     player->is_snapping = FALSE;
13151     player->is_pushing = FALSE;
13152   }
13153
13154   jx = player->jx;
13155   jy = player->jy;
13156
13157   if (moved & MP_MOVING && !ScreenMovPos &&
13158       (player->index_nr == game.centered_player_nr ||
13159        game.centered_player_nr == -1))
13160   {
13161     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13162
13163     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13164     {
13165       // actual player has left the screen -- scroll in that direction
13166       if (jx != old_jx)         // player has moved horizontally
13167         scroll_x += (jx - old_jx);
13168       else                      // player has moved vertically
13169         scroll_y += (jy - old_jy);
13170     }
13171     else
13172     {
13173       int offset_raw = game.scroll_delay_value;
13174
13175       if (jx != old_jx)         // player has moved horizontally
13176       {
13177         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13178         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13179         int new_scroll_x = jx - MIDPOSX + offset_x;
13180
13181         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13182             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13183           scroll_x = new_scroll_x;
13184
13185         // don't scroll over playfield boundaries
13186         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13187
13188         // don't scroll more than one field at a time
13189         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13190
13191         // don't scroll against the player's moving direction
13192         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13193             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13194           scroll_x = old_scroll_x;
13195       }
13196       else                      // player has moved vertically
13197       {
13198         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13199         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13200         int new_scroll_y = jy - MIDPOSY + offset_y;
13201
13202         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13203             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13204           scroll_y = new_scroll_y;
13205
13206         // don't scroll over playfield boundaries
13207         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13208
13209         // don't scroll more than one field at a time
13210         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13211
13212         // don't scroll against the player's moving direction
13213         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13214             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13215           scroll_y = old_scroll_y;
13216       }
13217     }
13218
13219     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13220     {
13221       if (!network.enabled && game.centered_player_nr == -1 &&
13222           !AllPlayersInVisibleScreen())
13223       {
13224         scroll_x = old_scroll_x;
13225         scroll_y = old_scroll_y;
13226       }
13227       else
13228       {
13229         ScrollScreen(player, SCROLL_INIT);
13230         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13231       }
13232     }
13233   }
13234
13235   player->StepFrame = 0;
13236
13237   if (moved & MP_MOVING)
13238   {
13239     if (old_jx != jx && old_jy == jy)
13240       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13241     else if (old_jx == jx && old_jy != jy)
13242       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13243
13244     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13245
13246     player->last_move_dir = player->MovDir;
13247     player->is_moving = TRUE;
13248     player->is_snapping = FALSE;
13249     player->is_switching = FALSE;
13250     player->is_dropping = FALSE;
13251     player->is_dropping_pressed = FALSE;
13252     player->drop_pressed_delay = 0;
13253
13254 #if 0
13255     // should better be called here than above, but this breaks some tapes
13256     ScrollPlayer(player, SCROLL_INIT);
13257 #endif
13258   }
13259   else
13260   {
13261     CheckGravityMovementWhenNotMoving(player);
13262
13263     player->is_moving = FALSE;
13264
13265     /* at this point, the player is allowed to move, but cannot move right now
13266        (e.g. because of something blocking the way) -- ensure that the player
13267        is also allowed to move in the next frame (in old versions before 3.1.1,
13268        the player was forced to wait again for eight frames before next try) */
13269
13270     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13271       player->move_delay = 0;   // allow direct movement in the next frame
13272   }
13273
13274   if (player->move_delay == -1)         // not yet initialized by DigField()
13275     player->move_delay = player->move_delay_value;
13276
13277   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13278   {
13279     TestIfPlayerTouchesBadThing(jx, jy);
13280     TestIfPlayerTouchesCustomElement(jx, jy);
13281   }
13282
13283   if (!player->active)
13284     RemovePlayer(player);
13285
13286   return moved;
13287 }
13288
13289 void ScrollPlayer(struct PlayerInfo *player, int mode)
13290 {
13291   int jx = player->jx, jy = player->jy;
13292   int last_jx = player->last_jx, last_jy = player->last_jy;
13293   int move_stepsize = TILEX / player->move_delay_value;
13294
13295   if (!player->active)
13296     return;
13297
13298   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13299     return;
13300
13301   if (mode == SCROLL_INIT)
13302   {
13303     player->actual_frame_counter.count = FrameCounter;
13304     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13305
13306     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13307         Tile[last_jx][last_jy] == EL_EMPTY)
13308     {
13309       int last_field_block_delay = 0;   // start with no blocking at all
13310       int block_delay_adjustment = player->block_delay_adjustment;
13311
13312       // if player blocks last field, add delay for exactly one move
13313       if (player->block_last_field)
13314       {
13315         last_field_block_delay += player->move_delay_value;
13316
13317         // when blocking enabled, prevent moving up despite gravity
13318         if (player->gravity && player->MovDir == MV_UP)
13319           block_delay_adjustment = -1;
13320       }
13321
13322       // add block delay adjustment (also possible when not blocking)
13323       last_field_block_delay += block_delay_adjustment;
13324
13325       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13326       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13327     }
13328
13329     if (player->MovPos != 0)    // player has not yet reached destination
13330       return;
13331   }
13332   else if (!FrameReached(&player->actual_frame_counter))
13333     return;
13334
13335   if (player->MovPos != 0)
13336   {
13337     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13338     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13339
13340     // before DrawPlayer() to draw correct player graphic for this case
13341     if (player->MovPos == 0)
13342       CheckGravityMovement(player);
13343   }
13344
13345   if (player->MovPos == 0)      // player reached destination field
13346   {
13347     if (player->move_delay_reset_counter > 0)
13348     {
13349       player->move_delay_reset_counter--;
13350
13351       if (player->move_delay_reset_counter == 0)
13352       {
13353         // continue with normal speed after quickly moving through gate
13354         HALVE_PLAYER_SPEED(player);
13355
13356         // be able to make the next move without delay
13357         player->move_delay = 0;
13358       }
13359     }
13360
13361     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13362         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13363         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13364         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13365         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13366         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13367         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13368         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13369     {
13370       ExitPlayer(player);
13371
13372       if (game.players_still_needed == 0 &&
13373           (game.friends_still_needed == 0 ||
13374            IS_SP_ELEMENT(Tile[jx][jy])))
13375         LevelSolved();
13376     }
13377
13378     player->last_jx = jx;
13379     player->last_jy = jy;
13380
13381     // this breaks one level: "machine", level 000
13382     {
13383       int move_direction = player->MovDir;
13384       int enter_side = MV_DIR_OPPOSITE(move_direction);
13385       int leave_side = move_direction;
13386       int old_jx = last_jx;
13387       int old_jy = last_jy;
13388       int old_element = Tile[old_jx][old_jy];
13389       int new_element = Tile[jx][jy];
13390
13391       if (IS_CUSTOM_ELEMENT(old_element))
13392         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13393                                    CE_LEFT_BY_PLAYER,
13394                                    player->index_bit, leave_side);
13395
13396       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13397                                           CE_PLAYER_LEAVES_X,
13398                                           player->index_bit, leave_side);
13399
13400       // needed because pushed element has not yet reached its destination,
13401       // so it would trigger a change event at its previous field location
13402       if (!player->is_pushing)
13403       {
13404         if (IS_CUSTOM_ELEMENT(new_element))
13405           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13406                                      player->index_bit, enter_side);
13407
13408         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13409                                             CE_PLAYER_ENTERS_X,
13410                                             player->index_bit, enter_side);
13411       }
13412
13413       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13414                                         CE_MOVE_OF_X, move_direction);
13415     }
13416
13417     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13418     {
13419       TestIfPlayerTouchesBadThing(jx, jy);
13420       TestIfPlayerTouchesCustomElement(jx, jy);
13421
13422       // needed because pushed element has not yet reached its destination,
13423       // so it would trigger a change event at its previous field location
13424       if (!player->is_pushing)
13425         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13426
13427       if (level.finish_dig_collect &&
13428           (player->is_digging || player->is_collecting))
13429       {
13430         int last_element = player->last_removed_element;
13431         int move_direction = player->MovDir;
13432         int enter_side = MV_DIR_OPPOSITE(move_direction);
13433         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13434                             CE_PLAYER_COLLECTS_X);
13435
13436         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13437                                             player->index_bit, enter_side);
13438
13439         player->last_removed_element = EL_UNDEFINED;
13440       }
13441
13442       if (!player->active)
13443         RemovePlayer(player);
13444     }
13445
13446     if (level.use_step_counter)
13447       CheckLevelTime_StepCounter();
13448
13449     if (tape.single_step && tape.recording && !tape.pausing &&
13450         !player->programmed_action)
13451       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13452
13453     if (!player->programmed_action)
13454       CheckSaveEngineSnapshot(player);
13455   }
13456 }
13457
13458 void ScrollScreen(struct PlayerInfo *player, int mode)
13459 {
13460   static DelayCounter screen_frame_counter = { 0 };
13461
13462   if (mode == SCROLL_INIT)
13463   {
13464     // set scrolling step size according to actual player's moving speed
13465     ScrollStepSize = TILEX / player->move_delay_value;
13466
13467     screen_frame_counter.count = FrameCounter;
13468     screen_frame_counter.value = 1;
13469
13470     ScreenMovDir = player->MovDir;
13471     ScreenMovPos = player->MovPos;
13472     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13473     return;
13474   }
13475   else if (!FrameReached(&screen_frame_counter))
13476     return;
13477
13478   if (ScreenMovPos)
13479   {
13480     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13481     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13482     redraw_mask |= REDRAW_FIELD;
13483   }
13484   else
13485     ScreenMovDir = MV_NONE;
13486 }
13487
13488 void CheckNextToConditions(int x, int y)
13489 {
13490   int element = Tile[x][y];
13491
13492   if (IS_PLAYER(x, y))
13493     TestIfPlayerNextToCustomElement(x, y);
13494
13495   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13496       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13497     TestIfElementNextToCustomElement(x, y);
13498 }
13499
13500 void TestIfPlayerNextToCustomElement(int x, int y)
13501 {
13502   struct XY *xy = xy_topdown;
13503   static int trigger_sides[4][2] =
13504   {
13505     // center side       border side
13506     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13507     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13508     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13509     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13510   };
13511   int i;
13512
13513   if (!IS_PLAYER(x, y))
13514     return;
13515
13516   struct PlayerInfo *player = PLAYERINFO(x, y);
13517
13518   if (player->is_moving)
13519     return;
13520
13521   for (i = 0; i < NUM_DIRECTIONS; i++)
13522   {
13523     int xx = x + xy[i].x;
13524     int yy = y + xy[i].y;
13525     int border_side = trigger_sides[i][1];
13526     int border_element;
13527
13528     if (!IN_LEV_FIELD(xx, yy))
13529       continue;
13530
13531     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13532       continue;         // center and border element not connected
13533
13534     border_element = Tile[xx][yy];
13535
13536     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13537                                player->index_bit, border_side);
13538     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13539                                         CE_PLAYER_NEXT_TO_X,
13540                                         player->index_bit, border_side);
13541
13542     /* use player element that is initially defined in the level playfield,
13543        not the player element that corresponds to the runtime player number
13544        (example: a level that contains EL_PLAYER_3 as the only player would
13545        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13546
13547     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13548                              CE_NEXT_TO_X, border_side);
13549   }
13550 }
13551
13552 void TestIfPlayerTouchesCustomElement(int x, int y)
13553 {
13554   struct XY *xy = xy_topdown;
13555   static int trigger_sides[4][2] =
13556   {
13557     // center side       border side
13558     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13559     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13560     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13561     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13562   };
13563   static int touch_dir[4] =
13564   {
13565     MV_LEFT | MV_RIGHT,
13566     MV_UP   | MV_DOWN,
13567     MV_UP   | MV_DOWN,
13568     MV_LEFT | MV_RIGHT
13569   };
13570   int center_element = Tile[x][y];      // should always be non-moving!
13571   int i;
13572
13573   for (i = 0; i < NUM_DIRECTIONS; i++)
13574   {
13575     int xx = x + xy[i].x;
13576     int yy = y + xy[i].y;
13577     int center_side = trigger_sides[i][0];
13578     int border_side = trigger_sides[i][1];
13579     int border_element;
13580
13581     if (!IN_LEV_FIELD(xx, yy))
13582       continue;
13583
13584     if (IS_PLAYER(x, y))                // player found at center element
13585     {
13586       struct PlayerInfo *player = PLAYERINFO(x, y);
13587
13588       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13589         border_element = Tile[xx][yy];          // may be moving!
13590       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13591         border_element = Tile[xx][yy];
13592       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13593         border_element = MovingOrBlocked2Element(xx, yy);
13594       else
13595         continue;               // center and border element do not touch
13596
13597       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13598                                  player->index_bit, border_side);
13599       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13600                                           CE_PLAYER_TOUCHES_X,
13601                                           player->index_bit, border_side);
13602
13603       {
13604         /* use player element that is initially defined in the level playfield,
13605            not the player element that corresponds to the runtime player number
13606            (example: a level that contains EL_PLAYER_3 as the only player would
13607            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13608         int player_element = PLAYERINFO(x, y)->initial_element;
13609
13610         // as element "X" is the player here, check opposite (center) side
13611         CheckElementChangeBySide(xx, yy, border_element, player_element,
13612                                  CE_TOUCHING_X, center_side);
13613       }
13614     }
13615     else if (IS_PLAYER(xx, yy))         // player found at border element
13616     {
13617       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13618
13619       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13620       {
13621         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13622           continue;             // center and border element do not touch
13623       }
13624
13625       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13626                                  player->index_bit, center_side);
13627       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13628                                           CE_PLAYER_TOUCHES_X,
13629                                           player->index_bit, center_side);
13630
13631       {
13632         /* use player element that is initially defined in the level playfield,
13633            not the player element that corresponds to the runtime player number
13634            (example: a level that contains EL_PLAYER_3 as the only player would
13635            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13636         int player_element = PLAYERINFO(xx, yy)->initial_element;
13637
13638         // as element "X" is the player here, check opposite (border) side
13639         CheckElementChangeBySide(x, y, center_element, player_element,
13640                                  CE_TOUCHING_X, border_side);
13641       }
13642
13643       break;
13644     }
13645   }
13646 }
13647
13648 void TestIfElementNextToCustomElement(int x, int y)
13649 {
13650   struct XY *xy = xy_topdown;
13651   static int trigger_sides[4][2] =
13652   {
13653     // center side      border side
13654     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13655     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13656     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13657     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13658   };
13659   int center_element = Tile[x][y];      // should always be non-moving!
13660   int i;
13661
13662   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13663     return;
13664
13665   for (i = 0; i < NUM_DIRECTIONS; i++)
13666   {
13667     int xx = x + xy[i].x;
13668     int yy = y + xy[i].y;
13669     int border_side = trigger_sides[i][1];
13670     int border_element;
13671
13672     if (!IN_LEV_FIELD(xx, yy))
13673       continue;
13674
13675     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13676       continue;                 // center and border element not connected
13677
13678     border_element = Tile[xx][yy];
13679
13680     // check for change of center element (but change it only once)
13681     if (CheckElementChangeBySide(x, y, center_element, border_element,
13682                                  CE_NEXT_TO_X, border_side))
13683       break;
13684   }
13685 }
13686
13687 void TestIfElementTouchesCustomElement(int x, int y)
13688 {
13689   struct XY *xy = xy_topdown;
13690   static int trigger_sides[4][2] =
13691   {
13692     // center side      border side
13693     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13694     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13695     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13696     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13697   };
13698   static int touch_dir[4] =
13699   {
13700     MV_LEFT | MV_RIGHT,
13701     MV_UP   | MV_DOWN,
13702     MV_UP   | MV_DOWN,
13703     MV_LEFT | MV_RIGHT
13704   };
13705   boolean change_center_element = FALSE;
13706   int center_element = Tile[x][y];      // should always be non-moving!
13707   int border_element_old[NUM_DIRECTIONS];
13708   int i;
13709
13710   for (i = 0; i < NUM_DIRECTIONS; i++)
13711   {
13712     int xx = x + xy[i].x;
13713     int yy = y + xy[i].y;
13714     int border_element;
13715
13716     border_element_old[i] = -1;
13717
13718     if (!IN_LEV_FIELD(xx, yy))
13719       continue;
13720
13721     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13722       border_element = Tile[xx][yy];    // may be moving!
13723     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13724       border_element = Tile[xx][yy];
13725     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13726       border_element = MovingOrBlocked2Element(xx, yy);
13727     else
13728       continue;                 // center and border element do not touch
13729
13730     border_element_old[i] = border_element;
13731   }
13732
13733   for (i = 0; i < NUM_DIRECTIONS; i++)
13734   {
13735     int xx = x + xy[i].x;
13736     int yy = y + xy[i].y;
13737     int center_side = trigger_sides[i][0];
13738     int border_element = border_element_old[i];
13739
13740     if (border_element == -1)
13741       continue;
13742
13743     // check for change of border element
13744     CheckElementChangeBySide(xx, yy, border_element, center_element,
13745                              CE_TOUCHING_X, center_side);
13746
13747     // (center element cannot be player, so we don't have to check this here)
13748   }
13749
13750   for (i = 0; i < NUM_DIRECTIONS; i++)
13751   {
13752     int xx = x + xy[i].x;
13753     int yy = y + xy[i].y;
13754     int border_side = trigger_sides[i][1];
13755     int border_element = border_element_old[i];
13756
13757     if (border_element == -1)
13758       continue;
13759
13760     // check for change of center element (but change it only once)
13761     if (!change_center_element)
13762       change_center_element =
13763         CheckElementChangeBySide(x, y, center_element, border_element,
13764                                  CE_TOUCHING_X, border_side);
13765
13766     if (IS_PLAYER(xx, yy))
13767     {
13768       /* use player element that is initially defined in the level playfield,
13769          not the player element that corresponds to the runtime player number
13770          (example: a level that contains EL_PLAYER_3 as the only player would
13771          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13772       int player_element = PLAYERINFO(xx, yy)->initial_element;
13773
13774       // as element "X" is the player here, check opposite (border) side
13775       CheckElementChangeBySide(x, y, center_element, player_element,
13776                                CE_TOUCHING_X, border_side);
13777     }
13778   }
13779 }
13780
13781 void TestIfElementHitsCustomElement(int x, int y, int direction)
13782 {
13783   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13784   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13785   int hitx = x + dx, hity = y + dy;
13786   int hitting_element = Tile[x][y];
13787   int touched_element;
13788
13789   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13790     return;
13791
13792   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13793                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13794
13795   if (IN_LEV_FIELD(hitx, hity))
13796   {
13797     int opposite_direction = MV_DIR_OPPOSITE(direction);
13798     int hitting_side = direction;
13799     int touched_side = opposite_direction;
13800     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13801                           MovDir[hitx][hity] != direction ||
13802                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13803
13804     object_hit = TRUE;
13805
13806     if (object_hit)
13807     {
13808       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13809                                CE_HITTING_X, touched_side);
13810
13811       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13812                                CE_HIT_BY_X, hitting_side);
13813
13814       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13815                                CE_HIT_BY_SOMETHING, opposite_direction);
13816
13817       if (IS_PLAYER(hitx, hity))
13818       {
13819         /* use player element that is initially defined in the level playfield,
13820            not the player element that corresponds to the runtime player number
13821            (example: a level that contains EL_PLAYER_3 as the only player would
13822            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13823         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13824
13825         CheckElementChangeBySide(x, y, hitting_element, player_element,
13826                                  CE_HITTING_X, touched_side);
13827       }
13828     }
13829   }
13830
13831   // "hitting something" is also true when hitting the playfield border
13832   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13833                            CE_HITTING_SOMETHING, direction);
13834 }
13835
13836 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13837 {
13838   int i, kill_x = -1, kill_y = -1;
13839
13840   int bad_element = -1;
13841   struct XY *test_xy = xy_topdown;
13842   static int test_dir[4] =
13843   {
13844     MV_UP,
13845     MV_LEFT,
13846     MV_RIGHT,
13847     MV_DOWN
13848   };
13849
13850   for (i = 0; i < NUM_DIRECTIONS; i++)
13851   {
13852     int test_x, test_y, test_move_dir, test_element;
13853
13854     test_x = good_x + test_xy[i].x;
13855     test_y = good_y + test_xy[i].y;
13856
13857     if (!IN_LEV_FIELD(test_x, test_y))
13858       continue;
13859
13860     test_move_dir =
13861       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13862
13863     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13864
13865     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13866        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13867     */
13868     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13869         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13870     {
13871       kill_x = test_x;
13872       kill_y = test_y;
13873       bad_element = test_element;
13874
13875       break;
13876     }
13877   }
13878
13879   if (kill_x != -1 || kill_y != -1)
13880   {
13881     if (IS_PLAYER(good_x, good_y))
13882     {
13883       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13884
13885       if (player->shield_deadly_time_left > 0 &&
13886           !IS_INDESTRUCTIBLE(bad_element))
13887         Bang(kill_x, kill_y);
13888       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13889         KillPlayer(player);
13890     }
13891     else
13892       Bang(good_x, good_y);
13893   }
13894 }
13895
13896 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13897 {
13898   int i, kill_x = -1, kill_y = -1;
13899   int bad_element = Tile[bad_x][bad_y];
13900   struct XY *test_xy = xy_topdown;
13901   static int touch_dir[4] =
13902   {
13903     MV_LEFT | MV_RIGHT,
13904     MV_UP   | MV_DOWN,
13905     MV_UP   | MV_DOWN,
13906     MV_LEFT | MV_RIGHT
13907   };
13908   static int test_dir[4] =
13909   {
13910     MV_UP,
13911     MV_LEFT,
13912     MV_RIGHT,
13913     MV_DOWN
13914   };
13915
13916   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13917     return;
13918
13919   for (i = 0; i < NUM_DIRECTIONS; i++)
13920   {
13921     int test_x, test_y, test_move_dir, test_element;
13922
13923     test_x = bad_x + test_xy[i].x;
13924     test_y = bad_y + test_xy[i].y;
13925
13926     if (!IN_LEV_FIELD(test_x, test_y))
13927       continue;
13928
13929     test_move_dir =
13930       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13931
13932     test_element = Tile[test_x][test_y];
13933
13934     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13935        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13936     */
13937     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13938         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13939     {
13940       // good thing is player or penguin that does not move away
13941       if (IS_PLAYER(test_x, test_y))
13942       {
13943         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13944
13945         if (bad_element == EL_ROBOT && player->is_moving)
13946           continue;     // robot does not kill player if he is moving
13947
13948         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13949         {
13950           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13951             continue;           // center and border element do not touch
13952         }
13953
13954         kill_x = test_x;
13955         kill_y = test_y;
13956
13957         break;
13958       }
13959       else if (test_element == EL_PENGUIN)
13960       {
13961         kill_x = test_x;
13962         kill_y = test_y;
13963
13964         break;
13965       }
13966     }
13967   }
13968
13969   if (kill_x != -1 || kill_y != -1)
13970   {
13971     if (IS_PLAYER(kill_x, kill_y))
13972     {
13973       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13974
13975       if (player->shield_deadly_time_left > 0 &&
13976           !IS_INDESTRUCTIBLE(bad_element))
13977         Bang(bad_x, bad_y);
13978       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13979         KillPlayer(player);
13980     }
13981     else
13982       Bang(kill_x, kill_y);
13983   }
13984 }
13985
13986 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13987 {
13988   int bad_element = Tile[bad_x][bad_y];
13989   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13990   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13991   int test_x = bad_x + dx, test_y = bad_y + dy;
13992   int test_move_dir, test_element;
13993   int kill_x = -1, kill_y = -1;
13994
13995   if (!IN_LEV_FIELD(test_x, test_y))
13996     return;
13997
13998   test_move_dir =
13999     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14000
14001   test_element = Tile[test_x][test_y];
14002
14003   if (test_move_dir != bad_move_dir)
14004   {
14005     // good thing can be player or penguin that does not move away
14006     if (IS_PLAYER(test_x, test_y))
14007     {
14008       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14009
14010       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14011          player as being hit when he is moving towards the bad thing, because
14012          the "get hit by" condition would be lost after the player stops) */
14013       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14014         return;         // player moves away from bad thing
14015
14016       kill_x = test_x;
14017       kill_y = test_y;
14018     }
14019     else if (test_element == EL_PENGUIN)
14020     {
14021       kill_x = test_x;
14022       kill_y = test_y;
14023     }
14024   }
14025
14026   if (kill_x != -1 || kill_y != -1)
14027   {
14028     if (IS_PLAYER(kill_x, kill_y))
14029     {
14030       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14031
14032       if (player->shield_deadly_time_left > 0 &&
14033           !IS_INDESTRUCTIBLE(bad_element))
14034         Bang(bad_x, bad_y);
14035       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14036         KillPlayer(player);
14037     }
14038     else
14039       Bang(kill_x, kill_y);
14040   }
14041 }
14042
14043 void TestIfPlayerTouchesBadThing(int x, int y)
14044 {
14045   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14046 }
14047
14048 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14049 {
14050   TestIfGoodThingHitsBadThing(x, y, move_dir);
14051 }
14052
14053 void TestIfBadThingTouchesPlayer(int x, int y)
14054 {
14055   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14056 }
14057
14058 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14059 {
14060   TestIfBadThingHitsGoodThing(x, y, move_dir);
14061 }
14062
14063 void TestIfFriendTouchesBadThing(int x, int y)
14064 {
14065   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14066 }
14067
14068 void TestIfBadThingTouchesFriend(int x, int y)
14069 {
14070   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14071 }
14072
14073 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14074 {
14075   int i, kill_x = bad_x, kill_y = bad_y;
14076   struct XY *xy = xy_topdown;
14077
14078   for (i = 0; i < NUM_DIRECTIONS; i++)
14079   {
14080     int x, y, element;
14081
14082     x = bad_x + xy[i].x;
14083     y = bad_y + xy[i].y;
14084     if (!IN_LEV_FIELD(x, y))
14085       continue;
14086
14087     element = Tile[x][y];
14088     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14089         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14090     {
14091       kill_x = x;
14092       kill_y = y;
14093       break;
14094     }
14095   }
14096
14097   if (kill_x != bad_x || kill_y != bad_y)
14098     Bang(bad_x, bad_y);
14099 }
14100
14101 void KillPlayer(struct PlayerInfo *player)
14102 {
14103   int jx = player->jx, jy = player->jy;
14104
14105   if (!player->active)
14106     return;
14107
14108 #if 0
14109   Debug("game:playing:KillPlayer",
14110         "0: killed == %d, active == %d, reanimated == %d",
14111         player->killed, player->active, player->reanimated);
14112 #endif
14113
14114   /* the following code was introduced to prevent an infinite loop when calling
14115      -> Bang()
14116      -> CheckTriggeredElementChangeExt()
14117      -> ExecuteCustomElementAction()
14118      -> KillPlayer()
14119      -> (infinitely repeating the above sequence of function calls)
14120      which occurs when killing the player while having a CE with the setting
14121      "kill player X when explosion of <player X>"; the solution using a new
14122      field "player->killed" was chosen for backwards compatibility, although
14123      clever use of the fields "player->active" etc. would probably also work */
14124 #if 1
14125   if (player->killed)
14126     return;
14127 #endif
14128
14129   player->killed = TRUE;
14130
14131   // remove accessible field at the player's position
14132   RemoveField(jx, jy);
14133
14134   // deactivate shield (else Bang()/Explode() would not work right)
14135   player->shield_normal_time_left = 0;
14136   player->shield_deadly_time_left = 0;
14137
14138 #if 0
14139   Debug("game:playing:KillPlayer",
14140         "1: killed == %d, active == %d, reanimated == %d",
14141         player->killed, player->active, player->reanimated);
14142 #endif
14143
14144   Bang(jx, jy);
14145
14146 #if 0
14147   Debug("game:playing:KillPlayer",
14148         "2: killed == %d, active == %d, reanimated == %d",
14149         player->killed, player->active, player->reanimated);
14150 #endif
14151
14152   if (player->reanimated)       // killed player may have been reanimated
14153     player->killed = player->reanimated = FALSE;
14154   else
14155     BuryPlayer(player);
14156 }
14157
14158 static void KillPlayerUnlessEnemyProtected(int x, int y)
14159 {
14160   if (!PLAYER_ENEMY_PROTECTED(x, y))
14161     KillPlayer(PLAYERINFO(x, y));
14162 }
14163
14164 static void KillPlayerUnlessExplosionProtected(int x, int y)
14165 {
14166   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14167     KillPlayer(PLAYERINFO(x, y));
14168 }
14169
14170 void BuryPlayer(struct PlayerInfo *player)
14171 {
14172   int jx = player->jx, jy = player->jy;
14173
14174   if (!player->active)
14175     return;
14176
14177   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14178
14179   RemovePlayer(player);
14180
14181   player->buried = TRUE;
14182
14183   if (game.all_players_gone)
14184     game.GameOver = TRUE;
14185 }
14186
14187 void RemovePlayer(struct PlayerInfo *player)
14188 {
14189   int jx = player->jx, jy = player->jy;
14190   int i, found = FALSE;
14191
14192   player->present = FALSE;
14193   player->active = FALSE;
14194
14195   // required for some CE actions (even if the player is not active anymore)
14196   player->MovPos = 0;
14197
14198   if (!ExplodeField[jx][jy])
14199     StorePlayer[jx][jy] = 0;
14200
14201   if (player->is_moving)
14202     TEST_DrawLevelField(player->last_jx, player->last_jy);
14203
14204   for (i = 0; i < MAX_PLAYERS; i++)
14205     if (stored_player[i].active)
14206       found = TRUE;
14207
14208   if (!found)
14209   {
14210     game.all_players_gone = TRUE;
14211     game.GameOver = TRUE;
14212   }
14213
14214   game.exit_x = game.robot_wheel_x = jx;
14215   game.exit_y = game.robot_wheel_y = jy;
14216 }
14217
14218 void ExitPlayer(struct PlayerInfo *player)
14219 {
14220   DrawPlayer(player);   // needed here only to cleanup last field
14221   RemovePlayer(player);
14222
14223   if (game.players_still_needed > 0)
14224     game.players_still_needed--;
14225 }
14226
14227 static void SetFieldForSnapping(int x, int y, int element, int direction,
14228                                 int player_index_bit)
14229 {
14230   struct ElementInfo *ei = &element_info[element];
14231   int direction_bit = MV_DIR_TO_BIT(direction);
14232   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14233   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14234                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14235
14236   Tile[x][y] = EL_ELEMENT_SNAPPING;
14237   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14238   MovDir[x][y] = direction;
14239   Store[x][y] = element;
14240   Store2[x][y] = player_index_bit;
14241
14242   ResetGfxAnimation(x, y);
14243
14244   GfxElement[x][y] = element;
14245   GfxAction[x][y] = action;
14246   GfxDir[x][y] = direction;
14247   GfxFrame[x][y] = -1;
14248 }
14249
14250 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14251                                    int player_index_bit)
14252 {
14253   TestIfElementTouchesCustomElement(x, y);      // for empty space
14254
14255   if (level.finish_dig_collect)
14256   {
14257     int dig_side = MV_DIR_OPPOSITE(direction);
14258     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14259                         CE_PLAYER_COLLECTS_X);
14260
14261     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14262                                         player_index_bit, dig_side);
14263     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14264                                         player_index_bit, dig_side);
14265   }
14266 }
14267
14268 /*
14269   =============================================================================
14270   checkDiagonalPushing()
14271   -----------------------------------------------------------------------------
14272   check if diagonal input device direction results in pushing of object
14273   (by checking if the alternative direction is walkable, diggable, ...)
14274   =============================================================================
14275 */
14276
14277 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14278                                     int x, int y, int real_dx, int real_dy)
14279 {
14280   int jx, jy, dx, dy, xx, yy;
14281
14282   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14283     return TRUE;
14284
14285   // diagonal direction: check alternative direction
14286   jx = player->jx;
14287   jy = player->jy;
14288   dx = x - jx;
14289   dy = y - jy;
14290   xx = jx + (dx == 0 ? real_dx : 0);
14291   yy = jy + (dy == 0 ? real_dy : 0);
14292
14293   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14294 }
14295
14296 /*
14297   =============================================================================
14298   DigField()
14299   -----------------------------------------------------------------------------
14300   x, y:                 field next to player (non-diagonal) to try to dig to
14301   real_dx, real_dy:     direction as read from input device (can be diagonal)
14302   =============================================================================
14303 */
14304
14305 static int DigField(struct PlayerInfo *player,
14306                     int oldx, int oldy, int x, int y,
14307                     int real_dx, int real_dy, int mode)
14308 {
14309   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14310   boolean player_was_pushing = player->is_pushing;
14311   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14312   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14313   int jx = oldx, jy = oldy;
14314   int dx = x - jx, dy = y - jy;
14315   int nextx = x + dx, nexty = y + dy;
14316   int move_direction = (dx == -1 ? MV_LEFT  :
14317                         dx == +1 ? MV_RIGHT :
14318                         dy == -1 ? MV_UP    :
14319                         dy == +1 ? MV_DOWN  : MV_NONE);
14320   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14321   int dig_side = MV_DIR_OPPOSITE(move_direction);
14322   int old_element = Tile[jx][jy];
14323   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14324   int collect_count;
14325
14326   if (is_player)                // function can also be called by EL_PENGUIN
14327   {
14328     if (player->MovPos == 0)
14329     {
14330       player->is_digging = FALSE;
14331       player->is_collecting = FALSE;
14332     }
14333
14334     if (player->MovPos == 0)    // last pushing move finished
14335       player->is_pushing = FALSE;
14336
14337     if (mode == DF_NO_PUSH)     // player just stopped pushing
14338     {
14339       player->is_switching = FALSE;
14340       player->push_delay = -1;
14341
14342       return MP_NO_ACTION;
14343     }
14344   }
14345   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14346     old_element = Back[jx][jy];
14347
14348   // in case of element dropped at player position, check background
14349   else if (Back[jx][jy] != EL_EMPTY &&
14350            game.engine_version >= VERSION_IDENT(2,2,0,0))
14351     old_element = Back[jx][jy];
14352
14353   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14354     return MP_NO_ACTION;        // field has no opening in this direction
14355
14356   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14357     return MP_NO_ACTION;        // field has no opening in this direction
14358
14359   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14360   {
14361     SplashAcid(x, y);
14362
14363     Tile[jx][jy] = player->artwork_element;
14364     InitMovingField(jx, jy, MV_DOWN);
14365     Store[jx][jy] = EL_ACID;
14366     ContinueMoving(jx, jy);
14367     BuryPlayer(player);
14368
14369     return MP_DONT_RUN_INTO;
14370   }
14371
14372   if (player_can_move && DONT_RUN_INTO(element))
14373   {
14374     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14375
14376     return MP_DONT_RUN_INTO;
14377   }
14378
14379   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14380     return MP_NO_ACTION;
14381
14382   collect_count = element_info[element].collect_count_initial;
14383
14384   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14385     return MP_NO_ACTION;
14386
14387   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14388     player_can_move = player_can_move_or_snap;
14389
14390   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14391       game.engine_version >= VERSION_IDENT(2,2,0,0))
14392   {
14393     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14394                                player->index_bit, dig_side);
14395     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14396                                         player->index_bit, dig_side);
14397
14398     if (element == EL_DC_LANDMINE)
14399       Bang(x, y);
14400
14401     if (Tile[x][y] != element)          // field changed by snapping
14402       return MP_ACTION;
14403
14404     return MP_NO_ACTION;
14405   }
14406
14407   if (player->gravity && is_player && !player->is_auto_moving &&
14408       canFallDown(player) && move_direction != MV_DOWN &&
14409       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14410     return MP_NO_ACTION;        // player cannot walk here due to gravity
14411
14412   if (player_can_move &&
14413       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14414   {
14415     int sound_element = SND_ELEMENT(element);
14416     int sound_action = ACTION_WALKING;
14417
14418     if (IS_RND_GATE(element))
14419     {
14420       if (!player->key[RND_GATE_NR(element)])
14421         return MP_NO_ACTION;
14422     }
14423     else if (IS_RND_GATE_GRAY(element))
14424     {
14425       if (!player->key[RND_GATE_GRAY_NR(element)])
14426         return MP_NO_ACTION;
14427     }
14428     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14429     {
14430       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14431         return MP_NO_ACTION;
14432     }
14433     else if (element == EL_EXIT_OPEN ||
14434              element == EL_EM_EXIT_OPEN ||
14435              element == EL_EM_EXIT_OPENING ||
14436              element == EL_STEEL_EXIT_OPEN ||
14437              element == EL_EM_STEEL_EXIT_OPEN ||
14438              element == EL_EM_STEEL_EXIT_OPENING ||
14439              element == EL_SP_EXIT_OPEN ||
14440              element == EL_SP_EXIT_OPENING)
14441     {
14442       sound_action = ACTION_PASSING;    // player is passing exit
14443     }
14444     else if (element == EL_EMPTY)
14445     {
14446       sound_action = ACTION_MOVING;             // nothing to walk on
14447     }
14448
14449     // play sound from background or player, whatever is available
14450     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14451       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14452     else
14453       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14454   }
14455   else if (player_can_move &&
14456            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14457   {
14458     if (!ACCESS_FROM(element, opposite_direction))
14459       return MP_NO_ACTION;      // field not accessible from this direction
14460
14461     if (CAN_MOVE(element))      // only fixed elements can be passed!
14462       return MP_NO_ACTION;
14463
14464     if (IS_EM_GATE(element))
14465     {
14466       if (!player->key[EM_GATE_NR(element)])
14467         return MP_NO_ACTION;
14468     }
14469     else if (IS_EM_GATE_GRAY(element))
14470     {
14471       if (!player->key[EM_GATE_GRAY_NR(element)])
14472         return MP_NO_ACTION;
14473     }
14474     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14475     {
14476       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14477         return MP_NO_ACTION;
14478     }
14479     else if (IS_EMC_GATE(element))
14480     {
14481       if (!player->key[EMC_GATE_NR(element)])
14482         return MP_NO_ACTION;
14483     }
14484     else if (IS_EMC_GATE_GRAY(element))
14485     {
14486       if (!player->key[EMC_GATE_GRAY_NR(element)])
14487         return MP_NO_ACTION;
14488     }
14489     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14490     {
14491       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14492         return MP_NO_ACTION;
14493     }
14494     else if (element == EL_DC_GATE_WHITE ||
14495              element == EL_DC_GATE_WHITE_GRAY ||
14496              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14497     {
14498       if (player->num_white_keys == 0)
14499         return MP_NO_ACTION;
14500
14501       player->num_white_keys--;
14502     }
14503     else if (IS_SP_PORT(element))
14504     {
14505       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14506           element == EL_SP_GRAVITY_PORT_RIGHT ||
14507           element == EL_SP_GRAVITY_PORT_UP ||
14508           element == EL_SP_GRAVITY_PORT_DOWN)
14509         player->gravity = !player->gravity;
14510       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14511                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14512                element == EL_SP_GRAVITY_ON_PORT_UP ||
14513                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14514         player->gravity = TRUE;
14515       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14516                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14517                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14518                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14519         player->gravity = FALSE;
14520     }
14521
14522     // automatically move to the next field with double speed
14523     player->programmed_action = move_direction;
14524
14525     if (player->move_delay_reset_counter == 0)
14526     {
14527       player->move_delay_reset_counter = 2;     // two double speed steps
14528
14529       DOUBLE_PLAYER_SPEED(player);
14530     }
14531
14532     PlayLevelSoundAction(x, y, ACTION_PASSING);
14533   }
14534   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14535   {
14536     RemoveField(x, y);
14537
14538     if (mode != DF_SNAP)
14539     {
14540       GfxElement[x][y] = GFX_ELEMENT(element);
14541       player->is_digging = TRUE;
14542     }
14543
14544     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14545
14546     // use old behaviour for old levels (digging)
14547     if (!level.finish_dig_collect)
14548     {
14549       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14550                                           player->index_bit, dig_side);
14551
14552       // if digging triggered player relocation, finish digging tile
14553       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14554         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14555     }
14556
14557     if (mode == DF_SNAP)
14558     {
14559       if (level.block_snap_field)
14560         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14561       else
14562         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14563
14564       // use old behaviour for old levels (snapping)
14565       if (!level.finish_dig_collect)
14566         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14567                                             player->index_bit, dig_side);
14568     }
14569   }
14570   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14571   {
14572     RemoveField(x, y);
14573
14574     if (is_player && mode != DF_SNAP)
14575     {
14576       GfxElement[x][y] = element;
14577       player->is_collecting = TRUE;
14578     }
14579
14580     if (element == EL_SPEED_PILL)
14581     {
14582       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14583     }
14584     else if (element == EL_EXTRA_TIME && level.time > 0)
14585     {
14586       TimeLeft += level.extra_time;
14587
14588       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14589
14590       DisplayGameControlValues();
14591     }
14592     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14593     {
14594       int shield_time = (element == EL_SHIELD_DEADLY ?
14595                          level.shield_deadly_time :
14596                          level.shield_normal_time);
14597
14598       player->shield_normal_time_left += shield_time;
14599       if (element == EL_SHIELD_DEADLY)
14600         player->shield_deadly_time_left += shield_time;
14601     }
14602     else if (element == EL_DYNAMITE ||
14603              element == EL_EM_DYNAMITE ||
14604              element == EL_SP_DISK_RED)
14605     {
14606       if (player->inventory_size < MAX_INVENTORY_SIZE)
14607         player->inventory_element[player->inventory_size++] = element;
14608
14609       DrawGameDoorValues();
14610     }
14611     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14612     {
14613       player->dynabomb_count++;
14614       player->dynabombs_left++;
14615     }
14616     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14617     {
14618       player->dynabomb_size++;
14619     }
14620     else if (element == EL_DYNABOMB_INCREASE_POWER)
14621     {
14622       player->dynabomb_xl = TRUE;
14623     }
14624     else if (IS_KEY(element))
14625     {
14626       player->key[KEY_NR(element)] = TRUE;
14627
14628       DrawGameDoorValues();
14629     }
14630     else if (element == EL_DC_KEY_WHITE)
14631     {
14632       player->num_white_keys++;
14633
14634       // display white keys?
14635       // DrawGameDoorValues();
14636     }
14637     else if (IS_ENVELOPE(element))
14638     {
14639       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14640
14641       if (!wait_for_snapping)
14642         player->show_envelope = element;
14643     }
14644     else if (element == EL_EMC_LENSES)
14645     {
14646       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14647
14648       RedrawAllInvisibleElementsForLenses();
14649     }
14650     else if (element == EL_EMC_MAGNIFIER)
14651     {
14652       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14653
14654       RedrawAllInvisibleElementsForMagnifier();
14655     }
14656     else if (IS_DROPPABLE(element) ||
14657              IS_THROWABLE(element))     // can be collected and dropped
14658     {
14659       int i;
14660
14661       if (collect_count == 0)
14662         player->inventory_infinite_element = element;
14663       else
14664         for (i = 0; i < collect_count; i++)
14665           if (player->inventory_size < MAX_INVENTORY_SIZE)
14666             player->inventory_element[player->inventory_size++] = element;
14667
14668       DrawGameDoorValues();
14669     }
14670     else if (collect_count > 0)
14671     {
14672       game.gems_still_needed -= collect_count;
14673       if (game.gems_still_needed < 0)
14674         game.gems_still_needed = 0;
14675
14676       game.snapshot.collected_item = TRUE;
14677
14678       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14679
14680       DisplayGameControlValues();
14681     }
14682
14683     RaiseScoreElement(element);
14684     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14685
14686     // use old behaviour for old levels (collecting)
14687     if (!level.finish_dig_collect && is_player)
14688     {
14689       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14690                                           player->index_bit, dig_side);
14691
14692       // if collecting triggered player relocation, finish collecting tile
14693       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14694         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14695     }
14696
14697     if (mode == DF_SNAP)
14698     {
14699       if (level.block_snap_field)
14700         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14701       else
14702         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14703
14704       // use old behaviour for old levels (snapping)
14705       if (!level.finish_dig_collect)
14706         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14707                                             player->index_bit, dig_side);
14708     }
14709   }
14710   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14711   {
14712     if (mode == DF_SNAP && element != EL_BD_ROCK)
14713       return MP_NO_ACTION;
14714
14715     if (CAN_FALL(element) && dy)
14716       return MP_NO_ACTION;
14717
14718     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14719         !(element == EL_SPRING && level.use_spring_bug))
14720       return MP_NO_ACTION;
14721
14722     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14723         ((move_direction & MV_VERTICAL &&
14724           ((element_info[element].move_pattern & MV_LEFT &&
14725             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14726            (element_info[element].move_pattern & MV_RIGHT &&
14727             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14728          (move_direction & MV_HORIZONTAL &&
14729           ((element_info[element].move_pattern & MV_UP &&
14730             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14731            (element_info[element].move_pattern & MV_DOWN &&
14732             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14733       return MP_NO_ACTION;
14734
14735     // do not push elements already moving away faster than player
14736     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14737         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14738       return MP_NO_ACTION;
14739
14740     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14741     {
14742       if (player->push_delay_value == -1 || !player_was_pushing)
14743         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14744     }
14745     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14746     {
14747       if (player->push_delay_value == -1)
14748         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14749     }
14750     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14751     {
14752       if (!player->is_pushing)
14753         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14754     }
14755
14756     player->is_pushing = TRUE;
14757     player->is_active = TRUE;
14758
14759     if (!(IN_LEV_FIELD(nextx, nexty) &&
14760           (IS_FREE(nextx, nexty) ||
14761            (IS_SB_ELEMENT(element) &&
14762             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14763            (IS_CUSTOM_ELEMENT(element) &&
14764             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14765       return MP_NO_ACTION;
14766
14767     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14768       return MP_NO_ACTION;
14769
14770     if (player->push_delay == -1)       // new pushing; restart delay
14771       player->push_delay = 0;
14772
14773     if (player->push_delay < player->push_delay_value &&
14774         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14775         element != EL_SPRING && element != EL_BALLOON)
14776     {
14777       // make sure that there is no move delay before next try to push
14778       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14779         player->move_delay = 0;
14780
14781       return MP_NO_ACTION;
14782     }
14783
14784     if (IS_CUSTOM_ELEMENT(element) &&
14785         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14786     {
14787       if (!DigFieldByCE(nextx, nexty, element))
14788         return MP_NO_ACTION;
14789     }
14790
14791     if (IS_SB_ELEMENT(element))
14792     {
14793       boolean sokoban_task_solved = FALSE;
14794
14795       if (element == EL_SOKOBAN_FIELD_FULL)
14796       {
14797         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14798
14799         IncrementSokobanFieldsNeeded();
14800         IncrementSokobanObjectsNeeded();
14801       }
14802
14803       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14804       {
14805         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14806
14807         DecrementSokobanFieldsNeeded();
14808         DecrementSokobanObjectsNeeded();
14809
14810         // sokoban object was pushed from empty field to sokoban field
14811         if (Back[x][y] == EL_EMPTY)
14812           sokoban_task_solved = TRUE;
14813       }
14814
14815       Tile[x][y] = EL_SOKOBAN_OBJECT;
14816
14817       if (Back[x][y] == Back[nextx][nexty])
14818         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14819       else if (Back[x][y] != 0)
14820         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14821                                     ACTION_EMPTYING);
14822       else
14823         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14824                                     ACTION_FILLING);
14825
14826       if (sokoban_task_solved &&
14827           game.sokoban_fields_still_needed == 0 &&
14828           game.sokoban_objects_still_needed == 0 &&
14829           level.auto_exit_sokoban)
14830       {
14831         game.players_still_needed = 0;
14832
14833         LevelSolved();
14834
14835         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14836       }
14837     }
14838     else
14839       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14840
14841     InitMovingField(x, y, move_direction);
14842     GfxAction[x][y] = ACTION_PUSHING;
14843
14844     if (mode == DF_SNAP)
14845       ContinueMoving(x, y);
14846     else
14847       MovPos[x][y] = (dx != 0 ? dx : dy);
14848
14849     Pushed[x][y] = TRUE;
14850     Pushed[nextx][nexty] = TRUE;
14851
14852     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14853       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14854     else
14855       player->push_delay_value = -1;    // get new value later
14856
14857     // check for element change _after_ element has been pushed
14858     if (game.use_change_when_pushing_bug)
14859     {
14860       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14861                                  player->index_bit, dig_side);
14862       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14863                                           player->index_bit, dig_side);
14864     }
14865   }
14866   else if (IS_SWITCHABLE(element))
14867   {
14868     if (PLAYER_SWITCHING(player, x, y))
14869     {
14870       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14871                                           player->index_bit, dig_side);
14872
14873       return MP_ACTION;
14874     }
14875
14876     player->is_switching = TRUE;
14877     player->switch_x = x;
14878     player->switch_y = y;
14879
14880     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14881
14882     if (element == EL_ROBOT_WHEEL)
14883     {
14884       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14885
14886       game.robot_wheel_x = x;
14887       game.robot_wheel_y = y;
14888       game.robot_wheel_active = TRUE;
14889
14890       TEST_DrawLevelField(x, y);
14891     }
14892     else if (element == EL_SP_TERMINAL)
14893     {
14894       int xx, yy;
14895
14896       SCAN_PLAYFIELD(xx, yy)
14897       {
14898         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14899         {
14900           Bang(xx, yy);
14901         }
14902         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14903         {
14904           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14905
14906           ResetGfxAnimation(xx, yy);
14907           TEST_DrawLevelField(xx, yy);
14908         }
14909       }
14910     }
14911     else if (IS_BELT_SWITCH(element))
14912     {
14913       ToggleBeltSwitch(x, y);
14914     }
14915     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14916              element == EL_SWITCHGATE_SWITCH_DOWN ||
14917              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14918              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14919     {
14920       ToggleSwitchgateSwitch();
14921     }
14922     else if (element == EL_LIGHT_SWITCH ||
14923              element == EL_LIGHT_SWITCH_ACTIVE)
14924     {
14925       ToggleLightSwitch(x, y);
14926     }
14927     else if (element == EL_TIMEGATE_SWITCH ||
14928              element == EL_DC_TIMEGATE_SWITCH)
14929     {
14930       ActivateTimegateSwitch(x, y);
14931     }
14932     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14933              element == EL_BALLOON_SWITCH_RIGHT ||
14934              element == EL_BALLOON_SWITCH_UP    ||
14935              element == EL_BALLOON_SWITCH_DOWN  ||
14936              element == EL_BALLOON_SWITCH_NONE  ||
14937              element == EL_BALLOON_SWITCH_ANY)
14938     {
14939       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14940                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14941                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14942                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14943                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14944                              move_direction);
14945     }
14946     else if (element == EL_LAMP)
14947     {
14948       Tile[x][y] = EL_LAMP_ACTIVE;
14949       game.lights_still_needed--;
14950
14951       ResetGfxAnimation(x, y);
14952       TEST_DrawLevelField(x, y);
14953     }
14954     else if (element == EL_TIME_ORB_FULL)
14955     {
14956       Tile[x][y] = EL_TIME_ORB_EMPTY;
14957
14958       if (level.time > 0 || level.use_time_orb_bug)
14959       {
14960         TimeLeft += level.time_orb_time;
14961         game.no_level_time_limit = FALSE;
14962
14963         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14964
14965         DisplayGameControlValues();
14966       }
14967
14968       ResetGfxAnimation(x, y);
14969       TEST_DrawLevelField(x, y);
14970     }
14971     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14972              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14973     {
14974       int xx, yy;
14975
14976       game.ball_active = !game.ball_active;
14977
14978       SCAN_PLAYFIELD(xx, yy)
14979       {
14980         int e = Tile[xx][yy];
14981
14982         if (game.ball_active)
14983         {
14984           if (e == EL_EMC_MAGIC_BALL)
14985             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14986           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14987             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14988         }
14989         else
14990         {
14991           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14992             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14993           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14994             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14995         }
14996       }
14997     }
14998
14999     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15000                                         player->index_bit, dig_side);
15001
15002     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15003                                         player->index_bit, dig_side);
15004
15005     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15006                                         player->index_bit, dig_side);
15007
15008     return MP_ACTION;
15009   }
15010   else
15011   {
15012     if (!PLAYER_SWITCHING(player, x, y))
15013     {
15014       player->is_switching = TRUE;
15015       player->switch_x = x;
15016       player->switch_y = y;
15017
15018       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15019                                  player->index_bit, dig_side);
15020       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15021                                           player->index_bit, dig_side);
15022
15023       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15024                                  player->index_bit, dig_side);
15025       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15026                                           player->index_bit, dig_side);
15027     }
15028
15029     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15030                                player->index_bit, dig_side);
15031     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15032                                         player->index_bit, dig_side);
15033
15034     return MP_NO_ACTION;
15035   }
15036
15037   player->push_delay = -1;
15038
15039   if (is_player)                // function can also be called by EL_PENGUIN
15040   {
15041     if (Tile[x][y] != element)          // really digged/collected something
15042     {
15043       player->is_collecting = !player->is_digging;
15044       player->is_active = TRUE;
15045
15046       player->last_removed_element = element;
15047     }
15048   }
15049
15050   return MP_MOVING;
15051 }
15052
15053 static boolean DigFieldByCE(int x, int y, int digging_element)
15054 {
15055   int element = Tile[x][y];
15056
15057   if (!IS_FREE(x, y))
15058   {
15059     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15060                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15061                   ACTION_BREAKING);
15062
15063     // no element can dig solid indestructible elements
15064     if (IS_INDESTRUCTIBLE(element) &&
15065         !IS_DIGGABLE(element) &&
15066         !IS_COLLECTIBLE(element))
15067       return FALSE;
15068
15069     if (AmoebaNr[x][y] &&
15070         (element == EL_AMOEBA_FULL ||
15071          element == EL_BD_AMOEBA ||
15072          element == EL_AMOEBA_GROWING))
15073     {
15074       AmoebaCnt[AmoebaNr[x][y]]--;
15075       AmoebaCnt2[AmoebaNr[x][y]]--;
15076     }
15077
15078     if (IS_MOVING(x, y))
15079       RemoveMovingField(x, y);
15080     else
15081     {
15082       RemoveField(x, y);
15083       TEST_DrawLevelField(x, y);
15084     }
15085
15086     // if digged element was about to explode, prevent the explosion
15087     ExplodeField[x][y] = EX_TYPE_NONE;
15088
15089     PlayLevelSoundAction(x, y, action);
15090   }
15091
15092   Store[x][y] = EL_EMPTY;
15093
15094   // this makes it possible to leave the removed element again
15095   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15096     Store[x][y] = element;
15097
15098   return TRUE;
15099 }
15100
15101 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15102 {
15103   int jx = player->jx, jy = player->jy;
15104   int x = jx + dx, y = jy + dy;
15105   int snap_direction = (dx == -1 ? MV_LEFT  :
15106                         dx == +1 ? MV_RIGHT :
15107                         dy == -1 ? MV_UP    :
15108                         dy == +1 ? MV_DOWN  : MV_NONE);
15109   boolean can_continue_snapping = (level.continuous_snapping &&
15110                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15111
15112   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15113     return FALSE;
15114
15115   if (!player->active || !IN_LEV_FIELD(x, y))
15116     return FALSE;
15117
15118   if (dx && dy)
15119     return FALSE;
15120
15121   if (!dx && !dy)
15122   {
15123     if (player->MovPos == 0)
15124       player->is_pushing = FALSE;
15125
15126     player->is_snapping = FALSE;
15127
15128     if (player->MovPos == 0)
15129     {
15130       player->is_moving = FALSE;
15131       player->is_digging = FALSE;
15132       player->is_collecting = FALSE;
15133     }
15134
15135     return FALSE;
15136   }
15137
15138   // prevent snapping with already pressed snap key when not allowed
15139   if (player->is_snapping && !can_continue_snapping)
15140     return FALSE;
15141
15142   player->MovDir = snap_direction;
15143
15144   if (player->MovPos == 0)
15145   {
15146     player->is_moving = FALSE;
15147     player->is_digging = FALSE;
15148     player->is_collecting = FALSE;
15149   }
15150
15151   player->is_dropping = FALSE;
15152   player->is_dropping_pressed = FALSE;
15153   player->drop_pressed_delay = 0;
15154
15155   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15156     return FALSE;
15157
15158   player->is_snapping = TRUE;
15159   player->is_active = TRUE;
15160
15161   if (player->MovPos == 0)
15162   {
15163     player->is_moving = FALSE;
15164     player->is_digging = FALSE;
15165     player->is_collecting = FALSE;
15166   }
15167
15168   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15169     TEST_DrawLevelField(player->last_jx, player->last_jy);
15170
15171   TEST_DrawLevelField(x, y);
15172
15173   return TRUE;
15174 }
15175
15176 static boolean DropElement(struct PlayerInfo *player)
15177 {
15178   int old_element, new_element;
15179   int dropx = player->jx, dropy = player->jy;
15180   int drop_direction = player->MovDir;
15181   int drop_side = drop_direction;
15182   int drop_element = get_next_dropped_element(player);
15183
15184   /* do not drop an element on top of another element; when holding drop key
15185      pressed without moving, dropped element must move away before the next
15186      element can be dropped (this is especially important if the next element
15187      is dynamite, which can be placed on background for historical reasons) */
15188   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15189     return MP_ACTION;
15190
15191   if (IS_THROWABLE(drop_element))
15192   {
15193     dropx += GET_DX_FROM_DIR(drop_direction);
15194     dropy += GET_DY_FROM_DIR(drop_direction);
15195
15196     if (!IN_LEV_FIELD(dropx, dropy))
15197       return FALSE;
15198   }
15199
15200   old_element = Tile[dropx][dropy];     // old element at dropping position
15201   new_element = drop_element;           // default: no change when dropping
15202
15203   // check if player is active, not moving and ready to drop
15204   if (!player->active || player->MovPos || player->drop_delay > 0)
15205     return FALSE;
15206
15207   // check if player has anything that can be dropped
15208   if (new_element == EL_UNDEFINED)
15209     return FALSE;
15210
15211   // only set if player has anything that can be dropped
15212   player->is_dropping_pressed = TRUE;
15213
15214   // check if drop key was pressed long enough for EM style dynamite
15215   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15216     return FALSE;
15217
15218   // check if anything can be dropped at the current position
15219   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15220     return FALSE;
15221
15222   // collected custom elements can only be dropped on empty fields
15223   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15224     return FALSE;
15225
15226   if (old_element != EL_EMPTY)
15227     Back[dropx][dropy] = old_element;   // store old element on this field
15228
15229   ResetGfxAnimation(dropx, dropy);
15230   ResetRandomAnimationValue(dropx, dropy);
15231
15232   if (player->inventory_size > 0 ||
15233       player->inventory_infinite_element != EL_UNDEFINED)
15234   {
15235     if (player->inventory_size > 0)
15236     {
15237       player->inventory_size--;
15238
15239       DrawGameDoorValues();
15240
15241       if (new_element == EL_DYNAMITE)
15242         new_element = EL_DYNAMITE_ACTIVE;
15243       else if (new_element == EL_EM_DYNAMITE)
15244         new_element = EL_EM_DYNAMITE_ACTIVE;
15245       else if (new_element == EL_SP_DISK_RED)
15246         new_element = EL_SP_DISK_RED_ACTIVE;
15247     }
15248
15249     Tile[dropx][dropy] = new_element;
15250
15251     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15252       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15253                           el2img(Tile[dropx][dropy]), 0);
15254
15255     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15256
15257     // needed if previous element just changed to "empty" in the last frame
15258     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15259
15260     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15261                                player->index_bit, drop_side);
15262     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15263                                         CE_PLAYER_DROPS_X,
15264                                         player->index_bit, drop_side);
15265
15266     TestIfElementTouchesCustomElement(dropx, dropy);
15267   }
15268   else          // player is dropping a dyna bomb
15269   {
15270     player->dynabombs_left--;
15271
15272     Tile[dropx][dropy] = new_element;
15273
15274     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15275       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15276                           el2img(Tile[dropx][dropy]), 0);
15277
15278     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15279   }
15280
15281   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15282     InitField_WithBug1(dropx, dropy, FALSE);
15283
15284   new_element = Tile[dropx][dropy];     // element might have changed
15285
15286   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15287       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15288   {
15289     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15290       MovDir[dropx][dropy] = drop_direction;
15291
15292     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15293
15294     // do not cause impact style collision by dropping elements that can fall
15295     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15296   }
15297
15298   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15299   player->is_dropping = TRUE;
15300
15301   player->drop_pressed_delay = 0;
15302   player->is_dropping_pressed = FALSE;
15303
15304   player->drop_x = dropx;
15305   player->drop_y = dropy;
15306
15307   return TRUE;
15308 }
15309
15310 // ----------------------------------------------------------------------------
15311 // game sound playing functions
15312 // ----------------------------------------------------------------------------
15313
15314 static int *loop_sound_frame = NULL;
15315 static int *loop_sound_volume = NULL;
15316
15317 void InitPlayLevelSound(void)
15318 {
15319   int num_sounds = getSoundListSize();
15320
15321   checked_free(loop_sound_frame);
15322   checked_free(loop_sound_volume);
15323
15324   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15325   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15326 }
15327
15328 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15329 {
15330   int sx = SCREENX(x), sy = SCREENY(y);
15331   int volume, stereo_position;
15332   int max_distance = 8;
15333   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15334
15335   if ((!setup.sound_simple && !is_loop_sound) ||
15336       (!setup.sound_loops && is_loop_sound))
15337     return;
15338
15339   if (!IN_LEV_FIELD(x, y) ||
15340       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15341       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15342     return;
15343
15344   volume = SOUND_MAX_VOLUME;
15345
15346   if (!IN_SCR_FIELD(sx, sy))
15347   {
15348     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15349     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15350
15351     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15352   }
15353
15354   stereo_position = (SOUND_MAX_LEFT +
15355                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15356                      (SCR_FIELDX + 2 * max_distance));
15357
15358   if (is_loop_sound)
15359   {
15360     /* This assures that quieter loop sounds do not overwrite louder ones,
15361        while restarting sound volume comparison with each new game frame. */
15362
15363     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15364       return;
15365
15366     loop_sound_volume[nr] = volume;
15367     loop_sound_frame[nr] = FrameCounter;
15368   }
15369
15370   PlaySoundExt(nr, volume, stereo_position, type);
15371 }
15372
15373 static void PlayLevelSound(int x, int y, int nr)
15374 {
15375   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15376 }
15377
15378 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15379 {
15380   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15381                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15382                  y < LEVELY(BY1) ? LEVELY(BY1) :
15383                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15384                  sound_action);
15385 }
15386
15387 static void PlayLevelSoundAction(int x, int y, int action)
15388 {
15389   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15390 }
15391
15392 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15393 {
15394   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15395
15396   if (sound_effect != SND_UNDEFINED)
15397     PlayLevelSound(x, y, sound_effect);
15398 }
15399
15400 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15401                                               int action)
15402 {
15403   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15404
15405   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15406     PlayLevelSound(x, y, sound_effect);
15407 }
15408
15409 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15410 {
15411   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15412
15413   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15414     PlayLevelSound(x, y, sound_effect);
15415 }
15416
15417 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15418 {
15419   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15420
15421   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15422     StopSound(sound_effect);
15423 }
15424
15425 static int getLevelMusicNr(void)
15426 {
15427   int level_pos = level_nr - leveldir_current->first_level;
15428
15429   if (levelset.music[level_nr] != MUS_UNDEFINED)
15430     return levelset.music[level_nr];            // from config file
15431   else
15432     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15433 }
15434
15435 static void FadeLevelSounds(void)
15436 {
15437   FadeSounds();
15438 }
15439
15440 static void FadeLevelMusic(void)
15441 {
15442   int music_nr = getLevelMusicNr();
15443   char *curr_music = getCurrentlyPlayingMusicFilename();
15444   char *next_music = getMusicInfoEntryFilename(music_nr);
15445
15446   if (!strEqual(curr_music, next_music))
15447     FadeMusic();
15448 }
15449
15450 void FadeLevelSoundsAndMusic(void)
15451 {
15452   FadeLevelSounds();
15453   FadeLevelMusic();
15454 }
15455
15456 static void PlayLevelMusic(void)
15457 {
15458   int music_nr = getLevelMusicNr();
15459   char *curr_music = getCurrentlyPlayingMusicFilename();
15460   char *next_music = getMusicInfoEntryFilename(music_nr);
15461
15462   if (!strEqual(curr_music, next_music))
15463     PlayMusicLoop(music_nr);
15464 }
15465
15466 static int getSoundAction_BD(int sample)
15467 {
15468   switch (sample)
15469   {
15470     case GD_S_STONE:
15471     case GD_S_NUT:
15472     case GD_S_DIRT_BALL:
15473     case GD_S_NITRO:
15474     case GD_S_FALLING_WALL:
15475       return ACTION_IMPACT;
15476
15477     case GD_S_NUT_CRACK:
15478       return ACTION_BREAKING;
15479
15480     case GD_S_EXPANDING_WALL:
15481     case GD_S_WALL_REAPPEAR:
15482     case GD_S_SLIME:
15483     case GD_S_LAVA:
15484     case GD_S_ACID_SPREAD:
15485       return ACTION_GROWING;
15486
15487     case GD_S_DIAMOND_COLLECT:
15488     case GD_S_SKELETON_COLLECT:
15489     case GD_S_PNEUMATIC_COLLECT:
15490     case GD_S_BOMB_COLLECT:
15491     case GD_S_CLOCK_COLLECT:
15492     case GD_S_SWEET_COLLECT:
15493     case GD_S_KEY_COLLECT:
15494     case GD_S_DIAMOND_KEY_COLLECT:
15495       return ACTION_COLLECTING;
15496
15497     case GD_S_BOMB_PLACE:
15498     case GD_S_REPLICATOR:
15499       return ACTION_DROPPING;
15500
15501     case GD_S_BLADDER_MOVE:
15502       return ACTION_MOVING;
15503
15504     case GD_S_BLADDER_SPENDER:
15505     case GD_S_BLADDER_CONVERT:
15506     case GD_S_GRAVITY_CHANGE:
15507       return ACTION_CHANGING;
15508
15509     case GD_S_BITER_EAT:
15510       return ACTION_EATING;
15511
15512     case GD_S_DOOR_OPEN:
15513     case GD_S_CRACK:
15514       return ACTION_OPENING;
15515
15516     case GD_S_WALK_EARTH:
15517       return ACTION_DIGGING;
15518
15519     case GD_S_WALK_EMPTY:
15520       return ACTION_WALKING;
15521
15522     case GD_S_SWITCH_BITER:
15523     case GD_S_SWITCH_CREATURES:
15524     case GD_S_SWITCH_GRAVITY:
15525     case GD_S_SWITCH_EXPANDING:
15526     case GD_S_SWITCH_CONVEYOR:
15527     case GD_S_SWITCH_REPLICATOR:
15528     case GD_S_STIRRING:
15529       return ACTION_ACTIVATING;
15530
15531     case GD_S_BOX_PUSH:
15532       return ACTION_PUSHING;
15533
15534     case GD_S_TELEPORTER:
15535       return ACTION_PASSING;
15536
15537     case GD_S_EXPLOSION:
15538     case GD_S_BOMB_EXPLOSION:
15539     case GD_S_GHOST_EXPLOSION:
15540     case GD_S_VOODOO_EXPLOSION:
15541     case GD_S_NITRO_EXPLOSION:
15542       return ACTION_EXPLODING;
15543
15544     case GD_S_COVER:
15545     case GD_S_AMOEBA:
15546     case GD_S_AMOEBA_MAGIC:
15547     case GD_S_MAGIC_WALL:
15548     case GD_S_PNEUMATIC_HAMMER:
15549     case GD_S_WATER:
15550       return ACTION_ACTIVE;
15551
15552     case GD_S_DIAMOND_RANDOM:
15553     case GD_S_DIAMOND_1:
15554     case GD_S_DIAMOND_2:
15555     case GD_S_DIAMOND_3:
15556     case GD_S_DIAMOND_4:
15557     case GD_S_DIAMOND_5:
15558     case GD_S_DIAMOND_6:
15559     case GD_S_DIAMOND_7:
15560     case GD_S_DIAMOND_8:
15561     case GD_S_TIMEOUT_0:
15562     case GD_S_TIMEOUT_1:
15563     case GD_S_TIMEOUT_2:
15564     case GD_S_TIMEOUT_3:
15565     case GD_S_TIMEOUT_4:
15566     case GD_S_TIMEOUT_5:
15567     case GD_S_TIMEOUT_6:
15568     case GD_S_TIMEOUT_7:
15569     case GD_S_TIMEOUT_8:
15570     case GD_S_TIMEOUT_9:
15571     case GD_S_TIMEOUT_10:
15572     case GD_S_BONUS_LIFE:
15573       // kludge to prevent playing as loop sound
15574       return ACTION_OTHER;
15575
15576     case GD_S_FINISHED:
15577       return ACTION_DEFAULT;
15578
15579     default:
15580       return ACTION_DEFAULT;
15581   }
15582 }
15583
15584 static int getSoundEffect_BD(int element_bd, int sample)
15585 {
15586   int sound_action = getSoundAction_BD(sample);
15587   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15588   int nr;
15589
15590   // standard sounds
15591   if (sound_action != ACTION_OTHER &&
15592       sound_action != ACTION_DEFAULT)
15593     return sound_effect;
15594
15595   // special sounds
15596   switch (sample)
15597   {
15598     case GD_S_DIAMOND_RANDOM:
15599       nr = GetSimpleRandom(8);
15600       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15601       break;
15602
15603     case GD_S_DIAMOND_1:
15604     case GD_S_DIAMOND_2:
15605     case GD_S_DIAMOND_3:
15606     case GD_S_DIAMOND_4:
15607     case GD_S_DIAMOND_5:
15608     case GD_S_DIAMOND_6:
15609     case GD_S_DIAMOND_7:
15610     case GD_S_DIAMOND_8:
15611       nr = sample - GD_S_DIAMOND_1;
15612       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15613       break;
15614
15615     case GD_S_TIMEOUT_0:
15616     case GD_S_TIMEOUT_1:
15617     case GD_S_TIMEOUT_2:
15618     case GD_S_TIMEOUT_3:
15619     case GD_S_TIMEOUT_4:
15620     case GD_S_TIMEOUT_5:
15621     case GD_S_TIMEOUT_6:
15622     case GD_S_TIMEOUT_7:
15623     case GD_S_TIMEOUT_8:
15624     case GD_S_TIMEOUT_9:
15625     case GD_S_TIMEOUT_10:
15626       nr = sample - GD_S_TIMEOUT_0;
15627       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15628
15629       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15630         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15631       break;
15632
15633     case GD_S_FINISHED:
15634       sound_effect = SND_GAME_LEVELTIME_BONUS;
15635       break;
15636
15637     case GD_S_BONUS_LIFE:
15638       sound_effect = SND_GAME_HEALTH_BONUS;
15639       break;
15640
15641     default:
15642       sound_effect = SND_UNDEFINED;
15643       break;
15644   }
15645
15646   return sound_effect;
15647 }
15648
15649 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15650 {
15651   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15652   int sound_effect = getSoundEffect_BD(element, sample);
15653   int sound_action = getSoundAction_BD(sample);
15654   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15655   int offset = 0;
15656   int x = xx - offset;
15657   int y = yy - offset;
15658
15659   if (sound_action == ACTION_OTHER)
15660     is_loop_sound = FALSE;
15661
15662   if (sound_effect != SND_UNDEFINED)
15663     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15664 }
15665
15666 void StopSound_BD(int element_bd, int sample)
15667 {
15668   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15669   int sound_effect = getSoundEffect_BD(element, sample);
15670
15671   if (sound_effect != SND_UNDEFINED)
15672     StopSound(sound_effect);
15673 }
15674
15675 boolean isSoundPlaying_BD(int element_bd, int sample)
15676 {
15677   int element = (element_bd > -1 ? map_element_BD_to_RND(element_bd) : 0);
15678   int sound_effect = getSoundEffect_BD(element, sample);
15679
15680   if (sound_effect != SND_UNDEFINED)
15681     return isSoundPlaying(sound_effect);
15682
15683   return FALSE;
15684 }
15685
15686 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15687 {
15688   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15689   int offset = 0;
15690   int x = xx - offset;
15691   int y = yy - offset;
15692
15693   switch (sample)
15694   {
15695     case SOUND_blank:
15696       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15697       break;
15698
15699     case SOUND_roll:
15700       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15701       break;
15702
15703     case SOUND_stone:
15704       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15705       break;
15706
15707     case SOUND_nut:
15708       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15709       break;
15710
15711     case SOUND_crack:
15712       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15713       break;
15714
15715     case SOUND_bug:
15716       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15717       break;
15718
15719     case SOUND_tank:
15720       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15721       break;
15722
15723     case SOUND_android_clone:
15724       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15725       break;
15726
15727     case SOUND_android_move:
15728       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15729       break;
15730
15731     case SOUND_spring:
15732       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15733       break;
15734
15735     case SOUND_slurp:
15736       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15737       break;
15738
15739     case SOUND_eater:
15740       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15741       break;
15742
15743     case SOUND_eater_eat:
15744       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15745       break;
15746
15747     case SOUND_alien:
15748       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15749       break;
15750
15751     case SOUND_collect:
15752       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15753       break;
15754
15755     case SOUND_diamond:
15756       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15757       break;
15758
15759     case SOUND_squash:
15760       // !!! CHECK THIS !!!
15761 #if 1
15762       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15763 #else
15764       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15765 #endif
15766       break;
15767
15768     case SOUND_wonderfall:
15769       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15770       break;
15771
15772     case SOUND_drip:
15773       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15774       break;
15775
15776     case SOUND_push:
15777       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15778       break;
15779
15780     case SOUND_dirt:
15781       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15782       break;
15783
15784     case SOUND_acid:
15785       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15786       break;
15787
15788     case SOUND_ball:
15789       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15790       break;
15791
15792     case SOUND_slide:
15793       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15794       break;
15795
15796     case SOUND_wonder:
15797       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15798       break;
15799
15800     case SOUND_door:
15801       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15802       break;
15803
15804     case SOUND_exit_open:
15805       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15806       break;
15807
15808     case SOUND_exit_leave:
15809       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15810       break;
15811
15812     case SOUND_dynamite:
15813       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15814       break;
15815
15816     case SOUND_tick:
15817       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15818       break;
15819
15820     case SOUND_press:
15821       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15822       break;
15823
15824     case SOUND_wheel:
15825       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15826       break;
15827
15828     case SOUND_boom:
15829       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15830       break;
15831
15832     case SOUND_die:
15833       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15834       break;
15835
15836     case SOUND_time:
15837       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15838       break;
15839
15840     default:
15841       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15842       break;
15843   }
15844 }
15845
15846 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15847 {
15848   int element = map_element_SP_to_RND(element_sp);
15849   int action = map_action_SP_to_RND(action_sp);
15850   int offset = (setup.sp_show_border_elements ? 0 : 1);
15851   int x = xx - offset;
15852   int y = yy - offset;
15853
15854   PlayLevelSoundElementAction(x, y, element, action);
15855 }
15856
15857 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15858 {
15859   int element = map_element_MM_to_RND(element_mm);
15860   int action = map_action_MM_to_RND(action_mm);
15861   int offset = 0;
15862   int x = xx - offset;
15863   int y = yy - offset;
15864
15865   if (!IS_MM_ELEMENT(element))
15866     element = EL_MM_DEFAULT;
15867
15868   PlayLevelSoundElementAction(x, y, element, action);
15869 }
15870
15871 void PlaySound_MM(int sound_mm)
15872 {
15873   int sound = map_sound_MM_to_RND(sound_mm);
15874
15875   if (sound == SND_UNDEFINED)
15876     return;
15877
15878   PlaySound(sound);
15879 }
15880
15881 void PlaySoundLoop_MM(int sound_mm)
15882 {
15883   int sound = map_sound_MM_to_RND(sound_mm);
15884
15885   if (sound == SND_UNDEFINED)
15886     return;
15887
15888   PlaySoundLoop(sound);
15889 }
15890
15891 void StopSound_MM(int sound_mm)
15892 {
15893   int sound = map_sound_MM_to_RND(sound_mm);
15894
15895   if (sound == SND_UNDEFINED)
15896     return;
15897
15898   StopSound(sound);
15899 }
15900
15901 void RaiseScore(int value)
15902 {
15903   game.score += value;
15904
15905   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15906
15907   DisplayGameControlValues();
15908 }
15909
15910 void RaiseScoreElement(int element)
15911 {
15912   switch (element)
15913   {
15914     case EL_EMERALD:
15915     case EL_BD_DIAMOND:
15916     case EL_EMERALD_YELLOW:
15917     case EL_EMERALD_RED:
15918     case EL_EMERALD_PURPLE:
15919     case EL_SP_INFOTRON:
15920       RaiseScore(level.score[SC_EMERALD]);
15921       break;
15922     case EL_DIAMOND:
15923       RaiseScore(level.score[SC_DIAMOND]);
15924       break;
15925     case EL_CRYSTAL:
15926       RaiseScore(level.score[SC_CRYSTAL]);
15927       break;
15928     case EL_PEARL:
15929       RaiseScore(level.score[SC_PEARL]);
15930       break;
15931     case EL_BUG:
15932     case EL_BD_BUTTERFLY:
15933     case EL_SP_ELECTRON:
15934       RaiseScore(level.score[SC_BUG]);
15935       break;
15936     case EL_SPACESHIP:
15937     case EL_BD_FIREFLY:
15938     case EL_SP_SNIKSNAK:
15939       RaiseScore(level.score[SC_SPACESHIP]);
15940       break;
15941     case EL_YAMYAM:
15942     case EL_DARK_YAMYAM:
15943       RaiseScore(level.score[SC_YAMYAM]);
15944       break;
15945     case EL_ROBOT:
15946       RaiseScore(level.score[SC_ROBOT]);
15947       break;
15948     case EL_PACMAN:
15949       RaiseScore(level.score[SC_PACMAN]);
15950       break;
15951     case EL_NUT:
15952       RaiseScore(level.score[SC_NUT]);
15953       break;
15954     case EL_DYNAMITE:
15955     case EL_EM_DYNAMITE:
15956     case EL_SP_DISK_RED:
15957     case EL_DYNABOMB_INCREASE_NUMBER:
15958     case EL_DYNABOMB_INCREASE_SIZE:
15959     case EL_DYNABOMB_INCREASE_POWER:
15960       RaiseScore(level.score[SC_DYNAMITE]);
15961       break;
15962     case EL_SHIELD_NORMAL:
15963     case EL_SHIELD_DEADLY:
15964       RaiseScore(level.score[SC_SHIELD]);
15965       break;
15966     case EL_EXTRA_TIME:
15967       RaiseScore(level.extra_time_score);
15968       break;
15969     case EL_KEY_1:
15970     case EL_KEY_2:
15971     case EL_KEY_3:
15972     case EL_KEY_4:
15973     case EL_EM_KEY_1:
15974     case EL_EM_KEY_2:
15975     case EL_EM_KEY_3:
15976     case EL_EM_KEY_4:
15977     case EL_EMC_KEY_5:
15978     case EL_EMC_KEY_6:
15979     case EL_EMC_KEY_7:
15980     case EL_EMC_KEY_8:
15981     case EL_DC_KEY_WHITE:
15982       RaiseScore(level.score[SC_KEY]);
15983       break;
15984     default:
15985       RaiseScore(element_info[element].collect_score);
15986       break;
15987   }
15988 }
15989
15990 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15991 {
15992   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15993   {
15994     if (!quick_quit)
15995     {
15996       // prevent short reactivation of overlay buttons while closing door
15997       SetOverlayActive(FALSE);
15998       UnmapGameButtons();
15999
16000       // door may still be open due to skipped or envelope style request
16001       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16002     }
16003
16004     if (network.enabled)
16005     {
16006       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16007     }
16008     else
16009     {
16010       if (quick_quit)
16011         FadeSkipNextFadeIn();
16012
16013       SetGameStatus(GAME_MODE_MAIN);
16014
16015       DrawMainMenu();
16016     }
16017   }
16018   else          // continue playing the game
16019   {
16020     if (tape.playing && tape.deactivate_display)
16021       TapeDeactivateDisplayOff(TRUE);
16022
16023     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16024
16025     if (tape.playing && tape.deactivate_display)
16026       TapeDeactivateDisplayOn();
16027   }
16028 }
16029
16030 void RequestQuitGame(boolean escape_key_pressed)
16031 {
16032   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16033   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16034                         level_editor_test_game);
16035   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16036                           quick_quit || score_info_tape_play);
16037
16038   RequestQuitGameExt(skip_request, quick_quit,
16039                      "Do you really want to quit the game?");
16040 }
16041
16042 static char *getRestartGameMessage(void)
16043 {
16044   boolean play_again = hasStartedNetworkGame();
16045   static char message[MAX_OUTPUT_LINESIZE];
16046   char *game_over_text = "Game over!";
16047   char *play_again_text = " Play it again?";
16048
16049   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16050       game_mm.game_over_message != NULL)
16051     game_over_text = game_mm.game_over_message;
16052
16053   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16054            (play_again ? play_again_text : ""));
16055
16056   return message;
16057 }
16058
16059 static void RequestRestartGame(void)
16060 {
16061   char *message = getRestartGameMessage();
16062   boolean has_started_game = hasStartedNetworkGame();
16063   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16064   int door_state = DOOR_CLOSE_1;
16065
16066   if (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game)
16067   {
16068     CloseDoor(door_state);
16069
16070     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16071   }
16072   else
16073   {
16074     // if game was invoked from level editor, also close tape recorder door
16075     if (level_editor_test_game)
16076       door_state = DOOR_CLOSE_ALL;
16077
16078     CloseDoor(door_state);
16079
16080     SetGameStatus(GAME_MODE_MAIN);
16081
16082     DrawMainMenu();
16083   }
16084 }
16085
16086 boolean CheckRestartGame(void)
16087 {
16088   static int game_over_delay = 0;
16089   int game_over_delay_value = 50;
16090   boolean game_over = checkGameFailed();
16091
16092   if (!game_over)
16093   {
16094     game_over_delay = game_over_delay_value;
16095
16096     return FALSE;
16097   }
16098
16099   if (game_over_delay > 0)
16100   {
16101     if (game_over_delay == game_over_delay_value / 2)
16102       PlaySound(SND_GAME_LOSING);
16103
16104     game_over_delay--;
16105
16106     return FALSE;
16107   }
16108
16109   // do not ask to play again if request dialog is already active
16110   if (game.request_active)
16111     return FALSE;
16112
16113   // do not ask to play again if request dialog already handled
16114   if (game.RestartGameRequested)
16115     return FALSE;
16116
16117   // do not ask to play again if game was never actually played
16118   if (!game.GamePlayed)
16119     return FALSE;
16120
16121   // do not ask to play again if this was disabled in setup menu
16122   if (!setup.ask_on_game_over)
16123     return FALSE;
16124
16125   game.RestartGameRequested = TRUE;
16126
16127   RequestRestartGame();
16128
16129   return TRUE;
16130 }
16131
16132 boolean checkGameSolved(void)
16133 {
16134   // set for all game engines if level was solved
16135   return game.LevelSolved_GameEnd;
16136 }
16137
16138 boolean checkGameFailed(void)
16139 {
16140   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16141     return (game_bd.game_over && !game_bd.level_solved);
16142   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16143     return (game_em.game_over && !game_em.level_solved);
16144   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16145     return (game_sp.game_over && !game_sp.level_solved);
16146   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16147     return (game_mm.game_over && !game_mm.level_solved);
16148   else                          // GAME_ENGINE_TYPE_RND
16149     return (game.GameOver && !game.LevelSolved);
16150 }
16151
16152 boolean checkGameEnded(void)
16153 {
16154   return (checkGameSolved() || checkGameFailed());
16155 }
16156
16157
16158 // ----------------------------------------------------------------------------
16159 // random generator functions
16160 // ----------------------------------------------------------------------------
16161
16162 unsigned int InitEngineRandom_RND(int seed)
16163 {
16164   game.num_random_calls = 0;
16165
16166   return InitEngineRandom(seed);
16167 }
16168
16169 unsigned int RND(int max)
16170 {
16171   if (max > 0)
16172   {
16173     game.num_random_calls++;
16174
16175     return GetEngineRandom(max);
16176   }
16177
16178   return 0;
16179 }
16180
16181
16182 // ----------------------------------------------------------------------------
16183 // game engine snapshot handling functions
16184 // ----------------------------------------------------------------------------
16185
16186 struct EngineSnapshotInfo
16187 {
16188   // runtime values for custom element collect score
16189   int collect_score[NUM_CUSTOM_ELEMENTS];
16190
16191   // runtime values for group element choice position
16192   int choice_pos[NUM_GROUP_ELEMENTS];
16193
16194   // runtime values for belt position animations
16195   int belt_graphic[4][NUM_BELT_PARTS];
16196   int belt_anim_mode[4][NUM_BELT_PARTS];
16197 };
16198
16199 static struct EngineSnapshotInfo engine_snapshot_rnd;
16200 static char *snapshot_level_identifier = NULL;
16201 static int snapshot_level_nr = -1;
16202
16203 static void SaveEngineSnapshotValues_RND(void)
16204 {
16205   static int belt_base_active_element[4] =
16206   {
16207     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16208     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16209     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16210     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16211   };
16212   int i, j;
16213
16214   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16215   {
16216     int element = EL_CUSTOM_START + i;
16217
16218     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16219   }
16220
16221   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16222   {
16223     int element = EL_GROUP_START + i;
16224
16225     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16226   }
16227
16228   for (i = 0; i < 4; i++)
16229   {
16230     for (j = 0; j < NUM_BELT_PARTS; j++)
16231     {
16232       int element = belt_base_active_element[i] + j;
16233       int graphic = el2img(element);
16234       int anim_mode = graphic_info[graphic].anim_mode;
16235
16236       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16237       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16238     }
16239   }
16240 }
16241
16242 static void LoadEngineSnapshotValues_RND(void)
16243 {
16244   unsigned int num_random_calls = game.num_random_calls;
16245   int i, j;
16246
16247   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16248   {
16249     int element = EL_CUSTOM_START + i;
16250
16251     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16252   }
16253
16254   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16255   {
16256     int element = EL_GROUP_START + i;
16257
16258     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16259   }
16260
16261   for (i = 0; i < 4; i++)
16262   {
16263     for (j = 0; j < NUM_BELT_PARTS; j++)
16264     {
16265       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16266       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16267
16268       graphic_info[graphic].anim_mode = anim_mode;
16269     }
16270   }
16271
16272   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16273   {
16274     InitRND(tape.random_seed);
16275     for (i = 0; i < num_random_calls; i++)
16276       RND(1);
16277   }
16278
16279   if (game.num_random_calls != num_random_calls)
16280   {
16281     Error("number of random calls out of sync");
16282     Error("number of random calls should be %d", num_random_calls);
16283     Error("number of random calls is %d", game.num_random_calls);
16284
16285     Fail("this should not happen -- please debug");
16286   }
16287 }
16288
16289 void FreeEngineSnapshotSingle(void)
16290 {
16291   FreeSnapshotSingle();
16292
16293   setString(&snapshot_level_identifier, NULL);
16294   snapshot_level_nr = -1;
16295 }
16296
16297 void FreeEngineSnapshotList(void)
16298 {
16299   FreeSnapshotList();
16300 }
16301
16302 static ListNode *SaveEngineSnapshotBuffers(void)
16303 {
16304   ListNode *buffers = NULL;
16305
16306   // copy some special values to a structure better suited for the snapshot
16307
16308   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16309     SaveEngineSnapshotValues_RND();
16310   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16311     SaveEngineSnapshotValues_EM();
16312   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16313     SaveEngineSnapshotValues_SP(&buffers);
16314   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16315     SaveEngineSnapshotValues_MM();
16316
16317   // save values stored in special snapshot structure
16318
16319   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16320     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16321   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16322     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16323   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16324     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16325   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16326     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16327
16328   // save further RND engine values
16329
16330   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16331   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16332   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16333
16334   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16335   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16336   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16337   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16338   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16339   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16340
16341   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16342   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16343   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16344
16345   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16346
16347   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16348   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16349
16350   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16351   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16352   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16353   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16354   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16355   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16356   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16357   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16358   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16359   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16360   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16361   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16362   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16363   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16364   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16365   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16366   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16367   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16368
16369   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16370   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16371
16372   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16373   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16374   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16375
16376   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16377   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16378
16379   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16380   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16381   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16382   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16383   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16384   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16385
16386   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16387   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16388
16389 #if 0
16390   ListNode *node = engine_snapshot_list_rnd;
16391   int num_bytes = 0;
16392
16393   while (node != NULL)
16394   {
16395     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16396
16397     node = node->next;
16398   }
16399
16400   Debug("game:playing:SaveEngineSnapshotBuffers",
16401         "size of engine snapshot: %d bytes", num_bytes);
16402 #endif
16403
16404   return buffers;
16405 }
16406
16407 void SaveEngineSnapshotSingle(void)
16408 {
16409   ListNode *buffers = SaveEngineSnapshotBuffers();
16410
16411   // finally save all snapshot buffers to single snapshot
16412   SaveSnapshotSingle(buffers);
16413
16414   // save level identification information
16415   setString(&snapshot_level_identifier, leveldir_current->identifier);
16416   snapshot_level_nr = level_nr;
16417 }
16418
16419 boolean CheckSaveEngineSnapshotToList(void)
16420 {
16421   boolean save_snapshot =
16422     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16423      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16424       game.snapshot.changed_action) ||
16425      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16426       game.snapshot.collected_item));
16427
16428   game.snapshot.changed_action = FALSE;
16429   game.snapshot.collected_item = FALSE;
16430   game.snapshot.save_snapshot = save_snapshot;
16431
16432   return save_snapshot;
16433 }
16434
16435 void SaveEngineSnapshotToList(void)
16436 {
16437   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16438       tape.quick_resume)
16439     return;
16440
16441   ListNode *buffers = SaveEngineSnapshotBuffers();
16442
16443   // finally save all snapshot buffers to snapshot list
16444   SaveSnapshotToList(buffers);
16445 }
16446
16447 void SaveEngineSnapshotToListInitial(void)
16448 {
16449   FreeEngineSnapshotList();
16450
16451   SaveEngineSnapshotToList();
16452 }
16453
16454 static void LoadEngineSnapshotValues(void)
16455 {
16456   // restore special values from snapshot structure
16457
16458   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16459     LoadEngineSnapshotValues_RND();
16460   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16461     LoadEngineSnapshotValues_EM();
16462   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16463     LoadEngineSnapshotValues_SP();
16464   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16465     LoadEngineSnapshotValues_MM();
16466 }
16467
16468 void LoadEngineSnapshotSingle(void)
16469 {
16470   LoadSnapshotSingle();
16471
16472   LoadEngineSnapshotValues();
16473 }
16474
16475 static void LoadEngineSnapshot_Undo(int steps)
16476 {
16477   LoadSnapshotFromList_Older(steps);
16478
16479   LoadEngineSnapshotValues();
16480 }
16481
16482 static void LoadEngineSnapshot_Redo(int steps)
16483 {
16484   LoadSnapshotFromList_Newer(steps);
16485
16486   LoadEngineSnapshotValues();
16487 }
16488
16489 boolean CheckEngineSnapshotSingle(void)
16490 {
16491   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16492           snapshot_level_nr == level_nr);
16493 }
16494
16495 boolean CheckEngineSnapshotList(void)
16496 {
16497   return CheckSnapshotList();
16498 }
16499
16500
16501 // ---------- new game button stuff -------------------------------------------
16502
16503 static struct
16504 {
16505   int graphic;
16506   struct XY *pos;
16507   int gadget_id;
16508   boolean *setup_value;
16509   boolean allowed_on_tape;
16510   boolean is_touch_button;
16511   char *infotext;
16512 } gamebutton_info[NUM_GAME_BUTTONS] =
16513 {
16514   {
16515     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16516     GAME_CTRL_ID_STOP,                          NULL,
16517     TRUE, FALSE,                                "stop game"
16518   },
16519   {
16520     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16521     GAME_CTRL_ID_PAUSE,                         NULL,
16522     TRUE, FALSE,                                "pause game"
16523   },
16524   {
16525     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16526     GAME_CTRL_ID_PLAY,                          NULL,
16527     TRUE, FALSE,                                "play game"
16528   },
16529   {
16530     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16531     GAME_CTRL_ID_UNDO,                          NULL,
16532     TRUE, FALSE,                                "undo step"
16533   },
16534   {
16535     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16536     GAME_CTRL_ID_REDO,                          NULL,
16537     TRUE, FALSE,                                "redo step"
16538   },
16539   {
16540     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16541     GAME_CTRL_ID_SAVE,                          NULL,
16542     TRUE, FALSE,                                "save game"
16543   },
16544   {
16545     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16546     GAME_CTRL_ID_PAUSE2,                        NULL,
16547     TRUE, FALSE,                                "pause game"
16548   },
16549   {
16550     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16551     GAME_CTRL_ID_LOAD,                          NULL,
16552     TRUE, FALSE,                                "load game"
16553   },
16554   {
16555     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16556     GAME_CTRL_ID_RESTART,                       NULL,
16557     TRUE, FALSE,                                "restart game"
16558   },
16559   {
16560     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16561     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16562     FALSE, FALSE,                               "stop game"
16563   },
16564   {
16565     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16566     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16567     FALSE, FALSE,                               "pause game"
16568   },
16569   {
16570     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16571     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16572     FALSE, FALSE,                               "play game"
16573   },
16574   {
16575     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16576     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16577     FALSE, FALSE,                               "restart game"
16578   },
16579   {
16580     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16581     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16582     FALSE, TRUE,                                "stop game"
16583   },
16584   {
16585     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16586     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16587     FALSE, TRUE,                                "pause game"
16588   },
16589   {
16590     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16591     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16592     FALSE, TRUE,                                "restart game"
16593   },
16594   {
16595     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16596     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16597     TRUE, FALSE,                                "background music on/off"
16598   },
16599   {
16600     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16601     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16602     TRUE, FALSE,                                "sound loops on/off"
16603   },
16604   {
16605     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16606     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16607     TRUE, FALSE,                                "normal sounds on/off"
16608   },
16609   {
16610     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16611     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16612     FALSE, FALSE,                               "background music on/off"
16613   },
16614   {
16615     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16616     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16617     FALSE, FALSE,                               "sound loops on/off"
16618   },
16619   {
16620     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16621     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16622     FALSE, FALSE,                               "normal sounds on/off"
16623   }
16624 };
16625
16626 void CreateGameButtons(void)
16627 {
16628   int i;
16629
16630   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16631   {
16632     int graphic = gamebutton_info[i].graphic;
16633     struct GraphicInfo *gfx = &graphic_info[graphic];
16634     struct XY *pos = gamebutton_info[i].pos;
16635     struct GadgetInfo *gi;
16636     int button_type;
16637     boolean checked;
16638     unsigned int event_mask;
16639     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16640     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16641     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16642     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16643     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16644     int gd_x   = gfx->src_x;
16645     int gd_y   = gfx->src_y;
16646     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16647     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16648     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16649     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16650     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16651     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16652     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16653     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16654     int id = i;
16655
16656     // do not use touch buttons if overlay touch buttons are disabled
16657     if (is_touch_button && !setup.touch.overlay_buttons)
16658       continue;
16659
16660     if (gfx->bitmap == NULL)
16661     {
16662       game_gadget[id] = NULL;
16663
16664       continue;
16665     }
16666
16667     if (id == GAME_CTRL_ID_STOP ||
16668         id == GAME_CTRL_ID_PANEL_STOP ||
16669         id == GAME_CTRL_ID_TOUCH_STOP ||
16670         id == GAME_CTRL_ID_PLAY ||
16671         id == GAME_CTRL_ID_PANEL_PLAY ||
16672         id == GAME_CTRL_ID_SAVE ||
16673         id == GAME_CTRL_ID_LOAD ||
16674         id == GAME_CTRL_ID_RESTART ||
16675         id == GAME_CTRL_ID_PANEL_RESTART ||
16676         id == GAME_CTRL_ID_TOUCH_RESTART)
16677     {
16678       button_type = GD_TYPE_NORMAL_BUTTON;
16679       checked = FALSE;
16680       event_mask = GD_EVENT_RELEASED;
16681     }
16682     else if (id == GAME_CTRL_ID_UNDO ||
16683              id == GAME_CTRL_ID_REDO)
16684     {
16685       button_type = GD_TYPE_NORMAL_BUTTON;
16686       checked = FALSE;
16687       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16688     }
16689     else
16690     {
16691       button_type = GD_TYPE_CHECK_BUTTON;
16692       checked = (gamebutton_info[i].setup_value != NULL ?
16693                  *gamebutton_info[i].setup_value : FALSE);
16694       event_mask = GD_EVENT_PRESSED;
16695     }
16696
16697     gi = CreateGadget(GDI_CUSTOM_ID, id,
16698                       GDI_IMAGE_ID, graphic,
16699                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16700                       GDI_X, base_x + x,
16701                       GDI_Y, base_y + y,
16702                       GDI_WIDTH, gfx->width,
16703                       GDI_HEIGHT, gfx->height,
16704                       GDI_TYPE, button_type,
16705                       GDI_STATE, GD_BUTTON_UNPRESSED,
16706                       GDI_CHECKED, checked,
16707                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16708                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16709                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16710                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16711                       GDI_DIRECT_DRAW, FALSE,
16712                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16713                       GDI_EVENT_MASK, event_mask,
16714                       GDI_CALLBACK_ACTION, HandleGameButtons,
16715                       GDI_END);
16716
16717     if (gi == NULL)
16718       Fail("cannot create gadget");
16719
16720     game_gadget[id] = gi;
16721   }
16722 }
16723
16724 void FreeGameButtons(void)
16725 {
16726   int i;
16727
16728   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16729     FreeGadget(game_gadget[i]);
16730 }
16731
16732 static void UnmapGameButtonsAtSamePosition(int id)
16733 {
16734   int i;
16735
16736   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16737     if (i != id &&
16738         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16739         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16740       UnmapGadget(game_gadget[i]);
16741 }
16742
16743 static void UnmapGameButtonsAtSamePosition_All(void)
16744 {
16745   if (setup.show_load_save_buttons)
16746   {
16747     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16748     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16749     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16750   }
16751   else if (setup.show_undo_redo_buttons)
16752   {
16753     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16754     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16755     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16756   }
16757   else
16758   {
16759     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16760     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16761     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16762
16763     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16764     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16765     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16766   }
16767 }
16768
16769 void MapLoadSaveButtons(void)
16770 {
16771   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16772   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16773
16774   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16775   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16776 }
16777
16778 void MapUndoRedoButtons(void)
16779 {
16780   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16781   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16782
16783   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16784   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16785 }
16786
16787 void ModifyPauseButtons(void)
16788 {
16789   static int ids[] =
16790   {
16791     GAME_CTRL_ID_PAUSE,
16792     GAME_CTRL_ID_PAUSE2,
16793     GAME_CTRL_ID_PANEL_PAUSE,
16794     GAME_CTRL_ID_TOUCH_PAUSE,
16795     -1
16796   };
16797   int i;
16798
16799   // do not redraw pause button on closed door (may happen when restarting game)
16800   if (!(GetDoorState() & DOOR_OPEN_1))
16801     return;
16802
16803   for (i = 0; ids[i] > -1; i++)
16804     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16805 }
16806
16807 static void MapGameButtonsExt(boolean on_tape)
16808 {
16809   int i;
16810
16811   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16812   {
16813     if ((i == GAME_CTRL_ID_UNDO ||
16814          i == GAME_CTRL_ID_REDO) &&
16815         game_status != GAME_MODE_PLAYING)
16816       continue;
16817
16818     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16819       MapGadget(game_gadget[i]);
16820   }
16821
16822   UnmapGameButtonsAtSamePosition_All();
16823
16824   RedrawGameButtons();
16825 }
16826
16827 static void UnmapGameButtonsExt(boolean on_tape)
16828 {
16829   int i;
16830
16831   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16832     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16833       UnmapGadget(game_gadget[i]);
16834 }
16835
16836 static void RedrawGameButtonsExt(boolean on_tape)
16837 {
16838   int i;
16839
16840   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16841     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16842       RedrawGadget(game_gadget[i]);
16843 }
16844
16845 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16846 {
16847   if (gi == NULL)
16848     return;
16849
16850   gi->checked = state;
16851 }
16852
16853 static void RedrawSoundButtonGadget(int id)
16854 {
16855   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16856              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16857              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16858              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16859              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16860              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16861              id);
16862
16863   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16864   RedrawGadget(game_gadget[id2]);
16865 }
16866
16867 void MapGameButtons(void)
16868 {
16869   MapGameButtonsExt(FALSE);
16870 }
16871
16872 void UnmapGameButtons(void)
16873 {
16874   UnmapGameButtonsExt(FALSE);
16875 }
16876
16877 void RedrawGameButtons(void)
16878 {
16879   RedrawGameButtonsExt(FALSE);
16880 }
16881
16882 void MapGameButtonsOnTape(void)
16883 {
16884   MapGameButtonsExt(TRUE);
16885 }
16886
16887 void UnmapGameButtonsOnTape(void)
16888 {
16889   UnmapGameButtonsExt(TRUE);
16890 }
16891
16892 void RedrawGameButtonsOnTape(void)
16893 {
16894   RedrawGameButtonsExt(TRUE);
16895 }
16896
16897 static void GameUndoRedoExt(void)
16898 {
16899   ClearPlayerAction();
16900
16901   tape.pausing = TRUE;
16902
16903   RedrawPlayfield();
16904   UpdateAndDisplayGameControlValues();
16905
16906   DrawCompleteVideoDisplay();
16907   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16908   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16909   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16910
16911   ModifyPauseButtons();
16912
16913   BackToFront();
16914 }
16915
16916 static void GameUndo(int steps)
16917 {
16918   if (!CheckEngineSnapshotList())
16919     return;
16920
16921   int tape_property_bits = tape.property_bits;
16922
16923   LoadEngineSnapshot_Undo(steps);
16924
16925   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16926
16927   GameUndoRedoExt();
16928 }
16929
16930 static void GameRedo(int steps)
16931 {
16932   if (!CheckEngineSnapshotList())
16933     return;
16934
16935   int tape_property_bits = tape.property_bits;
16936
16937   LoadEngineSnapshot_Redo(steps);
16938
16939   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16940
16941   GameUndoRedoExt();
16942 }
16943
16944 static void HandleGameButtonsExt(int id, int button)
16945 {
16946   static boolean game_undo_executed = FALSE;
16947   int steps = BUTTON_STEPSIZE(button);
16948   boolean handle_game_buttons =
16949     (game_status == GAME_MODE_PLAYING ||
16950      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16951
16952   if (!handle_game_buttons)
16953     return;
16954
16955   switch (id)
16956   {
16957     case GAME_CTRL_ID_STOP:
16958     case GAME_CTRL_ID_PANEL_STOP:
16959     case GAME_CTRL_ID_TOUCH_STOP:
16960       TapeStopGame();
16961
16962       break;
16963
16964     case GAME_CTRL_ID_PAUSE:
16965     case GAME_CTRL_ID_PAUSE2:
16966     case GAME_CTRL_ID_PANEL_PAUSE:
16967     case GAME_CTRL_ID_TOUCH_PAUSE:
16968       if (network.enabled && game_status == GAME_MODE_PLAYING)
16969       {
16970         if (tape.pausing)
16971           SendToServer_ContinuePlaying();
16972         else
16973           SendToServer_PausePlaying();
16974       }
16975       else
16976         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16977
16978       game_undo_executed = FALSE;
16979
16980       break;
16981
16982     case GAME_CTRL_ID_PLAY:
16983     case GAME_CTRL_ID_PANEL_PLAY:
16984       if (game_status == GAME_MODE_MAIN)
16985       {
16986         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16987       }
16988       else if (tape.pausing)
16989       {
16990         if (network.enabled)
16991           SendToServer_ContinuePlaying();
16992         else
16993           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16994       }
16995       break;
16996
16997     case GAME_CTRL_ID_UNDO:
16998       // Important: When using "save snapshot when collecting an item" mode,
16999       // load last (current) snapshot for first "undo" after pressing "pause"
17000       // (else the last-but-one snapshot would be loaded, because the snapshot
17001       // pointer already points to the last snapshot when pressing "pause",
17002       // which is fine for "every step/move" mode, but not for "every collect")
17003       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17004           !game_undo_executed)
17005         steps--;
17006
17007       game_undo_executed = TRUE;
17008
17009       GameUndo(steps);
17010       break;
17011
17012     case GAME_CTRL_ID_REDO:
17013       GameRedo(steps);
17014       break;
17015
17016     case GAME_CTRL_ID_SAVE:
17017       TapeQuickSave();
17018       break;
17019
17020     case GAME_CTRL_ID_LOAD:
17021       TapeQuickLoad();
17022       break;
17023
17024     case GAME_CTRL_ID_RESTART:
17025     case GAME_CTRL_ID_PANEL_RESTART:
17026     case GAME_CTRL_ID_TOUCH_RESTART:
17027       TapeRestartGame();
17028
17029       break;
17030
17031     case SOUND_CTRL_ID_MUSIC:
17032     case SOUND_CTRL_ID_PANEL_MUSIC:
17033       if (setup.sound_music)
17034       { 
17035         setup.sound_music = FALSE;
17036
17037         FadeMusic();
17038       }
17039       else if (audio.music_available)
17040       { 
17041         setup.sound = setup.sound_music = TRUE;
17042
17043         SetAudioMode(setup.sound);
17044
17045         if (game_status == GAME_MODE_PLAYING)
17046           PlayLevelMusic();
17047       }
17048
17049       RedrawSoundButtonGadget(id);
17050
17051       break;
17052
17053     case SOUND_CTRL_ID_LOOPS:
17054     case SOUND_CTRL_ID_PANEL_LOOPS:
17055       if (setup.sound_loops)
17056         setup.sound_loops = FALSE;
17057       else if (audio.loops_available)
17058       {
17059         setup.sound = setup.sound_loops = TRUE;
17060
17061         SetAudioMode(setup.sound);
17062       }
17063
17064       RedrawSoundButtonGadget(id);
17065
17066       break;
17067
17068     case SOUND_CTRL_ID_SIMPLE:
17069     case SOUND_CTRL_ID_PANEL_SIMPLE:
17070       if (setup.sound_simple)
17071         setup.sound_simple = FALSE;
17072       else if (audio.sound_available)
17073       {
17074         setup.sound = setup.sound_simple = TRUE;
17075
17076         SetAudioMode(setup.sound);
17077       }
17078
17079       RedrawSoundButtonGadget(id);
17080
17081       break;
17082
17083     default:
17084       break;
17085   }
17086 }
17087
17088 static void HandleGameButtons(struct GadgetInfo *gi)
17089 {
17090   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17091 }
17092
17093 void HandleSoundButtonKeys(Key key)
17094 {
17095   if (key == setup.shortcut.sound_simple)
17096     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17097   else if (key == setup.shortcut.sound_loops)
17098     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17099   else if (key == setup.shortcut.sound_music)
17100     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17101 }