2c16aa62c93d95c8a5344eb9f5d44aea92955c54
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  https://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22 #include "anim.h"
23
24
25 // DEBUG SETTINGS
26 #define DEBUG_INIT_PLAYER       1
27 #define DEBUG_PLAYER_ACTIONS    0
28
29 // EXPERIMENTAL STUFF
30 #define USE_NEW_AMOEBA_CODE     FALSE
31
32 // EXPERIMENTAL STUFF
33 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
34 #define USE_QUICKSAND_IMPACT_BUGFIX     0
35 #define USE_DELAYED_GFX_REDRAW          0
36 #define USE_NEW_PLAYER_ASSIGNMENTS      1
37
38 #if USE_DELAYED_GFX_REDRAW
39 #define TEST_DrawLevelField(x, y)                               \
40         GfxRedraw[x][y] |= GFX_REDRAW_TILE
41 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
42         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
43 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
44         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
45 #define TEST_DrawTwinkleOnField(x, y)                           \
46         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
47 #else
48 #define TEST_DrawLevelField(x, y)                               \
49              DrawLevelField(x, y)
50 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
51              DrawLevelFieldCrumbled(x, y)
52 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
53              DrawLevelFieldCrumbledNeighbours(x, y)
54 #define TEST_DrawTwinkleOnField(x, y)                           \
55              DrawTwinkleOnField(x, y)
56 #endif
57
58
59 // for DigField()
60 #define DF_NO_PUSH              0
61 #define DF_DIG                  1
62 #define DF_SNAP                 2
63
64 // for MovePlayer()
65 #define MP_NO_ACTION            0
66 #define MP_MOVING               1
67 #define MP_ACTION               2
68 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
69
70 // for ScrollPlayer()
71 #define SCROLL_INIT             0
72 #define SCROLL_GO_ON            1
73
74 // for Bang()/Explode()
75 #define EX_PHASE_START          0
76 #define EX_TYPE_NONE            0
77 #define EX_TYPE_NORMAL          (1 << 0)
78 #define EX_TYPE_CENTER          (1 << 1)
79 #define EX_TYPE_BORDER          (1 << 2)
80 #define EX_TYPE_CROSS           (1 << 3)
81 #define EX_TYPE_DYNA            (1 << 4)
82 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
83
84 #define PANEL_OFF()             (game.panel.active == FALSE)
85 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
86 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
87 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
88
89 // game panel display and control definitions
90 #define GAME_PANEL_LEVEL_NUMBER                 0
91 #define GAME_PANEL_GEMS                         1
92 #define GAME_PANEL_GEMS_NEEDED                  2
93 #define GAME_PANEL_GEMS_COLLECTED               3
94 #define GAME_PANEL_GEMS_SCORE                   4
95 #define GAME_PANEL_INVENTORY_COUNT              5
96 #define GAME_PANEL_INVENTORY_FIRST_1            6
97 #define GAME_PANEL_INVENTORY_FIRST_2            7
98 #define GAME_PANEL_INVENTORY_FIRST_3            8
99 #define GAME_PANEL_INVENTORY_FIRST_4            9
100 #define GAME_PANEL_INVENTORY_FIRST_5            10
101 #define GAME_PANEL_INVENTORY_FIRST_6            11
102 #define GAME_PANEL_INVENTORY_FIRST_7            12
103 #define GAME_PANEL_INVENTORY_FIRST_8            13
104 #define GAME_PANEL_INVENTORY_LAST_1             14
105 #define GAME_PANEL_INVENTORY_LAST_2             15
106 #define GAME_PANEL_INVENTORY_LAST_3             16
107 #define GAME_PANEL_INVENTORY_LAST_4             17
108 #define GAME_PANEL_INVENTORY_LAST_5             18
109 #define GAME_PANEL_INVENTORY_LAST_6             19
110 #define GAME_PANEL_INVENTORY_LAST_7             20
111 #define GAME_PANEL_INVENTORY_LAST_8             21
112 #define GAME_PANEL_KEY_1                        22
113 #define GAME_PANEL_KEY_2                        23
114 #define GAME_PANEL_KEY_3                        24
115 #define GAME_PANEL_KEY_4                        25
116 #define GAME_PANEL_KEY_5                        26
117 #define GAME_PANEL_KEY_6                        27
118 #define GAME_PANEL_KEY_7                        28
119 #define GAME_PANEL_KEY_8                        29
120 #define GAME_PANEL_KEY_WHITE                    30
121 #define GAME_PANEL_KEY_WHITE_COUNT              31
122 #define GAME_PANEL_SCORE                        32
123 #define GAME_PANEL_HIGHSCORE                    33
124 #define GAME_PANEL_TIME                         34
125 #define GAME_PANEL_TIME_HH                      35
126 #define GAME_PANEL_TIME_MM                      36
127 #define GAME_PANEL_TIME_SS                      37
128 #define GAME_PANEL_TIME_ANIM                    38
129 #define GAME_PANEL_HEALTH                       39
130 #define GAME_PANEL_HEALTH_ANIM                  40
131 #define GAME_PANEL_FRAME                        41
132 #define GAME_PANEL_SHIELD_NORMAL                42
133 #define GAME_PANEL_SHIELD_NORMAL_TIME           43
134 #define GAME_PANEL_SHIELD_DEADLY                44
135 #define GAME_PANEL_SHIELD_DEADLY_TIME           45
136 #define GAME_PANEL_EXIT                         46
137 #define GAME_PANEL_EMC_MAGIC_BALL               47
138 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        48
139 #define GAME_PANEL_LIGHT_SWITCH                 49
140 #define GAME_PANEL_LIGHT_SWITCH_TIME            50
141 #define GAME_PANEL_TIMEGATE_SWITCH              51
142 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         52
143 #define GAME_PANEL_SWITCHGATE_SWITCH            53
144 #define GAME_PANEL_EMC_LENSES                   54
145 #define GAME_PANEL_EMC_LENSES_TIME              55
146 #define GAME_PANEL_EMC_MAGNIFIER                56
147 #define GAME_PANEL_EMC_MAGNIFIER_TIME           57
148 #define GAME_PANEL_BALLOON_SWITCH               58
149 #define GAME_PANEL_DYNABOMB_NUMBER              59
150 #define GAME_PANEL_DYNABOMB_SIZE                60
151 #define GAME_PANEL_DYNABOMB_POWER               61
152 #define GAME_PANEL_PENGUINS                     62
153 #define GAME_PANEL_SOKOBAN_OBJECTS              63
154 #define GAME_PANEL_SOKOBAN_FIELDS               64
155 #define GAME_PANEL_ROBOT_WHEEL                  65
156 #define GAME_PANEL_CONVEYOR_BELT_1              66
157 #define GAME_PANEL_CONVEYOR_BELT_2              67
158 #define GAME_PANEL_CONVEYOR_BELT_3              68
159 #define GAME_PANEL_CONVEYOR_BELT_4              69
160 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       70
161 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       71
162 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       72
163 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       73
164 #define GAME_PANEL_MAGIC_WALL                   74
165 #define GAME_PANEL_MAGIC_WALL_TIME              75
166 #define GAME_PANEL_GRAVITY_STATE                76
167 #define GAME_PANEL_GRAPHIC_1                    77
168 #define GAME_PANEL_GRAPHIC_2                    78
169 #define GAME_PANEL_GRAPHIC_3                    79
170 #define GAME_PANEL_GRAPHIC_4                    80
171 #define GAME_PANEL_GRAPHIC_5                    81
172 #define GAME_PANEL_GRAPHIC_6                    82
173 #define GAME_PANEL_GRAPHIC_7                    83
174 #define GAME_PANEL_GRAPHIC_8                    84
175 #define GAME_PANEL_ELEMENT_1                    85
176 #define GAME_PANEL_ELEMENT_2                    86
177 #define GAME_PANEL_ELEMENT_3                    87
178 #define GAME_PANEL_ELEMENT_4                    88
179 #define GAME_PANEL_ELEMENT_5                    89
180 #define GAME_PANEL_ELEMENT_6                    90
181 #define GAME_PANEL_ELEMENT_7                    91
182 #define GAME_PANEL_ELEMENT_8                    92
183 #define GAME_PANEL_ELEMENT_COUNT_1              93
184 #define GAME_PANEL_ELEMENT_COUNT_2              94
185 #define GAME_PANEL_ELEMENT_COUNT_3              95
186 #define GAME_PANEL_ELEMENT_COUNT_4              96
187 #define GAME_PANEL_ELEMENT_COUNT_5              97
188 #define GAME_PANEL_ELEMENT_COUNT_6              98
189 #define GAME_PANEL_ELEMENT_COUNT_7              99
190 #define GAME_PANEL_ELEMENT_COUNT_8              100
191 #define GAME_PANEL_CE_SCORE_1                   101
192 #define GAME_PANEL_CE_SCORE_2                   102
193 #define GAME_PANEL_CE_SCORE_3                   103
194 #define GAME_PANEL_CE_SCORE_4                   104
195 #define GAME_PANEL_CE_SCORE_5                   105
196 #define GAME_PANEL_CE_SCORE_6                   106
197 #define GAME_PANEL_CE_SCORE_7                   107
198 #define GAME_PANEL_CE_SCORE_8                   108
199 #define GAME_PANEL_CE_SCORE_1_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_2_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_3_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_4_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_5_ELEMENT           113
204 #define GAME_PANEL_CE_SCORE_6_ELEMENT           114
205 #define GAME_PANEL_CE_SCORE_7_ELEMENT           115
206 #define GAME_PANEL_CE_SCORE_8_ELEMENT           116
207 #define GAME_PANEL_PLAYER_NAME                  117
208 #define GAME_PANEL_LEVEL_NAME                   118
209 #define GAME_PANEL_LEVEL_AUTHOR                 119
210
211 #define NUM_GAME_PANEL_CONTROLS                 120
212
213 struct GamePanelOrderInfo
214 {
215   int nr;
216   int sort_priority;
217 };
218
219 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
220
221 struct GamePanelControlInfo
222 {
223   int nr;
224
225   struct TextPosInfo *pos;
226   int type;
227
228   int graphic, graphic_active;
229
230   int value, last_value;
231   int frame, last_frame;
232   int gfx_frame;
233   int gfx_random;
234 };
235
236 static struct GamePanelControlInfo game_panel_controls[] =
237 {
238   {
239     GAME_PANEL_LEVEL_NUMBER,
240     &game.panel.level_number,
241     TYPE_INTEGER,
242   },
243   {
244     GAME_PANEL_GEMS,
245     &game.panel.gems,
246     TYPE_INTEGER,
247   },
248   {
249     GAME_PANEL_GEMS_NEEDED,
250     &game.panel.gems_needed,
251     TYPE_INTEGER,
252   },
253   {
254     GAME_PANEL_GEMS_COLLECTED,
255     &game.panel.gems_collected,
256     TYPE_INTEGER,
257   },
258   {
259     GAME_PANEL_GEMS_SCORE,
260     &game.panel.gems_score,
261     TYPE_INTEGER,
262   },
263   {
264     GAME_PANEL_INVENTORY_COUNT,
265     &game.panel.inventory_count,
266     TYPE_INTEGER,
267   },
268   {
269     GAME_PANEL_INVENTORY_FIRST_1,
270     &game.panel.inventory_first[0],
271     TYPE_ELEMENT,
272   },
273   {
274     GAME_PANEL_INVENTORY_FIRST_2,
275     &game.panel.inventory_first[1],
276     TYPE_ELEMENT,
277   },
278   {
279     GAME_PANEL_INVENTORY_FIRST_3,
280     &game.panel.inventory_first[2],
281     TYPE_ELEMENT,
282   },
283   {
284     GAME_PANEL_INVENTORY_FIRST_4,
285     &game.panel.inventory_first[3],
286     TYPE_ELEMENT,
287   },
288   {
289     GAME_PANEL_INVENTORY_FIRST_5,
290     &game.panel.inventory_first[4],
291     TYPE_ELEMENT,
292   },
293   {
294     GAME_PANEL_INVENTORY_FIRST_6,
295     &game.panel.inventory_first[5],
296     TYPE_ELEMENT,
297   },
298   {
299     GAME_PANEL_INVENTORY_FIRST_7,
300     &game.panel.inventory_first[6],
301     TYPE_ELEMENT,
302   },
303   {
304     GAME_PANEL_INVENTORY_FIRST_8,
305     &game.panel.inventory_first[7],
306     TYPE_ELEMENT,
307   },
308   {
309     GAME_PANEL_INVENTORY_LAST_1,
310     &game.panel.inventory_last[0],
311     TYPE_ELEMENT,
312   },
313   {
314     GAME_PANEL_INVENTORY_LAST_2,
315     &game.panel.inventory_last[1],
316     TYPE_ELEMENT,
317   },
318   {
319     GAME_PANEL_INVENTORY_LAST_3,
320     &game.panel.inventory_last[2],
321     TYPE_ELEMENT,
322   },
323   {
324     GAME_PANEL_INVENTORY_LAST_4,
325     &game.panel.inventory_last[3],
326     TYPE_ELEMENT,
327   },
328   {
329     GAME_PANEL_INVENTORY_LAST_5,
330     &game.panel.inventory_last[4],
331     TYPE_ELEMENT,
332   },
333   {
334     GAME_PANEL_INVENTORY_LAST_6,
335     &game.panel.inventory_last[5],
336     TYPE_ELEMENT,
337   },
338   {
339     GAME_PANEL_INVENTORY_LAST_7,
340     &game.panel.inventory_last[6],
341     TYPE_ELEMENT,
342   },
343   {
344     GAME_PANEL_INVENTORY_LAST_8,
345     &game.panel.inventory_last[7],
346     TYPE_ELEMENT,
347   },
348   {
349     GAME_PANEL_KEY_1,
350     &game.panel.key[0],
351     TYPE_ELEMENT,
352   },
353   {
354     GAME_PANEL_KEY_2,
355     &game.panel.key[1],
356     TYPE_ELEMENT,
357   },
358   {
359     GAME_PANEL_KEY_3,
360     &game.panel.key[2],
361     TYPE_ELEMENT,
362   },
363   {
364     GAME_PANEL_KEY_4,
365     &game.panel.key[3],
366     TYPE_ELEMENT,
367   },
368   {
369     GAME_PANEL_KEY_5,
370     &game.panel.key[4],
371     TYPE_ELEMENT,
372   },
373   {
374     GAME_PANEL_KEY_6,
375     &game.panel.key[5],
376     TYPE_ELEMENT,
377   },
378   {
379     GAME_PANEL_KEY_7,
380     &game.panel.key[6],
381     TYPE_ELEMENT,
382   },
383   {
384     GAME_PANEL_KEY_8,
385     &game.panel.key[7],
386     TYPE_ELEMENT,
387   },
388   {
389     GAME_PANEL_KEY_WHITE,
390     &game.panel.key_white,
391     TYPE_ELEMENT,
392   },
393   {
394     GAME_PANEL_KEY_WHITE_COUNT,
395     &game.panel.key_white_count,
396     TYPE_INTEGER,
397   },
398   {
399     GAME_PANEL_SCORE,
400     &game.panel.score,
401     TYPE_INTEGER,
402   },
403   {
404     GAME_PANEL_HIGHSCORE,
405     &game.panel.highscore,
406     TYPE_INTEGER,
407   },
408   {
409     GAME_PANEL_TIME,
410     &game.panel.time,
411     TYPE_INTEGER,
412   },
413   {
414     GAME_PANEL_TIME_HH,
415     &game.panel.time_hh,
416     TYPE_INTEGER,
417   },
418   {
419     GAME_PANEL_TIME_MM,
420     &game.panel.time_mm,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_TIME_SS,
425     &game.panel.time_ss,
426     TYPE_INTEGER,
427   },
428   {
429     GAME_PANEL_TIME_ANIM,
430     &game.panel.time_anim,
431     TYPE_GRAPHIC,
432
433     IMG_GFX_GAME_PANEL_TIME_ANIM,
434     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
435   },
436   {
437     GAME_PANEL_HEALTH,
438     &game.panel.health,
439     TYPE_INTEGER,
440   },
441   {
442     GAME_PANEL_HEALTH_ANIM,
443     &game.panel.health_anim,
444     TYPE_GRAPHIC,
445
446     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
447     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
448   },
449   {
450     GAME_PANEL_FRAME,
451     &game.panel.frame,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_SHIELD_NORMAL,
456     &game.panel.shield_normal,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_SHIELD_NORMAL_TIME,
461     &game.panel.shield_normal_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SHIELD_DEADLY,
466     &game.panel.shield_deadly,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_SHIELD_DEADLY_TIME,
471     &game.panel.shield_deadly_time,
472     TYPE_INTEGER,
473   },
474   {
475     GAME_PANEL_EXIT,
476     &game.panel.exit,
477     TYPE_ELEMENT,
478   },
479   {
480     GAME_PANEL_EMC_MAGIC_BALL,
481     &game.panel.emc_magic_ball,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
486     &game.panel.emc_magic_ball_switch,
487     TYPE_ELEMENT,
488   },
489   {
490     GAME_PANEL_LIGHT_SWITCH,
491     &game.panel.light_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_LIGHT_SWITCH_TIME,
496     &game.panel.light_switch_time,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_TIMEGATE_SWITCH,
501     &game.panel.timegate_switch,
502     TYPE_ELEMENT,
503   },
504   {
505     GAME_PANEL_TIMEGATE_SWITCH_TIME,
506     &game.panel.timegate_switch_time,
507     TYPE_INTEGER,
508   },
509   {
510     GAME_PANEL_SWITCHGATE_SWITCH,
511     &game.panel.switchgate_switch,
512     TYPE_ELEMENT,
513   },
514   {
515     GAME_PANEL_EMC_LENSES,
516     &game.panel.emc_lenses,
517     TYPE_ELEMENT,
518   },
519   {
520     GAME_PANEL_EMC_LENSES_TIME,
521     &game.panel.emc_lenses_time,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_EMC_MAGNIFIER,
526     &game.panel.emc_magnifier,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_EMC_MAGNIFIER_TIME,
531     &game.panel.emc_magnifier_time,
532     TYPE_INTEGER,
533   },
534   {
535     GAME_PANEL_BALLOON_SWITCH,
536     &game.panel.balloon_switch,
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_DYNABOMB_NUMBER,
541     &game.panel.dynabomb_number,
542     TYPE_INTEGER,
543   },
544   {
545     GAME_PANEL_DYNABOMB_SIZE,
546     &game.panel.dynabomb_size,
547     TYPE_INTEGER,
548   },
549   {
550     GAME_PANEL_DYNABOMB_POWER,
551     &game.panel.dynabomb_power,
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_PENGUINS,
556     &game.panel.penguins,
557     TYPE_INTEGER,
558   },
559   {
560     GAME_PANEL_SOKOBAN_OBJECTS,
561     &game.panel.sokoban_objects,
562     TYPE_INTEGER,
563   },
564   {
565     GAME_PANEL_SOKOBAN_FIELDS,
566     &game.panel.sokoban_fields,
567     TYPE_INTEGER,
568   },
569   {
570     GAME_PANEL_ROBOT_WHEEL,
571     &game.panel.robot_wheel,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_CONVEYOR_BELT_1,
576     &game.panel.conveyor_belt[0],
577     TYPE_ELEMENT,
578   },
579   {
580     GAME_PANEL_CONVEYOR_BELT_2,
581     &game.panel.conveyor_belt[1],
582     TYPE_ELEMENT,
583   },
584   {
585     GAME_PANEL_CONVEYOR_BELT_3,
586     &game.panel.conveyor_belt[2],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_CONVEYOR_BELT_4,
591     &game.panel.conveyor_belt[3],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
596     &game.panel.conveyor_belt_switch[0],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
601     &game.panel.conveyor_belt_switch[1],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
606     &game.panel.conveyor_belt_switch[2],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
611     &game.panel.conveyor_belt_switch[3],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_MAGIC_WALL,
616     &game.panel.magic_wall,
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_MAGIC_WALL_TIME,
621     &game.panel.magic_wall_time,
622     TYPE_INTEGER,
623   },
624   {
625     GAME_PANEL_GRAVITY_STATE,
626     &game.panel.gravity_state,
627     TYPE_STRING,
628   },
629   {
630     GAME_PANEL_GRAPHIC_1,
631     &game.panel.graphic[0],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_GRAPHIC_2,
636     &game.panel.graphic[1],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_GRAPHIC_3,
641     &game.panel.graphic[2],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_GRAPHIC_4,
646     &game.panel.graphic[3],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_GRAPHIC_5,
651     &game.panel.graphic[4],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_GRAPHIC_6,
656     &game.panel.graphic[5],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_GRAPHIC_7,
661     &game.panel.graphic[6],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_GRAPHIC_8,
666     &game.panel.graphic[7],
667     TYPE_ELEMENT,
668   },
669   {
670     GAME_PANEL_ELEMENT_1,
671     &game.panel.element[0],
672     TYPE_ELEMENT,
673   },
674   {
675     GAME_PANEL_ELEMENT_2,
676     &game.panel.element[1],
677     TYPE_ELEMENT,
678   },
679   {
680     GAME_PANEL_ELEMENT_3,
681     &game.panel.element[2],
682     TYPE_ELEMENT,
683   },
684   {
685     GAME_PANEL_ELEMENT_4,
686     &game.panel.element[3],
687     TYPE_ELEMENT,
688   },
689   {
690     GAME_PANEL_ELEMENT_5,
691     &game.panel.element[4],
692     TYPE_ELEMENT,
693   },
694   {
695     GAME_PANEL_ELEMENT_6,
696     &game.panel.element[5],
697     TYPE_ELEMENT,
698   },
699   {
700     GAME_PANEL_ELEMENT_7,
701     &game.panel.element[6],
702     TYPE_ELEMENT,
703   },
704   {
705     GAME_PANEL_ELEMENT_8,
706     &game.panel.element[7],
707     TYPE_ELEMENT,
708   },
709   {
710     GAME_PANEL_ELEMENT_COUNT_1,
711     &game.panel.element_count[0],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_ELEMENT_COUNT_2,
716     &game.panel.element_count[1],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_ELEMENT_COUNT_3,
721     &game.panel.element_count[2],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_ELEMENT_COUNT_4,
726     &game.panel.element_count[3],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_ELEMENT_COUNT_5,
731     &game.panel.element_count[4],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_ELEMENT_COUNT_6,
736     &game.panel.element_count[5],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_ELEMENT_COUNT_7,
741     &game.panel.element_count[6],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_ELEMENT_COUNT_8,
746     &game.panel.element_count[7],
747     TYPE_INTEGER,
748   },
749   {
750     GAME_PANEL_CE_SCORE_1,
751     &game.panel.ce_score[0],
752     TYPE_INTEGER,
753   },
754   {
755     GAME_PANEL_CE_SCORE_2,
756     &game.panel.ce_score[1],
757     TYPE_INTEGER,
758   },
759   {
760     GAME_PANEL_CE_SCORE_3,
761     &game.panel.ce_score[2],
762     TYPE_INTEGER,
763   },
764   {
765     GAME_PANEL_CE_SCORE_4,
766     &game.panel.ce_score[3],
767     TYPE_INTEGER,
768   },
769   {
770     GAME_PANEL_CE_SCORE_5,
771     &game.panel.ce_score[4],
772     TYPE_INTEGER,
773   },
774   {
775     GAME_PANEL_CE_SCORE_6,
776     &game.panel.ce_score[5],
777     TYPE_INTEGER,
778   },
779   {
780     GAME_PANEL_CE_SCORE_7,
781     &game.panel.ce_score[6],
782     TYPE_INTEGER,
783   },
784   {
785     GAME_PANEL_CE_SCORE_8,
786     &game.panel.ce_score[7],
787     TYPE_INTEGER,
788   },
789   {
790     GAME_PANEL_CE_SCORE_1_ELEMENT,
791     &game.panel.ce_score_element[0],
792     TYPE_ELEMENT,
793   },
794   {
795     GAME_PANEL_CE_SCORE_2_ELEMENT,
796     &game.panel.ce_score_element[1],
797     TYPE_ELEMENT,
798   },
799   {
800     GAME_PANEL_CE_SCORE_3_ELEMENT,
801     &game.panel.ce_score_element[2],
802     TYPE_ELEMENT,
803   },
804   {
805     GAME_PANEL_CE_SCORE_4_ELEMENT,
806     &game.panel.ce_score_element[3],
807     TYPE_ELEMENT,
808   },
809   {
810     GAME_PANEL_CE_SCORE_5_ELEMENT,
811     &game.panel.ce_score_element[4],
812     TYPE_ELEMENT,
813   },
814   {
815     GAME_PANEL_CE_SCORE_6_ELEMENT,
816     &game.panel.ce_score_element[5],
817     TYPE_ELEMENT,
818   },
819   {
820     GAME_PANEL_CE_SCORE_7_ELEMENT,
821     &game.panel.ce_score_element[6],
822     TYPE_ELEMENT,
823   },
824   {
825     GAME_PANEL_CE_SCORE_8_ELEMENT,
826     &game.panel.ce_score_element[7],
827     TYPE_ELEMENT,
828   },
829   {
830     GAME_PANEL_PLAYER_NAME,
831     &game.panel.player_name,
832     TYPE_STRING,
833   },
834   {
835     GAME_PANEL_LEVEL_NAME,
836     &game.panel.level_name,
837     TYPE_STRING,
838   },
839   {
840     GAME_PANEL_LEVEL_AUTHOR,
841     &game.panel.level_author,
842     TYPE_STRING,
843   },
844
845   {
846     -1,
847     NULL,
848     -1,
849   }
850 };
851
852 // values for delayed check of falling and moving elements and for collision
853 #define CHECK_DELAY_MOVING      3
854 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
855 #define CHECK_DELAY_COLLISION   2
856 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
857
858 // values for initial player move delay (initial delay counter value)
859 #define INITIAL_MOVE_DELAY_OFF  -1
860 #define INITIAL_MOVE_DELAY_ON   0
861
862 // values for player movement speed (which is in fact a delay value)
863 #define MOVE_DELAY_MIN_SPEED    32
864 #define MOVE_DELAY_NORMAL_SPEED 8
865 #define MOVE_DELAY_HIGH_SPEED   4
866 #define MOVE_DELAY_MAX_SPEED    1
867
868 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
869 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
870
871 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
872 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
873
874 // values for scroll positions
875 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
876                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
877                                  (x) - MIDPOSX)
878 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
879                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
880                                  (y) - MIDPOSY)
881
882 // values for other actions
883 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
884 #define MOVE_STEPSIZE_MIN       (1)
885 #define MOVE_STEPSIZE_MAX       (TILEX)
886
887 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
888 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
889
890 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
891
892 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
893                                  RND(element_info[e].push_delay_random))
894 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
895                                  RND(element_info[e].drop_delay_random))
896 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
897                                  RND(element_info[e].move_delay_random))
898 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
899                                     (element_info[e].move_delay_random))
900 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
901                                  RND(element_info[e].step_delay_random))
902 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
903                                     (element_info[e].step_delay_random))
904 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
905                                  RND(element_info[e].ce_value_random_initial))
906 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
907 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
908                                  RND((c)->delay_random * (c)->delay_frames))
909 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
910                                  RND((c)->delay_random))
911
912
913 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
914          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
915
916 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
917         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
918          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
919          (be) + (e) - EL_SELF)
920
921 #define GET_PLAYER_FROM_BITS(p)                                         \
922         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
923
924 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
925         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
926          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
927          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
928          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
929          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
930          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
931          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
932          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
933          (e))
934
935 #define CAN_GROW_INTO(e)                                                \
936         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
937
938 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
939                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
940                                         (condition)))
941
942 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
943                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
944                                         (CAN_MOVE_INTO_ACID(e) &&       \
945                                          Tile[x][y] == EL_ACID) ||      \
946                                         (condition)))
947
948 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
949                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
950                                         (CAN_MOVE_INTO_ACID(e) &&       \
951                                          Tile[x][y] == EL_ACID) ||      \
952                                         (condition)))
953
954 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
955                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
956                                         (condition) ||                  \
957                                         (CAN_MOVE_INTO_ACID(e) &&       \
958                                          Tile[x][y] == EL_ACID) ||      \
959                                         (DONT_COLLIDE_WITH(e) &&        \
960                                          IS_PLAYER(x, y) &&             \
961                                          !PLAYER_ENEMY_PROTECTED(x, y))))
962
963 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
964         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
965
966 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
967         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
968
969 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
970         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
971
972 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
973         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
974                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
975
976 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
977         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
978
979 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
981
982 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
983         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
984
985 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
987
988 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
989         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
990
991 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
992         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
993                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
994                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
995                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
996                                                  IS_FOOD_PENGUIN(Tile[x][y])))
997 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
998         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
999
1000 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
1001         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
1002
1003 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
1004         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
1005
1006 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
1007         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
1008                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
1009
1010 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
1011
1012 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
1013                 (!IS_PLAYER(x, y) &&                                    \
1014                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
1015
1016 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
1017         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1018
1019 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1020 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1021
1022 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1023 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1024 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1025 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1026
1027 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1028
1029 // game button identifiers
1030 #define GAME_CTRL_ID_STOP               0
1031 #define GAME_CTRL_ID_PAUSE              1
1032 #define GAME_CTRL_ID_PLAY               2
1033 #define GAME_CTRL_ID_UNDO               3
1034 #define GAME_CTRL_ID_REDO               4
1035 #define GAME_CTRL_ID_SAVE               5
1036 #define GAME_CTRL_ID_PAUSE2             6
1037 #define GAME_CTRL_ID_LOAD               7
1038 #define GAME_CTRL_ID_RESTART            8
1039 #define GAME_CTRL_ID_PANEL_STOP         9
1040 #define GAME_CTRL_ID_PANEL_PAUSE        10
1041 #define GAME_CTRL_ID_PANEL_PLAY         11
1042 #define GAME_CTRL_ID_PANEL_RESTART      12
1043 #define GAME_CTRL_ID_TOUCH_STOP         13
1044 #define GAME_CTRL_ID_TOUCH_PAUSE        14
1045 #define GAME_CTRL_ID_TOUCH_RESTART      15
1046 #define SOUND_CTRL_ID_MUSIC             16
1047 #define SOUND_CTRL_ID_LOOPS             17
1048 #define SOUND_CTRL_ID_SIMPLE            18
1049 #define SOUND_CTRL_ID_PANEL_MUSIC       19
1050 #define SOUND_CTRL_ID_PANEL_LOOPS       20
1051 #define SOUND_CTRL_ID_PANEL_SIMPLE      21
1052
1053 #define NUM_GAME_BUTTONS                22
1054
1055
1056 // forward declaration for internal use
1057
1058 static void CreateField(int, int, int);
1059
1060 static void ResetGfxAnimation(int, int);
1061
1062 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1063 static void AdvanceFrameAndPlayerCounters(int);
1064
1065 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1066 static boolean MovePlayer(struct PlayerInfo *, int, int);
1067 static void ScrollPlayer(struct PlayerInfo *, int);
1068 static void ScrollScreen(struct PlayerInfo *, int);
1069
1070 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1071 static boolean DigFieldByCE(int, int, int);
1072 static boolean SnapField(struct PlayerInfo *, int, int);
1073 static boolean DropElement(struct PlayerInfo *);
1074
1075 static void InitBeltMovement(void);
1076 static void CloseAllOpenTimegates(void);
1077 static void CheckGravityMovement(struct PlayerInfo *);
1078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1079 static void KillPlayerUnlessEnemyProtected(int, int);
1080 static void KillPlayerUnlessExplosionProtected(int, int);
1081
1082 static void CheckNextToConditions(int, int);
1083 static void TestIfPlayerNextToCustomElement(int, int);
1084 static void TestIfPlayerTouchesCustomElement(int, int);
1085 static void TestIfElementNextToCustomElement(int, int);
1086 static void TestIfElementTouchesCustomElement(int, int);
1087 static void TestIfElementHitsCustomElement(int, int, int);
1088
1089 static void HandleElementChange(int, int, int);
1090 static void ExecuteCustomElementAction(int, int, int, int);
1091 static boolean ChangeElement(int, int, int, int);
1092
1093 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int, int, int);
1094 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1095         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, -1)
1096 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1097         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1098 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1099         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1100 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1101         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1102 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1103         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1104
1105 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1106 #define CheckElementChange(x, y, e, te, ev)                             \
1107         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1108 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1109         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1110 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1111         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1112 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1113         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1114
1115 static void PlayLevelSound(int, int, int);
1116 static void PlayLevelSoundNearest(int, int, int);
1117 static void PlayLevelSoundAction(int, int, int);
1118 static void PlayLevelSoundElementAction(int, int, int, int);
1119 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1120 static void PlayLevelSoundActionIfLoop(int, int, int);
1121 static void StopLevelSoundActionIfLoop(int, int, int);
1122 static void PlayLevelMusic(void);
1123 static void FadeLevelSoundsAndMusic(void);
1124
1125 static void HandleGameButtons(struct GadgetInfo *);
1126
1127 int AmoebaNeighbourNr(int, int);
1128 void AmoebaToDiamond(int, int);
1129 void ContinueMoving(int, int);
1130 void Bang(int, int);
1131 void InitMovDir(int, int);
1132 void InitAmoebaNr(int, int);
1133 void NewHighScore(int, boolean);
1134
1135 void TestIfGoodThingHitsBadThing(int, int, int);
1136 void TestIfBadThingHitsGoodThing(int, int, int);
1137 void TestIfPlayerTouchesBadThing(int, int);
1138 void TestIfPlayerRunsIntoBadThing(int, int, int);
1139 void TestIfBadThingTouchesPlayer(int, int);
1140 void TestIfBadThingRunsIntoPlayer(int, int, int);
1141 void TestIfFriendTouchesBadThing(int, int);
1142 void TestIfBadThingTouchesFriend(int, int);
1143 void TestIfBadThingTouchesOtherBadThing(int, int);
1144 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1145
1146 void KillPlayer(struct PlayerInfo *);
1147 void BuryPlayer(struct PlayerInfo *);
1148 void RemovePlayer(struct PlayerInfo *);
1149 void ExitPlayer(struct PlayerInfo *);
1150
1151 static int getInvisibleActiveFromInvisibleElement(int);
1152 static int getInvisibleFromInvisibleActiveElement(int);
1153
1154 static void TestFieldAfterSnapping(int, int, int, int, int);
1155
1156 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1157
1158 // for detection of endless loops, caused by custom element programming
1159 // (using maximal playfield width x 10 is just a rough approximation)
1160 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1161
1162 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1163 {                                                                       \
1164   if (recursion_loop_detected)                                          \
1165     return (rc);                                                        \
1166                                                                         \
1167   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1168   {                                                                     \
1169     recursion_loop_detected = TRUE;                                     \
1170     recursion_loop_element = (e);                                       \
1171   }                                                                     \
1172                                                                         \
1173   recursion_loop_depth++;                                               \
1174 }
1175
1176 #define RECURSION_LOOP_DETECTION_END()                                  \
1177 {                                                                       \
1178   recursion_loop_depth--;                                               \
1179 }
1180
1181 static int recursion_loop_depth;
1182 static boolean recursion_loop_detected;
1183 static boolean recursion_loop_element;
1184
1185 static int map_player_action[MAX_PLAYERS];
1186
1187
1188 // ----------------------------------------------------------------------------
1189 // definition of elements that automatically change to other elements after
1190 // a specified time, eventually calling a function when changing
1191 // ----------------------------------------------------------------------------
1192
1193 // forward declaration for changer functions
1194 static void InitBuggyBase(int, int);
1195 static void WarnBuggyBase(int, int);
1196
1197 static void InitTrap(int, int);
1198 static void ActivateTrap(int, int);
1199 static void ChangeActiveTrap(int, int);
1200
1201 static void InitRobotWheel(int, int);
1202 static void RunRobotWheel(int, int);
1203 static void StopRobotWheel(int, int);
1204
1205 static void InitTimegateWheel(int, int);
1206 static void RunTimegateWheel(int, int);
1207
1208 static void InitMagicBallDelay(int, int);
1209 static void ActivateMagicBall(int, int);
1210
1211 struct ChangingElementInfo
1212 {
1213   int element;
1214   int target_element;
1215   int change_delay;
1216   void (*pre_change_function)(int x, int y);
1217   void (*change_function)(int x, int y);
1218   void (*post_change_function)(int x, int y);
1219 };
1220
1221 static struct ChangingElementInfo change_delay_list[] =
1222 {
1223   {
1224     EL_NUT_BREAKING,
1225     EL_EMERALD,
1226     6,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_PEARL_BREAKING,
1233     EL_EMPTY,
1234     8,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_EXIT_OPENING,
1241     EL_EXIT_OPEN,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EXIT_CLOSING,
1249     EL_EXIT_CLOSED,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_STEEL_EXIT_OPENING,
1257     EL_STEEL_EXIT_OPEN,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_STEEL_EXIT_CLOSING,
1265     EL_STEEL_EXIT_CLOSED,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_EXIT_OPENING,
1273     EL_EM_EXIT_OPEN,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_EM_EXIT_CLOSING,
1281     EL_EMPTY,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_EM_STEEL_EXIT_OPENING,
1289     EL_EM_STEEL_EXIT_OPEN,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_EM_STEEL_EXIT_CLOSING,
1297     EL_STEELWALL,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_EXIT_OPENING,
1305     EL_SP_EXIT_OPEN,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_SP_EXIT_CLOSING,
1313     EL_SP_EXIT_CLOSED,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_SWITCHGATE_OPENING,
1321     EL_SWITCHGATE_OPEN,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327   {
1328     EL_SWITCHGATE_CLOSING,
1329     EL_SWITCHGATE_CLOSED,
1330     29,
1331     NULL,
1332     NULL,
1333     NULL
1334   },
1335   {
1336     EL_TIMEGATE_OPENING,
1337     EL_TIMEGATE_OPEN,
1338     29,
1339     NULL,
1340     NULL,
1341     NULL
1342   },
1343   {
1344     EL_TIMEGATE_CLOSING,
1345     EL_TIMEGATE_CLOSED,
1346     29,
1347     NULL,
1348     NULL,
1349     NULL
1350   },
1351
1352   {
1353     EL_ACID_SPLASH_LEFT,
1354     EL_EMPTY,
1355     8,
1356     NULL,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_ACID_SPLASH_RIGHT,
1362     EL_EMPTY,
1363     8,
1364     NULL,
1365     NULL,
1366     NULL
1367   },
1368   {
1369     EL_SP_BUGGY_BASE,
1370     EL_SP_BUGGY_BASE_ACTIVATING,
1371     0,
1372     InitBuggyBase,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_SP_BUGGY_BASE_ACTIVATING,
1378     EL_SP_BUGGY_BASE_ACTIVE,
1379     0,
1380     InitBuggyBase,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_SP_BUGGY_BASE_ACTIVE,
1386     EL_SP_BUGGY_BASE,
1387     0,
1388     InitBuggyBase,
1389     WarnBuggyBase,
1390     NULL
1391   },
1392   {
1393     EL_TRAP,
1394     EL_TRAP_ACTIVE,
1395     0,
1396     InitTrap,
1397     NULL,
1398     ActivateTrap
1399   },
1400   {
1401     EL_TRAP_ACTIVE,
1402     EL_TRAP,
1403     31,
1404     NULL,
1405     ChangeActiveTrap,
1406     NULL
1407   },
1408   {
1409     EL_ROBOT_WHEEL_ACTIVE,
1410     EL_ROBOT_WHEEL,
1411     0,
1412     InitRobotWheel,
1413     RunRobotWheel,
1414     StopRobotWheel
1415   },
1416   {
1417     EL_TIMEGATE_SWITCH_ACTIVE,
1418     EL_TIMEGATE_SWITCH,
1419     0,
1420     InitTimegateWheel,
1421     RunTimegateWheel,
1422     NULL
1423   },
1424   {
1425     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1426     EL_DC_TIMEGATE_SWITCH,
1427     0,
1428     InitTimegateWheel,
1429     RunTimegateWheel,
1430     NULL
1431   },
1432   {
1433     EL_EMC_MAGIC_BALL_ACTIVE,
1434     EL_EMC_MAGIC_BALL_ACTIVE,
1435     0,
1436     InitMagicBallDelay,
1437     NULL,
1438     ActivateMagicBall
1439   },
1440   {
1441     EL_EMC_SPRING_BUMPER_ACTIVE,
1442     EL_EMC_SPRING_BUMPER,
1443     8,
1444     NULL,
1445     NULL,
1446     NULL
1447   },
1448   {
1449     EL_DIAGONAL_SHRINKING,
1450     EL_UNDEFINED,
1451     0,
1452     NULL,
1453     NULL,
1454     NULL
1455   },
1456   {
1457     EL_DIAGONAL_GROWING,
1458     EL_UNDEFINED,
1459     0,
1460     NULL,
1461     NULL,
1462     NULL,
1463   },
1464
1465   {
1466     EL_UNDEFINED,
1467     EL_UNDEFINED,
1468     -1,
1469     NULL,
1470     NULL,
1471     NULL
1472   }
1473 };
1474
1475 struct
1476 {
1477   int element;
1478   int push_delay_fixed, push_delay_random;
1479 }
1480 push_delay_list[] =
1481 {
1482   { EL_SPRING,                  0, 0 },
1483   { EL_BALLOON,                 0, 0 },
1484
1485   { EL_SOKOBAN_OBJECT,          2, 0 },
1486   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1487   { EL_SATELLITE,               2, 0 },
1488   { EL_SP_DISK_YELLOW,          2, 0 },
1489
1490   { EL_UNDEFINED,               0, 0 },
1491 };
1492
1493 struct
1494 {
1495   int element;
1496   int move_stepsize;
1497 }
1498 move_stepsize_list[] =
1499 {
1500   { EL_AMOEBA_DROP,             2 },
1501   { EL_AMOEBA_DROPPING,         2 },
1502   { EL_QUICKSAND_FILLING,       1 },
1503   { EL_QUICKSAND_EMPTYING,      1 },
1504   { EL_QUICKSAND_FAST_FILLING,  2 },
1505   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1506   { EL_MAGIC_WALL_FILLING,      2 },
1507   { EL_MAGIC_WALL_EMPTYING,     2 },
1508   { EL_BD_MAGIC_WALL_FILLING,   2 },
1509   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1510   { EL_DC_MAGIC_WALL_FILLING,   2 },
1511   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1512
1513   { EL_UNDEFINED,               0 },
1514 };
1515
1516 struct
1517 {
1518   int element;
1519   int count;
1520 }
1521 collect_count_list[] =
1522 {
1523   { EL_EMERALD,                 1 },
1524   { EL_BD_DIAMOND,              1 },
1525   { EL_EMERALD_YELLOW,          1 },
1526   { EL_EMERALD_RED,             1 },
1527   { EL_EMERALD_PURPLE,          1 },
1528   { EL_DIAMOND,                 3 },
1529   { EL_SP_INFOTRON,             1 },
1530   { EL_PEARL,                   5 },
1531   { EL_CRYSTAL,                 8 },
1532
1533   { EL_UNDEFINED,               0 },
1534 };
1535
1536 struct
1537 {
1538   int element;
1539   int direction;
1540 }
1541 access_direction_list[] =
1542 {
1543   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1544   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1545   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1546   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1547   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1548   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1549   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1550   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1551   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1552   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1553   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1554
1555   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1556   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1557   { EL_SP_PORT_UP,                                                   MV_DOWN },
1558   { EL_SP_PORT_DOWN,                                         MV_UP           },
1559   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1560   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1561   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1562   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1563   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1564   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1565   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1566   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1567   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1568   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1569   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1570   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1571   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1572   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1573   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1574
1575   { EL_UNDEFINED,                       MV_NONE                              }
1576 };
1577
1578 static struct XY xy_topdown[] =
1579 {
1580   {  0, -1 },
1581   { -1,  0 },
1582   { +1,  0 },
1583   {  0, +1 }
1584 };
1585
1586 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1587
1588 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1589 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1590 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1591                                  IS_JUST_CHANGING(x, y))
1592
1593 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1594
1595 // static variables for playfield scan mode (scanning forward or backward)
1596 static int playfield_scan_start_x = 0;
1597 static int playfield_scan_start_y = 0;
1598 static int playfield_scan_delta_x = 1;
1599 static int playfield_scan_delta_y = 1;
1600
1601 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1602                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1603                                      (y) += playfield_scan_delta_y)     \
1604                                 for ((x) = playfield_scan_start_x;      \
1605                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1606                                      (x) += playfield_scan_delta_x)
1607
1608 #ifdef DEBUG
1609 void DEBUG_SetMaximumDynamite(void)
1610 {
1611   int i;
1612
1613   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1614     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1615       local_player->inventory_element[local_player->inventory_size++] =
1616         EL_DYNAMITE;
1617 }
1618 #endif
1619
1620 static void InitPlayfieldScanModeVars(void)
1621 {
1622   if (game.use_reverse_scan_direction)
1623   {
1624     playfield_scan_start_x = lev_fieldx - 1;
1625     playfield_scan_start_y = lev_fieldy - 1;
1626
1627     playfield_scan_delta_x = -1;
1628     playfield_scan_delta_y = -1;
1629   }
1630   else
1631   {
1632     playfield_scan_start_x = 0;
1633     playfield_scan_start_y = 0;
1634
1635     playfield_scan_delta_x = 1;
1636     playfield_scan_delta_y = 1;
1637   }
1638 }
1639
1640 static void InitPlayfieldScanMode(int mode)
1641 {
1642   game.use_reverse_scan_direction =
1643     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1644
1645   InitPlayfieldScanModeVars();
1646 }
1647
1648 static int get_move_delay_from_stepsize(int move_stepsize)
1649 {
1650   move_stepsize =
1651     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1652
1653   // make sure that stepsize value is always a power of 2
1654   move_stepsize = (1 << log_2(move_stepsize));
1655
1656   return TILEX / move_stepsize;
1657 }
1658
1659 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1660                                boolean init_game)
1661 {
1662   int player_nr = player->index_nr;
1663   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1664   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1665
1666   // do no immediately change move delay -- the player might just be moving
1667   player->move_delay_value_next = move_delay;
1668
1669   // information if player can move must be set separately
1670   player->cannot_move = cannot_move;
1671
1672   if (init_game)
1673   {
1674     player->move_delay       = game.initial_move_delay[player_nr];
1675     player->move_delay_value = game.initial_move_delay_value[player_nr];
1676
1677     player->move_delay_value_next = -1;
1678
1679     player->move_delay_reset_counter = 0;
1680   }
1681 }
1682
1683 void GetPlayerConfig(void)
1684 {
1685   GameFrameDelay = setup.game_frame_delay;
1686
1687   if (!audio.sound_available)
1688     setup.sound_simple = FALSE;
1689
1690   if (!audio.loops_available)
1691     setup.sound_loops = FALSE;
1692
1693   if (!audio.music_available)
1694     setup.sound_music = FALSE;
1695
1696   if (!video.fullscreen_available)
1697     setup.fullscreen = FALSE;
1698
1699   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1700
1701   SetAudioMode(setup.sound);
1702 }
1703
1704 int GetElementFromGroupElement(int element)
1705 {
1706   if (IS_GROUP_ELEMENT(element))
1707   {
1708     struct ElementGroupInfo *group = element_info[element].group;
1709     int last_anim_random_frame = gfx.anim_random_frame;
1710     int element_pos;
1711
1712     if (group->choice_mode == ANIM_RANDOM)
1713       gfx.anim_random_frame = RND(group->num_elements_resolved);
1714
1715     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1716                                     group->choice_mode, 0,
1717                                     group->choice_pos);
1718
1719     if (group->choice_mode == ANIM_RANDOM)
1720       gfx.anim_random_frame = last_anim_random_frame;
1721
1722     group->choice_pos++;
1723
1724     element = group->element_resolved[element_pos];
1725   }
1726
1727   return element;
1728 }
1729
1730 static void IncrementSokobanFieldsNeeded(void)
1731 {
1732   if (level.sb_fields_needed)
1733     game.sokoban_fields_still_needed++;
1734 }
1735
1736 static void IncrementSokobanObjectsNeeded(void)
1737 {
1738   if (level.sb_objects_needed)
1739     game.sokoban_objects_still_needed++;
1740 }
1741
1742 static void DecrementSokobanFieldsNeeded(void)
1743 {
1744   if (game.sokoban_fields_still_needed > 0)
1745     game.sokoban_fields_still_needed--;
1746 }
1747
1748 static void DecrementSokobanObjectsNeeded(void)
1749 {
1750   if (game.sokoban_objects_still_needed > 0)
1751     game.sokoban_objects_still_needed--;
1752 }
1753
1754 static void InitPlayerField(int x, int y, int element, boolean init_game)
1755 {
1756   if (element == EL_SP_MURPHY)
1757   {
1758     if (init_game)
1759     {
1760       if (stored_player[0].present)
1761       {
1762         Tile[x][y] = EL_SP_MURPHY_CLONE;
1763
1764         return;
1765       }
1766       else
1767       {
1768         stored_player[0].initial_element = element;
1769         stored_player[0].use_murphy = TRUE;
1770
1771         if (!level.use_artwork_element[0])
1772           stored_player[0].artwork_element = EL_SP_MURPHY;
1773       }
1774
1775       Tile[x][y] = EL_PLAYER_1;
1776     }
1777   }
1778
1779   if (init_game)
1780   {
1781     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1782     int jx = player->jx, jy = player->jy;
1783
1784     player->present = TRUE;
1785
1786     player->block_last_field = (element == EL_SP_MURPHY ?
1787                                 level.sp_block_last_field :
1788                                 level.block_last_field);
1789
1790     // ---------- initialize player's last field block delay ------------------
1791
1792     // always start with reliable default value (no adjustment needed)
1793     player->block_delay_adjustment = 0;
1794
1795     // special case 1: in Supaplex, Murphy blocks last field one more frame
1796     if (player->block_last_field && element == EL_SP_MURPHY)
1797       player->block_delay_adjustment = 1;
1798
1799     // special case 2: in game engines before 3.1.1, blocking was different
1800     if (game.use_block_last_field_bug)
1801       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1802
1803     if (!network.enabled || player->connected_network)
1804     {
1805       player->active = TRUE;
1806
1807       // remove potentially duplicate players
1808       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1809         StorePlayer[jx][jy] = 0;
1810
1811       StorePlayer[x][y] = Tile[x][y];
1812
1813 #if DEBUG_INIT_PLAYER
1814       Debug("game:init:player", "- player element %d activated",
1815             player->element_nr);
1816       Debug("game:init:player", "  (local player is %d and currently %s)",
1817             local_player->element_nr,
1818             local_player->active ? "active" : "not active");
1819     }
1820 #endif
1821
1822     Tile[x][y] = EL_EMPTY;
1823
1824     player->jx = player->last_jx = x;
1825     player->jy = player->last_jy = y;
1826   }
1827
1828   // always check if player was just killed and should be reanimated
1829   {
1830     int player_nr = GET_PLAYER_NR(element);
1831     struct PlayerInfo *player = &stored_player[player_nr];
1832
1833     if (player->active && player->killed)
1834       player->reanimated = TRUE; // if player was just killed, reanimate him
1835   }
1836 }
1837
1838 static void InitFieldForEngine_RND(int x, int y)
1839 {
1840   int element = Tile[x][y];
1841
1842   // convert BD engine elements to corresponding R'n'D engine elements
1843   element = (element == EL_BD_EMPTY             ? EL_EMPTY :
1844              element == EL_BD_PLAYER            ? EL_PLAYER_1 :
1845              element == EL_BD_INBOX             ? EL_PLAYER_1 :
1846              element == EL_BD_SAND              ? EL_SAND :
1847              element == EL_BD_STEELWALL         ? EL_STEELWALL :
1848              element == EL_BD_EXIT_CLOSED       ? EL_EXIT_CLOSED :
1849              element == EL_BD_EXIT_OPEN         ? EL_EXIT_OPEN :
1850              element);
1851
1852   Tile[x][y] = element;
1853 }
1854
1855 static void InitFieldForEngine(int x, int y)
1856 {
1857   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
1858     InitFieldForEngine_RND(x, y);
1859 }
1860
1861 static void InitField(int x, int y, boolean init_game)
1862 {
1863   int element = Tile[x][y];
1864
1865   switch (element)
1866   {
1867     case EL_SP_MURPHY:
1868     case EL_PLAYER_1:
1869     case EL_PLAYER_2:
1870     case EL_PLAYER_3:
1871     case EL_PLAYER_4:
1872       InitPlayerField(x, y, element, init_game);
1873       break;
1874
1875     case EL_SOKOBAN_FIELD_PLAYER:
1876       element = Tile[x][y] = EL_PLAYER_1;
1877       InitField(x, y, init_game);
1878
1879       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1880       InitField(x, y, init_game);
1881       break;
1882
1883     case EL_SOKOBAN_FIELD_EMPTY:
1884       IncrementSokobanFieldsNeeded();
1885       break;
1886
1887     case EL_SOKOBAN_OBJECT:
1888       IncrementSokobanObjectsNeeded();
1889       break;
1890
1891     case EL_STONEBLOCK:
1892       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1893         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1894       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1895         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1896       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1897         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1898       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1899         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1900       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1901         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1902       break;
1903
1904     case EL_BUG:
1905     case EL_BUG_RIGHT:
1906     case EL_BUG_UP:
1907     case EL_BUG_LEFT:
1908     case EL_BUG_DOWN:
1909     case EL_SPACESHIP:
1910     case EL_SPACESHIP_RIGHT:
1911     case EL_SPACESHIP_UP:
1912     case EL_SPACESHIP_LEFT:
1913     case EL_SPACESHIP_DOWN:
1914     case EL_BD_BUTTERFLY:
1915     case EL_BD_BUTTERFLY_RIGHT:
1916     case EL_BD_BUTTERFLY_UP:
1917     case EL_BD_BUTTERFLY_LEFT:
1918     case EL_BD_BUTTERFLY_DOWN:
1919     case EL_BD_FIREFLY:
1920     case EL_BD_FIREFLY_RIGHT:
1921     case EL_BD_FIREFLY_UP:
1922     case EL_BD_FIREFLY_LEFT:
1923     case EL_BD_FIREFLY_DOWN:
1924     case EL_PACMAN_RIGHT:
1925     case EL_PACMAN_UP:
1926     case EL_PACMAN_LEFT:
1927     case EL_PACMAN_DOWN:
1928     case EL_YAMYAM:
1929     case EL_YAMYAM_LEFT:
1930     case EL_YAMYAM_RIGHT:
1931     case EL_YAMYAM_UP:
1932     case EL_YAMYAM_DOWN:
1933     case EL_DARK_YAMYAM:
1934     case EL_ROBOT:
1935     case EL_PACMAN:
1936     case EL_SP_SNIKSNAK:
1937     case EL_SP_ELECTRON:
1938     case EL_MOLE:
1939     case EL_MOLE_LEFT:
1940     case EL_MOLE_RIGHT:
1941     case EL_MOLE_UP:
1942     case EL_MOLE_DOWN:
1943     case EL_SPRING_LEFT:
1944     case EL_SPRING_RIGHT:
1945       InitMovDir(x, y);
1946       break;
1947
1948     case EL_AMOEBA_FULL:
1949     case EL_BD_AMOEBA:
1950       InitAmoebaNr(x, y);
1951       break;
1952
1953     case EL_AMOEBA_DROP:
1954       if (y == lev_fieldy - 1)
1955       {
1956         Tile[x][y] = EL_AMOEBA_GROWING;
1957         Store[x][y] = EL_AMOEBA_WET;
1958       }
1959       break;
1960
1961     case EL_DYNAMITE_ACTIVE:
1962     case EL_SP_DISK_RED_ACTIVE:
1963     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1964     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1965     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1966     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1967       MovDelay[x][y] = 96;
1968       break;
1969
1970     case EL_EM_DYNAMITE_ACTIVE:
1971       MovDelay[x][y] = 32;
1972       break;
1973
1974     case EL_LAMP:
1975       game.lights_still_needed++;
1976       break;
1977
1978     case EL_PENGUIN:
1979       game.friends_still_needed++;
1980       break;
1981
1982     case EL_PIG:
1983     case EL_DRAGON:
1984       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1985       break;
1986
1987     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1988     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1989     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1990     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1991     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1992     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1993     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1994     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1995     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1996     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1997     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1998     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1999       if (init_game)
2000       {
2001         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
2002         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
2003         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
2004
2005         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
2006         {
2007           game.belt_dir[belt_nr] = belt_dir;
2008           game.belt_dir_nr[belt_nr] = belt_dir_nr;
2009         }
2010         else    // more than one switch -- set it like the first switch
2011         {
2012           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
2013         }
2014       }
2015       break;
2016
2017     case EL_LIGHT_SWITCH_ACTIVE:
2018       if (init_game)
2019         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
2020       break;
2021
2022     case EL_INVISIBLE_STEELWALL:
2023     case EL_INVISIBLE_WALL:
2024     case EL_INVISIBLE_SAND:
2025       if (game.light_time_left > 0 ||
2026           game.lenses_time_left > 0)
2027         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
2028       break;
2029
2030     case EL_EMC_MAGIC_BALL:
2031       if (game.ball_active)
2032         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
2033       break;
2034
2035     case EL_EMC_MAGIC_BALL_SWITCH:
2036       if (game.ball_active)
2037         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
2038       break;
2039
2040     case EL_TRIGGER_PLAYER:
2041     case EL_TRIGGER_ELEMENT:
2042     case EL_TRIGGER_CE_VALUE:
2043     case EL_TRIGGER_CE_SCORE:
2044     case EL_SELF:
2045     case EL_ANY_ELEMENT:
2046     case EL_CURRENT_CE_VALUE:
2047     case EL_CURRENT_CE_SCORE:
2048     case EL_PREV_CE_1:
2049     case EL_PREV_CE_2:
2050     case EL_PREV_CE_3:
2051     case EL_PREV_CE_4:
2052     case EL_PREV_CE_5:
2053     case EL_PREV_CE_6:
2054     case EL_PREV_CE_7:
2055     case EL_PREV_CE_8:
2056     case EL_NEXT_CE_1:
2057     case EL_NEXT_CE_2:
2058     case EL_NEXT_CE_3:
2059     case EL_NEXT_CE_4:
2060     case EL_NEXT_CE_5:
2061     case EL_NEXT_CE_6:
2062     case EL_NEXT_CE_7:
2063     case EL_NEXT_CE_8:
2064       // reference elements should not be used on the playfield
2065       Tile[x][y] = EL_EMPTY;
2066       break;
2067
2068     default:
2069       if (IS_CUSTOM_ELEMENT(element))
2070       {
2071         if (CAN_MOVE(element))
2072           InitMovDir(x, y);
2073
2074         if (!element_info[element].use_last_ce_value || init_game)
2075           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2076       }
2077       else if (IS_GROUP_ELEMENT(element))
2078       {
2079         Tile[x][y] = GetElementFromGroupElement(element);
2080
2081         InitField(x, y, init_game);
2082       }
2083       else if (IS_EMPTY_ELEMENT(element))
2084       {
2085         GfxElementEmpty[x][y] = element;
2086         Tile[x][y] = EL_EMPTY;
2087
2088         if (element_info[element].use_gfx_element)
2089           game.use_masked_elements = TRUE;
2090       }
2091
2092       break;
2093   }
2094
2095   if (!init_game)
2096     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2097 }
2098
2099 static void InitField_WithBug1(int x, int y, boolean init_game)
2100 {
2101   InitField(x, y, init_game);
2102
2103   // not needed to call InitMovDir() -- already done by InitField()!
2104   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2105       CAN_MOVE(Tile[x][y]))
2106     InitMovDir(x, y);
2107 }
2108
2109 static void InitField_WithBug2(int x, int y, boolean init_game)
2110 {
2111   int old_element = Tile[x][y];
2112
2113   InitField(x, y, init_game);
2114
2115   // not needed to call InitMovDir() -- already done by InitField()!
2116   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2117       CAN_MOVE(old_element) &&
2118       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2119     InitMovDir(x, y);
2120
2121   /* this case is in fact a combination of not less than three bugs:
2122      first, it calls InitMovDir() for elements that can move, although this is
2123      already done by InitField(); then, it checks the element that was at this
2124      field _before_ the call to InitField() (which can change it); lastly, it
2125      was not called for "mole with direction" elements, which were treated as
2126      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2127   */
2128 }
2129
2130 static int get_key_element_from_nr(int key_nr)
2131 {
2132   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2133                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2134                           EL_EM_KEY_1 : EL_KEY_1);
2135
2136   return key_base_element + key_nr;
2137 }
2138
2139 static int get_next_dropped_element(struct PlayerInfo *player)
2140 {
2141   return (player->inventory_size > 0 ?
2142           player->inventory_element[player->inventory_size - 1] :
2143           player->inventory_infinite_element != EL_UNDEFINED ?
2144           player->inventory_infinite_element :
2145           player->dynabombs_left > 0 ?
2146           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2147           EL_UNDEFINED);
2148 }
2149
2150 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2151 {
2152   // pos >= 0: get element from bottom of the stack;
2153   // pos <  0: get element from top of the stack
2154
2155   if (pos < 0)
2156   {
2157     int min_inventory_size = -pos;
2158     int inventory_pos = player->inventory_size - min_inventory_size;
2159     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2160
2161     return (player->inventory_size >= min_inventory_size ?
2162             player->inventory_element[inventory_pos] :
2163             player->inventory_infinite_element != EL_UNDEFINED ?
2164             player->inventory_infinite_element :
2165             player->dynabombs_left >= min_dynabombs_left ?
2166             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2167             EL_UNDEFINED);
2168   }
2169   else
2170   {
2171     int min_dynabombs_left = pos + 1;
2172     int min_inventory_size = pos + 1 - player->dynabombs_left;
2173     int inventory_pos = pos - player->dynabombs_left;
2174
2175     return (player->inventory_infinite_element != EL_UNDEFINED ?
2176             player->inventory_infinite_element :
2177             player->dynabombs_left >= min_dynabombs_left ?
2178             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2179             player->inventory_size >= min_inventory_size ?
2180             player->inventory_element[inventory_pos] :
2181             EL_UNDEFINED);
2182   }
2183 }
2184
2185 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2186 {
2187   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2188   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2189   int compare_result;
2190
2191   if (gpo1->sort_priority != gpo2->sort_priority)
2192     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2193   else
2194     compare_result = gpo1->nr - gpo2->nr;
2195
2196   return compare_result;
2197 }
2198
2199 int getPlayerInventorySize(int player_nr)
2200 {
2201   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2202     return game_em.ply[player_nr]->dynamite;
2203   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2204     return game_sp.red_disk_count;
2205   else
2206     return stored_player[player_nr].inventory_size;
2207 }
2208
2209 static void InitGameControlValues(void)
2210 {
2211   int i;
2212
2213   for (i = 0; game_panel_controls[i].nr != -1; i++)
2214   {
2215     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2216     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2217     struct TextPosInfo *pos = gpc->pos;
2218     int nr = gpc->nr;
2219     int type = gpc->type;
2220
2221     if (nr != i)
2222     {
2223       Error("'game_panel_controls' structure corrupted at %d", i);
2224
2225       Fail("this should not happen -- please debug");
2226     }
2227
2228     // force update of game controls after initialization
2229     gpc->value = gpc->last_value = -1;
2230     gpc->frame = gpc->last_frame = -1;
2231     gpc->gfx_frame = -1;
2232
2233     // determine panel value width for later calculation of alignment
2234     if (type == TYPE_INTEGER || type == TYPE_STRING)
2235     {
2236       pos->width = pos->size * getFontWidth(pos->font);
2237       pos->height = getFontHeight(pos->font);
2238     }
2239     else if (type == TYPE_ELEMENT)
2240     {
2241       pos->width = pos->size;
2242       pos->height = pos->size;
2243     }
2244
2245     // fill structure for game panel draw order
2246     gpo->nr = gpc->nr;
2247     gpo->sort_priority = pos->sort_priority;
2248   }
2249
2250   // sort game panel controls according to sort_priority and control number
2251   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2252         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2253 }
2254
2255 static void UpdatePlayfieldElementCount(void)
2256 {
2257   boolean use_element_count = FALSE;
2258   int i, j, x, y;
2259
2260   // first check if it is needed at all to calculate playfield element count
2261   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2262     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2263       use_element_count = TRUE;
2264
2265   if (!use_element_count)
2266     return;
2267
2268   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2269     element_info[i].element_count = 0;
2270
2271   SCAN_PLAYFIELD(x, y)
2272   {
2273     element_info[Tile[x][y]].element_count++;
2274   }
2275
2276   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2277     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2278       if (IS_IN_GROUP(j, i))
2279         element_info[EL_GROUP_START + i].element_count +=
2280           element_info[j].element_count;
2281 }
2282
2283 static void UpdateGameControlValues(void)
2284 {
2285   int i, k;
2286   int time = (game.LevelSolved ?
2287               game.LevelSolved_CountingTime :
2288               level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2289               game_bd.time_played :
2290               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2291               game_em.lev->time :
2292               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2293               game_sp.time_played :
2294               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2295               game_mm.energy_left :
2296               game.no_level_time_limit ? TimePlayed : TimeLeft);
2297   int score = (game.LevelSolved ?
2298                game.LevelSolved_CountingScore :
2299                level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2300                game_bd.score :
2301                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2302                game_em.lev->score :
2303                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2304                game_sp.score :
2305                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2306                game_mm.score :
2307                game.score);
2308   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2309               game_bd.gems_still_needed :
2310               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2311               game_em.lev->gems_needed :
2312               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2313               game_sp.infotrons_still_needed :
2314               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2315               game_mm.kettles_still_needed :
2316               game.gems_still_needed);
2317   int gems_needed = level.gems_needed;
2318   int gems_collected = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2319                         game_bd.game->cave->diamonds_collected :
2320                         gems_needed - gems);
2321   int gems_score = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2322                     game_bd.game->cave->diamond_value :
2323                     level.score[SC_EMERALD]);
2324   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_BD ?
2325                      game_bd.gems_still_needed > 0 :
2326                      level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2327                      game_em.lev->gems_needed > 0 :
2328                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2329                      game_sp.infotrons_still_needed > 0 :
2330                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2331                      game_mm.kettles_still_needed > 0 ||
2332                      game_mm.lights_still_needed > 0 :
2333                      game.gems_still_needed > 0 ||
2334                      game.sokoban_fields_still_needed > 0 ||
2335                      game.sokoban_objects_still_needed > 0 ||
2336                      game.lights_still_needed > 0);
2337   int health = (game.LevelSolved ?
2338                 game.LevelSolved_CountingHealth :
2339                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2340                 MM_HEALTH(game_mm.laser_overload_value) :
2341                 game.health);
2342   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2343
2344   UpdatePlayfieldElementCount();
2345
2346   // update game panel control values
2347
2348   // used instead of "level_nr" (for network games)
2349   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2350   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2351   game_panel_controls[GAME_PANEL_GEMS_NEEDED].value = gems_needed;
2352   game_panel_controls[GAME_PANEL_GEMS_COLLECTED].value = gems_collected;
2353   game_panel_controls[GAME_PANEL_GEMS_SCORE].value = gems_score;
2354
2355   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2356   for (i = 0; i < MAX_NUM_KEYS; i++)
2357     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2358   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2359   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2360
2361   if (game.centered_player_nr == -1)
2362   {
2363     for (i = 0; i < MAX_PLAYERS; i++)
2364     {
2365       // only one player in Supaplex game engine
2366       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2367         break;
2368
2369       for (k = 0; k < MAX_NUM_KEYS; k++)
2370       {
2371         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2372         {
2373           if (game_em.ply[i]->keys & (1 << k))
2374             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2375               get_key_element_from_nr(k);
2376         }
2377         else if (stored_player[i].key[k])
2378           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2379             get_key_element_from_nr(k);
2380       }
2381
2382       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2383         getPlayerInventorySize(i);
2384
2385       if (stored_player[i].num_white_keys > 0)
2386         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2387           EL_DC_KEY_WHITE;
2388
2389       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2390         stored_player[i].num_white_keys;
2391     }
2392   }
2393   else
2394   {
2395     int player_nr = game.centered_player_nr;
2396
2397     for (k = 0; k < MAX_NUM_KEYS; k++)
2398     {
2399       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2400       {
2401         if (game_em.ply[player_nr]->keys & (1 << k))
2402           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2403             get_key_element_from_nr(k);
2404       }
2405       else if (stored_player[player_nr].key[k])
2406         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2407           get_key_element_from_nr(k);
2408     }
2409
2410     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2411       getPlayerInventorySize(player_nr);
2412
2413     if (stored_player[player_nr].num_white_keys > 0)
2414       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2415
2416     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2417       stored_player[player_nr].num_white_keys;
2418   }
2419
2420   // re-arrange keys on game panel, if needed or if defined by style settings
2421   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2422   {
2423     int nr = GAME_PANEL_KEY_1 + i;
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2425     struct TextPosInfo *pos = gpc->pos;
2426
2427     // skip check if key is not in the player's inventory
2428     if (gpc->value == EL_EMPTY)
2429       continue;
2430
2431     // check if keys should be arranged on panel from left to right
2432     if (pos->style == STYLE_LEFTMOST_POSITION)
2433     {
2434       // check previous key positions (left from current key)
2435       for (k = 0; k < i; k++)
2436       {
2437         int nr_new = GAME_PANEL_KEY_1 + k;
2438
2439         if (game_panel_controls[nr_new].value == EL_EMPTY)
2440         {
2441           game_panel_controls[nr_new].value = gpc->value;
2442           gpc->value = EL_EMPTY;
2443
2444           break;
2445         }
2446       }
2447     }
2448
2449     // check if "undefined" keys can be placed at some other position
2450     if (pos->x == -1 && pos->y == -1)
2451     {
2452       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2453
2454       // 1st try: display key at the same position as normal or EM keys
2455       if (game_panel_controls[nr_new].value == EL_EMPTY)
2456       {
2457         game_panel_controls[nr_new].value = gpc->value;
2458       }
2459       else
2460       {
2461         // 2nd try: display key at the next free position in the key panel
2462         for (k = 0; k < STD_NUM_KEYS; k++)
2463         {
2464           nr_new = GAME_PANEL_KEY_1 + k;
2465
2466           if (game_panel_controls[nr_new].value == EL_EMPTY)
2467           {
2468             game_panel_controls[nr_new].value = gpc->value;
2469
2470             break;
2471           }
2472         }
2473       }
2474     }
2475   }
2476
2477   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2478   {
2479     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2480       get_inventory_element_from_pos(local_player, i);
2481     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2482       get_inventory_element_from_pos(local_player, -i - 1);
2483   }
2484
2485   game_panel_controls[GAME_PANEL_SCORE].value = score;
2486   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2487
2488   game_panel_controls[GAME_PANEL_TIME].value = time;
2489
2490   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2491   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2492   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2493
2494   if (level.time == 0)
2495     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2496   else
2497     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2498
2499   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2500   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2501
2502   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2503
2504   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2505     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2506      EL_EMPTY);
2507   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2508     local_player->shield_normal_time_left;
2509   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2510     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2511      EL_EMPTY);
2512   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2513     local_player->shield_deadly_time_left;
2514
2515   game_panel_controls[GAME_PANEL_EXIT].value =
2516     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2517
2518   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2519     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2520   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2521     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2522      EL_EMC_MAGIC_BALL_SWITCH);
2523
2524   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2525     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2526   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2527     game.light_time_left;
2528
2529   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2530     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2531   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2532     game.timegate_time_left;
2533
2534   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2535     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2536
2537   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2538     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2539   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2540     game.lenses_time_left;
2541
2542   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2543     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2544   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2545     game.magnify_time_left;
2546
2547   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2548     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2549      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2550      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2551      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2552      EL_BALLOON_SWITCH_NONE);
2553
2554   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2555     local_player->dynabomb_count;
2556   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2557     local_player->dynabomb_size;
2558   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2559     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2560
2561   game_panel_controls[GAME_PANEL_PENGUINS].value =
2562     game.friends_still_needed;
2563
2564   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2565     game.sokoban_objects_still_needed;
2566   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2567     game.sokoban_fields_still_needed;
2568
2569   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2570     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2571
2572   for (i = 0; i < NUM_BELTS; i++)
2573   {
2574     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2575       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2576        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2577     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2578       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2579   }
2580
2581   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2582     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2583   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2584     game.magic_wall_time_left;
2585
2586   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2587     local_player->gravity;
2588
2589   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2590     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2591
2592   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2593     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2594       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2595        game.panel.element[i].id : EL_UNDEFINED);
2596
2597   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2598     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2599       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2600        element_info[game.panel.element_count[i].id].element_count : 0);
2601
2602   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2603     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2604       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2605        element_info[game.panel.ce_score[i].id].collect_score : 0);
2606
2607   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2608     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2609       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2610        element_info[game.panel.ce_score_element[i].id].collect_score :
2611        EL_UNDEFINED);
2612
2613   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2614   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2615   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2616
2617   // update game panel control frames
2618
2619   for (i = 0; game_panel_controls[i].nr != -1; i++)
2620   {
2621     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2622
2623     if (gpc->type == TYPE_ELEMENT)
2624     {
2625       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2626       {
2627         int last_anim_random_frame = gfx.anim_random_frame;
2628         int element = gpc->value;
2629         int graphic = el2panelimg(element);
2630         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2631                                sync_random_frame :
2632                                graphic_info[graphic].anim_global_anim_sync ?
2633                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2634
2635         if (gpc->value != gpc->last_value)
2636         {
2637           gpc->gfx_frame = 0;
2638           gpc->gfx_random = init_gfx_random;
2639         }
2640         else
2641         {
2642           gpc->gfx_frame++;
2643
2644           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2645               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2646             gpc->gfx_random = init_gfx_random;
2647         }
2648
2649         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2650           gfx.anim_random_frame = gpc->gfx_random;
2651
2652         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2653           gpc->gfx_frame = element_info[element].collect_score;
2654
2655         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2656
2657         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2658           gfx.anim_random_frame = last_anim_random_frame;
2659       }
2660     }
2661     else if (gpc->type == TYPE_GRAPHIC)
2662     {
2663       if (gpc->graphic != IMG_UNDEFINED)
2664       {
2665         int last_anim_random_frame = gfx.anim_random_frame;
2666         int graphic = gpc->graphic;
2667         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2668                                sync_random_frame :
2669                                graphic_info[graphic].anim_global_anim_sync ?
2670                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2671
2672         if (gpc->value != gpc->last_value)
2673         {
2674           gpc->gfx_frame = 0;
2675           gpc->gfx_random = init_gfx_random;
2676         }
2677         else
2678         {
2679           gpc->gfx_frame++;
2680
2681           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2682               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2683             gpc->gfx_random = init_gfx_random;
2684         }
2685
2686         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2687           gfx.anim_random_frame = gpc->gfx_random;
2688
2689         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2690
2691         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2692           gfx.anim_random_frame = last_anim_random_frame;
2693       }
2694     }
2695   }
2696 }
2697
2698 static void DisplayGameControlValues(void)
2699 {
2700   boolean redraw_panel = FALSE;
2701   int i;
2702
2703   for (i = 0; game_panel_controls[i].nr != -1; i++)
2704   {
2705     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2706
2707     if (PANEL_DEACTIVATED(gpc->pos))
2708       continue;
2709
2710     if (gpc->value == gpc->last_value &&
2711         gpc->frame == gpc->last_frame)
2712       continue;
2713
2714     redraw_panel = TRUE;
2715   }
2716
2717   if (!redraw_panel)
2718     return;
2719
2720   // copy default game door content to main double buffer
2721
2722   // !!! CHECK AGAIN !!!
2723   SetPanelBackground();
2724   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2725   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2726
2727   // redraw game control buttons
2728   RedrawGameButtons();
2729
2730   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2731
2732   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2733   {
2734     int nr = game_panel_order[i].nr;
2735     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2736     struct TextPosInfo *pos = gpc->pos;
2737     int type = gpc->type;
2738     int value = gpc->value;
2739     int frame = gpc->frame;
2740     int size = pos->size;
2741     int font = pos->font;
2742     boolean draw_masked = pos->draw_masked;
2743     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2744
2745     if (PANEL_DEACTIVATED(pos))
2746       continue;
2747
2748     if (pos->class == get_hash_from_string("extra_panel_items") &&
2749         !setup.prefer_extra_panel_items)
2750       continue;
2751
2752     gpc->last_value = value;
2753     gpc->last_frame = frame;
2754
2755     if (type == TYPE_INTEGER)
2756     {
2757       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2758           nr == GAME_PANEL_INVENTORY_COUNT ||
2759           nr == GAME_PANEL_SCORE ||
2760           nr == GAME_PANEL_HIGHSCORE ||
2761           nr == GAME_PANEL_TIME)
2762       {
2763         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2764
2765         if (use_dynamic_size)           // use dynamic number of digits
2766         {
2767           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2768                               nr == GAME_PANEL_INVENTORY_COUNT ||
2769                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2770           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2771                           nr == GAME_PANEL_INVENTORY_COUNT ||
2772                           nr == GAME_PANEL_TIME ? 1 : 2);
2773           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2774                        nr == GAME_PANEL_INVENTORY_COUNT ||
2775                        nr == GAME_PANEL_TIME ? 3 : 5);
2776           int size2 = size1 + size_add;
2777           int font1 = pos->font;
2778           int font2 = pos->font_alt;
2779
2780           size = (value < value_change ? size1 : size2);
2781           font = (value < value_change ? font1 : font2);
2782         }
2783       }
2784
2785       // correct text size if "digits" is zero or less
2786       if (size <= 0)
2787         size = strlen(int2str(value, size));
2788
2789       // dynamically correct text alignment
2790       pos->width = size * getFontWidth(font);
2791
2792       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2793                   int2str(value, size), font, mask_mode);
2794     }
2795     else if (type == TYPE_ELEMENT)
2796     {
2797       int element, graphic;
2798       Bitmap *src_bitmap;
2799       int src_x, src_y;
2800       int width, height;
2801       int dst_x = PANEL_XPOS(pos);
2802       int dst_y = PANEL_YPOS(pos);
2803
2804       if (value != EL_UNDEFINED && value != EL_EMPTY)
2805       {
2806         element = value;
2807         graphic = el2panelimg(value);
2808
2809 #if 0
2810         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2811               element, EL_NAME(element), size);
2812 #endif
2813
2814         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2815           size = TILESIZE;
2816
2817         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2818                               &src_x, &src_y);
2819
2820         width  = graphic_info[graphic].width  * size / TILESIZE;
2821         height = graphic_info[graphic].height * size / TILESIZE;
2822
2823         if (draw_masked)
2824           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2825                            dst_x, dst_y);
2826         else
2827           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2828                      dst_x, dst_y);
2829       }
2830     }
2831     else if (type == TYPE_GRAPHIC)
2832     {
2833       int graphic        = gpc->graphic;
2834       int graphic_active = gpc->graphic_active;
2835       Bitmap *src_bitmap;
2836       int src_x, src_y;
2837       int width, height;
2838       int dst_x = PANEL_XPOS(pos);
2839       int dst_y = PANEL_YPOS(pos);
2840       boolean skip = (pos->class == get_hash_from_string("mm_engine_only") &&
2841                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2842
2843       if (graphic != IMG_UNDEFINED && !skip)
2844       {
2845         if (pos->style == STYLE_REVERSE)
2846           value = 100 - value;
2847
2848         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2849
2850         if (pos->direction & MV_HORIZONTAL)
2851         {
2852           width  = graphic_info[graphic_active].width * value / 100;
2853           height = graphic_info[graphic_active].height;
2854
2855           if (pos->direction == MV_LEFT)
2856           {
2857             src_x += graphic_info[graphic_active].width - width;
2858             dst_x += graphic_info[graphic_active].width - width;
2859           }
2860         }
2861         else
2862         {
2863           width  = graphic_info[graphic_active].width;
2864           height = graphic_info[graphic_active].height * value / 100;
2865
2866           if (pos->direction == MV_UP)
2867           {
2868             src_y += graphic_info[graphic_active].height - height;
2869             dst_y += graphic_info[graphic_active].height - height;
2870           }
2871         }
2872
2873         if (draw_masked)
2874           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2875                            dst_x, dst_y);
2876         else
2877           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2878                      dst_x, dst_y);
2879
2880         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2881
2882         if (pos->direction & MV_HORIZONTAL)
2883         {
2884           if (pos->direction == MV_RIGHT)
2885           {
2886             src_x += width;
2887             dst_x += width;
2888           }
2889           else
2890           {
2891             dst_x = PANEL_XPOS(pos);
2892           }
2893
2894           width = graphic_info[graphic].width - width;
2895         }
2896         else
2897         {
2898           if (pos->direction == MV_DOWN)
2899           {
2900             src_y += height;
2901             dst_y += height;
2902           }
2903           else
2904           {
2905             dst_y = PANEL_YPOS(pos);
2906           }
2907
2908           height = graphic_info[graphic].height - height;
2909         }
2910
2911         if (draw_masked)
2912           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2913                            dst_x, dst_y);
2914         else
2915           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2916                      dst_x, dst_y);
2917       }
2918     }
2919     else if (type == TYPE_STRING)
2920     {
2921       boolean active = (value != 0);
2922       char *state_normal = "off";
2923       char *state_active = "on";
2924       char *state = (active ? state_active : state_normal);
2925       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2926                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2927                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2928                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2929
2930       if (nr == GAME_PANEL_GRAVITY_STATE)
2931       {
2932         int font1 = pos->font;          // (used for normal state)
2933         int font2 = pos->font_alt;      // (used for active state)
2934
2935         font = (active ? font2 : font1);
2936       }
2937
2938       if (s != NULL)
2939       {
2940         char *s_cut;
2941
2942         if (size <= 0)
2943         {
2944           // don't truncate output if "chars" is zero or less
2945           size = strlen(s);
2946
2947           // dynamically correct text alignment
2948           pos->width = size * getFontWidth(font);
2949         }
2950
2951         s_cut = getStringCopyN(s, size);
2952
2953         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2954                     s_cut, font, mask_mode);
2955
2956         free(s_cut);
2957       }
2958     }
2959
2960     redraw_mask |= REDRAW_DOOR_1;
2961   }
2962
2963   SetGameStatus(GAME_MODE_PLAYING);
2964 }
2965
2966 void UpdateAndDisplayGameControlValues(void)
2967 {
2968   if (tape.deactivate_display)
2969     return;
2970
2971   UpdateGameControlValues();
2972   DisplayGameControlValues();
2973 }
2974
2975 void UpdateGameDoorValues(void)
2976 {
2977   UpdateGameControlValues();
2978 }
2979
2980 void DrawGameDoorValues(void)
2981 {
2982   DisplayGameControlValues();
2983 }
2984
2985
2986 // ============================================================================
2987 // InitGameEngine()
2988 // ----------------------------------------------------------------------------
2989 // initialize game engine due to level / tape version number
2990 // ============================================================================
2991
2992 static void InitGameEngine(void)
2993 {
2994   int i, j, k, l, x, y;
2995
2996   // set game engine from tape file when re-playing, else from level file
2997   game.engine_version = (tape.playing ? tape.engine_version :
2998                          level.game_version);
2999
3000   // set single or multi-player game mode (needed for re-playing tapes)
3001   game.team_mode = setup.team_mode;
3002
3003   if (tape.playing)
3004   {
3005     int num_players = 0;
3006
3007     for (i = 0; i < MAX_PLAYERS; i++)
3008       if (tape.player_participates[i])
3009         num_players++;
3010
3011     // multi-player tapes contain input data for more than one player
3012     game.team_mode = (num_players > 1);
3013   }
3014
3015 #if 0
3016   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
3017         level.game_version);
3018   Debug("game:init:level", "          tape.file_version   == %06d",
3019         tape.file_version);
3020   Debug("game:init:level", "          tape.game_version   == %06d",
3021         tape.game_version);
3022   Debug("game:init:level", "          tape.engine_version == %06d",
3023         tape.engine_version);
3024   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
3025         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
3026 #endif
3027
3028   // --------------------------------------------------------------------------
3029   // set flags for bugs and changes according to active game engine version
3030   // --------------------------------------------------------------------------
3031
3032   /*
3033     Summary of bugfix:
3034     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
3035
3036     Bug was introduced in version:
3037     2.0.1
3038
3039     Bug was fixed in version:
3040     4.2.0.0
3041
3042     Description:
3043     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
3044     but the property "can fall" was missing, which caused some levels to be
3045     unsolvable. This was fixed in version 4.2.0.0.
3046
3047     Affected levels/tapes:
3048     An example for a tape that was fixed by this bugfix is tape 029 from the
3049     level set "rnd_sam_bateman".
3050     The wrong behaviour will still be used for all levels or tapes that were
3051     created/recorded with it. An example for this is tape 023 from the level
3052     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
3053   */
3054
3055   boolean use_amoeba_dropping_cannot_fall_bug =
3056     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
3057       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
3058      (tape.playing &&
3059       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3060       tape.game_version <  VERSION_IDENT(4,2,0,0)));
3061
3062   /*
3063     Summary of bugfix/change:
3064     Fixed move speed of elements entering or leaving magic wall.
3065
3066     Fixed/changed in version:
3067     2.0.1
3068
3069     Description:
3070     Before 2.0.1, move speed of elements entering or leaving magic wall was
3071     twice as fast as it is now.
3072     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3073
3074     Affected levels/tapes:
3075     The first condition is generally needed for all levels/tapes before version
3076     2.0.1, which might use the old behaviour before it was changed; known tapes
3077     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3078     The second condition is an exception from the above case and is needed for
3079     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3080     above, but before it was known that this change would break tapes like the
3081     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3082     although the engine version while recording maybe was before 2.0.1. There
3083     are a lot of tapes that are affected by this exception, like tape 006 from
3084     the level set "rnd_conor_mancone".
3085   */
3086
3087   boolean use_old_move_stepsize_for_magic_wall =
3088     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3089      !(tape.playing &&
3090        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3091        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3092
3093   /*
3094     Summary of bugfix/change:
3095     Fixed handling for custom elements that change when pushed by the player.
3096
3097     Fixed/changed in version:
3098     3.1.0
3099
3100     Description:
3101     Before 3.1.0, custom elements that "change when pushing" changed directly
3102     after the player started pushing them (until then handled in "DigField()").
3103     Since 3.1.0, these custom elements are not changed until the "pushing"
3104     move of the element is finished (now handled in "ContinueMoving()").
3105
3106     Affected levels/tapes:
3107     The first condition is generally needed for all levels/tapes before version
3108     3.1.0, which might use the old behaviour before it was changed; known tapes
3109     that are affected are some tapes from the level set "Walpurgis Gardens" by
3110     Jamie Cullen.
3111     The second condition is an exception from the above case and is needed for
3112     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3113     above (including some development versions of 3.1.0), but before it was
3114     known that this change would break tapes like the above and was fixed in
3115     3.1.1, so that the changed behaviour was active although the engine version
3116     while recording maybe was before 3.1.0. There is at least one tape that is
3117     affected by this exception, which is the tape for the one-level set "Bug
3118     Machine" by Juergen Bonhagen.
3119   */
3120
3121   game.use_change_when_pushing_bug =
3122     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3123      !(tape.playing &&
3124        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3125        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3126
3127   /*
3128     Summary of bugfix/change:
3129     Fixed handling for blocking the field the player leaves when moving.
3130
3131     Fixed/changed in version:
3132     3.1.1
3133
3134     Description:
3135     Before 3.1.1, when "block last field when moving" was enabled, the field
3136     the player is leaving when moving was blocked for the time of the move,
3137     and was directly unblocked afterwards. This resulted in the last field
3138     being blocked for exactly one less than the number of frames of one player
3139     move. Additionally, even when blocking was disabled, the last field was
3140     blocked for exactly one frame.
3141     Since 3.1.1, due to changes in player movement handling, the last field
3142     is not blocked at all when blocking is disabled. When blocking is enabled,
3143     the last field is blocked for exactly the number of frames of one player
3144     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3145     last field is blocked for exactly one more than the number of frames of
3146     one player move.
3147
3148     Affected levels/tapes:
3149     (!!! yet to be determined -- probably many !!!)
3150   */
3151
3152   game.use_block_last_field_bug =
3153     (game.engine_version < VERSION_IDENT(3,1,1,0));
3154
3155   /* various special flags and settings for native Emerald Mine game engine */
3156
3157   game_em.use_single_button =
3158     (game.engine_version > VERSION_IDENT(4,0,0,2));
3159
3160   game_em.use_push_delay =
3161     (game.engine_version > VERSION_IDENT(4,3,7,1));
3162
3163   game_em.use_snap_key_bug =
3164     (game.engine_version < VERSION_IDENT(4,0,1,0));
3165
3166   game_em.use_random_bug =
3167     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3168
3169   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3170
3171   game_em.use_old_explosions            = use_old_em_engine;
3172   game_em.use_old_android               = use_old_em_engine;
3173   game_em.use_old_push_elements         = use_old_em_engine;
3174   game_em.use_old_push_into_acid        = use_old_em_engine;
3175
3176   game_em.use_wrap_around               = !use_old_em_engine;
3177
3178   // --------------------------------------------------------------------------
3179
3180   // set maximal allowed number of custom element changes per game frame
3181   game.max_num_changes_per_frame = 1;
3182
3183   // default scan direction: scan playfield from top/left to bottom/right
3184   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3185
3186   // dynamically adjust element properties according to game engine version
3187   InitElementPropertiesEngine(game.engine_version);
3188
3189   // ---------- initialize special element properties -------------------------
3190
3191   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3192   if (use_amoeba_dropping_cannot_fall_bug)
3193     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3194
3195   // ---------- initialize player's initial move delay ------------------------
3196
3197   // dynamically adjust player properties according to level information
3198   for (i = 0; i < MAX_PLAYERS; i++)
3199     game.initial_move_delay_value[i] =
3200       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3201
3202   // dynamically adjust player properties according to game engine version
3203   for (i = 0; i < MAX_PLAYERS; i++)
3204     game.initial_move_delay[i] =
3205       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3206        game.initial_move_delay_value[i] : 0);
3207
3208   // ---------- initialize player's initial push delay ------------------------
3209
3210   // dynamically adjust player properties according to game engine version
3211   game.initial_push_delay_value =
3212     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3213
3214   // ---------- initialize changing elements ----------------------------------
3215
3216   // initialize changing elements information
3217   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3218   {
3219     struct ElementInfo *ei = &element_info[i];
3220
3221     // this pointer might have been changed in the level editor
3222     ei->change = &ei->change_page[0];
3223
3224     if (!IS_CUSTOM_ELEMENT(i))
3225     {
3226       ei->change->target_element = EL_EMPTY_SPACE;
3227       ei->change->delay_fixed = 0;
3228       ei->change->delay_random = 0;
3229       ei->change->delay_frames = 1;
3230     }
3231
3232     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3233     {
3234       ei->has_change_event[j] = FALSE;
3235
3236       ei->event_page_nr[j] = 0;
3237       ei->event_page[j] = &ei->change_page[0];
3238     }
3239   }
3240
3241   // add changing elements from pre-defined list
3242   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3243   {
3244     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3245     struct ElementInfo *ei = &element_info[ch_delay->element];
3246
3247     ei->change->target_element       = ch_delay->target_element;
3248     ei->change->delay_fixed          = ch_delay->change_delay;
3249
3250     ei->change->pre_change_function  = ch_delay->pre_change_function;
3251     ei->change->change_function      = ch_delay->change_function;
3252     ei->change->post_change_function = ch_delay->post_change_function;
3253
3254     ei->change->can_change = TRUE;
3255     ei->change->can_change_or_has_action = TRUE;
3256
3257     ei->has_change_event[CE_DELAY] = TRUE;
3258
3259     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3260     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3261   }
3262
3263   // ---------- initialize if element can trigger global animations -----------
3264
3265   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3266   {
3267     struct ElementInfo *ei = &element_info[i];
3268
3269     ei->has_anim_event = FALSE;
3270   }
3271
3272   InitGlobalAnimEventsForCustomElements();
3273
3274   // ---------- initialize internal run-time variables ------------------------
3275
3276   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       ei->change_page[j].can_change_or_has_action =
3283         (ei->change_page[j].can_change |
3284          ei->change_page[j].has_action);
3285     }
3286   }
3287
3288   // add change events from custom element configuration
3289   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3290   {
3291     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3292
3293     for (j = 0; j < ei->num_change_pages; j++)
3294     {
3295       if (!ei->change_page[j].can_change_or_has_action)
3296         continue;
3297
3298       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3299       {
3300         // only add event page for the first page found with this event
3301         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3302         {
3303           ei->has_change_event[k] = TRUE;
3304
3305           ei->event_page_nr[k] = j;
3306           ei->event_page[k] = &ei->change_page[j];
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize reference elements in change conditions ------------
3313
3314   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3315   {
3316     int element = EL_CUSTOM_START + i;
3317     struct ElementInfo *ei = &element_info[element];
3318
3319     for (j = 0; j < ei->num_change_pages; j++)
3320     {
3321       int trigger_element = ei->change_page[j].initial_trigger_element;
3322
3323       if (trigger_element >= EL_PREV_CE_8 &&
3324           trigger_element <= EL_NEXT_CE_8)
3325         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3326
3327       ei->change_page[j].trigger_element = trigger_element;
3328     }
3329   }
3330
3331   // ---------- initialize run-time trigger player and element ----------------
3332
3333   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3334   {
3335     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3336
3337     for (j = 0; j < ei->num_change_pages; j++)
3338     {
3339       struct ElementChangeInfo *change = &ei->change_page[j];
3340
3341       change->actual_trigger_element = EL_EMPTY;
3342       change->actual_trigger_player = EL_EMPTY;
3343       change->actual_trigger_player_bits = CH_PLAYER_NONE;
3344       change->actual_trigger_side = CH_SIDE_NONE;
3345       change->actual_trigger_ce_value = 0;
3346       change->actual_trigger_ce_score = 0;
3347       change->actual_trigger_x = -1;
3348       change->actual_trigger_y = -1;
3349     }
3350   }
3351
3352   // ---------- initialize trigger events -------------------------------------
3353
3354   // initialize trigger events information
3355   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3356     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3357       trigger_events[i][j] = FALSE;
3358
3359   // add trigger events from element change event properties
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361   {
3362     struct ElementInfo *ei = &element_info[i];
3363
3364     for (j = 0; j < ei->num_change_pages; j++)
3365     {
3366       struct ElementChangeInfo *change = &ei->change_page[j];
3367
3368       if (!change->can_change_or_has_action)
3369         continue;
3370
3371       if (change->has_event[CE_BY_OTHER_ACTION])
3372       {
3373         int trigger_element = change->trigger_element;
3374
3375         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3376         {
3377           if (change->has_event[k])
3378           {
3379             if (IS_GROUP_ELEMENT(trigger_element))
3380             {
3381               struct ElementGroupInfo *group =
3382                 element_info[trigger_element].group;
3383
3384               for (l = 0; l < group->num_elements_resolved; l++)
3385                 trigger_events[group->element_resolved[l]][k] = TRUE;
3386             }
3387             else if (trigger_element == EL_ANY_ELEMENT)
3388               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3389                 trigger_events[l][k] = TRUE;
3390             else
3391               trigger_events[trigger_element][k] = TRUE;
3392           }
3393         }
3394       }
3395     }
3396   }
3397
3398   // ---------- initialize push delay -----------------------------------------
3399
3400   // initialize push delay values to default
3401   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3402   {
3403     if (!IS_CUSTOM_ELEMENT(i))
3404     {
3405       // set default push delay values (corrected since version 3.0.7-1)
3406       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3407       {
3408         element_info[i].push_delay_fixed = 2;
3409         element_info[i].push_delay_random = 8;
3410       }
3411       else
3412       {
3413         element_info[i].push_delay_fixed = 8;
3414         element_info[i].push_delay_random = 8;
3415       }
3416     }
3417   }
3418
3419   // set push delay value for certain elements from pre-defined list
3420   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3421   {
3422     int e = push_delay_list[i].element;
3423
3424     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3425     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3426   }
3427
3428   // set push delay value for Supaplex elements for newer engine versions
3429   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3430   {
3431     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432     {
3433       if (IS_SP_ELEMENT(i))
3434       {
3435         // set SP push delay to just enough to push under a falling zonk
3436         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3437
3438         element_info[i].push_delay_fixed  = delay;
3439         element_info[i].push_delay_random = 0;
3440       }
3441     }
3442   }
3443
3444   // ---------- initialize move stepsize --------------------------------------
3445
3446   // initialize move stepsize values to default
3447   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3448     if (!IS_CUSTOM_ELEMENT(i))
3449       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3450
3451   // set move stepsize value for certain elements from pre-defined list
3452   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3453   {
3454     int e = move_stepsize_list[i].element;
3455
3456     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3457
3458     // set move stepsize value for certain elements for older engine versions
3459     if (use_old_move_stepsize_for_magic_wall)
3460     {
3461       if (e == EL_MAGIC_WALL_FILLING ||
3462           e == EL_MAGIC_WALL_EMPTYING ||
3463           e == EL_BD_MAGIC_WALL_FILLING ||
3464           e == EL_BD_MAGIC_WALL_EMPTYING)
3465         element_info[e].move_stepsize *= 2;
3466     }
3467   }
3468
3469   // ---------- initialize collect score --------------------------------------
3470
3471   // initialize collect score values for custom elements from initial value
3472   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3473     if (IS_CUSTOM_ELEMENT(i))
3474       element_info[i].collect_score = element_info[i].collect_score_initial;
3475
3476   // ---------- initialize collect count --------------------------------------
3477
3478   // initialize collect count values for non-custom elements
3479   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3480     if (!IS_CUSTOM_ELEMENT(i))
3481       element_info[i].collect_count_initial = 0;
3482
3483   // add collect count values for all elements from pre-defined list
3484   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3485     element_info[collect_count_list[i].element].collect_count_initial =
3486       collect_count_list[i].count;
3487
3488   // ---------- initialize access direction -----------------------------------
3489
3490   // initialize access direction values to default (access from every side)
3491   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3492     if (!IS_CUSTOM_ELEMENT(i))
3493       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3494
3495   // set access direction value for certain elements from pre-defined list
3496   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3497     element_info[access_direction_list[i].element].access_direction =
3498       access_direction_list[i].direction;
3499
3500   // ---------- initialize explosion content ----------------------------------
3501   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3502   {
3503     if (IS_CUSTOM_ELEMENT(i))
3504       continue;
3505
3506     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3507     {
3508       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3509
3510       element_info[i].content.e[x][y] =
3511         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3512          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3513          i == EL_PLAYER_3 ? EL_EMERALD :
3514          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3515          i == EL_MOLE ? EL_EMERALD_RED :
3516          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3517          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3518          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3519          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3520          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3521          i == EL_WALL_EMERALD ? EL_EMERALD :
3522          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3523          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3524          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3525          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3526          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3527          i == EL_WALL_PEARL ? EL_PEARL :
3528          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3529          EL_EMPTY);
3530     }
3531   }
3532
3533   // ---------- initialize recursion detection --------------------------------
3534   recursion_loop_depth = 0;
3535   recursion_loop_detected = FALSE;
3536   recursion_loop_element = EL_UNDEFINED;
3537
3538   // ---------- initialize graphics engine ------------------------------------
3539   game.scroll_delay_value =
3540     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3541      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3542      !setup.forced_scroll_delay           ? 0 :
3543      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3544   if (game.forced_scroll_delay_value == -1)
3545     game.scroll_delay_value =
3546       MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3547
3548   // ---------- initialize game engine snapshots ------------------------------
3549   for (i = 0; i < MAX_PLAYERS; i++)
3550     game.snapshot.last_action[i] = 0;
3551   game.snapshot.changed_action = FALSE;
3552   game.snapshot.collected_item = FALSE;
3553   game.snapshot.mode =
3554     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3555      SNAPSHOT_MODE_EVERY_STEP :
3556      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3557      SNAPSHOT_MODE_EVERY_MOVE :
3558      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3559      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3560   game.snapshot.save_snapshot = FALSE;
3561
3562   // ---------- initialize level time for Supaplex engine ---------------------
3563   // Supaplex levels with time limit currently unsupported -- should be added
3564   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3565     level.time = 0;
3566
3567   // ---------- initialize flags for handling game actions --------------------
3568
3569   // set flags for game actions to default values
3570   game.use_key_actions = TRUE;
3571   game.use_mouse_actions = FALSE;
3572
3573   // when using Mirror Magic game engine, handle mouse events only
3574   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3575   {
3576     game.use_key_actions = FALSE;
3577     game.use_mouse_actions = TRUE;
3578   }
3579
3580   // check for custom elements with mouse click events
3581   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3582   {
3583     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3584     {
3585       int element = EL_CUSTOM_START + i;
3586
3587       if (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3588           HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3589           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3590           HAS_ANY_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3591         game.use_mouse_actions = TRUE;
3592     }
3593   }
3594 }
3595
3596 static int get_num_special_action(int element, int action_first,
3597                                   int action_last)
3598 {
3599   int num_special_action = 0;
3600   int i, j;
3601
3602   for (i = action_first; i <= action_last; i++)
3603   {
3604     boolean found = FALSE;
3605
3606     for (j = 0; j < NUM_DIRECTIONS; j++)
3607       if (el_act_dir2img(element, i, j) !=
3608           el_act_dir2img(element, ACTION_DEFAULT, j))
3609         found = TRUE;
3610
3611     if (found)
3612       num_special_action++;
3613     else
3614       break;
3615   }
3616
3617   return num_special_action;
3618 }
3619
3620
3621 // ============================================================================
3622 // InitGame()
3623 // ----------------------------------------------------------------------------
3624 // initialize and start new game
3625 // ============================================================================
3626
3627 #if DEBUG_INIT_PLAYER
3628 static void DebugPrintPlayerStatus(char *message)
3629 {
3630   int i;
3631
3632   if (!options.debug)
3633     return;
3634
3635   Debug("game:init:player", "%s:", message);
3636
3637   for (i = 0; i < MAX_PLAYERS; i++)
3638   {
3639     struct PlayerInfo *player = &stored_player[i];
3640
3641     Debug("game:init:player",
3642           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3643           i + 1,
3644           player->present,
3645           player->connected,
3646           player->connected_locally,
3647           player->connected_network,
3648           player->active,
3649           (local_player == player ? " (local player)" : ""));
3650   }
3651 }
3652 #endif
3653
3654 void InitGame(void)
3655 {
3656   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3657   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3658   int fade_mask = REDRAW_FIELD;
3659   boolean restarting = (game_status == GAME_MODE_PLAYING);
3660   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3661   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3662   int initial_move_dir = MV_DOWN;
3663   int i, j, x, y;
3664
3665   // required here to update video display before fading (FIX THIS)
3666   DrawMaskedBorder(REDRAW_DOOR_2);
3667
3668   if (!game.restart_level)
3669     CloseDoor(DOOR_CLOSE_1);
3670
3671   if (restarting)
3672   {
3673     // force fading out global animations displayed during game play
3674     SetGameStatus(GAME_MODE_PSEUDO_RESTARTING);
3675   }
3676   else
3677   {
3678     SetGameStatus(GAME_MODE_PLAYING);
3679   }
3680
3681   if (level_editor_test_game)
3682     FadeSkipNextFadeOut();
3683   else
3684     FadeSetEnterScreen();
3685
3686   if (CheckFadeAll())
3687     fade_mask = REDRAW_ALL;
3688
3689   FadeLevelSoundsAndMusic();
3690
3691   ExpireSoundLoops(TRUE);
3692
3693   FadeOut(fade_mask);
3694
3695   if (restarting)
3696   {
3697     // force restarting global animations displayed during game play
3698     RestartGlobalAnimsByStatus(GAME_MODE_PSEUDO_RESTARTING);
3699
3700     // this is required for "transforming" fade modes like cross-fading
3701     // (else global animations will be stopped, but not restarted here)
3702     SetAnimStatusBeforeFading(GAME_MODE_PSEUDO_RESTARTING);
3703
3704     SetGameStatus(GAME_MODE_PLAYING);
3705   }
3706
3707   if (level_editor_test_game)
3708     FadeSkipNextFadeIn();
3709
3710   // needed if different viewport properties defined for playing
3711   ChangeViewportPropertiesIfNeeded();
3712
3713   ClearField();
3714
3715   DrawCompleteVideoDisplay();
3716
3717   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3718
3719   InitGameEngine();
3720   InitGameControlValues();
3721
3722   if (tape.recording)
3723   {
3724     // initialize tape actions from game when recording tape
3725     tape.use_key_actions   = game.use_key_actions;
3726     tape.use_mouse_actions = game.use_mouse_actions;
3727
3728     // initialize visible playfield size when recording tape (for team mode)
3729     tape.scr_fieldx = SCR_FIELDX;
3730     tape.scr_fieldy = SCR_FIELDY;
3731   }
3732
3733   // don't play tapes over network
3734   network_playing = (network.enabled && !tape.playing);
3735
3736   for (i = 0; i < MAX_PLAYERS; i++)
3737   {
3738     struct PlayerInfo *player = &stored_player[i];
3739
3740     player->index_nr = i;
3741     player->index_bit = (1 << i);
3742     player->element_nr = EL_PLAYER_1 + i;
3743
3744     player->present = FALSE;
3745     player->active = FALSE;
3746     player->mapped = FALSE;
3747
3748     player->killed = FALSE;
3749     player->reanimated = FALSE;
3750     player->buried = FALSE;
3751
3752     player->action = 0;
3753     player->effective_action = 0;
3754     player->programmed_action = 0;
3755     player->snap_action = 0;
3756
3757     player->mouse_action.lx = 0;
3758     player->mouse_action.ly = 0;
3759     player->mouse_action.button = 0;
3760     player->mouse_action.button_hint = 0;
3761
3762     player->effective_mouse_action.lx = 0;
3763     player->effective_mouse_action.ly = 0;
3764     player->effective_mouse_action.button = 0;
3765     player->effective_mouse_action.button_hint = 0;
3766
3767     for (j = 0; j < MAX_NUM_KEYS; j++)
3768       player->key[j] = FALSE;
3769
3770     player->num_white_keys = 0;
3771
3772     player->dynabomb_count = 0;
3773     player->dynabomb_size = 1;
3774     player->dynabombs_left = 0;
3775     player->dynabomb_xl = FALSE;
3776
3777     player->MovDir = initial_move_dir;
3778     player->MovPos = 0;
3779     player->GfxPos = 0;
3780     player->GfxDir = initial_move_dir;
3781     player->GfxAction = ACTION_DEFAULT;
3782     player->Frame = 0;
3783     player->StepFrame = 0;
3784
3785     player->initial_element = player->element_nr;
3786     player->artwork_element =
3787       (level.use_artwork_element[i] ? level.artwork_element[i] :
3788        player->element_nr);
3789     player->use_murphy = FALSE;
3790
3791     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3792     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3793
3794     player->gravity = level.initial_player_gravity[i];
3795
3796     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3797
3798     player->actual_frame_counter.count = 0;
3799     player->actual_frame_counter.value = 1;
3800
3801     player->step_counter = 0;
3802
3803     player->last_move_dir = initial_move_dir;
3804
3805     player->is_active = FALSE;
3806
3807     player->is_waiting = FALSE;
3808     player->is_moving = FALSE;
3809     player->is_auto_moving = FALSE;
3810     player->is_digging = FALSE;
3811     player->is_snapping = FALSE;
3812     player->is_collecting = FALSE;
3813     player->is_pushing = FALSE;
3814     player->is_switching = FALSE;
3815     player->is_dropping = FALSE;
3816     player->is_dropping_pressed = FALSE;
3817
3818     player->is_bored = FALSE;
3819     player->is_sleeping = FALSE;
3820
3821     player->was_waiting = TRUE;
3822     player->was_moving = FALSE;
3823     player->was_snapping = FALSE;
3824     player->was_dropping = FALSE;
3825
3826     player->force_dropping = FALSE;
3827
3828     player->frame_counter_bored = -1;
3829     player->frame_counter_sleeping = -1;
3830
3831     player->anim_delay_counter = 0;
3832     player->post_delay_counter = 0;
3833
3834     player->dir_waiting = initial_move_dir;
3835     player->action_waiting = ACTION_DEFAULT;
3836     player->last_action_waiting = ACTION_DEFAULT;
3837     player->special_action_bored = ACTION_DEFAULT;
3838     player->special_action_sleeping = ACTION_DEFAULT;
3839
3840     player->switch_x = -1;
3841     player->switch_y = -1;
3842
3843     player->drop_x = -1;
3844     player->drop_y = -1;
3845
3846     player->show_envelope = 0;
3847
3848     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3849
3850     player->push_delay       = -1;      // initialized when pushing starts
3851     player->push_delay_value = game.initial_push_delay_value;
3852
3853     player->drop_delay = 0;
3854     player->drop_pressed_delay = 0;
3855
3856     player->last_jx = -1;
3857     player->last_jy = -1;
3858     player->jx = -1;
3859     player->jy = -1;
3860
3861     player->shield_normal_time_left = 0;
3862     player->shield_deadly_time_left = 0;
3863
3864     player->last_removed_element = EL_UNDEFINED;
3865
3866     player->inventory_infinite_element = EL_UNDEFINED;
3867     player->inventory_size = 0;
3868
3869     if (level.use_initial_inventory[i])
3870     {
3871       for (j = 0; j < level.initial_inventory_size[i]; j++)
3872       {
3873         int element = level.initial_inventory_content[i][j];
3874         int collect_count = element_info[element].collect_count_initial;
3875         int k;
3876
3877         if (!IS_CUSTOM_ELEMENT(element))
3878           collect_count = 1;
3879
3880         if (collect_count == 0)
3881           player->inventory_infinite_element = element;
3882         else
3883           for (k = 0; k < collect_count; k++)
3884             if (player->inventory_size < MAX_INVENTORY_SIZE)
3885               player->inventory_element[player->inventory_size++] = element;
3886       }
3887     }
3888
3889     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3890     SnapField(player, 0, 0);
3891
3892     map_player_action[i] = i;
3893   }
3894
3895   network_player_action_received = FALSE;
3896
3897   // initial null action
3898   if (network_playing)
3899     SendToServer_MovePlayer(MV_NONE);
3900
3901   FrameCounter = 0;
3902   TimeFrames = 0;
3903   TimePlayed = 0;
3904   TimeLeft = level.time;
3905
3906   TapeTimeFrames = 0;
3907   TapeTime = 0;
3908
3909   ScreenMovDir = MV_NONE;
3910   ScreenMovPos = 0;
3911   ScreenGfxPos = 0;
3912
3913   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3914
3915   game.robot_wheel_x = -1;
3916   game.robot_wheel_y = -1;
3917
3918   game.exit_x = -1;
3919   game.exit_y = -1;
3920
3921   game.all_players_gone = FALSE;
3922
3923   game.LevelSolved = FALSE;
3924   game.GameOver = FALSE;
3925
3926   game.GamePlayed = !tape.playing;
3927
3928   game.LevelSolved_GameWon = FALSE;
3929   game.LevelSolved_GameEnd = FALSE;
3930   game.LevelSolved_SaveTape = FALSE;
3931   game.LevelSolved_SaveScore = FALSE;
3932
3933   game.LevelSolved_CountingTime = 0;
3934   game.LevelSolved_CountingScore = 0;
3935   game.LevelSolved_CountingHealth = 0;
3936
3937   game.RestartGameRequested = FALSE;
3938
3939   game.panel.active = TRUE;
3940
3941   game.no_level_time_limit = (level.time == 0);
3942   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3943
3944   game.yamyam_content_nr = 0;
3945   game.robot_wheel_active = FALSE;
3946   game.magic_wall_active = FALSE;
3947   game.magic_wall_time_left = 0;
3948   game.light_time_left = 0;
3949   game.timegate_time_left = 0;
3950   game.switchgate_pos = 0;
3951   game.wind_direction = level.wind_direction_initial;
3952
3953   game.time_final = 0;
3954   game.score_time_final = 0;
3955
3956   game.score = 0;
3957   game.score_final = 0;
3958
3959   game.health = MAX_HEALTH;
3960   game.health_final = MAX_HEALTH;
3961
3962   game.gems_still_needed = level.gems_needed;
3963   game.sokoban_fields_still_needed = 0;
3964   game.sokoban_objects_still_needed = 0;
3965   game.lights_still_needed = 0;
3966   game.players_still_needed = 0;
3967   game.friends_still_needed = 0;
3968
3969   game.lenses_time_left = 0;
3970   game.magnify_time_left = 0;
3971
3972   game.ball_active = level.ball_active_initial;
3973   game.ball_content_nr = 0;
3974
3975   game.explosions_delayed = TRUE;
3976
3977   // special case: set custom artwork setting to initial value
3978   game.use_masked_elements = game.use_masked_elements_initial;
3979
3980   for (i = 0; i < NUM_BELTS; i++)
3981   {
3982     game.belt_dir[i] = MV_NONE;
3983     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3984   }
3985
3986   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3987     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3988
3989 #if DEBUG_INIT_PLAYER
3990   DebugPrintPlayerStatus("Player status at level initialization");
3991 #endif
3992
3993   SCAN_PLAYFIELD(x, y)
3994   {
3995     Tile[x][y] = Last[x][y] = level.field[x][y];
3996     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3997     ChangeDelay[x][y] = 0;
3998     ChangePage[x][y] = -1;
3999     CustomValue[x][y] = 0;              // initialized in InitField()
4000     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
4001     AmoebaNr[x][y] = 0;
4002     WasJustMoving[x][y] = 0;
4003     WasJustFalling[x][y] = 0;
4004     CheckCollision[x][y] = 0;
4005     CheckImpact[x][y] = 0;
4006     Stop[x][y] = FALSE;
4007     Pushed[x][y] = FALSE;
4008
4009     ChangeCount[x][y] = 0;
4010     ChangeEvent[x][y] = -1;
4011
4012     ExplodePhase[x][y] = 0;
4013     ExplodeDelay[x][y] = 0;
4014     ExplodeField[x][y] = EX_TYPE_NONE;
4015
4016     RunnerVisit[x][y] = 0;
4017     PlayerVisit[x][y] = 0;
4018
4019     GfxFrame[x][y] = 0;
4020     GfxRandom[x][y] = INIT_GFX_RANDOM();
4021     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
4022     GfxElement[x][y] = EL_UNDEFINED;
4023     GfxElementEmpty[x][y] = EL_EMPTY;
4024     GfxAction[x][y] = ACTION_DEFAULT;
4025     GfxDir[x][y] = MV_NONE;
4026     GfxRedraw[x][y] = GFX_REDRAW_NONE;
4027   }
4028
4029   SCAN_PLAYFIELD(x, y)
4030   {
4031     InitFieldForEngine(x, y);
4032
4033     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
4034       emulate_bd = FALSE;
4035     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
4036       emulate_sp = FALSE;
4037
4038     InitField(x, y, TRUE);
4039
4040     ResetGfxAnimation(x, y);
4041   }
4042
4043   InitBeltMovement();
4044
4045   // required if level does not contain any "empty space" element
4046   if (element_info[EL_EMPTY].use_gfx_element)
4047     game.use_masked_elements = TRUE;
4048
4049   for (i = 0; i < MAX_PLAYERS; i++)
4050   {
4051     struct PlayerInfo *player = &stored_player[i];
4052
4053     // set number of special actions for bored and sleeping animation
4054     player->num_special_action_bored =
4055       get_num_special_action(player->artwork_element,
4056                              ACTION_BORING_1, ACTION_BORING_LAST);
4057     player->num_special_action_sleeping =
4058       get_num_special_action(player->artwork_element,
4059                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
4060   }
4061
4062   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
4063                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
4064
4065   // initialize type of slippery elements
4066   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4067   {
4068     if (!IS_CUSTOM_ELEMENT(i))
4069     {
4070       // default: elements slip down either to the left or right randomly
4071       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
4072
4073       // SP style elements prefer to slip down on the left side
4074       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
4075         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4076
4077       // BD style elements prefer to slip down on the left side
4078       if (game.emulation == EMU_BOULDERDASH)
4079         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
4080     }
4081   }
4082
4083   // initialize explosion and ignition delay
4084   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
4085   {
4086     if (!IS_CUSTOM_ELEMENT(i))
4087     {
4088       int num_phase = 8;
4089       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
4090                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
4091                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
4092       int last_phase = (num_phase + 1) * delay;
4093       int half_phase = (num_phase / 2) * delay;
4094
4095       element_info[i].explosion_delay = last_phase - 1;
4096       element_info[i].ignition_delay = half_phase;
4097
4098       if (i == EL_BLACK_ORB)
4099         element_info[i].ignition_delay = 1;
4100     }
4101   }
4102
4103   // correct non-moving belts to start moving left
4104   for (i = 0; i < NUM_BELTS; i++)
4105     if (game.belt_dir[i] == MV_NONE)
4106       game.belt_dir_nr[i] = 3;          // not moving, next moving left
4107
4108 #if USE_NEW_PLAYER_ASSIGNMENTS
4109   // use preferred player also in local single-player mode
4110   if (!network.enabled && !game.team_mode)
4111   {
4112     int new_index_nr = setup.network_player_nr;
4113
4114     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4115     {
4116       for (i = 0; i < MAX_PLAYERS; i++)
4117         stored_player[i].connected_locally = FALSE;
4118
4119       stored_player[new_index_nr].connected_locally = TRUE;
4120     }
4121   }
4122
4123   for (i = 0; i < MAX_PLAYERS; i++)
4124   {
4125     stored_player[i].connected = FALSE;
4126
4127     // in network game mode, the local player might not be the first player
4128     if (stored_player[i].connected_locally)
4129       local_player = &stored_player[i];
4130   }
4131
4132   if (!network.enabled)
4133     local_player->connected = TRUE;
4134
4135   if (tape.playing)
4136   {
4137     for (i = 0; i < MAX_PLAYERS; i++)
4138       stored_player[i].connected = tape.player_participates[i];
4139   }
4140   else if (network.enabled)
4141   {
4142     // add team mode players connected over the network (needed for correct
4143     // assignment of player figures from level to locally playing players)
4144
4145     for (i = 0; i < MAX_PLAYERS; i++)
4146       if (stored_player[i].connected_network)
4147         stored_player[i].connected = TRUE;
4148   }
4149   else if (game.team_mode)
4150   {
4151     // try to guess locally connected team mode players (needed for correct
4152     // assignment of player figures from level to locally playing players)
4153
4154     for (i = 0; i < MAX_PLAYERS; i++)
4155       if (setup.input[i].use_joystick ||
4156           setup.input[i].key.left != KSYM_UNDEFINED)
4157         stored_player[i].connected = TRUE;
4158   }
4159
4160 #if DEBUG_INIT_PLAYER
4161   DebugPrintPlayerStatus("Player status after level initialization");
4162 #endif
4163
4164 #if DEBUG_INIT_PLAYER
4165   Debug("game:init:player", "Reassigning players ...");
4166 #endif
4167
4168   // check if any connected player was not found in playfield
4169   for (i = 0; i < MAX_PLAYERS; i++)
4170   {
4171     struct PlayerInfo *player = &stored_player[i];
4172
4173     if (player->connected && !player->present)
4174     {
4175       struct PlayerInfo *field_player = NULL;
4176
4177 #if DEBUG_INIT_PLAYER
4178       Debug("game:init:player",
4179             "- looking for field player for player %d ...", i + 1);
4180 #endif
4181
4182       // assign first free player found that is present in the playfield
4183
4184       // first try: look for unmapped playfield player that is not connected
4185       for (j = 0; j < MAX_PLAYERS; j++)
4186         if (field_player == NULL &&
4187             stored_player[j].present &&
4188             !stored_player[j].mapped &&
4189             !stored_player[j].connected)
4190           field_player = &stored_player[j];
4191
4192       // second try: look for *any* unmapped playfield player
4193       for (j = 0; j < MAX_PLAYERS; j++)
4194         if (field_player == NULL &&
4195             stored_player[j].present &&
4196             !stored_player[j].mapped)
4197           field_player = &stored_player[j];
4198
4199       if (field_player != NULL)
4200       {
4201         int jx = field_player->jx, jy = field_player->jy;
4202
4203 #if DEBUG_INIT_PLAYER
4204         Debug("game:init:player", "- found player %d",
4205               field_player->index_nr + 1);
4206 #endif
4207
4208         player->present = FALSE;
4209         player->active = FALSE;
4210
4211         field_player->present = TRUE;
4212         field_player->active = TRUE;
4213
4214         /*
4215         player->initial_element = field_player->initial_element;
4216         player->artwork_element = field_player->artwork_element;
4217
4218         player->block_last_field       = field_player->block_last_field;
4219         player->block_delay_adjustment = field_player->block_delay_adjustment;
4220         */
4221
4222         StorePlayer[jx][jy] = field_player->element_nr;
4223
4224         field_player->jx = field_player->last_jx = jx;
4225         field_player->jy = field_player->last_jy = jy;
4226
4227         if (local_player == player)
4228           local_player = field_player;
4229
4230         map_player_action[field_player->index_nr] = i;
4231
4232         field_player->mapped = TRUE;
4233
4234 #if DEBUG_INIT_PLAYER
4235         Debug("game:init:player", "- map_player_action[%d] == %d",
4236               field_player->index_nr + 1, i + 1);
4237 #endif
4238       }
4239     }
4240
4241     if (player->connected && player->present)
4242       player->mapped = TRUE;
4243   }
4244
4245 #if DEBUG_INIT_PLAYER
4246   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4247 #endif
4248
4249 #else
4250
4251   // check if any connected player was not found in playfield
4252   for (i = 0; i < MAX_PLAYERS; i++)
4253   {
4254     struct PlayerInfo *player = &stored_player[i];
4255
4256     if (player->connected && !player->present)
4257     {
4258       for (j = 0; j < MAX_PLAYERS; j++)
4259       {
4260         struct PlayerInfo *field_player = &stored_player[j];
4261         int jx = field_player->jx, jy = field_player->jy;
4262
4263         // assign first free player found that is present in the playfield
4264         if (field_player->present && !field_player->connected)
4265         {
4266           player->present = TRUE;
4267           player->active = TRUE;
4268
4269           field_player->present = FALSE;
4270           field_player->active = FALSE;
4271
4272           player->initial_element = field_player->initial_element;
4273           player->artwork_element = field_player->artwork_element;
4274
4275           player->block_last_field       = field_player->block_last_field;
4276           player->block_delay_adjustment = field_player->block_delay_adjustment;
4277
4278           StorePlayer[jx][jy] = player->element_nr;
4279
4280           player->jx = player->last_jx = jx;
4281           player->jy = player->last_jy = jy;
4282
4283           break;
4284         }
4285       }
4286     }
4287   }
4288 #endif
4289
4290 #if 0
4291   Debug("game:init:player", "local_player->present == %d",
4292         local_player->present);
4293 #endif
4294
4295   // set focus to local player for network games, else to all players
4296   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4297   game.centered_player_nr_next = game.centered_player_nr;
4298   game.set_centered_player = FALSE;
4299   game.set_centered_player_wrap = FALSE;
4300
4301   if (network_playing && tape.recording)
4302   {
4303     // store client dependent player focus when recording network games
4304     tape.centered_player_nr_next = game.centered_player_nr_next;
4305     tape.set_centered_player = TRUE;
4306   }
4307
4308   if (tape.playing)
4309   {
4310     // when playing a tape, eliminate all players who do not participate
4311
4312 #if USE_NEW_PLAYER_ASSIGNMENTS
4313
4314     if (!game.team_mode)
4315     {
4316       for (i = 0; i < MAX_PLAYERS; i++)
4317       {
4318         if (stored_player[i].active &&
4319             !tape.player_participates[map_player_action[i]])
4320         {
4321           struct PlayerInfo *player = &stored_player[i];
4322           int jx = player->jx, jy = player->jy;
4323
4324 #if DEBUG_INIT_PLAYER
4325           Debug("game:init:player", "Removing player %d at (%d, %d)",
4326                 i + 1, jx, jy);
4327 #endif
4328
4329           player->active = FALSE;
4330           StorePlayer[jx][jy] = 0;
4331           Tile[jx][jy] = EL_EMPTY;
4332         }
4333       }
4334     }
4335
4336 #else
4337
4338     for (i = 0; i < MAX_PLAYERS; i++)
4339     {
4340       if (stored_player[i].active &&
4341           !tape.player_participates[i])
4342       {
4343         struct PlayerInfo *player = &stored_player[i];
4344         int jx = player->jx, jy = player->jy;
4345
4346         player->active = FALSE;
4347         StorePlayer[jx][jy] = 0;
4348         Tile[jx][jy] = EL_EMPTY;
4349       }
4350     }
4351 #endif
4352   }
4353   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4354   {
4355     // when in single player mode, eliminate all but the local player
4356
4357     for (i = 0; i < MAX_PLAYERS; i++)
4358     {
4359       struct PlayerInfo *player = &stored_player[i];
4360
4361       if (player->active && player != local_player)
4362       {
4363         int jx = player->jx, jy = player->jy;
4364
4365         player->active = FALSE;
4366         player->present = FALSE;
4367
4368         StorePlayer[jx][jy] = 0;
4369         Tile[jx][jy] = EL_EMPTY;
4370       }
4371     }
4372   }
4373
4374   for (i = 0; i < MAX_PLAYERS; i++)
4375     if (stored_player[i].active)
4376       game.players_still_needed++;
4377
4378   if (level.solved_by_one_player)
4379     game.players_still_needed = 1;
4380
4381   // when recording the game, store which players take part in the game
4382   if (tape.recording)
4383   {
4384 #if USE_NEW_PLAYER_ASSIGNMENTS
4385     for (i = 0; i < MAX_PLAYERS; i++)
4386       if (stored_player[i].connected)
4387         tape.player_participates[i] = TRUE;
4388 #else
4389     for (i = 0; i < MAX_PLAYERS; i++)
4390       if (stored_player[i].active)
4391         tape.player_participates[i] = TRUE;
4392 #endif
4393   }
4394
4395 #if DEBUG_INIT_PLAYER
4396   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4397 #endif
4398
4399   if (BorderElement == EL_EMPTY)
4400   {
4401     SBX_Left = 0;
4402     SBX_Right = lev_fieldx - SCR_FIELDX;
4403     SBY_Upper = 0;
4404     SBY_Lower = lev_fieldy - SCR_FIELDY;
4405   }
4406   else
4407   {
4408     SBX_Left = -1;
4409     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4410     SBY_Upper = -1;
4411     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4412   }
4413
4414   if (full_lev_fieldx <= SCR_FIELDX)
4415     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4416   if (full_lev_fieldy <= SCR_FIELDY)
4417     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4418
4419   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4420     SBX_Left--;
4421   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4422     SBY_Upper--;
4423
4424   // if local player not found, look for custom element that might create
4425   // the player (make some assumptions about the right custom element)
4426   if (!local_player->present)
4427   {
4428     int start_x = 0, start_y = 0;
4429     int found_rating = 0;
4430     int found_element = EL_UNDEFINED;
4431     int player_nr = local_player->index_nr;
4432
4433     SCAN_PLAYFIELD(x, y)
4434     {
4435       int element = Tile[x][y];
4436       int content;
4437       int xx, yy;
4438       boolean is_player;
4439
4440       if (level.use_start_element[player_nr] &&
4441           level.start_element[player_nr] == element &&
4442           found_rating < 4)
4443       {
4444         start_x = x;
4445         start_y = y;
4446
4447         found_rating = 4;
4448         found_element = element;
4449       }
4450
4451       if (!IS_CUSTOM_ELEMENT(element))
4452         continue;
4453
4454       if (CAN_CHANGE(element))
4455       {
4456         for (i = 0; i < element_info[element].num_change_pages; i++)
4457         {
4458           // check for player created from custom element as single target
4459           content = element_info[element].change_page[i].target_element;
4460           is_player = IS_PLAYER_ELEMENT(content);
4461
4462           if (is_player && (found_rating < 3 ||
4463                             (found_rating == 3 && element < found_element)))
4464           {
4465             start_x = x;
4466             start_y = y;
4467
4468             found_rating = 3;
4469             found_element = element;
4470           }
4471         }
4472       }
4473
4474       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4475       {
4476         // check for player created from custom element as explosion content
4477         content = element_info[element].content.e[xx][yy];
4478         is_player = IS_PLAYER_ELEMENT(content);
4479
4480         if (is_player && (found_rating < 2 ||
4481                           (found_rating == 2 && element < found_element)))
4482         {
4483           start_x = x + xx - 1;
4484           start_y = y + yy - 1;
4485
4486           found_rating = 2;
4487           found_element = element;
4488         }
4489
4490         if (!CAN_CHANGE(element))
4491           continue;
4492
4493         for (i = 0; i < element_info[element].num_change_pages; i++)
4494         {
4495           // check for player created from custom element as extended target
4496           content =
4497             element_info[element].change_page[i].target_content.e[xx][yy];
4498
4499           is_player = IS_PLAYER_ELEMENT(content);
4500
4501           if (is_player && (found_rating < 1 ||
4502                             (found_rating == 1 && element < found_element)))
4503           {
4504             start_x = x + xx - 1;
4505             start_y = y + yy - 1;
4506
4507             found_rating = 1;
4508             found_element = element;
4509           }
4510         }
4511       }
4512     }
4513
4514     scroll_x = SCROLL_POSITION_X(start_x);
4515     scroll_y = SCROLL_POSITION_Y(start_y);
4516   }
4517   else
4518   {
4519     scroll_x = SCROLL_POSITION_X(local_player->jx);
4520     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4521   }
4522
4523   if (game.forced_scroll_x != ARG_UNDEFINED_VALUE)
4524     scroll_x = game.forced_scroll_x;
4525   if (game.forced_scroll_y != ARG_UNDEFINED_VALUE)
4526     scroll_y = game.forced_scroll_y;
4527
4528   // !!! FIX THIS (START) !!!
4529   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
4530   {
4531     InitGameEngine_BD();
4532   }
4533   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4534   {
4535     InitGameEngine_EM();
4536   }
4537   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4538   {
4539     InitGameEngine_SP();
4540   }
4541   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4542   {
4543     InitGameEngine_MM();
4544   }
4545   else
4546   {
4547     DrawLevel(REDRAW_FIELD);
4548     DrawAllPlayers();
4549
4550     // after drawing the level, correct some elements
4551     if (game.timegate_time_left == 0)
4552       CloseAllOpenTimegates();
4553   }
4554
4555   // blit playfield from scroll buffer to normal back buffer for fading in
4556   BlitScreenToBitmap(backbuffer);
4557   // !!! FIX THIS (END) !!!
4558
4559   DrawMaskedBorder(fade_mask);
4560
4561   FadeIn(fade_mask);
4562
4563 #if 1
4564   // full screen redraw is required at this point in the following cases:
4565   // - special editor door undrawn when game was started from level editor
4566   // - drawing area (playfield) was changed and has to be removed completely
4567   redraw_mask = REDRAW_ALL;
4568   BackToFront();
4569 #endif
4570
4571   if (!game.restart_level)
4572   {
4573     // copy default game door content to main double buffer
4574
4575     // !!! CHECK AGAIN !!!
4576     SetPanelBackground();
4577     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4578     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4579   }
4580
4581   SetPanelBackground();
4582   SetDrawBackgroundMask(REDRAW_DOOR_1);
4583
4584   UpdateAndDisplayGameControlValues();
4585
4586   if (!game.restart_level)
4587   {
4588     UnmapGameButtons();
4589     UnmapTapeButtons();
4590
4591     FreeGameButtons();
4592     CreateGameButtons();
4593
4594     MapGameButtons();
4595     MapTapeButtons();
4596
4597     // copy actual game door content to door double buffer for OpenDoor()
4598     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4599
4600     OpenDoor(DOOR_OPEN_ALL);
4601
4602     KeyboardAutoRepeatOffUnlessAutoplay();
4603
4604 #if DEBUG_INIT_PLAYER
4605     DebugPrintPlayerStatus("Player status (final)");
4606 #endif
4607   }
4608
4609   UnmapAllGadgets();
4610
4611   MapGameButtons();
4612   MapTapeButtons();
4613
4614   if (!game.restart_level && !tape.playing)
4615   {
4616     LevelStats_incPlayed(level_nr);
4617
4618     SaveLevelSetup_SeriesInfo();
4619   }
4620
4621   game.restart_level = FALSE;
4622   game.request_active = FALSE;
4623   game.envelope_active = FALSE;
4624
4625   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4626     InitGameActions_MM();
4627
4628   SaveEngineSnapshotToListInitial();
4629
4630   if (!game.restart_level)
4631   {
4632     PlaySound(SND_GAME_STARTING);
4633
4634     if (setup.sound_music)
4635       PlayLevelMusic();
4636   }
4637
4638   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4639 }
4640
4641 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4642                         int actual_player_x, int actual_player_y)
4643 {
4644   // this is used for non-R'n'D game engines to update certain engine values
4645
4646   // needed to determine if sounds are played within the visible screen area
4647   scroll_x = actual_scroll_x;
4648   scroll_y = actual_scroll_y;
4649
4650   // needed to get player position for "follow finger" playing input method
4651   local_player->jx = actual_player_x;
4652   local_player->jy = actual_player_y;
4653 }
4654
4655 void InitMovDir(int x, int y)
4656 {
4657   int i, element = Tile[x][y];
4658   static int xy[4][2] =
4659   {
4660     {  0, +1 },
4661     { +1,  0 },
4662     {  0, -1 },
4663     { -1,  0 }
4664   };
4665   static int direction[3][4] =
4666   {
4667     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4668     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4669     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4670   };
4671
4672   switch (element)
4673   {
4674     case EL_BUG_RIGHT:
4675     case EL_BUG_UP:
4676     case EL_BUG_LEFT:
4677     case EL_BUG_DOWN:
4678       Tile[x][y] = EL_BUG;
4679       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4680       break;
4681
4682     case EL_SPACESHIP_RIGHT:
4683     case EL_SPACESHIP_UP:
4684     case EL_SPACESHIP_LEFT:
4685     case EL_SPACESHIP_DOWN:
4686       Tile[x][y] = EL_SPACESHIP;
4687       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4688       break;
4689
4690     case EL_BD_BUTTERFLY_RIGHT:
4691     case EL_BD_BUTTERFLY_UP:
4692     case EL_BD_BUTTERFLY_LEFT:
4693     case EL_BD_BUTTERFLY_DOWN:
4694       Tile[x][y] = EL_BD_BUTTERFLY;
4695       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4696       break;
4697
4698     case EL_BD_FIREFLY_RIGHT:
4699     case EL_BD_FIREFLY_UP:
4700     case EL_BD_FIREFLY_LEFT:
4701     case EL_BD_FIREFLY_DOWN:
4702       Tile[x][y] = EL_BD_FIREFLY;
4703       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4704       break;
4705
4706     case EL_PACMAN_RIGHT:
4707     case EL_PACMAN_UP:
4708     case EL_PACMAN_LEFT:
4709     case EL_PACMAN_DOWN:
4710       Tile[x][y] = EL_PACMAN;
4711       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4712       break;
4713
4714     case EL_YAMYAM_LEFT:
4715     case EL_YAMYAM_RIGHT:
4716     case EL_YAMYAM_UP:
4717     case EL_YAMYAM_DOWN:
4718       Tile[x][y] = EL_YAMYAM;
4719       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4720       break;
4721
4722     case EL_SP_SNIKSNAK:
4723       MovDir[x][y] = MV_UP;
4724       break;
4725
4726     case EL_SP_ELECTRON:
4727       MovDir[x][y] = MV_LEFT;
4728       break;
4729
4730     case EL_MOLE_LEFT:
4731     case EL_MOLE_RIGHT:
4732     case EL_MOLE_UP:
4733     case EL_MOLE_DOWN:
4734       Tile[x][y] = EL_MOLE;
4735       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4736       break;
4737
4738     case EL_SPRING_LEFT:
4739     case EL_SPRING_RIGHT:
4740       Tile[x][y] = EL_SPRING;
4741       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4742       break;
4743
4744     default:
4745       if (IS_CUSTOM_ELEMENT(element))
4746       {
4747         struct ElementInfo *ei = &element_info[element];
4748         int move_direction_initial = ei->move_direction_initial;
4749         int move_pattern = ei->move_pattern;
4750
4751         if (move_direction_initial == MV_START_PREVIOUS)
4752         {
4753           if (MovDir[x][y] != MV_NONE)
4754             return;
4755
4756           move_direction_initial = MV_START_AUTOMATIC;
4757         }
4758
4759         if (move_direction_initial == MV_START_RANDOM)
4760           MovDir[x][y] = 1 << RND(4);
4761         else if (move_direction_initial & MV_ANY_DIRECTION)
4762           MovDir[x][y] = move_direction_initial;
4763         else if (move_pattern == MV_ALL_DIRECTIONS ||
4764                  move_pattern == MV_TURNING_LEFT ||
4765                  move_pattern == MV_TURNING_RIGHT ||
4766                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4767                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4768                  move_pattern == MV_TURNING_RANDOM)
4769           MovDir[x][y] = 1 << RND(4);
4770         else if (move_pattern == MV_HORIZONTAL)
4771           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4772         else if (move_pattern == MV_VERTICAL)
4773           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4774         else if (move_pattern & MV_ANY_DIRECTION)
4775           MovDir[x][y] = element_info[element].move_pattern;
4776         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4777                  move_pattern == MV_ALONG_RIGHT_SIDE)
4778         {
4779           // use random direction as default start direction
4780           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4781             MovDir[x][y] = 1 << RND(4);
4782
4783           for (i = 0; i < NUM_DIRECTIONS; i++)
4784           {
4785             int x1 = x + xy[i][0];
4786             int y1 = y + xy[i][1];
4787
4788             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4789             {
4790               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4791                 MovDir[x][y] = direction[0][i];
4792               else
4793                 MovDir[x][y] = direction[1][i];
4794
4795               break;
4796             }
4797           }
4798         }                
4799       }
4800       else
4801       {
4802         MovDir[x][y] = 1 << RND(4);
4803
4804         if (element != EL_BUG &&
4805             element != EL_SPACESHIP &&
4806             element != EL_BD_BUTTERFLY &&
4807             element != EL_BD_FIREFLY)
4808           break;
4809
4810         for (i = 0; i < NUM_DIRECTIONS; i++)
4811         {
4812           int x1 = x + xy[i][0];
4813           int y1 = y + xy[i][1];
4814
4815           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4816           {
4817             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4818             {
4819               MovDir[x][y] = direction[0][i];
4820               break;
4821             }
4822             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4823                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4824             {
4825               MovDir[x][y] = direction[1][i];
4826               break;
4827             }
4828           }
4829         }
4830       }
4831       break;
4832   }
4833
4834   GfxDir[x][y] = MovDir[x][y];
4835 }
4836
4837 void InitAmoebaNr(int x, int y)
4838 {
4839   int i;
4840   int group_nr = AmoebaNeighbourNr(x, y);
4841
4842   if (group_nr == 0)
4843   {
4844     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4845     {
4846       if (AmoebaCnt[i] == 0)
4847       {
4848         group_nr = i;
4849         break;
4850       }
4851     }
4852   }
4853
4854   AmoebaNr[x][y] = group_nr;
4855   AmoebaCnt[group_nr]++;
4856   AmoebaCnt2[group_nr]++;
4857 }
4858
4859 static void LevelSolved_SetFinalGameValues(void)
4860 {
4861   game.time_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.time_played :
4862                      game.no_level_time_limit ? TimePlayed : TimeLeft);
4863   game.score_time_final = (level.use_step_counter ? TimePlayed :
4864                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4865
4866   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_BD ? game_bd.score :
4867                       level.game_engine_type == GAME_ENGINE_TYPE_EM ? game_em.lev->score :
4868                       level.game_engine_type == GAME_ENGINE_TYPE_MM ? game_mm.score :
4869                       game.score);
4870
4871   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4872                        MM_HEALTH(game_mm.laser_overload_value) :
4873                        game.health);
4874
4875   game.LevelSolved_CountingTime = game.time_final;
4876   game.LevelSolved_CountingScore = game.score_final;
4877   game.LevelSolved_CountingHealth = game.health_final;
4878 }
4879
4880 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4881 {
4882   game.LevelSolved_CountingTime = time;
4883   game.LevelSolved_CountingScore = score;
4884   game.LevelSolved_CountingHealth = health;
4885
4886   game_panel_controls[GAME_PANEL_TIME].value = time;
4887   game_panel_controls[GAME_PANEL_SCORE].value = score;
4888   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4889
4890   DisplayGameControlValues();
4891 }
4892
4893 static void LevelSolved(void)
4894 {
4895   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4896       game.players_still_needed > 0)
4897     return;
4898
4899   game.LevelSolved = TRUE;
4900   game.GameOver = TRUE;
4901
4902   tape.solved = TRUE;
4903
4904   // needed here to display correct panel values while player walks into exit
4905   LevelSolved_SetFinalGameValues();
4906 }
4907
4908 static boolean AdvanceToNextLevel(void)
4909 {
4910   if (setup.increment_levels &&
4911       level_nr < leveldir_current->last_level &&
4912       !network_playing)
4913   {
4914     level_nr++;         // advance to next level
4915     TapeErase();        // start with empty tape
4916
4917     if (setup.auto_play_next_level)
4918     {
4919       scores.continue_playing = TRUE;
4920       scores.next_level_nr = level_nr;
4921
4922       LoadLevel(level_nr);
4923
4924       SaveLevelSetup_SeriesInfo();
4925     }
4926
4927     return TRUE;
4928   }
4929
4930   return FALSE;
4931 }
4932
4933 void GameWon(void)
4934 {
4935   static int time_count_steps;
4936   static int time, time_final;
4937   static float score, score_final; // needed for time score < 10 for 10 seconds
4938   static int health, health_final;
4939   static int game_over_delay_1 = 0;
4940   static int game_over_delay_2 = 0;
4941   static int game_over_delay_3 = 0;
4942   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4943   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4944
4945   if (!game.LevelSolved_GameWon)
4946   {
4947     int i;
4948
4949     // do not start end game actions before the player stops moving (to exit)
4950     if (local_player->active && local_player->MovPos)
4951       return;
4952
4953     // calculate final game values after player finished walking into exit
4954     LevelSolved_SetFinalGameValues();
4955
4956     game.LevelSolved_GameWon = TRUE;
4957     game.LevelSolved_SaveTape = tape.recording;
4958     game.LevelSolved_SaveScore = !tape.playing;
4959
4960     if (!tape.playing)
4961     {
4962       LevelStats_incSolved(level_nr);
4963
4964       SaveLevelSetup_SeriesInfo();
4965     }
4966
4967     if (tape.auto_play)         // tape might already be stopped here
4968       tape.auto_play_level_solved = TRUE;
4969
4970     TapeStop();
4971
4972     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4973     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4974     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4975
4976     time = time_final = game.time_final;
4977     score = score_final = game.score_final;
4978     health = health_final = game.health_final;
4979
4980     // update game panel values before (delayed) counting of score (if any)
4981     LevelSolved_DisplayFinalGameValues(time, score, health);
4982
4983     // if level has time score defined, calculate new final game values
4984     if (time_score > 0)
4985     {
4986       int time_final_max = 999;
4987       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4988       int time_frames = 0;
4989       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4990       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4991
4992       if (TimeLeft > 0)
4993       {
4994         time_final = 0;
4995         time_frames = time_frames_left;
4996       }
4997       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4998       {
4999         time_final = time_final_max;
5000         time_frames = time_frames_final_max - time_frames_played;
5001       }
5002
5003       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
5004
5005       time_count_steps = MAX(1, ABS(time_final - time) / 100);
5006
5007       if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
5008       {
5009         // keep previous values (final values already processed here)
5010         time_final = time;
5011         score_final = score;
5012       }
5013       else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
5014       {
5015         health_final = 0;
5016         score_final += health * time_score;
5017       }
5018
5019       game.score_final = score_final;
5020       game.health_final = health_final;
5021     }
5022
5023     // if not counting score after game, immediately update game panel values
5024     if (level_editor_test_game || !setup.count_score_after_game ||
5025         level.game_engine_type == GAME_ENGINE_TYPE_BD)
5026     {
5027       time = time_final;
5028       score = score_final;
5029
5030       LevelSolved_DisplayFinalGameValues(time, score, health);
5031     }
5032
5033     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
5034     {
5035       // check if last player has left the level
5036       if (game.exit_x >= 0 &&
5037           game.exit_y >= 0)
5038       {
5039         int x = game.exit_x;
5040         int y = game.exit_y;
5041         int element = Tile[x][y];
5042
5043         // close exit door after last player
5044         if ((game.all_players_gone &&
5045              (element == EL_EXIT_OPEN ||
5046               element == EL_SP_EXIT_OPEN ||
5047               element == EL_STEEL_EXIT_OPEN)) ||
5048             element == EL_EM_EXIT_OPEN ||
5049             element == EL_EM_STEEL_EXIT_OPEN)
5050         {
5051
5052           Tile[x][y] =
5053             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
5054              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
5055              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
5056              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
5057              EL_EM_STEEL_EXIT_CLOSING);
5058
5059           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
5060         }
5061
5062         // player disappears
5063         DrawLevelField(x, y);
5064       }
5065
5066       for (i = 0; i < MAX_PLAYERS; i++)
5067       {
5068         struct PlayerInfo *player = &stored_player[i];
5069
5070         if (player->present)
5071         {
5072           RemovePlayer(player);
5073
5074           // player disappears
5075           DrawLevelField(player->jx, player->jy);
5076         }
5077       }
5078     }
5079
5080     PlaySound(SND_GAME_WINNING);
5081   }
5082
5083   if (setup.count_score_after_game)
5084   {
5085     if (time != time_final)
5086     {
5087       if (game_over_delay_1 > 0)
5088       {
5089         game_over_delay_1--;
5090
5091         return;
5092       }
5093
5094       int time_to_go = ABS(time_final - time);
5095       int time_count_dir = (time < time_final ? +1 : -1);
5096
5097       if (time_to_go < time_count_steps)
5098         time_count_steps = 1;
5099
5100       time  += time_count_steps * time_count_dir;
5101       score += time_count_steps * time_score;
5102
5103       // set final score to correct rounding differences after counting score
5104       if (time == time_final)
5105         score = score_final;
5106
5107       LevelSolved_DisplayFinalGameValues(time, score, health);
5108
5109       if (time == time_final)
5110         StopSound(SND_GAME_LEVELTIME_BONUS);
5111       else if (setup.sound_loops)
5112         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5113       else
5114         PlaySound(SND_GAME_LEVELTIME_BONUS);
5115
5116       return;
5117     }
5118
5119     if (health != health_final)
5120     {
5121       if (game_over_delay_2 > 0)
5122       {
5123         game_over_delay_2--;
5124
5125         return;
5126       }
5127
5128       int health_count_dir = (health < health_final ? +1 : -1);
5129
5130       health += health_count_dir;
5131       score  += time_score;
5132
5133       LevelSolved_DisplayFinalGameValues(time, score, health);
5134
5135       if (health == health_final)
5136         StopSound(SND_GAME_LEVELTIME_BONUS);
5137       else if (setup.sound_loops)
5138         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
5139       else
5140         PlaySound(SND_GAME_LEVELTIME_BONUS);
5141
5142       return;
5143     }
5144   }
5145
5146   game.panel.active = FALSE;
5147
5148   if (game_over_delay_3 > 0)
5149   {
5150     game_over_delay_3--;
5151
5152     return;
5153   }
5154
5155   GameEnd();
5156 }
5157
5158 void GameEnd(void)
5159 {
5160   // used instead of "level_nr" (needed for network games)
5161   int last_level_nr = levelset.level_nr;
5162   boolean tape_saved = FALSE;
5163
5164   // Important note: This function is not only called after "GameWon()", but also after
5165   // "game over" (if automatically asking for restarting the game is disabled in setup)
5166
5167   if (game.LevelSolved)
5168     game.LevelSolved_GameEnd = TRUE;
5169
5170   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5171   {
5172     // make sure that request dialog to save tape does not open door again
5173     if (!global.use_envelope_request)
5174       CloseDoor(DOOR_CLOSE_1);
5175
5176     // ask to save tape
5177     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5178
5179     // set unique basename for score tape (also saved in high score table)
5180     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5181   }
5182
5183   // if no tape is to be saved, close both doors simultaneously
5184   CloseDoor(DOOR_CLOSE_ALL);
5185
5186   if (level_editor_test_game || score_info_tape_play)
5187   {
5188     SetGameStatus(GAME_MODE_MAIN);
5189
5190     DrawMainMenu();
5191
5192     return;
5193   }
5194
5195   if (!game.GamePlayed || (!game.LevelSolved_SaveScore && !level.bd_intermission))
5196   {
5197     SetGameStatus(GAME_MODE_MAIN);
5198
5199     DrawMainMenu();
5200
5201     return;
5202   }
5203
5204   if (level_nr == leveldir_current->handicap_level)
5205   {
5206     leveldir_current->handicap_level++;
5207
5208     SaveLevelSetup_SeriesInfo();
5209   }
5210
5211   // save score and score tape before potentially erasing tape below
5212   if (game.LevelSolved_SaveScore)
5213     NewHighScore(last_level_nr, tape_saved);
5214
5215   // increment and load next level (if possible and not configured otherwise)
5216   AdvanceToNextLevel();
5217
5218   if (game.LevelSolved_SaveScore && scores.last_added >= 0 && setup.show_scores_after_game)
5219   {
5220     SetGameStatus(GAME_MODE_SCORES);
5221
5222     DrawHallOfFame(last_level_nr);
5223   }
5224   else if (scores.continue_playing)
5225   {
5226     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5227   }
5228   else
5229   {
5230     SetGameStatus(GAME_MODE_MAIN);
5231
5232     DrawMainMenu();
5233   }
5234 }
5235
5236 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5237                          boolean one_score_entry_per_name)
5238 {
5239   int i;
5240
5241   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5242     return -1;
5243
5244   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5245   {
5246     struct ScoreEntry *entry = &list->entry[i];
5247     boolean score_is_better = (new_entry->score >  entry->score);
5248     boolean score_is_equal  = (new_entry->score == entry->score);
5249     boolean time_is_better  = (new_entry->time  <  entry->time);
5250     boolean time_is_equal   = (new_entry->time  == entry->time);
5251     boolean better_by_score = (score_is_better ||
5252                                (score_is_equal && time_is_better));
5253     boolean better_by_time  = (time_is_better ||
5254                                (time_is_equal && score_is_better));
5255     boolean is_better = (level.rate_time_over_score ? better_by_time :
5256                          better_by_score);
5257     boolean entry_is_empty = (entry->score == 0 &&
5258                               entry->time == 0);
5259
5260     // prevent adding server score entries if also existing in local score file
5261     // (special case: historic score entries have an empty tape basename entry)
5262     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5263         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5264     {
5265       // add fields from server score entry not stored in local score entry
5266       // (currently, this means setting platform, version and country fields;
5267       // in rare cases, this may also correct an invalid score value, as
5268       // historic scores might have been truncated to 16-bit values locally)
5269       *entry = *new_entry;
5270
5271       return -1;
5272     }
5273
5274     if (is_better || entry_is_empty)
5275     {
5276       // player has made it to the hall of fame
5277
5278       if (i < MAX_SCORE_ENTRIES - 1)
5279       {
5280         int m = MAX_SCORE_ENTRIES - 1;
5281         int l;
5282
5283         if (one_score_entry_per_name)
5284         {
5285           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5286             if (strEqual(list->entry[l].name, new_entry->name))
5287               m = l;
5288
5289           if (m == i)   // player's new highscore overwrites his old one
5290             goto put_into_list;
5291         }
5292
5293         for (l = m; l > i; l--)
5294           list->entry[l] = list->entry[l - 1];
5295       }
5296
5297       put_into_list:
5298
5299       *entry = *new_entry;
5300
5301       return i;
5302     }
5303     else if (one_score_entry_per_name &&
5304              strEqual(entry->name, new_entry->name))
5305     {
5306       // player already in high score list with better score or time
5307
5308       return -1;
5309     }
5310   }
5311
5312   // special case: new score is beyond the last high score list position
5313   return MAX_SCORE_ENTRIES;
5314 }
5315
5316 void NewHighScore(int level_nr, boolean tape_saved)
5317 {
5318   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5319   boolean one_per_name = FALSE;
5320
5321   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5322   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5323
5324   new_entry.score = game.score_final;
5325   new_entry.time = game.score_time_final;
5326
5327   LoadScore(level_nr);
5328
5329   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5330
5331   if (scores.last_added >= MAX_SCORE_ENTRIES)
5332   {
5333     scores.last_added = MAX_SCORE_ENTRIES - 1;
5334     scores.force_last_added = TRUE;
5335
5336     scores.entry[scores.last_added] = new_entry;
5337
5338     // store last added local score entry (before merging server scores)
5339     scores.last_added_local = scores.last_added;
5340
5341     return;
5342   }
5343
5344   if (scores.last_added < 0)
5345     return;
5346
5347   SaveScore(level_nr);
5348
5349   // store last added local score entry (before merging server scores)
5350   scores.last_added_local = scores.last_added;
5351
5352   if (!game.LevelSolved_SaveTape)
5353     return;
5354
5355   SaveScoreTape(level_nr);
5356
5357   if (setup.ask_for_using_api_server)
5358   {
5359     setup.use_api_server =
5360       Request("Upload your score and tape to the high score server?", REQ_ASK);
5361
5362     if (!setup.use_api_server)
5363       Request("Not using high score server! Use setup menu to enable again!",
5364               REQ_CONFIRM);
5365
5366     runtime.use_api_server = setup.use_api_server;
5367
5368     // after asking for using API server once, do not ask again
5369     setup.ask_for_using_api_server = FALSE;
5370
5371     SaveSetup_ServerSetup();
5372   }
5373
5374   SaveServerScore(level_nr, tape_saved);
5375 }
5376
5377 void MergeServerScore(void)
5378 {
5379   struct ScoreEntry last_added_entry;
5380   boolean one_per_name = FALSE;
5381   int i;
5382
5383   if (scores.last_added >= 0)
5384     last_added_entry = scores.entry[scores.last_added];
5385
5386   for (i = 0; i < server_scores.num_entries; i++)
5387   {
5388     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5389
5390     if (pos >= 0 && pos <= scores.last_added)
5391       scores.last_added++;
5392   }
5393
5394   if (scores.last_added >= MAX_SCORE_ENTRIES)
5395   {
5396     scores.last_added = MAX_SCORE_ENTRIES - 1;
5397     scores.force_last_added = TRUE;
5398
5399     scores.entry[scores.last_added] = last_added_entry;
5400   }
5401 }
5402
5403 static int getElementMoveStepsizeExt(int x, int y, int direction)
5404 {
5405   int element = Tile[x][y];
5406   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5407   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5408   int horiz_move = (dx != 0);
5409   int sign = (horiz_move ? dx : dy);
5410   int step = sign * element_info[element].move_stepsize;
5411
5412   // special values for move stepsize for spring and things on conveyor belt
5413   if (horiz_move)
5414   {
5415     if (CAN_FALL(element) &&
5416         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5417       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5418     else if (element == EL_SPRING)
5419       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5420   }
5421
5422   return step;
5423 }
5424
5425 static int getElementMoveStepsize(int x, int y)
5426 {
5427   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5428 }
5429
5430 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5431 {
5432   if (player->GfxAction != action || player->GfxDir != dir)
5433   {
5434     player->GfxAction = action;
5435     player->GfxDir = dir;
5436     player->Frame = 0;
5437     player->StepFrame = 0;
5438   }
5439 }
5440
5441 static void ResetGfxFrame(int x, int y)
5442 {
5443   // profiling showed that "autotest" spends 10~20% of its time in this function
5444   if (DrawingDeactivatedField())
5445     return;
5446
5447   int element = Tile[x][y];
5448   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5449
5450   if (graphic_info[graphic].anim_global_sync)
5451     GfxFrame[x][y] = FrameCounter;
5452   else if (graphic_info[graphic].anim_global_anim_sync)
5453     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5454   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5455     GfxFrame[x][y] = CustomValue[x][y];
5456   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5457     GfxFrame[x][y] = element_info[element].collect_score;
5458   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5459     GfxFrame[x][y] = ChangeDelay[x][y];
5460 }
5461
5462 static void ResetGfxAnimation(int x, int y)
5463 {
5464   GfxAction[x][y] = ACTION_DEFAULT;
5465   GfxDir[x][y] = MovDir[x][y];
5466   GfxFrame[x][y] = 0;
5467
5468   ResetGfxFrame(x, y);
5469 }
5470
5471 static void ResetRandomAnimationValue(int x, int y)
5472 {
5473   GfxRandom[x][y] = INIT_GFX_RANDOM();
5474 }
5475
5476 static void InitMovingField(int x, int y, int direction)
5477 {
5478   int element = Tile[x][y];
5479   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5480   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5481   int newx = x + dx;
5482   int newy = y + dy;
5483   boolean is_moving_before, is_moving_after;
5484
5485   // check if element was/is moving or being moved before/after mode change
5486   is_moving_before = (WasJustMoving[x][y] != 0);
5487   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5488
5489   // reset animation only for moving elements which change direction of moving
5490   // or which just started or stopped moving
5491   // (else CEs with property "can move" / "not moving" are reset each frame)
5492   if (is_moving_before != is_moving_after ||
5493       direction != MovDir[x][y])
5494     ResetGfxAnimation(x, y);
5495
5496   MovDir[x][y] = direction;
5497   GfxDir[x][y] = direction;
5498
5499   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5500                      direction == MV_DOWN && CAN_FALL(element) ?
5501                      ACTION_FALLING : ACTION_MOVING);
5502
5503   // this is needed for CEs with property "can move" / "not moving"
5504
5505   if (is_moving_after)
5506   {
5507     if (Tile[newx][newy] == EL_EMPTY)
5508       Tile[newx][newy] = EL_BLOCKED;
5509
5510     MovDir[newx][newy] = MovDir[x][y];
5511
5512     CustomValue[newx][newy] = CustomValue[x][y];
5513
5514     GfxFrame[newx][newy] = GfxFrame[x][y];
5515     GfxRandom[newx][newy] = GfxRandom[x][y];
5516     GfxAction[newx][newy] = GfxAction[x][y];
5517     GfxDir[newx][newy] = GfxDir[x][y];
5518   }
5519 }
5520
5521 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5522 {
5523   int direction = MovDir[x][y];
5524   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5525   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5526
5527   *goes_to_x = newx;
5528   *goes_to_y = newy;
5529 }
5530
5531 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5532 {
5533   int direction = MovDir[x][y];
5534   int oldx = x + (direction & MV_LEFT ? +1 : direction & MV_RIGHT ? -1 : 0);
5535   int oldy = y + (direction & MV_UP   ? +1 : direction & MV_DOWN  ? -1 : 0);
5536
5537   *comes_from_x = oldx;
5538   *comes_from_y = oldy;
5539 }
5540
5541 static int MovingOrBlocked2Element(int x, int y)
5542 {
5543   int element = Tile[x][y];
5544
5545   if (element == EL_BLOCKED)
5546   {
5547     int oldx, oldy;
5548
5549     Blocked2Moving(x, y, &oldx, &oldy);
5550
5551     return Tile[oldx][oldy];
5552   }
5553
5554   return element;
5555 }
5556
5557 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5558 {
5559   // like MovingOrBlocked2Element(), but if element is moving
5560   // and (x, y) is the field the moving element is just leaving,
5561   // return EL_BLOCKED instead of the element value
5562   int element = Tile[x][y];
5563
5564   if (IS_MOVING(x, y))
5565   {
5566     if (element == EL_BLOCKED)
5567     {
5568       int oldx, oldy;
5569
5570       Blocked2Moving(x, y, &oldx, &oldy);
5571       return Tile[oldx][oldy];
5572     }
5573     else
5574       return EL_BLOCKED;
5575   }
5576   else
5577     return element;
5578 }
5579
5580 static void RemoveField(int x, int y)
5581 {
5582   Tile[x][y] = EL_EMPTY;
5583
5584   MovPos[x][y] = 0;
5585   MovDir[x][y] = 0;
5586   MovDelay[x][y] = 0;
5587
5588   CustomValue[x][y] = 0;
5589
5590   AmoebaNr[x][y] = 0;
5591   ChangeDelay[x][y] = 0;
5592   ChangePage[x][y] = -1;
5593   Pushed[x][y] = FALSE;
5594
5595   GfxElement[x][y] = EL_UNDEFINED;
5596   GfxAction[x][y] = ACTION_DEFAULT;
5597   GfxDir[x][y] = MV_NONE;
5598 }
5599
5600 static void RemoveMovingField(int x, int y)
5601 {
5602   int oldx = x, oldy = y, newx = x, newy = y;
5603   int element = Tile[x][y];
5604   int next_element = EL_UNDEFINED;
5605
5606   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5607     return;
5608
5609   if (IS_MOVING(x, y))
5610   {
5611     Moving2Blocked(x, y, &newx, &newy);
5612
5613     if (Tile[newx][newy] != EL_BLOCKED)
5614     {
5615       // element is moving, but target field is not free (blocked), but
5616       // already occupied by something different (example: acid pool);
5617       // in this case, only remove the moving field, but not the target
5618
5619       RemoveField(oldx, oldy);
5620
5621       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5622
5623       TEST_DrawLevelField(oldx, oldy);
5624
5625       return;
5626     }
5627   }
5628   else if (element == EL_BLOCKED)
5629   {
5630     Blocked2Moving(x, y, &oldx, &oldy);
5631     if (!IS_MOVING(oldx, oldy))
5632       return;
5633   }
5634
5635   if (element == EL_BLOCKED &&
5636       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5637        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5638        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5639        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5640        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5641        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5642     next_element = get_next_element(Tile[oldx][oldy]);
5643
5644   RemoveField(oldx, oldy);
5645   RemoveField(newx, newy);
5646
5647   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5648
5649   if (next_element != EL_UNDEFINED)
5650     Tile[oldx][oldy] = next_element;
5651
5652   TEST_DrawLevelField(oldx, oldy);
5653   TEST_DrawLevelField(newx, newy);
5654 }
5655
5656 void DrawDynamite(int x, int y)
5657 {
5658   int sx = SCREENX(x), sy = SCREENY(y);
5659   int graphic = el2img(Tile[x][y]);
5660   int frame;
5661
5662   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5663     return;
5664
5665   if (IS_WALKABLE_INSIDE(Back[x][y]))
5666     return;
5667
5668   if (Back[x][y])
5669     DrawLevelElement(x, y, Back[x][y]);
5670   else if (Store[x][y])
5671     DrawLevelElement(x, y, Store[x][y]);
5672   else if (game.use_masked_elements)
5673     DrawLevelElement(x, y, EL_EMPTY);
5674
5675   frame = getGraphicAnimationFrameXY(graphic, x, y);
5676
5677   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5678     DrawGraphicThruMask(sx, sy, graphic, frame);
5679   else
5680     DrawGraphic(sx, sy, graphic, frame);
5681 }
5682
5683 static void CheckDynamite(int x, int y)
5684 {
5685   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5686   {
5687     MovDelay[x][y]--;
5688
5689     if (MovDelay[x][y] != 0)
5690     {
5691       DrawDynamite(x, y);
5692       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5693
5694       return;
5695     }
5696   }
5697
5698   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5699
5700   Bang(x, y);
5701 }
5702
5703 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5704 {
5705   boolean num_checked_players = 0;
5706   int i;
5707
5708   for (i = 0; i < MAX_PLAYERS; i++)
5709   {
5710     if (stored_player[i].active)
5711     {
5712       int sx = stored_player[i].jx;
5713       int sy = stored_player[i].jy;
5714
5715       if (num_checked_players == 0)
5716       {
5717         *sx1 = *sx2 = sx;
5718         *sy1 = *sy2 = sy;
5719       }
5720       else
5721       {
5722         *sx1 = MIN(*sx1, sx);
5723         *sy1 = MIN(*sy1, sy);
5724         *sx2 = MAX(*sx2, sx);
5725         *sy2 = MAX(*sy2, sy);
5726       }
5727
5728       num_checked_players++;
5729     }
5730   }
5731 }
5732
5733 static boolean checkIfAllPlayersFitToScreen_RND(void)
5734 {
5735   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5736
5737   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5738
5739   return (sx2 - sx1 < SCR_FIELDX &&
5740           sy2 - sy1 < SCR_FIELDY);
5741 }
5742
5743 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5744 {
5745   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5746
5747   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5748
5749   *sx = (sx1 + sx2) / 2;
5750   *sy = (sy1 + sy2) / 2;
5751 }
5752
5753 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5754                                boolean center_screen, boolean quick_relocation)
5755 {
5756   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5757   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5758   boolean no_delay = (tape.warp_forward);
5759   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5760   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5761   int new_scroll_x, new_scroll_y;
5762
5763   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5764   {
5765     // case 1: quick relocation inside visible screen (without scrolling)
5766
5767     RedrawPlayfield();
5768
5769     return;
5770   }
5771
5772   if (!level.shifted_relocation || center_screen)
5773   {
5774     // relocation _with_ centering of screen
5775
5776     new_scroll_x = SCROLL_POSITION_X(x);
5777     new_scroll_y = SCROLL_POSITION_Y(y);
5778   }
5779   else
5780   {
5781     // relocation _without_ centering of screen
5782
5783     // apply distance between old and new player position to scroll position
5784     int shifted_scroll_x = scroll_x + (x - old_x);
5785     int shifted_scroll_y = scroll_y + (y - old_y);
5786
5787     // make sure that shifted scroll position does not scroll beyond screen
5788     new_scroll_x = SCROLL_POSITION_X(shifted_scroll_x + MIDPOSX);
5789     new_scroll_y = SCROLL_POSITION_Y(shifted_scroll_y + MIDPOSY);
5790
5791     // special case for teleporting from one end of the playfield to the other
5792     // (this kludge prevents the destination area to be shifted by half a tile
5793     // against the source destination for even screen width or screen height;
5794     // probably most useful when used with high "game.forced_scroll_delay_value"
5795     // in combination with "game.forced_scroll_x" and "game.forced_scroll_y")
5796     if (quick_relocation)
5797     {
5798       if (EVEN(SCR_FIELDX))
5799       {
5800         // relocate (teleport) between left and right border (half or full)
5801         if (scroll_x == SBX_Left && new_scroll_x == SBX_Right - 1)
5802           new_scroll_x = SBX_Right;
5803         else if (scroll_x == SBX_Left + 1 && new_scroll_x == SBX_Right)
5804           new_scroll_x = SBX_Right - 1;
5805         else if (scroll_x == SBX_Right && new_scroll_x == SBX_Left + 1)
5806           new_scroll_x = SBX_Left;
5807         else if (scroll_x == SBX_Right - 1 && new_scroll_x == SBX_Left)
5808           new_scroll_x = SBX_Left + 1;
5809       }
5810
5811       if (EVEN(SCR_FIELDY))
5812       {
5813         // relocate (teleport) between top and bottom border (half or full)
5814         if (scroll_y == SBY_Upper && new_scroll_y == SBY_Lower - 1)
5815           new_scroll_y = SBY_Lower;
5816         else if (scroll_y == SBY_Upper + 1 && new_scroll_y == SBY_Lower)
5817           new_scroll_y = SBY_Lower - 1;
5818         else if (scroll_y == SBY_Lower && new_scroll_y == SBY_Upper + 1)
5819           new_scroll_y = SBY_Upper;
5820         else if (scroll_y == SBY_Lower - 1 && new_scroll_y == SBY_Upper)
5821           new_scroll_y = SBY_Upper + 1;
5822       }
5823     }
5824   }
5825
5826   if (quick_relocation)
5827   {
5828     // case 2: quick relocation (redraw without visible scrolling)
5829
5830     scroll_x = new_scroll_x;
5831     scroll_y = new_scroll_y;
5832
5833     RedrawPlayfield();
5834
5835     return;
5836   }
5837
5838   // case 3: visible relocation (with scrolling to new position)
5839
5840   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5841
5842   SetVideoFrameDelay(wait_delay_value);
5843
5844   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5845   {
5846     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5847     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5848
5849     if (dx == 0 && dy == 0)             // no scrolling needed at all
5850       break;
5851
5852     scroll_x -= dx;
5853     scroll_y -= dy;
5854
5855     // set values for horizontal/vertical screen scrolling (half tile size)
5856     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5857     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5858     int pos_x = dx * TILEX / 2;
5859     int pos_y = dy * TILEY / 2;
5860     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5861     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5862
5863     ScrollLevel(dx, dy);
5864     DrawAllPlayers();
5865
5866     // scroll in two steps of half tile size to make things smoother
5867     BlitScreenToBitmapExt_RND(window, fx, fy);
5868
5869     // scroll second step to align at full tile size
5870     BlitScreenToBitmap(window);
5871   }
5872
5873   DrawAllPlayers();
5874   BackToFront();
5875
5876   SetVideoFrameDelay(frame_delay_value_old);
5877 }
5878
5879 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5880 {
5881   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5882   int player_nr = GET_PLAYER_NR(el_player);
5883   struct PlayerInfo *player = &stored_player[player_nr];
5884   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5885   boolean no_delay = (tape.warp_forward);
5886   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5887   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5888   int old_jx = player->jx;
5889   int old_jy = player->jy;
5890   int old_element = Tile[old_jx][old_jy];
5891   int element = Tile[jx][jy];
5892   boolean player_relocated = (old_jx != jx || old_jy != jy);
5893
5894   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5895   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5896   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5897   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5898   int leave_side_horiz = move_dir_horiz;
5899   int leave_side_vert  = move_dir_vert;
5900   int enter_side = enter_side_horiz | enter_side_vert;
5901   int leave_side = leave_side_horiz | leave_side_vert;
5902
5903   if (player->buried)           // do not reanimate dead player
5904     return;
5905
5906   if (!player_relocated)        // no need to relocate the player
5907     return;
5908
5909   if (IS_PLAYER(jx, jy))        // player already placed at new position
5910   {
5911     RemoveField(jx, jy);        // temporarily remove newly placed player
5912     DrawLevelField(jx, jy);
5913   }
5914
5915   if (player->present)
5916   {
5917     while (player->MovPos)
5918     {
5919       ScrollPlayer(player, SCROLL_GO_ON);
5920       ScrollScreen(NULL, SCROLL_GO_ON);
5921
5922       AdvanceFrameAndPlayerCounters(player->index_nr);
5923
5924       DrawPlayer(player);
5925
5926       BackToFront_WithFrameDelay(wait_delay_value);
5927     }
5928
5929     DrawPlayer(player);         // needed here only to cleanup last field
5930     DrawLevelField(player->jx, player->jy);     // remove player graphic
5931
5932     player->is_moving = FALSE;
5933   }
5934
5935   if (IS_CUSTOM_ELEMENT(old_element))
5936     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5937                                CE_LEFT_BY_PLAYER,
5938                                player->index_bit, leave_side);
5939
5940   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5941                                       CE_PLAYER_LEAVES_X,
5942                                       player->index_bit, leave_side);
5943
5944   Tile[jx][jy] = el_player;
5945   InitPlayerField(jx, jy, el_player, TRUE);
5946
5947   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5948      possible that the relocation target field did not contain a player element,
5949      but a walkable element, to which the new player was relocated -- in this
5950      case, restore that (already initialized!) element on the player field */
5951   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5952   {
5953     Tile[jx][jy] = element;     // restore previously existing element
5954   }
5955
5956   // only visually relocate centered player
5957   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5958                      FALSE, level.instant_relocation);
5959
5960   TestIfPlayerTouchesBadThing(jx, jy);
5961   TestIfPlayerTouchesCustomElement(jx, jy);
5962
5963   if (IS_CUSTOM_ELEMENT(element))
5964     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5965                                player->index_bit, enter_side);
5966
5967   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5968                                       player->index_bit, enter_side);
5969
5970   if (player->is_switching)
5971   {
5972     /* ensure that relocation while still switching an element does not cause
5973        a new element to be treated as also switched directly after relocation
5974        (this is important for teleporter switches that teleport the player to
5975        a place where another teleporter switch is in the same direction, which
5976        would then incorrectly be treated as immediately switched before the
5977        direction key that caused the switch was released) */
5978
5979     player->switch_x += jx - old_jx;
5980     player->switch_y += jy - old_jy;
5981   }
5982 }
5983
5984 static void Explode(int ex, int ey, int phase, int mode)
5985 {
5986   int x, y;
5987   int last_phase;
5988   int border_element;
5989
5990   if (game.explosions_delayed)
5991   {
5992     ExplodeField[ex][ey] = mode;
5993     return;
5994   }
5995
5996   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5997   {
5998     int center_element = Tile[ex][ey];
5999     int ce_value = CustomValue[ex][ey];
6000     int ce_score = element_info[center_element].collect_score;
6001     int artwork_element, explosion_element;     // set these values later
6002
6003     // remove things displayed in background while burning dynamite
6004     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
6005       Back[ex][ey] = 0;
6006
6007     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
6008     {
6009       // put moving element to center field (and let it explode there)
6010       center_element = MovingOrBlocked2Element(ex, ey);
6011       RemoveMovingField(ex, ey);
6012       Tile[ex][ey] = center_element;
6013     }
6014
6015     // now "center_element" is finally determined -- set related values now
6016     artwork_element = center_element;           // for custom player artwork
6017     explosion_element = center_element;         // for custom player artwork
6018
6019     if (IS_PLAYER(ex, ey))
6020     {
6021       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
6022
6023       artwork_element = stored_player[player_nr].artwork_element;
6024
6025       if (level.use_explosion_element[player_nr])
6026       {
6027         explosion_element = level.explosion_element[player_nr];
6028         artwork_element = explosion_element;
6029       }
6030     }
6031
6032     if (mode == EX_TYPE_NORMAL ||
6033         mode == EX_TYPE_CENTER ||
6034         mode == EX_TYPE_CROSS)
6035       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
6036
6037     last_phase = element_info[explosion_element].explosion_delay + 1;
6038
6039     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
6040     {
6041       int xx = x - ex + 1;
6042       int yy = y - ey + 1;
6043       int element;
6044
6045       if (!IN_LEV_FIELD(x, y) ||
6046           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
6047           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
6048         continue;
6049
6050       element = Tile[x][y];
6051
6052       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
6053       {
6054         element = MovingOrBlocked2Element(x, y);
6055
6056         if (!IS_EXPLOSION_PROOF(element))
6057           RemoveMovingField(x, y);
6058       }
6059
6060       // indestructible elements can only explode in center (but not flames)
6061       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
6062                                            mode == EX_TYPE_BORDER)) ||
6063           element == EL_FLAMES)
6064         continue;
6065
6066       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
6067          behaviour, for example when touching a yamyam that explodes to rocks
6068          with active deadly shield, a rock is created under the player !!! */
6069       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
6070 #if 0
6071       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
6072           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
6073            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
6074 #else
6075       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
6076 #endif
6077       {
6078         if (IS_ACTIVE_BOMB(element))
6079         {
6080           // re-activate things under the bomb like gate or penguin
6081           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
6082           Back[x][y] = 0;
6083         }
6084
6085         continue;
6086       }
6087
6088       // save walkable background elements while explosion on same tile
6089       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
6090           (x != ex || y != ey || mode == EX_TYPE_BORDER))
6091         Back[x][y] = element;
6092
6093       // ignite explodable elements reached by other explosion
6094       if (element == EL_EXPLOSION)
6095         element = Store2[x][y];
6096
6097       if (AmoebaNr[x][y] &&
6098           (element == EL_AMOEBA_FULL ||
6099            element == EL_BD_AMOEBA ||
6100            element == EL_AMOEBA_GROWING))
6101       {
6102         AmoebaCnt[AmoebaNr[x][y]]--;
6103         AmoebaCnt2[AmoebaNr[x][y]]--;
6104       }
6105
6106       RemoveField(x, y);
6107
6108       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
6109       {
6110         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
6111
6112         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
6113
6114         if (PLAYERINFO(ex, ey)->use_murphy)
6115           Store[x][y] = EL_EMPTY;
6116       }
6117
6118       // !!! check this case -- currently needed for rnd_rado_negundo_v,
6119       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
6120       else if (IS_PLAYER_ELEMENT(center_element))
6121         Store[x][y] = EL_EMPTY;
6122       else if (center_element == EL_YAMYAM)
6123         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
6124       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
6125         Store[x][y] = element_info[center_element].content.e[xx][yy];
6126 #if 1
6127       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
6128       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
6129       // otherwise) -- FIX THIS !!!
6130       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
6131         Store[x][y] = element_info[element].content.e[1][1];
6132 #else
6133       else if (!CAN_EXPLODE(element))
6134         Store[x][y] = element_info[element].content.e[1][1];
6135 #endif
6136       else
6137         Store[x][y] = EL_EMPTY;
6138
6139       if (IS_CUSTOM_ELEMENT(center_element))
6140         Store[x][y] = (Store[x][y] == EL_CURRENT_CE_VALUE ? ce_value :
6141                        Store[x][y] == EL_CURRENT_CE_SCORE ? ce_score :
6142                        Store[x][y] >= EL_PREV_CE_8 &&
6143                        Store[x][y] <= EL_NEXT_CE_8 ?
6144                        RESOLVED_REFERENCE_ELEMENT(center_element, Store[x][y]) :
6145                        Store[x][y]);
6146
6147       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
6148           center_element == EL_AMOEBA_TO_DIAMOND)
6149         Store2[x][y] = element;
6150
6151       Tile[x][y] = EL_EXPLOSION;
6152       GfxElement[x][y] = artwork_element;
6153
6154       ExplodePhase[x][y] = 1;
6155       ExplodeDelay[x][y] = last_phase;
6156
6157       Stop[x][y] = TRUE;
6158     }
6159
6160     if (center_element == EL_YAMYAM)
6161       game.yamyam_content_nr =
6162         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
6163
6164     return;
6165   }
6166
6167   if (Stop[ex][ey])
6168     return;
6169
6170   x = ex;
6171   y = ey;
6172
6173   if (phase == 1)
6174     GfxFrame[x][y] = 0;         // restart explosion animation
6175
6176   last_phase = ExplodeDelay[x][y];
6177
6178   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6179
6180   // this can happen if the player leaves an explosion just in time
6181   if (GfxElement[x][y] == EL_UNDEFINED)
6182     GfxElement[x][y] = EL_EMPTY;
6183
6184   border_element = Store2[x][y];
6185   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6186     border_element = StorePlayer[x][y];
6187
6188   if (phase == element_info[border_element].ignition_delay ||
6189       phase == last_phase)
6190   {
6191     boolean border_explosion = FALSE;
6192
6193     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6194         !PLAYER_EXPLOSION_PROTECTED(x, y))
6195     {
6196       KillPlayerUnlessExplosionProtected(x, y);
6197       border_explosion = TRUE;
6198     }
6199     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6200     {
6201       Tile[x][y] = Store2[x][y];
6202       Store2[x][y] = 0;
6203       Bang(x, y);
6204       border_explosion = TRUE;
6205     }
6206     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6207     {
6208       AmoebaToDiamond(x, y);
6209       Store2[x][y] = 0;
6210       border_explosion = TRUE;
6211     }
6212
6213     // if an element just explodes due to another explosion (chain-reaction),
6214     // do not immediately end the new explosion when it was the last frame of
6215     // the explosion (as it would be done in the following "if"-statement!)
6216     if (border_explosion && phase == last_phase)
6217       return;
6218   }
6219
6220   // this can happen if the player was just killed by an explosion
6221   if (GfxElement[x][y] == EL_UNDEFINED)
6222     GfxElement[x][y] = EL_EMPTY;
6223
6224   if (phase == last_phase)
6225   {
6226     int element;
6227
6228     element = Tile[x][y] = Store[x][y];
6229     Store[x][y] = Store2[x][y] = 0;
6230     GfxElement[x][y] = EL_UNDEFINED;
6231
6232     // player can escape from explosions and might therefore be still alive
6233     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6234         element <= EL_PLAYER_IS_EXPLODING_4)
6235     {
6236       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6237       int explosion_element = EL_PLAYER_1 + player_nr;
6238       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6239       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6240
6241       if (level.use_explosion_element[player_nr])
6242         explosion_element = level.explosion_element[player_nr];
6243
6244       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6245                     element_info[explosion_element].content.e[xx][yy]);
6246     }
6247
6248     // restore probably existing indestructible background element
6249     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6250       element = Tile[x][y] = Back[x][y];
6251     Back[x][y] = 0;
6252
6253     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6254     GfxDir[x][y] = MV_NONE;
6255     ChangeDelay[x][y] = 0;
6256     ChangePage[x][y] = -1;
6257
6258     CustomValue[x][y] = 0;
6259
6260     InitField_WithBug2(x, y, FALSE);
6261
6262     TEST_DrawLevelField(x, y);
6263
6264     TestIfElementTouchesCustomElement(x, y);
6265
6266     if (GFX_CRUMBLED(element))
6267       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6268
6269     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6270       StorePlayer[x][y] = 0;
6271
6272     if (IS_PLAYER_ELEMENT(element))
6273       RelocatePlayer(x, y, element);
6274   }
6275   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6276   {
6277     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6278     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6279
6280     if (phase == 1)
6281       TEST_DrawLevelFieldCrumbled(x, y);
6282
6283     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6284     {
6285       DrawLevelElement(x, y, Back[x][y]);
6286       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6287     }
6288     else if (IS_WALKABLE_UNDER(Back[x][y]))
6289     {
6290       DrawLevelGraphic(x, y, graphic, frame);
6291       DrawLevelElementThruMask(x, y, Back[x][y]);
6292     }
6293     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6294       DrawLevelGraphic(x, y, graphic, frame);
6295   }
6296 }
6297
6298 static void DynaExplode(int ex, int ey)
6299 {
6300   int i, j;
6301   int dynabomb_element = Tile[ex][ey];
6302   int dynabomb_size = 1;
6303   boolean dynabomb_xl = FALSE;
6304   struct PlayerInfo *player;
6305   struct XY *xy = xy_topdown;
6306
6307   if (IS_ACTIVE_BOMB(dynabomb_element))
6308   {
6309     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6310     dynabomb_size = player->dynabomb_size;
6311     dynabomb_xl = player->dynabomb_xl;
6312     player->dynabombs_left++;
6313   }
6314
6315   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6316
6317   for (i = 0; i < NUM_DIRECTIONS; i++)
6318   {
6319     for (j = 1; j <= dynabomb_size; j++)
6320     {
6321       int x = ex + j * xy[i].x;
6322       int y = ey + j * xy[i].y;
6323       int element;
6324
6325       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6326         break;
6327
6328       element = Tile[x][y];
6329
6330       // do not restart explosions of fields with active bombs
6331       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6332         continue;
6333
6334       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6335
6336       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6337           !IS_DIGGABLE(element) && !dynabomb_xl)
6338         break;
6339     }
6340   }
6341 }
6342
6343 void Bang(int x, int y)
6344 {
6345   int element = MovingOrBlocked2Element(x, y);
6346   int explosion_type = EX_TYPE_NORMAL;
6347
6348   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6349   {
6350     struct PlayerInfo *player = PLAYERINFO(x, y);
6351
6352     element = Tile[x][y] = player->initial_element;
6353
6354     if (level.use_explosion_element[player->index_nr])
6355     {
6356       int explosion_element = level.explosion_element[player->index_nr];
6357
6358       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6359         explosion_type = EX_TYPE_CROSS;
6360       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6361         explosion_type = EX_TYPE_CENTER;
6362     }
6363   }
6364
6365   switch (element)
6366   {
6367     case EL_BUG:
6368     case EL_SPACESHIP:
6369     case EL_BD_BUTTERFLY:
6370     case EL_BD_FIREFLY:
6371     case EL_YAMYAM:
6372     case EL_DARK_YAMYAM:
6373     case EL_ROBOT:
6374     case EL_PACMAN:
6375     case EL_MOLE:
6376       RaiseScoreElement(element);
6377       break;
6378
6379     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6380     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6381     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6382     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6383     case EL_DYNABOMB_INCREASE_NUMBER:
6384     case EL_DYNABOMB_INCREASE_SIZE:
6385     case EL_DYNABOMB_INCREASE_POWER:
6386       explosion_type = EX_TYPE_DYNA;
6387       break;
6388
6389     case EL_DC_LANDMINE:
6390       explosion_type = EX_TYPE_CENTER;
6391       break;
6392
6393     case EL_PENGUIN:
6394     case EL_LAMP:
6395     case EL_LAMP_ACTIVE:
6396     case EL_AMOEBA_TO_DIAMOND:
6397       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6398         explosion_type = EX_TYPE_CENTER;
6399       break;
6400
6401     default:
6402       if (element_info[element].explosion_type == EXPLODES_CROSS)
6403         explosion_type = EX_TYPE_CROSS;
6404       else if (element_info[element].explosion_type == EXPLODES_1X1)
6405         explosion_type = EX_TYPE_CENTER;
6406       break;
6407   }
6408
6409   if (explosion_type == EX_TYPE_DYNA)
6410     DynaExplode(x, y);
6411   else
6412     Explode(x, y, EX_PHASE_START, explosion_type);
6413
6414   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6415 }
6416
6417 static void SplashAcid(int x, int y)
6418 {
6419   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6420       (!IN_LEV_FIELD(x - 1, y - 2) ||
6421        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6422     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6423
6424   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6425       (!IN_LEV_FIELD(x + 1, y - 2) ||
6426        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6427     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6428
6429   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6430 }
6431
6432 static void InitBeltMovement(void)
6433 {
6434   static int belt_base_element[4] =
6435   {
6436     EL_CONVEYOR_BELT_1_LEFT,
6437     EL_CONVEYOR_BELT_2_LEFT,
6438     EL_CONVEYOR_BELT_3_LEFT,
6439     EL_CONVEYOR_BELT_4_LEFT
6440   };
6441   static int belt_base_active_element[4] =
6442   {
6443     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6444     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6445     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6446     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6447   };
6448
6449   int x, y, i, j;
6450
6451   // set frame order for belt animation graphic according to belt direction
6452   for (i = 0; i < NUM_BELTS; i++)
6453   {
6454     int belt_nr = i;
6455
6456     for (j = 0; j < NUM_BELT_PARTS; j++)
6457     {
6458       int element = belt_base_active_element[belt_nr] + j;
6459       int graphic_1 = el2img(element);
6460       int graphic_2 = el2panelimg(element);
6461
6462       if (game.belt_dir[i] == MV_LEFT)
6463       {
6464         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6465         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6466       }
6467       else
6468       {
6469         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6470         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6471       }
6472     }
6473   }
6474
6475   SCAN_PLAYFIELD(x, y)
6476   {
6477     int element = Tile[x][y];
6478
6479     for (i = 0; i < NUM_BELTS; i++)
6480     {
6481       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6482       {
6483         int e_belt_nr = getBeltNrFromBeltElement(element);
6484         int belt_nr = i;
6485
6486         if (e_belt_nr == belt_nr)
6487         {
6488           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6489
6490           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6491         }
6492       }
6493     }
6494   }
6495 }
6496
6497 static void ToggleBeltSwitch(int x, int y)
6498 {
6499   static int belt_base_element[4] =
6500   {
6501     EL_CONVEYOR_BELT_1_LEFT,
6502     EL_CONVEYOR_BELT_2_LEFT,
6503     EL_CONVEYOR_BELT_3_LEFT,
6504     EL_CONVEYOR_BELT_4_LEFT
6505   };
6506   static int belt_base_active_element[4] =
6507   {
6508     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6509     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6510     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6511     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6512   };
6513   static int belt_base_switch_element[4] =
6514   {
6515     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6516     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6517     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6518     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6519   };
6520   static int belt_move_dir[4] =
6521   {
6522     MV_LEFT,
6523     MV_NONE,
6524     MV_RIGHT,
6525     MV_NONE,
6526   };
6527
6528   int element = Tile[x][y];
6529   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6530   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6531   int belt_dir = belt_move_dir[belt_dir_nr];
6532   int xx, yy, i;
6533
6534   if (!IS_BELT_SWITCH(element))
6535     return;
6536
6537   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6538   game.belt_dir[belt_nr] = belt_dir;
6539
6540   if (belt_dir_nr == 3)
6541     belt_dir_nr = 1;
6542
6543   // set frame order for belt animation graphic according to belt direction
6544   for (i = 0; i < NUM_BELT_PARTS; i++)
6545   {
6546     int element = belt_base_active_element[belt_nr] + i;
6547     int graphic_1 = el2img(element);
6548     int graphic_2 = el2panelimg(element);
6549
6550     if (belt_dir == MV_LEFT)
6551     {
6552       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6553       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6554     }
6555     else
6556     {
6557       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6558       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6559     }
6560   }
6561
6562   SCAN_PLAYFIELD(xx, yy)
6563   {
6564     int element = Tile[xx][yy];
6565
6566     if (IS_BELT_SWITCH(element))
6567     {
6568       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6569
6570       if (e_belt_nr == belt_nr)
6571       {
6572         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6573         TEST_DrawLevelField(xx, yy);
6574       }
6575     }
6576     else if (IS_BELT(element) && belt_dir != MV_NONE)
6577     {
6578       int e_belt_nr = getBeltNrFromBeltElement(element);
6579
6580       if (e_belt_nr == belt_nr)
6581       {
6582         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6583
6584         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6585         TEST_DrawLevelField(xx, yy);
6586       }
6587     }
6588     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6589     {
6590       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6591
6592       if (e_belt_nr == belt_nr)
6593       {
6594         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6595
6596         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6597         TEST_DrawLevelField(xx, yy);
6598       }
6599     }
6600   }
6601 }
6602
6603 static void ToggleSwitchgateSwitch(void)
6604 {
6605   int xx, yy;
6606
6607   game.switchgate_pos = !game.switchgate_pos;
6608
6609   SCAN_PLAYFIELD(xx, yy)
6610   {
6611     int element = Tile[xx][yy];
6612
6613     if (element == EL_SWITCHGATE_SWITCH_UP)
6614     {
6615       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6616       TEST_DrawLevelField(xx, yy);
6617     }
6618     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6619     {
6620       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6621       TEST_DrawLevelField(xx, yy);
6622     }
6623     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6624     {
6625       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6626       TEST_DrawLevelField(xx, yy);
6627     }
6628     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6629     {
6630       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6631       TEST_DrawLevelField(xx, yy);
6632     }
6633     else if (element == EL_SWITCHGATE_OPEN ||
6634              element == EL_SWITCHGATE_OPENING)
6635     {
6636       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6637
6638       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6639     }
6640     else if (element == EL_SWITCHGATE_CLOSED ||
6641              element == EL_SWITCHGATE_CLOSING)
6642     {
6643       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6644
6645       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6646     }
6647   }
6648 }
6649
6650 static int getInvisibleActiveFromInvisibleElement(int element)
6651 {
6652   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6653           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6654           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6655           element);
6656 }
6657
6658 static int getInvisibleFromInvisibleActiveElement(int element)
6659 {
6660   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6661           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6662           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6663           element);
6664 }
6665
6666 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6667 {
6668   int x, y;
6669
6670   SCAN_PLAYFIELD(x, y)
6671   {
6672     int element = Tile[x][y];
6673
6674     if (element == EL_LIGHT_SWITCH &&
6675         game.light_time_left > 0)
6676     {
6677       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6678       TEST_DrawLevelField(x, y);
6679     }
6680     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6681              game.light_time_left == 0)
6682     {
6683       Tile[x][y] = EL_LIGHT_SWITCH;
6684       TEST_DrawLevelField(x, y);
6685     }
6686     else if (element == EL_EMC_DRIPPER &&
6687              game.light_time_left > 0)
6688     {
6689       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6690       TEST_DrawLevelField(x, y);
6691     }
6692     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6693              game.light_time_left == 0)
6694     {
6695       Tile[x][y] = EL_EMC_DRIPPER;
6696       TEST_DrawLevelField(x, y);
6697     }
6698     else if (element == EL_INVISIBLE_STEELWALL ||
6699              element == EL_INVISIBLE_WALL ||
6700              element == EL_INVISIBLE_SAND)
6701     {
6702       if (game.light_time_left > 0)
6703         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6704
6705       TEST_DrawLevelField(x, y);
6706
6707       // uncrumble neighbour fields, if needed
6708       if (element == EL_INVISIBLE_SAND)
6709         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6710     }
6711     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6712              element == EL_INVISIBLE_WALL_ACTIVE ||
6713              element == EL_INVISIBLE_SAND_ACTIVE)
6714     {
6715       if (game.light_time_left == 0)
6716         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6717
6718       TEST_DrawLevelField(x, y);
6719
6720       // re-crumble neighbour fields, if needed
6721       if (element == EL_INVISIBLE_SAND)
6722         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6723     }
6724   }
6725 }
6726
6727 static void RedrawAllInvisibleElementsForLenses(void)
6728 {
6729   int x, y;
6730
6731   SCAN_PLAYFIELD(x, y)
6732   {
6733     int element = Tile[x][y];
6734
6735     if (element == EL_EMC_DRIPPER &&
6736         game.lenses_time_left > 0)
6737     {
6738       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6739       TEST_DrawLevelField(x, y);
6740     }
6741     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6742              game.lenses_time_left == 0)
6743     {
6744       Tile[x][y] = EL_EMC_DRIPPER;
6745       TEST_DrawLevelField(x, y);
6746     }
6747     else if (element == EL_INVISIBLE_STEELWALL ||
6748              element == EL_INVISIBLE_WALL ||
6749              element == EL_INVISIBLE_SAND)
6750     {
6751       if (game.lenses_time_left > 0)
6752         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6753
6754       TEST_DrawLevelField(x, y);
6755
6756       // uncrumble neighbour fields, if needed
6757       if (element == EL_INVISIBLE_SAND)
6758         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6759     }
6760     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6761              element == EL_INVISIBLE_WALL_ACTIVE ||
6762              element == EL_INVISIBLE_SAND_ACTIVE)
6763     {
6764       if (game.lenses_time_left == 0)
6765         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6766
6767       TEST_DrawLevelField(x, y);
6768
6769       // re-crumble neighbour fields, if needed
6770       if (element == EL_INVISIBLE_SAND)
6771         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6772     }
6773   }
6774 }
6775
6776 static void RedrawAllInvisibleElementsForMagnifier(void)
6777 {
6778   int x, y;
6779
6780   SCAN_PLAYFIELD(x, y)
6781   {
6782     int element = Tile[x][y];
6783
6784     if (element == EL_EMC_FAKE_GRASS &&
6785         game.magnify_time_left > 0)
6786     {
6787       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6788       TEST_DrawLevelField(x, y);
6789     }
6790     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6791              game.magnify_time_left == 0)
6792     {
6793       Tile[x][y] = EL_EMC_FAKE_GRASS;
6794       TEST_DrawLevelField(x, y);
6795     }
6796     else if (IS_GATE_GRAY(element) &&
6797              game.magnify_time_left > 0)
6798     {
6799       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6800                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6801                     IS_EM_GATE_GRAY(element) ?
6802                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6803                     IS_EMC_GATE_GRAY(element) ?
6804                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6805                     IS_DC_GATE_GRAY(element) ?
6806                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6807                     element);
6808       TEST_DrawLevelField(x, y);
6809     }
6810     else if (IS_GATE_GRAY_ACTIVE(element) &&
6811              game.magnify_time_left == 0)
6812     {
6813       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6814                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6815                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6816                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6817                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6818                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6819                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6820                     EL_DC_GATE_WHITE_GRAY :
6821                     element);
6822       TEST_DrawLevelField(x, y);
6823     }
6824   }
6825 }
6826
6827 static void ToggleLightSwitch(int x, int y)
6828 {
6829   int element = Tile[x][y];
6830
6831   game.light_time_left =
6832     (element == EL_LIGHT_SWITCH ?
6833      level.time_light * FRAMES_PER_SECOND : 0);
6834
6835   RedrawAllLightSwitchesAndInvisibleElements();
6836 }
6837
6838 static void ActivateTimegateSwitch(int x, int y)
6839 {
6840   int xx, yy;
6841
6842   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6843
6844   SCAN_PLAYFIELD(xx, yy)
6845   {
6846     int element = Tile[xx][yy];
6847
6848     if (element == EL_TIMEGATE_CLOSED ||
6849         element == EL_TIMEGATE_CLOSING)
6850     {
6851       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6852       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6853     }
6854
6855     /*
6856     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6857     {
6858       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6859       TEST_DrawLevelField(xx, yy);
6860     }
6861     */
6862
6863   }
6864
6865   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6866                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6867 }
6868
6869 static void Impact(int x, int y)
6870 {
6871   boolean last_line = (y == lev_fieldy - 1);
6872   boolean object_hit = FALSE;
6873   boolean impact = (last_line || object_hit);
6874   int element = Tile[x][y];
6875   int smashed = EL_STEELWALL;
6876
6877   if (!last_line)       // check if element below was hit
6878   {
6879     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6880       return;
6881
6882     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6883                                          MovDir[x][y + 1] != MV_DOWN ||
6884                                          MovPos[x][y + 1] <= TILEY / 2));
6885
6886     // do not smash moving elements that left the smashed field in time
6887     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6888         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6889       object_hit = FALSE;
6890
6891 #if USE_QUICKSAND_IMPACT_BUGFIX
6892     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6893     {
6894       RemoveMovingField(x, y + 1);
6895       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6896       Tile[x][y + 2] = EL_ROCK;
6897       TEST_DrawLevelField(x, y + 2);
6898
6899       object_hit = TRUE;
6900     }
6901
6902     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6903     {
6904       RemoveMovingField(x, y + 1);
6905       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6906       Tile[x][y + 2] = EL_ROCK;
6907       TEST_DrawLevelField(x, y + 2);
6908
6909       object_hit = TRUE;
6910     }
6911 #endif
6912
6913     if (object_hit)
6914       smashed = MovingOrBlocked2Element(x, y + 1);
6915
6916     impact = (last_line || object_hit);
6917   }
6918
6919   if (!last_line && smashed == EL_ACID) // element falls into acid
6920   {
6921     SplashAcid(x, y + 1);
6922     return;
6923   }
6924
6925   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6926   // only reset graphic animation if graphic really changes after impact
6927   if (impact &&
6928       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6929   {
6930     ResetGfxAnimation(x, y);
6931     TEST_DrawLevelField(x, y);
6932   }
6933
6934   if (impact && CAN_EXPLODE_IMPACT(element))
6935   {
6936     Bang(x, y);
6937     return;
6938   }
6939   else if (impact && element == EL_PEARL &&
6940            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6941   {
6942     ResetGfxAnimation(x, y);
6943
6944     Tile[x][y] = EL_PEARL_BREAKING;
6945     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6946     return;
6947   }
6948   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6949   {
6950     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6951
6952     return;
6953   }
6954
6955   if (impact && element == EL_AMOEBA_DROP)
6956   {
6957     if (object_hit && IS_PLAYER(x, y + 1))
6958       KillPlayerUnlessEnemyProtected(x, y + 1);
6959     else if (object_hit && smashed == EL_PENGUIN)
6960       Bang(x, y + 1);
6961     else
6962     {
6963       Tile[x][y] = EL_AMOEBA_GROWING;
6964       Store[x][y] = EL_AMOEBA_WET;
6965
6966       ResetRandomAnimationValue(x, y);
6967     }
6968     return;
6969   }
6970
6971   if (object_hit)               // check which object was hit
6972   {
6973     if ((CAN_PASS_MAGIC_WALL(element) && 
6974          (smashed == EL_MAGIC_WALL ||
6975           smashed == EL_BD_MAGIC_WALL)) ||
6976         (CAN_PASS_DC_MAGIC_WALL(element) &&
6977          smashed == EL_DC_MAGIC_WALL))
6978     {
6979       int xx, yy;
6980       int activated_magic_wall =
6981         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6982          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6983          EL_DC_MAGIC_WALL_ACTIVE);
6984
6985       // activate magic wall / mill
6986       SCAN_PLAYFIELD(xx, yy)
6987       {
6988         if (Tile[xx][yy] == smashed)
6989           Tile[xx][yy] = activated_magic_wall;
6990       }
6991
6992       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6993       game.magic_wall_active = TRUE;
6994
6995       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6996                             SND_MAGIC_WALL_ACTIVATING :
6997                             smashed == EL_BD_MAGIC_WALL ?
6998                             SND_BD_MAGIC_WALL_ACTIVATING :
6999                             SND_DC_MAGIC_WALL_ACTIVATING));
7000     }
7001
7002     if (IS_PLAYER(x, y + 1))
7003     {
7004       if (CAN_SMASH_PLAYER(element))
7005       {
7006         KillPlayerUnlessEnemyProtected(x, y + 1);
7007         return;
7008       }
7009     }
7010     else if (smashed == EL_PENGUIN)
7011     {
7012       if (CAN_SMASH_PLAYER(element))
7013       {
7014         Bang(x, y + 1);
7015         return;
7016       }
7017     }
7018     else if (element == EL_BD_DIAMOND)
7019     {
7020       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
7021       {
7022         Bang(x, y + 1);
7023         return;
7024       }
7025     }
7026     else if (((element == EL_SP_INFOTRON ||
7027                element == EL_SP_ZONK) &&
7028               (smashed == EL_SP_SNIKSNAK ||
7029                smashed == EL_SP_ELECTRON ||
7030                smashed == EL_SP_DISK_ORANGE)) ||
7031              (element == EL_SP_INFOTRON &&
7032               smashed == EL_SP_DISK_YELLOW))
7033     {
7034       Bang(x, y + 1);
7035       return;
7036     }
7037     else if (CAN_SMASH_EVERYTHING(element))
7038     {
7039       if (IS_CLASSIC_ENEMY(smashed) ||
7040           CAN_EXPLODE_SMASHED(smashed))
7041       {
7042         Bang(x, y + 1);
7043         return;
7044       }
7045       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
7046       {
7047         if (smashed == EL_LAMP ||
7048             smashed == EL_LAMP_ACTIVE)
7049         {
7050           Bang(x, y + 1);
7051           return;
7052         }
7053         else if (smashed == EL_NUT)
7054         {
7055           Tile[x][y + 1] = EL_NUT_BREAKING;
7056           PlayLevelSound(x, y, SND_NUT_BREAKING);
7057           RaiseScoreElement(EL_NUT);
7058           return;
7059         }
7060         else if (smashed == EL_PEARL)
7061         {
7062           ResetGfxAnimation(x, y);
7063
7064           Tile[x][y + 1] = EL_PEARL_BREAKING;
7065           PlayLevelSound(x, y, SND_PEARL_BREAKING);
7066           return;
7067         }
7068         else if (smashed == EL_DIAMOND)
7069         {
7070           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
7071           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
7072           return;
7073         }
7074         else if (IS_BELT_SWITCH(smashed))
7075         {
7076           ToggleBeltSwitch(x, y + 1);
7077         }
7078         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
7079                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
7080                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
7081                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
7082         {
7083           ToggleSwitchgateSwitch();
7084         }
7085         else if (smashed == EL_LIGHT_SWITCH ||
7086                  smashed == EL_LIGHT_SWITCH_ACTIVE)
7087         {
7088           ToggleLightSwitch(x, y + 1);
7089         }
7090         else
7091         {
7092           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7093
7094           CheckElementChangeBySide(x, y + 1, smashed, element,
7095                                    CE_SWITCHED, CH_SIDE_TOP);
7096           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
7097                                             CH_SIDE_TOP);
7098         }
7099       }
7100       else
7101       {
7102         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
7103       }
7104     }
7105   }
7106
7107   // play sound of magic wall / mill
7108   if (!last_line &&
7109       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7110        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
7111        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
7112   {
7113     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7114       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
7115     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7116       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
7117     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7118       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
7119
7120     return;
7121   }
7122
7123   // play sound of object that hits the ground
7124   if (last_line || object_hit)
7125     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
7126 }
7127
7128 static void TurnRoundExt(int x, int y)
7129 {
7130   static struct
7131   {
7132     int dx, dy;
7133   } move_xy[] =
7134   {
7135     {  0,  0 },
7136     { -1,  0 },
7137     { +1,  0 },
7138     {  0,  0 },
7139     {  0, -1 },
7140     {  0,  0 }, { 0, 0 }, { 0, 0 },
7141     {  0, +1 }
7142   };
7143   static struct
7144   {
7145     int left, right, back;
7146   } turn[] =
7147   {
7148     { 0,        0,              0        },
7149     { MV_DOWN,  MV_UP,          MV_RIGHT },
7150     { MV_UP,    MV_DOWN,        MV_LEFT  },
7151     { 0,        0,              0        },
7152     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
7153     { 0,        0,              0        },
7154     { 0,        0,              0        },
7155     { 0,        0,              0        },
7156     { MV_RIGHT, MV_LEFT,        MV_UP    }
7157   };
7158
7159   int element = Tile[x][y];
7160   int move_pattern = element_info[element].move_pattern;
7161
7162   int old_move_dir = MovDir[x][y];
7163   int left_dir  = turn[old_move_dir].left;
7164   int right_dir = turn[old_move_dir].right;
7165   int back_dir  = turn[old_move_dir].back;
7166
7167   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
7168   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
7169   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
7170   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
7171
7172   int left_x  = x + left_dx,  left_y  = y + left_dy;
7173   int right_x = x + right_dx, right_y = y + right_dy;
7174   int move_x  = x + move_dx,  move_y  = y + move_dy;
7175
7176   int xx, yy;
7177
7178   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7179   {
7180     TestIfBadThingTouchesOtherBadThing(x, y);
7181
7182     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7183       MovDir[x][y] = right_dir;
7184     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7185       MovDir[x][y] = left_dir;
7186
7187     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7188       MovDelay[x][y] = 9;
7189     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7190       MovDelay[x][y] = 1;
7191   }
7192   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7193   {
7194     TestIfBadThingTouchesOtherBadThing(x, y);
7195
7196     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7197       MovDir[x][y] = left_dir;
7198     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7199       MovDir[x][y] = right_dir;
7200
7201     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7202       MovDelay[x][y] = 9;
7203     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7204       MovDelay[x][y] = 1;
7205   }
7206   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7207   {
7208     TestIfBadThingTouchesOtherBadThing(x, y);
7209
7210     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7211       MovDir[x][y] = left_dir;
7212     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7213       MovDir[x][y] = right_dir;
7214
7215     if (MovDir[x][y] != old_move_dir)
7216       MovDelay[x][y] = 9;
7217   }
7218   else if (element == EL_YAMYAM)
7219   {
7220     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7221     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7222
7223     if (can_turn_left && can_turn_right)
7224       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7225     else if (can_turn_left)
7226       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7227     else if (can_turn_right)
7228       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7229     else
7230       MovDir[x][y] = back_dir;
7231
7232     MovDelay[x][y] = 16 + 16 * RND(3);
7233   }
7234   else if (element == EL_DARK_YAMYAM)
7235   {
7236     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7237                                                          left_x, left_y);
7238     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7239                                                          right_x, right_y);
7240
7241     if (can_turn_left && can_turn_right)
7242       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7243     else if (can_turn_left)
7244       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7245     else if (can_turn_right)
7246       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7247     else
7248       MovDir[x][y] = back_dir;
7249
7250     MovDelay[x][y] = 16 + 16 * RND(3);
7251   }
7252   else if (element == EL_PACMAN)
7253   {
7254     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7255     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7256
7257     if (can_turn_left && can_turn_right)
7258       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7259     else if (can_turn_left)
7260       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7261     else if (can_turn_right)
7262       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7263     else
7264       MovDir[x][y] = back_dir;
7265
7266     MovDelay[x][y] = 6 + RND(40);
7267   }
7268   else if (element == EL_PIG)
7269   {
7270     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7271     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7272     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7273     boolean should_turn_left, should_turn_right, should_move_on;
7274     int rnd_value = 24;
7275     int rnd = RND(rnd_value);
7276
7277     should_turn_left = (can_turn_left &&
7278                         (!can_move_on ||
7279                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7280                                                    y + back_dy + left_dy)));
7281     should_turn_right = (can_turn_right &&
7282                          (!can_move_on ||
7283                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7284                                                     y + back_dy + right_dy)));
7285     should_move_on = (can_move_on &&
7286                       (!can_turn_left ||
7287                        !can_turn_right ||
7288                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7289                                                  y + move_dy + left_dy) ||
7290                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7291                                                  y + move_dy + right_dy)));
7292
7293     if (should_turn_left || should_turn_right || should_move_on)
7294     {
7295       if (should_turn_left && should_turn_right && should_move_on)
7296         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7297                         rnd < 2 * rnd_value / 3 ? right_dir :
7298                         old_move_dir);
7299       else if (should_turn_left && should_turn_right)
7300         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7301       else if (should_turn_left && should_move_on)
7302         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7303       else if (should_turn_right && should_move_on)
7304         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7305       else if (should_turn_left)
7306         MovDir[x][y] = left_dir;
7307       else if (should_turn_right)
7308         MovDir[x][y] = right_dir;
7309       else if (should_move_on)
7310         MovDir[x][y] = old_move_dir;
7311     }
7312     else if (can_move_on && rnd > rnd_value / 8)
7313       MovDir[x][y] = old_move_dir;
7314     else if (can_turn_left && can_turn_right)
7315       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7316     else if (can_turn_left && rnd > rnd_value / 8)
7317       MovDir[x][y] = left_dir;
7318     else if (can_turn_right && rnd > rnd_value/8)
7319       MovDir[x][y] = right_dir;
7320     else
7321       MovDir[x][y] = back_dir;
7322
7323     xx = x + move_xy[MovDir[x][y]].dx;
7324     yy = y + move_xy[MovDir[x][y]].dy;
7325
7326     if (!IN_LEV_FIELD(xx, yy) ||
7327         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7328       MovDir[x][y] = old_move_dir;
7329
7330     MovDelay[x][y] = 0;
7331   }
7332   else if (element == EL_DRAGON)
7333   {
7334     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7335     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7336     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7337     int rnd_value = 24;
7338     int rnd = RND(rnd_value);
7339
7340     if (can_move_on && rnd > rnd_value / 8)
7341       MovDir[x][y] = old_move_dir;
7342     else if (can_turn_left && can_turn_right)
7343       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7344     else if (can_turn_left && rnd > rnd_value / 8)
7345       MovDir[x][y] = left_dir;
7346     else if (can_turn_right && rnd > rnd_value / 8)
7347       MovDir[x][y] = right_dir;
7348     else
7349       MovDir[x][y] = back_dir;
7350
7351     xx = x + move_xy[MovDir[x][y]].dx;
7352     yy = y + move_xy[MovDir[x][y]].dy;
7353
7354     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7355       MovDir[x][y] = old_move_dir;
7356
7357     MovDelay[x][y] = 0;
7358   }
7359   else if (element == EL_MOLE)
7360   {
7361     boolean can_move_on =
7362       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7363                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7364                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7365     if (!can_move_on)
7366     {
7367       boolean can_turn_left =
7368         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7369                               IS_AMOEBOID(Tile[left_x][left_y])));
7370
7371       boolean can_turn_right =
7372         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7373                               IS_AMOEBOID(Tile[right_x][right_y])));
7374
7375       if (can_turn_left && can_turn_right)
7376         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7377       else if (can_turn_left)
7378         MovDir[x][y] = left_dir;
7379       else
7380         MovDir[x][y] = right_dir;
7381     }
7382
7383     if (MovDir[x][y] != old_move_dir)
7384       MovDelay[x][y] = 9;
7385   }
7386   else if (element == EL_BALLOON)
7387   {
7388     MovDir[x][y] = game.wind_direction;
7389     MovDelay[x][y] = 0;
7390   }
7391   else if (element == EL_SPRING)
7392   {
7393     if (MovDir[x][y] & MV_HORIZONTAL)
7394     {
7395       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7396           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7397       {
7398         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7399         ResetGfxAnimation(move_x, move_y);
7400         TEST_DrawLevelField(move_x, move_y);
7401
7402         MovDir[x][y] = back_dir;
7403       }
7404       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7405                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7406         MovDir[x][y] = MV_NONE;
7407     }
7408
7409     MovDelay[x][y] = 0;
7410   }
7411   else if (element == EL_ROBOT ||
7412            element == EL_SATELLITE ||
7413            element == EL_PENGUIN ||
7414            element == EL_EMC_ANDROID)
7415   {
7416     int attr_x = -1, attr_y = -1;
7417
7418     if (game.all_players_gone)
7419     {
7420       attr_x = game.exit_x;
7421       attr_y = game.exit_y;
7422     }
7423     else
7424     {
7425       int i;
7426
7427       for (i = 0; i < MAX_PLAYERS; i++)
7428       {
7429         struct PlayerInfo *player = &stored_player[i];
7430         int jx = player->jx, jy = player->jy;
7431
7432         if (!player->active)
7433           continue;
7434
7435         if (attr_x == -1 ||
7436             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7437         {
7438           attr_x = jx;
7439           attr_y = jy;
7440         }
7441       }
7442     }
7443
7444     if (element == EL_ROBOT &&
7445         game.robot_wheel_x >= 0 &&
7446         game.robot_wheel_y >= 0 &&
7447         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7448          game.engine_version < VERSION_IDENT(3,1,0,0)))
7449     {
7450       attr_x = game.robot_wheel_x;
7451       attr_y = game.robot_wheel_y;
7452     }
7453
7454     if (element == EL_PENGUIN)
7455     {
7456       int i;
7457       struct XY *xy = xy_topdown;
7458
7459       for (i = 0; i < NUM_DIRECTIONS; i++)
7460       {
7461         int ex = x + xy[i].x;
7462         int ey = y + xy[i].y;
7463
7464         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7465                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7466                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7467                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7468         {
7469           attr_x = ex;
7470           attr_y = ey;
7471           break;
7472         }
7473       }
7474     }
7475
7476     MovDir[x][y] = MV_NONE;
7477     if (attr_x < x)
7478       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7479     else if (attr_x > x)
7480       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7481     if (attr_y < y)
7482       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7483     else if (attr_y > y)
7484       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7485
7486     if (element == EL_ROBOT)
7487     {
7488       int newx, newy;
7489
7490       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7491         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7492       Moving2Blocked(x, y, &newx, &newy);
7493
7494       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7495         MovDelay[x][y] = 8 + 8 * !RND(3);
7496       else
7497         MovDelay[x][y] = 16;
7498     }
7499     else if (element == EL_PENGUIN)
7500     {
7501       int newx, newy;
7502
7503       MovDelay[x][y] = 1;
7504
7505       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7506       {
7507         boolean first_horiz = RND(2);
7508         int new_move_dir = MovDir[x][y];
7509
7510         MovDir[x][y] =
7511           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7512         Moving2Blocked(x, y, &newx, &newy);
7513
7514         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7515           return;
7516
7517         MovDir[x][y] =
7518           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7519         Moving2Blocked(x, y, &newx, &newy);
7520
7521         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7522           return;
7523
7524         MovDir[x][y] = old_move_dir;
7525         return;
7526       }
7527     }
7528     else if (element == EL_SATELLITE)
7529     {
7530       int newx, newy;
7531
7532       MovDelay[x][y] = 1;
7533
7534       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7535       {
7536         boolean first_horiz = RND(2);
7537         int new_move_dir = MovDir[x][y];
7538
7539         MovDir[x][y] =
7540           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7541         Moving2Blocked(x, y, &newx, &newy);
7542
7543         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7544           return;
7545
7546         MovDir[x][y] =
7547           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7548         Moving2Blocked(x, y, &newx, &newy);
7549
7550         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7551           return;
7552
7553         MovDir[x][y] = old_move_dir;
7554         return;
7555       }
7556     }
7557     else if (element == EL_EMC_ANDROID)
7558     {
7559       static int check_pos[16] =
7560       {
7561         -1,             //  0 => (invalid)
7562         7,              //  1 => MV_LEFT
7563         3,              //  2 => MV_RIGHT
7564         -1,             //  3 => (invalid)
7565         1,              //  4 =>            MV_UP
7566         0,              //  5 => MV_LEFT  | MV_UP
7567         2,              //  6 => MV_RIGHT | MV_UP
7568         -1,             //  7 => (invalid)
7569         5,              //  8 =>            MV_DOWN
7570         6,              //  9 => MV_LEFT  | MV_DOWN
7571         4,              // 10 => MV_RIGHT | MV_DOWN
7572         -1,             // 11 => (invalid)
7573         -1,             // 12 => (invalid)
7574         -1,             // 13 => (invalid)
7575         -1,             // 14 => (invalid)
7576         -1,             // 15 => (invalid)
7577       };
7578       static struct
7579       {
7580         int dx, dy;
7581         int dir;
7582       } check_xy[8] =
7583       {
7584         { -1, -1,       MV_LEFT  | MV_UP   },
7585         {  0, -1,                  MV_UP   },
7586         { +1, -1,       MV_RIGHT | MV_UP   },
7587         { +1,  0,       MV_RIGHT           },
7588         { +1, +1,       MV_RIGHT | MV_DOWN },
7589         {  0, +1,                  MV_DOWN },
7590         { -1, +1,       MV_LEFT  | MV_DOWN },
7591         { -1,  0,       MV_LEFT            },
7592       };
7593       int start_pos, check_order;
7594       boolean can_clone = FALSE;
7595       int i;
7596
7597       // check if there is any free field around current position
7598       for (i = 0; i < 8; i++)
7599       {
7600         int newx = x + check_xy[i].dx;
7601         int newy = y + check_xy[i].dy;
7602
7603         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7604         {
7605           can_clone = TRUE;
7606
7607           break;
7608         }
7609       }
7610
7611       if (can_clone)            // randomly find an element to clone
7612       {
7613         can_clone = FALSE;
7614
7615         start_pos = check_pos[RND(8)];
7616         check_order = (RND(2) ? -1 : +1);
7617
7618         for (i = 0; i < 8; i++)
7619         {
7620           int pos_raw = start_pos + i * check_order;
7621           int pos = (pos_raw + 8) % 8;
7622           int newx = x + check_xy[pos].dx;
7623           int newy = y + check_xy[pos].dy;
7624
7625           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7626           {
7627             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7628             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7629
7630             Store[x][y] = Tile[newx][newy];
7631
7632             can_clone = TRUE;
7633
7634             break;
7635           }
7636         }
7637       }
7638
7639       if (can_clone)            // randomly find a direction to move
7640       {
7641         can_clone = FALSE;
7642
7643         start_pos = check_pos[RND(8)];
7644         check_order = (RND(2) ? -1 : +1);
7645
7646         for (i = 0; i < 8; i++)
7647         {
7648           int pos_raw = start_pos + i * check_order;
7649           int pos = (pos_raw + 8) % 8;
7650           int newx = x + check_xy[pos].dx;
7651           int newy = y + check_xy[pos].dy;
7652           int new_move_dir = check_xy[pos].dir;
7653
7654           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7655           {
7656             MovDir[x][y] = new_move_dir;
7657             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7658
7659             can_clone = TRUE;
7660
7661             break;
7662           }
7663         }
7664       }
7665
7666       if (can_clone)            // cloning and moving successful
7667         return;
7668
7669       // cannot clone -- try to move towards player
7670
7671       start_pos = check_pos[MovDir[x][y] & 0x0f];
7672       check_order = (RND(2) ? -1 : +1);
7673
7674       for (i = 0; i < 3; i++)
7675       {
7676         // first check start_pos, then previous/next or (next/previous) pos
7677         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7678         int pos = (pos_raw + 8) % 8;
7679         int newx = x + check_xy[pos].dx;
7680         int newy = y + check_xy[pos].dy;
7681         int new_move_dir = check_xy[pos].dir;
7682
7683         if (IS_PLAYER(newx, newy))
7684           break;
7685
7686         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7687         {
7688           MovDir[x][y] = new_move_dir;
7689           MovDelay[x][y] = level.android_move_time * 8 + 1;
7690
7691           break;
7692         }
7693       }
7694     }
7695   }
7696   else if (move_pattern == MV_TURNING_LEFT ||
7697            move_pattern == MV_TURNING_RIGHT ||
7698            move_pattern == MV_TURNING_LEFT_RIGHT ||
7699            move_pattern == MV_TURNING_RIGHT_LEFT ||
7700            move_pattern == MV_TURNING_RANDOM ||
7701            move_pattern == MV_ALL_DIRECTIONS)
7702   {
7703     boolean can_turn_left =
7704       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7705     boolean can_turn_right =
7706       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y);
7707
7708     if (element_info[element].move_stepsize == 0)       // "not moving"
7709       return;
7710
7711     if (move_pattern == MV_TURNING_LEFT)
7712       MovDir[x][y] = left_dir;
7713     else if (move_pattern == MV_TURNING_RIGHT)
7714       MovDir[x][y] = right_dir;
7715     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7716       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7717     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7718       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7719     else if (move_pattern == MV_TURNING_RANDOM)
7720       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7721                       can_turn_right && !can_turn_left ? right_dir :
7722                       RND(2) ? left_dir : right_dir);
7723     else if (can_turn_left && can_turn_right)
7724       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7725     else if (can_turn_left)
7726       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7727     else if (can_turn_right)
7728       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7729     else
7730       MovDir[x][y] = back_dir;
7731
7732     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7733   }
7734   else if (move_pattern == MV_HORIZONTAL ||
7735            move_pattern == MV_VERTICAL)
7736   {
7737     if (move_pattern & old_move_dir)
7738       MovDir[x][y] = back_dir;
7739     else if (move_pattern == MV_HORIZONTAL)
7740       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7741     else if (move_pattern == MV_VERTICAL)
7742       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7743
7744     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7745   }
7746   else if (move_pattern & MV_ANY_DIRECTION)
7747   {
7748     MovDir[x][y] = move_pattern;
7749     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7750   }
7751   else if (move_pattern & MV_WIND_DIRECTION)
7752   {
7753     MovDir[x][y] = game.wind_direction;
7754     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7755   }
7756   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7757   {
7758     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7759       MovDir[x][y] = left_dir;
7760     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7761       MovDir[x][y] = right_dir;
7762
7763     if (MovDir[x][y] != old_move_dir)
7764       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7765   }
7766   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7767   {
7768     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7769       MovDir[x][y] = right_dir;
7770     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7771       MovDir[x][y] = left_dir;
7772
7773     if (MovDir[x][y] != old_move_dir)
7774       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7775   }
7776   else if (move_pattern == MV_TOWARDS_PLAYER ||
7777            move_pattern == MV_AWAY_FROM_PLAYER)
7778   {
7779     int attr_x = -1, attr_y = -1;
7780     int newx, newy;
7781     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7782
7783     if (game.all_players_gone)
7784     {
7785       attr_x = game.exit_x;
7786       attr_y = game.exit_y;
7787     }
7788     else
7789     {
7790       int i;
7791
7792       for (i = 0; i < MAX_PLAYERS; i++)
7793       {
7794         struct PlayerInfo *player = &stored_player[i];
7795         int jx = player->jx, jy = player->jy;
7796
7797         if (!player->active)
7798           continue;
7799
7800         if (attr_x == -1 ||
7801             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7802         {
7803           attr_x = jx;
7804           attr_y = jy;
7805         }
7806       }
7807     }
7808
7809     MovDir[x][y] = MV_NONE;
7810     if (attr_x < x)
7811       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7812     else if (attr_x > x)
7813       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7814     if (attr_y < y)
7815       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7816     else if (attr_y > y)
7817       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7818
7819     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7820
7821     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7822     {
7823       boolean first_horiz = RND(2);
7824       int new_move_dir = MovDir[x][y];
7825
7826       if (element_info[element].move_stepsize == 0)     // "not moving"
7827       {
7828         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7829         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7830
7831         return;
7832       }
7833
7834       MovDir[x][y] =
7835         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7836       Moving2Blocked(x, y, &newx, &newy);
7837
7838       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7839         return;
7840
7841       MovDir[x][y] =
7842         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7843       Moving2Blocked(x, y, &newx, &newy);
7844
7845       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7846         return;
7847
7848       MovDir[x][y] = old_move_dir;
7849     }
7850   }
7851   else if (move_pattern == MV_WHEN_PUSHED ||
7852            move_pattern == MV_WHEN_DROPPED)
7853   {
7854     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7855       MovDir[x][y] = MV_NONE;
7856
7857     MovDelay[x][y] = 0;
7858   }
7859   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7860   {
7861     struct XY *test_xy = xy_topdown;
7862     static int test_dir[4] =
7863     {
7864       MV_UP,
7865       MV_LEFT,
7866       MV_RIGHT,
7867       MV_DOWN
7868     };
7869     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7870     int move_preference = -1000000;     // start with very low preference
7871     int new_move_dir = MV_NONE;
7872     int start_test = RND(4);
7873     int i;
7874
7875     for (i = 0; i < NUM_DIRECTIONS; i++)
7876     {
7877       int j = (start_test + i) % 4;
7878       int move_dir = test_dir[j];
7879       int move_dir_preference;
7880
7881       xx = x + test_xy[j].x;
7882       yy = y + test_xy[j].y;
7883
7884       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7885           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7886       {
7887         new_move_dir = move_dir;
7888
7889         break;
7890       }
7891
7892       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7893         continue;
7894
7895       move_dir_preference = -1 * RunnerVisit[xx][yy];
7896       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7897         move_dir_preference = PlayerVisit[xx][yy];
7898
7899       if (move_dir_preference > move_preference)
7900       {
7901         // prefer field that has not been visited for the longest time
7902         move_preference = move_dir_preference;
7903         new_move_dir = move_dir;
7904       }
7905       else if (move_dir_preference == move_preference &&
7906                move_dir == old_move_dir)
7907       {
7908         // prefer last direction when all directions are preferred equally
7909         move_preference = move_dir_preference;
7910         new_move_dir = move_dir;
7911       }
7912     }
7913
7914     MovDir[x][y] = new_move_dir;
7915     if (old_move_dir != new_move_dir)
7916       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7917   }
7918 }
7919
7920 static void TurnRound(int x, int y)
7921 {
7922   int direction = MovDir[x][y];
7923
7924   TurnRoundExt(x, y);
7925
7926   GfxDir[x][y] = MovDir[x][y];
7927
7928   if (direction != MovDir[x][y])
7929     GfxFrame[x][y] = 0;
7930
7931   if (MovDelay[x][y])
7932     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7933
7934   ResetGfxFrame(x, y);
7935 }
7936
7937 static boolean JustBeingPushed(int x, int y)
7938 {
7939   int i;
7940
7941   for (i = 0; i < MAX_PLAYERS; i++)
7942   {
7943     struct PlayerInfo *player = &stored_player[i];
7944
7945     if (player->active && player->is_pushing && player->MovPos)
7946     {
7947       int next_jx = player->jx + (player->jx - player->last_jx);
7948       int next_jy = player->jy + (player->jy - player->last_jy);
7949
7950       if (x == next_jx && y == next_jy)
7951         return TRUE;
7952     }
7953   }
7954
7955   return FALSE;
7956 }
7957
7958 static void StartMoving(int x, int y)
7959 {
7960   boolean started_moving = FALSE;       // some elements can fall _and_ move
7961   int element = Tile[x][y];
7962
7963   if (Stop[x][y])
7964     return;
7965
7966   if (MovDelay[x][y] == 0)
7967     GfxAction[x][y] = ACTION_DEFAULT;
7968
7969   if (CAN_FALL(element) && y < lev_fieldy - 1)
7970   {
7971     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7972         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7973       if (JustBeingPushed(x, y))
7974         return;
7975
7976     if (element == EL_QUICKSAND_FULL)
7977     {
7978       if (IS_FREE(x, y + 1))
7979       {
7980         InitMovingField(x, y, MV_DOWN);
7981         started_moving = TRUE;
7982
7983         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7984 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7985         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7986           Store[x][y] = EL_ROCK;
7987 #else
7988         Store[x][y] = EL_ROCK;
7989 #endif
7990
7991         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7992       }
7993       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7994       {
7995         if (!MovDelay[x][y])
7996         {
7997           MovDelay[x][y] = TILEY + 1;
7998
7999           ResetGfxAnimation(x, y);
8000           ResetGfxAnimation(x, y + 1);
8001         }
8002
8003         if (MovDelay[x][y])
8004         {
8005           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8006           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8007
8008           MovDelay[x][y]--;
8009           if (MovDelay[x][y])
8010             return;
8011         }
8012
8013         Tile[x][y] = EL_QUICKSAND_EMPTY;
8014         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8015         Store[x][y + 1] = Store[x][y];
8016         Store[x][y] = 0;
8017
8018         PlayLevelSoundAction(x, y, ACTION_FILLING);
8019       }
8020       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8021       {
8022         if (!MovDelay[x][y])
8023         {
8024           MovDelay[x][y] = TILEY + 1;
8025
8026           ResetGfxAnimation(x, y);
8027           ResetGfxAnimation(x, y + 1);
8028         }
8029
8030         if (MovDelay[x][y])
8031         {
8032           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
8033           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8034
8035           MovDelay[x][y]--;
8036           if (MovDelay[x][y])
8037             return;
8038         }
8039
8040         Tile[x][y] = EL_QUICKSAND_EMPTY;
8041         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8042         Store[x][y + 1] = Store[x][y];
8043         Store[x][y] = 0;
8044
8045         PlayLevelSoundAction(x, y, ACTION_FILLING);
8046       }
8047     }
8048     else if (element == EL_QUICKSAND_FAST_FULL)
8049     {
8050       if (IS_FREE(x, y + 1))
8051       {
8052         InitMovingField(x, y, MV_DOWN);
8053         started_moving = TRUE;
8054
8055         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
8056 #if USE_QUICKSAND_BD_ROCK_BUGFIX
8057         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
8058           Store[x][y] = EL_ROCK;
8059 #else
8060         Store[x][y] = EL_ROCK;
8061 #endif
8062
8063         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
8064       }
8065       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8066       {
8067         if (!MovDelay[x][y])
8068         {
8069           MovDelay[x][y] = TILEY + 1;
8070
8071           ResetGfxAnimation(x, y);
8072           ResetGfxAnimation(x, y + 1);
8073         }
8074
8075         if (MovDelay[x][y])
8076         {
8077           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8078           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
8079
8080           MovDelay[x][y]--;
8081           if (MovDelay[x][y])
8082             return;
8083         }
8084
8085         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8086         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
8087         Store[x][y + 1] = Store[x][y];
8088         Store[x][y] = 0;
8089
8090         PlayLevelSoundAction(x, y, ACTION_FILLING);
8091       }
8092       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8093       {
8094         if (!MovDelay[x][y])
8095         {
8096           MovDelay[x][y] = TILEY + 1;
8097
8098           ResetGfxAnimation(x, y);
8099           ResetGfxAnimation(x, y + 1);
8100         }
8101
8102         if (MovDelay[x][y])
8103         {
8104           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
8105           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
8106
8107           MovDelay[x][y]--;
8108           if (MovDelay[x][y])
8109             return;
8110         }
8111
8112         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
8113         Tile[x][y + 1] = EL_QUICKSAND_FULL;
8114         Store[x][y + 1] = Store[x][y];
8115         Store[x][y] = 0;
8116
8117         PlayLevelSoundAction(x, y, ACTION_FILLING);
8118       }
8119     }
8120     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8121              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
8122     {
8123       InitMovingField(x, y, MV_DOWN);
8124       started_moving = TRUE;
8125
8126       Tile[x][y] = EL_QUICKSAND_FILLING;
8127       Store[x][y] = element;
8128
8129       PlayLevelSoundAction(x, y, ACTION_FILLING);
8130     }
8131     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
8132              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
8133     {
8134       InitMovingField(x, y, MV_DOWN);
8135       started_moving = TRUE;
8136
8137       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
8138       Store[x][y] = element;
8139
8140       PlayLevelSoundAction(x, y, ACTION_FILLING);
8141     }
8142     else if (element == EL_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_MAGIC_WALL_EMPTYING;
8150         Store[x][y] = EL_CHANGED(Store[x][y]);
8151       }
8152       else if (Tile[x][y + 1] == EL_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_MAGIC_WALL_ACTIVE;
8165         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
8166         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
8167         Store[x][y] = 0;
8168       }
8169     }
8170     else if (element == EL_BD_MAGIC_WALL_FULL)
8171     {
8172       if (IS_FREE(x, y + 1))
8173       {
8174         InitMovingField(x, y, MV_DOWN);
8175         started_moving = TRUE;
8176
8177         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8178         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8179       }
8180       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8181       {
8182         if (!MovDelay[x][y])
8183           MovDelay[x][y] = TILEY / 4 + 1;
8184
8185         if (MovDelay[x][y])
8186         {
8187           MovDelay[x][y]--;
8188           if (MovDelay[x][y])
8189             return;
8190         }
8191
8192         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8193         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8194         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8195         Store[x][y] = 0;
8196       }
8197     }
8198     else if (element == EL_DC_MAGIC_WALL_FULL)
8199     {
8200       if (IS_FREE(x, y + 1))
8201       {
8202         InitMovingField(x, y, MV_DOWN);
8203         started_moving = TRUE;
8204
8205         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8206         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8207       }
8208       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8209       {
8210         if (!MovDelay[x][y])
8211           MovDelay[x][y] = TILEY / 4 + 1;
8212
8213         if (MovDelay[x][y])
8214         {
8215           MovDelay[x][y]--;
8216           if (MovDelay[x][y])
8217             return;
8218         }
8219
8220         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8221         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8222         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8223         Store[x][y] = 0;
8224       }
8225     }
8226     else if ((CAN_PASS_MAGIC_WALL(element) &&
8227               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8228                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8229              (CAN_PASS_DC_MAGIC_WALL(element) &&
8230               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8231
8232     {
8233       InitMovingField(x, y, MV_DOWN);
8234       started_moving = TRUE;
8235
8236       Tile[x][y] =
8237         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8238          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8239          EL_DC_MAGIC_WALL_FILLING);
8240       Store[x][y] = element;
8241     }
8242     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8243     {
8244       SplashAcid(x, y + 1);
8245
8246       InitMovingField(x, y, MV_DOWN);
8247       started_moving = TRUE;
8248
8249       Store[x][y] = EL_ACID;
8250     }
8251     else if (
8252              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8253               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8254              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8255               CAN_FALL(element) && WasJustFalling[x][y] &&
8256               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8257
8258              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8259               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8260               (Tile[x][y + 1] == EL_BLOCKED)))
8261     {
8262       /* this is needed for a special case not covered by calling "Impact()"
8263          from "ContinueMoving()": if an element moves to a tile directly below
8264          another element which was just falling on that tile (which was empty
8265          in the previous frame), the falling element above would just stop
8266          instead of smashing the element below (in previous version, the above
8267          element was just checked for "moving" instead of "falling", resulting
8268          in incorrect smashes caused by horizontal movement of the above
8269          element; also, the case of the player being the element to smash was
8270          simply not covered here... :-/ ) */
8271
8272       CheckCollision[x][y] = 0;
8273       CheckImpact[x][y] = 0;
8274
8275       Impact(x, y);
8276     }
8277     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8278     {
8279       if (MovDir[x][y] == MV_NONE)
8280       {
8281         InitMovingField(x, y, MV_DOWN);
8282         started_moving = TRUE;
8283       }
8284     }
8285     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8286     {
8287       if (WasJustFalling[x][y]) // prevent animation from being restarted
8288         MovDir[x][y] = MV_DOWN;
8289
8290       InitMovingField(x, y, MV_DOWN);
8291       started_moving = TRUE;
8292     }
8293     else if (element == EL_AMOEBA_DROP)
8294     {
8295       Tile[x][y] = EL_AMOEBA_GROWING;
8296       Store[x][y] = EL_AMOEBA_WET;
8297     }
8298     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8299               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8300              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8301              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8302     {
8303       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8304                                 (IS_FREE(x - 1, y + 1) ||
8305                                  Tile[x - 1][y + 1] == EL_ACID));
8306       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8307                                 (IS_FREE(x + 1, y + 1) ||
8308                                  Tile[x + 1][y + 1] == EL_ACID));
8309       boolean can_fall_any  = (can_fall_left || can_fall_right);
8310       boolean can_fall_both = (can_fall_left && can_fall_right);
8311       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8312
8313       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8314       {
8315         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8316           can_fall_right = FALSE;
8317         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8318           can_fall_left = FALSE;
8319         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8320           can_fall_right = FALSE;
8321         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8322           can_fall_left = FALSE;
8323
8324         can_fall_any  = (can_fall_left || can_fall_right);
8325         can_fall_both = FALSE;
8326       }
8327
8328       if (can_fall_both)
8329       {
8330         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8331           can_fall_right = FALSE;       // slip down on left side
8332         else
8333           can_fall_left = !(can_fall_right = RND(2));
8334
8335         can_fall_both = FALSE;
8336       }
8337
8338       if (can_fall_any)
8339       {
8340         // if not determined otherwise, prefer left side for slipping down
8341         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8342         started_moving = TRUE;
8343       }
8344     }
8345     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8346     {
8347       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8348       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8349       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8350       int belt_dir = game.belt_dir[belt_nr];
8351
8352       if ((belt_dir == MV_LEFT  && left_is_free) ||
8353           (belt_dir == MV_RIGHT && right_is_free))
8354       {
8355         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8356
8357         InitMovingField(x, y, belt_dir);
8358         started_moving = TRUE;
8359
8360         Pushed[x][y] = TRUE;
8361         Pushed[nextx][y] = TRUE;
8362
8363         GfxAction[x][y] = ACTION_DEFAULT;
8364       }
8365       else
8366       {
8367         MovDir[x][y] = 0;       // if element was moving, stop it
8368       }
8369     }
8370   }
8371
8372   // not "else if" because of elements that can fall and move (EL_SPRING)
8373   if (CAN_MOVE(element) && !started_moving)
8374   {
8375     int move_pattern = element_info[element].move_pattern;
8376     int newx, newy;
8377
8378     Moving2Blocked(x, y, &newx, &newy);
8379
8380     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8381       return;
8382
8383     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8384         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8385     {
8386       WasJustMoving[x][y] = 0;
8387       CheckCollision[x][y] = 0;
8388
8389       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8390
8391       if (Tile[x][y] != element)        // element has changed
8392         return;
8393     }
8394
8395     if (!MovDelay[x][y])        // start new movement phase
8396     {
8397       // all objects that can change their move direction after each step
8398       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8399
8400       if (element != EL_YAMYAM &&
8401           element != EL_DARK_YAMYAM &&
8402           element != EL_PACMAN &&
8403           !(move_pattern & MV_ANY_DIRECTION) &&
8404           move_pattern != MV_TURNING_LEFT &&
8405           move_pattern != MV_TURNING_RIGHT &&
8406           move_pattern != MV_TURNING_LEFT_RIGHT &&
8407           move_pattern != MV_TURNING_RIGHT_LEFT &&
8408           move_pattern != MV_TURNING_RANDOM)
8409       {
8410         TurnRound(x, y);
8411
8412         if (MovDelay[x][y] && (element == EL_BUG ||
8413                                element == EL_SPACESHIP ||
8414                                element == EL_SP_SNIKSNAK ||
8415                                element == EL_SP_ELECTRON ||
8416                                element == EL_MOLE))
8417           TEST_DrawLevelField(x, y);
8418       }
8419     }
8420
8421     if (MovDelay[x][y])         // wait some time before next movement
8422     {
8423       MovDelay[x][y]--;
8424
8425       if (element == EL_ROBOT ||
8426           element == EL_YAMYAM ||
8427           element == EL_DARK_YAMYAM)
8428       {
8429         DrawLevelElementAnimationIfNeeded(x, y, element);
8430         PlayLevelSoundAction(x, y, ACTION_WAITING);
8431       }
8432       else if (element == EL_SP_ELECTRON)
8433         DrawLevelElementAnimationIfNeeded(x, y, element);
8434       else if (element == EL_DRAGON)
8435       {
8436         int i;
8437         int dir = MovDir[x][y];
8438         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8439         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8440         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8441                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8442                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8443                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8444         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8445
8446         GfxAction[x][y] = ACTION_ATTACKING;
8447
8448         if (IS_PLAYER(x, y))
8449           DrawPlayerField(x, y);
8450         else
8451           TEST_DrawLevelField(x, y);
8452
8453         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8454
8455         for (i = 1; i <= 3; i++)
8456         {
8457           int xx = x + i * dx;
8458           int yy = y + i * dy;
8459           int sx = SCREENX(xx);
8460           int sy = SCREENY(yy);
8461           int flame_graphic = graphic + (i - 1);
8462
8463           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8464             break;
8465
8466           if (MovDelay[x][y])
8467           {
8468             int flamed = MovingOrBlocked2Element(xx, yy);
8469
8470             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8471               Bang(xx, yy);
8472             else
8473               RemoveMovingField(xx, yy);
8474
8475             ChangeDelay[xx][yy] = 0;
8476
8477             Tile[xx][yy] = EL_FLAMES;
8478
8479             if (IN_SCR_FIELD(sx, sy))
8480             {
8481               TEST_DrawLevelFieldCrumbled(xx, yy);
8482               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8483             }
8484           }
8485           else
8486           {
8487             if (Tile[xx][yy] == EL_FLAMES)
8488               Tile[xx][yy] = EL_EMPTY;
8489             TEST_DrawLevelField(xx, yy);
8490           }
8491         }
8492       }
8493
8494       if (MovDelay[x][y])       // element still has to wait some time
8495       {
8496         PlayLevelSoundAction(x, y, ACTION_WAITING);
8497
8498         return;
8499       }
8500     }
8501
8502     // now make next step
8503
8504     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8505
8506     if (DONT_COLLIDE_WITH(element) &&
8507         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8508         !PLAYER_ENEMY_PROTECTED(newx, newy))
8509     {
8510       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8511
8512       return;
8513     }
8514
8515     else if (CAN_MOVE_INTO_ACID(element) &&
8516              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8517              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8518              (MovDir[x][y] == MV_DOWN ||
8519               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8520     {
8521       SplashAcid(newx, newy);
8522       Store[x][y] = EL_ACID;
8523     }
8524     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8525     {
8526       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8527           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8528           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8529           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8530       {
8531         RemoveField(x, y);
8532         TEST_DrawLevelField(x, y);
8533
8534         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8535         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8536           DrawGraphicThruMask(SCREENX(newx), SCREENY(newy), el2img(element), 0);
8537
8538         game.friends_still_needed--;
8539         if (!game.friends_still_needed &&
8540             !game.GameOver &&
8541             game.all_players_gone)
8542           LevelSolved();
8543
8544         return;
8545       }
8546       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8547       {
8548         if (DigField(local_player, x, y, newx, newy, 0, 0, DF_DIG) == MP_MOVING)
8549           TEST_DrawLevelField(newx, newy);
8550         else
8551           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8552       }
8553       else if (!IS_FREE(newx, newy))
8554       {
8555         GfxAction[x][y] = ACTION_WAITING;
8556
8557         if (IS_PLAYER(x, y))
8558           DrawPlayerField(x, y);
8559         else
8560           TEST_DrawLevelField(x, y);
8561
8562         return;
8563       }
8564     }
8565     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8566     {
8567       if (IS_FOOD_PIG(Tile[newx][newy]))
8568       {
8569         if (IS_MOVING(newx, newy))
8570           RemoveMovingField(newx, newy);
8571         else
8572         {
8573           Tile[newx][newy] = EL_EMPTY;
8574           TEST_DrawLevelField(newx, newy);
8575         }
8576
8577         PlayLevelSound(x, y, SND_PIG_DIGGING);
8578       }
8579       else if (!IS_FREE(newx, newy))
8580       {
8581         if (IS_PLAYER(x, y))
8582           DrawPlayerField(x, y);
8583         else
8584           TEST_DrawLevelField(x, y);
8585
8586         return;
8587       }
8588     }
8589     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8590     {
8591       if (Store[x][y] != EL_EMPTY)
8592       {
8593         boolean can_clone = FALSE;
8594         int xx, yy;
8595
8596         // check if element to clone is still there
8597         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8598         {
8599           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8600           {
8601             can_clone = TRUE;
8602
8603             break;
8604           }
8605         }
8606
8607         // cannot clone or target field not free anymore -- do not clone
8608         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8609           Store[x][y] = EL_EMPTY;
8610       }
8611
8612       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8613       {
8614         if (IS_MV_DIAGONAL(MovDir[x][y]))
8615         {
8616           int diagonal_move_dir = MovDir[x][y];
8617           int stored = Store[x][y];
8618           int change_delay = 8;
8619           int graphic;
8620
8621           // android is moving diagonally
8622
8623           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8624
8625           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8626           GfxElement[x][y] = EL_EMC_ANDROID;
8627           GfxAction[x][y] = ACTION_SHRINKING;
8628           GfxDir[x][y] = diagonal_move_dir;
8629           ChangeDelay[x][y] = change_delay;
8630
8631           if (Store[x][y] == EL_EMPTY)
8632             Store[x][y] = GfxElementEmpty[x][y];
8633
8634           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8635                                    GfxDir[x][y]);
8636
8637           DrawLevelGraphicAnimation(x, y, graphic);
8638           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8639
8640           if (Tile[newx][newy] == EL_ACID)
8641           {
8642             SplashAcid(newx, newy);
8643
8644             return;
8645           }
8646
8647           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8648
8649           Store[newx][newy] = EL_EMC_ANDROID;
8650           GfxElement[newx][newy] = EL_EMC_ANDROID;
8651           GfxAction[newx][newy] = ACTION_GROWING;
8652           GfxDir[newx][newy] = diagonal_move_dir;
8653           ChangeDelay[newx][newy] = change_delay;
8654
8655           graphic = el_act_dir2img(GfxElement[newx][newy],
8656                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8657
8658           DrawLevelGraphicAnimation(newx, newy, graphic);
8659           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8660
8661           return;
8662         }
8663         else
8664         {
8665           Tile[newx][newy] = EL_EMPTY;
8666           TEST_DrawLevelField(newx, newy);
8667
8668           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8669         }
8670       }
8671       else if (!IS_FREE(newx, newy))
8672       {
8673         return;
8674       }
8675     }
8676     else if (IS_CUSTOM_ELEMENT(element) &&
8677              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8678     {
8679       if (!DigFieldByCE(newx, newy, element))
8680         return;
8681
8682       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8683       {
8684         RunnerVisit[x][y] = FrameCounter;
8685         PlayerVisit[x][y] /= 8;         // expire player visit path
8686       }
8687     }
8688     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8689     {
8690       if (!IS_FREE(newx, newy))
8691       {
8692         if (IS_PLAYER(x, y))
8693           DrawPlayerField(x, y);
8694         else
8695           TEST_DrawLevelField(x, y);
8696
8697         return;
8698       }
8699       else
8700       {
8701         boolean wanna_flame = !RND(10);
8702         int dx = newx - x, dy = newy - y;
8703         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8704         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8705         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8706                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8707         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8708                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8709
8710         if ((wanna_flame ||
8711              IS_CLASSIC_ENEMY(element1) ||
8712              IS_CLASSIC_ENEMY(element2)) &&
8713             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8714             element1 != EL_FLAMES && element2 != EL_FLAMES)
8715         {
8716           ResetGfxAnimation(x, y);
8717           GfxAction[x][y] = ACTION_ATTACKING;
8718
8719           if (IS_PLAYER(x, y))
8720             DrawPlayerField(x, y);
8721           else
8722             TEST_DrawLevelField(x, y);
8723
8724           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8725
8726           MovDelay[x][y] = 50;
8727
8728           Tile[newx][newy] = EL_FLAMES;
8729           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8730             Tile[newx1][newy1] = EL_FLAMES;
8731           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8732             Tile[newx2][newy2] = EL_FLAMES;
8733
8734           return;
8735         }
8736       }
8737     }
8738     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8739              Tile[newx][newy] == EL_DIAMOND)
8740     {
8741       if (IS_MOVING(newx, newy))
8742         RemoveMovingField(newx, newy);
8743       else
8744       {
8745         Tile[newx][newy] = EL_EMPTY;
8746         TEST_DrawLevelField(newx, newy);
8747       }
8748
8749       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8750     }
8751     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8752              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8753     {
8754       if (AmoebaNr[newx][newy])
8755       {
8756         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8757         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8758             Tile[newx][newy] == EL_BD_AMOEBA)
8759           AmoebaCnt[AmoebaNr[newx][newy]]--;
8760       }
8761
8762       if (IS_MOVING(newx, newy))
8763       {
8764         RemoveMovingField(newx, newy);
8765       }
8766       else
8767       {
8768         Tile[newx][newy] = EL_EMPTY;
8769         TEST_DrawLevelField(newx, newy);
8770       }
8771
8772       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8773     }
8774     else if ((element == EL_PACMAN || element == EL_MOLE)
8775              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8776     {
8777       if (AmoebaNr[newx][newy])
8778       {
8779         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8780         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8781             Tile[newx][newy] == EL_BD_AMOEBA)
8782           AmoebaCnt[AmoebaNr[newx][newy]]--;
8783       }
8784
8785       if (element == EL_MOLE)
8786       {
8787         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8788         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8789
8790         ResetGfxAnimation(x, y);
8791         GfxAction[x][y] = ACTION_DIGGING;
8792         TEST_DrawLevelField(x, y);
8793
8794         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8795
8796         return;                         // wait for shrinking amoeba
8797       }
8798       else      // element == EL_PACMAN
8799       {
8800         Tile[newx][newy] = EL_EMPTY;
8801         TEST_DrawLevelField(newx, newy);
8802         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8803       }
8804     }
8805     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8806              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8807               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8808     {
8809       // wait for shrinking amoeba to completely disappear
8810       return;
8811     }
8812     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8813     {
8814       // object was running against a wall
8815
8816       TurnRound(x, y);
8817
8818       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8819         DrawLevelElementAnimation(x, y, element);
8820
8821       if (DONT_TOUCH(element))
8822         TestIfBadThingTouchesPlayer(x, y);
8823
8824       return;
8825     }
8826
8827     InitMovingField(x, y, MovDir[x][y]);
8828
8829     PlayLevelSoundAction(x, y, ACTION_MOVING);
8830   }
8831
8832   if (MovDir[x][y])
8833     ContinueMoving(x, y);
8834 }
8835
8836 void ContinueMoving(int x, int y)
8837 {
8838   int element = Tile[x][y];
8839   struct ElementInfo *ei = &element_info[element];
8840   int direction = MovDir[x][y];
8841   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8842   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8843   int newx = x + dx, newy = y + dy;
8844   int stored = Store[x][y];
8845   int stored_new = Store[newx][newy];
8846   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8847   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8848   boolean last_line = (newy == lev_fieldy - 1);
8849   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8850
8851   if (pushed_by_player)         // special case: moving object pushed by player
8852   {
8853     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x, y)->MovPos));
8854   }
8855   else if (use_step_delay)      // special case: moving object has step delay
8856   {
8857     if (!MovDelay[x][y])
8858       MovPos[x][y] += getElementMoveStepsize(x, y);
8859
8860     if (MovDelay[x][y])
8861       MovDelay[x][y]--;
8862     else
8863       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8864
8865     if (MovDelay[x][y])
8866     {
8867       TEST_DrawLevelField(x, y);
8868
8869       return;   // element is still waiting
8870     }
8871   }
8872   else                          // normal case: generically moving object
8873   {
8874     MovPos[x][y] += getElementMoveStepsize(x, y);
8875   }
8876
8877   if (ABS(MovPos[x][y]) < TILEX)
8878   {
8879     TEST_DrawLevelField(x, y);
8880
8881     return;     // element is still moving
8882   }
8883
8884   // element reached destination field
8885
8886   Tile[x][y] = EL_EMPTY;
8887   Tile[newx][newy] = element;
8888   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8889
8890   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8891   {
8892     element = Tile[newx][newy] = EL_ACID;
8893   }
8894   else if (element == EL_MOLE)
8895   {
8896     Tile[x][y] = EL_SAND;
8897
8898     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8899   }
8900   else if (element == EL_QUICKSAND_FILLING)
8901   {
8902     element = Tile[newx][newy] = get_next_element(element);
8903     Store[newx][newy] = Store[x][y];
8904   }
8905   else if (element == EL_QUICKSAND_EMPTYING)
8906   {
8907     Tile[x][y] = get_next_element(element);
8908     element = Tile[newx][newy] = Store[x][y];
8909   }
8910   else if (element == EL_QUICKSAND_FAST_FILLING)
8911   {
8912     element = Tile[newx][newy] = get_next_element(element);
8913     Store[newx][newy] = Store[x][y];
8914   }
8915   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8916   {
8917     Tile[x][y] = get_next_element(element);
8918     element = Tile[newx][newy] = Store[x][y];
8919   }
8920   else if (element == EL_MAGIC_WALL_FILLING)
8921   {
8922     element = Tile[newx][newy] = get_next_element(element);
8923     if (!game.magic_wall_active)
8924       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8925     Store[newx][newy] = Store[x][y];
8926   }
8927   else if (element == EL_MAGIC_WALL_EMPTYING)
8928   {
8929     Tile[x][y] = get_next_element(element);
8930     if (!game.magic_wall_active)
8931       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8932     element = Tile[newx][newy] = Store[x][y];
8933
8934     InitField(newx, newy, FALSE);
8935   }
8936   else if (element == EL_BD_MAGIC_WALL_FILLING)
8937   {
8938     element = Tile[newx][newy] = get_next_element(element);
8939     if (!game.magic_wall_active)
8940       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8941     Store[newx][newy] = Store[x][y];
8942   }
8943   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8944   {
8945     Tile[x][y] = get_next_element(element);
8946     if (!game.magic_wall_active)
8947       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8948     element = Tile[newx][newy] = Store[x][y];
8949
8950     InitField(newx, newy, FALSE);
8951   }
8952   else if (element == EL_DC_MAGIC_WALL_FILLING)
8953   {
8954     element = Tile[newx][newy] = get_next_element(element);
8955     if (!game.magic_wall_active)
8956       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8957     Store[newx][newy] = Store[x][y];
8958   }
8959   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8960   {
8961     Tile[x][y] = get_next_element(element);
8962     if (!game.magic_wall_active)
8963       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8964     element = Tile[newx][newy] = Store[x][y];
8965
8966     InitField(newx, newy, FALSE);
8967   }
8968   else if (element == EL_AMOEBA_DROPPING)
8969   {
8970     Tile[x][y] = get_next_element(element);
8971     element = Tile[newx][newy] = Store[x][y];
8972   }
8973   else if (element == EL_SOKOBAN_OBJECT)
8974   {
8975     if (Back[x][y])
8976       Tile[x][y] = Back[x][y];
8977
8978     if (Back[newx][newy])
8979       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8980
8981     Back[x][y] = Back[newx][newy] = 0;
8982   }
8983
8984   Store[x][y] = EL_EMPTY;
8985   MovPos[x][y] = 0;
8986   MovDir[x][y] = 0;
8987   MovDelay[x][y] = 0;
8988
8989   MovDelay[newx][newy] = 0;
8990
8991   if (CAN_CHANGE_OR_HAS_ACTION(element))
8992   {
8993     // copy element change control values to new field
8994     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8995     ChangePage[newx][newy]  = ChangePage[x][y];
8996     ChangeCount[newx][newy] = ChangeCount[x][y];
8997     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8998   }
8999
9000   CustomValue[newx][newy] = CustomValue[x][y];
9001
9002   ChangeDelay[x][y] = 0;
9003   ChangePage[x][y] = -1;
9004   ChangeCount[x][y] = 0;
9005   ChangeEvent[x][y] = -1;
9006
9007   CustomValue[x][y] = 0;
9008
9009   // copy animation control values to new field
9010   GfxFrame[newx][newy]  = GfxFrame[x][y];
9011   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
9012   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
9013   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
9014
9015   Pushed[x][y] = Pushed[newx][newy] = FALSE;
9016
9017   // some elements can leave other elements behind after moving
9018   if (ei->move_leave_element != EL_EMPTY &&
9019       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
9020       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
9021   {
9022     int move_leave_element = ei->move_leave_element;
9023
9024     // this makes it possible to leave the removed element again
9025     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
9026       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
9027
9028     Tile[x][y] = move_leave_element;
9029
9030     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
9031       MovDir[x][y] = direction;
9032
9033     InitField(x, y, FALSE);
9034
9035     if (GFX_CRUMBLED(Tile[x][y]))
9036       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9037
9038     if (IS_PLAYER_ELEMENT(move_leave_element))
9039       RelocatePlayer(x, y, move_leave_element);
9040   }
9041
9042   // do this after checking for left-behind element
9043   ResetGfxAnimation(x, y);      // reset animation values for old field
9044
9045   if (!CAN_MOVE(element) ||
9046       (CAN_FALL(element) && direction == MV_DOWN &&
9047        (element == EL_SPRING ||
9048         element_info[element].move_pattern == MV_WHEN_PUSHED ||
9049         element_info[element].move_pattern == MV_WHEN_DROPPED)))
9050     GfxDir[x][y] = MovDir[newx][newy] = 0;
9051
9052   TEST_DrawLevelField(x, y);
9053   TEST_DrawLevelField(newx, newy);
9054
9055   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
9056
9057   // prevent pushed element from moving on in pushed direction
9058   if (pushed_by_player && CAN_MOVE(element) &&
9059       element_info[element].move_pattern & MV_ANY_DIRECTION &&
9060       !(element_info[element].move_pattern & direction))
9061     TurnRound(newx, newy);
9062
9063   // prevent elements on conveyor belt from moving on in last direction
9064   if (pushed_by_conveyor && CAN_FALL(element) &&
9065       direction & MV_HORIZONTAL)
9066     MovDir[newx][newy] = 0;
9067
9068   if (!pushed_by_player)
9069   {
9070     int nextx = newx + dx, nexty = newy + dy;
9071     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
9072
9073     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
9074
9075     if (CAN_FALL(element) && direction == MV_DOWN)
9076       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
9077
9078     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
9079       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
9080
9081     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
9082       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
9083   }
9084
9085   if (DONT_TOUCH(element))      // object may be nasty to player or others
9086   {
9087     TestIfBadThingTouchesPlayer(newx, newy);
9088     TestIfBadThingTouchesFriend(newx, newy);
9089
9090     if (!IS_CUSTOM_ELEMENT(element))
9091       TestIfBadThingTouchesOtherBadThing(newx, newy);
9092   }
9093   else if (element == EL_PENGUIN)
9094     TestIfFriendTouchesBadThing(newx, newy);
9095
9096   if (DONT_GET_HIT_BY(element))
9097   {
9098     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
9099   }
9100
9101   // give the player one last chance (one more frame) to move away
9102   if (CAN_FALL(element) && direction == MV_DOWN &&
9103       (last_line || (!IS_FREE(x, newy + 1) &&
9104                      (!IS_PLAYER(x, newy + 1) ||
9105                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
9106     Impact(x, newy);
9107
9108   if (pushed_by_player && !game.use_change_when_pushing_bug)
9109   {
9110     int push_side = MV_DIR_OPPOSITE(direction);
9111     struct PlayerInfo *player = PLAYERINFO(x, y);
9112
9113     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
9114                                player->index_bit, push_side);
9115     CheckTriggeredElementChangeByPlayer(newx, newy, element, CE_PLAYER_PUSHES_X,
9116                                         player->index_bit, push_side);
9117   }
9118
9119   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
9120     MovDelay[newx][newy] = 1;
9121
9122   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
9123
9124   TestIfElementTouchesCustomElement(x, y);      // empty or new element
9125   TestIfElementHitsCustomElement(newx, newy, direction);
9126   TestIfPlayerTouchesCustomElement(newx, newy);
9127   TestIfElementTouchesCustomElement(newx, newy);
9128
9129   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
9130       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
9131     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
9132                              MV_DIR_OPPOSITE(direction));
9133 }
9134
9135 int AmoebaNeighbourNr(int ax, int ay)
9136 {
9137   int i;
9138   int element = Tile[ax][ay];
9139   int group_nr = 0;
9140   struct XY *xy = xy_topdown;
9141
9142   for (i = 0; i < NUM_DIRECTIONS; i++)
9143   {
9144     int x = ax + xy[i].x;
9145     int y = ay + xy[i].y;
9146
9147     if (!IN_LEV_FIELD(x, y))
9148       continue;
9149
9150     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
9151       group_nr = AmoebaNr[x][y];
9152   }
9153
9154   return group_nr;
9155 }
9156
9157 static void AmoebaMerge(int ax, int ay)
9158 {
9159   int i, x, y, xx, yy;
9160   int new_group_nr = AmoebaNr[ax][ay];
9161   struct XY *xy = xy_topdown;
9162
9163   if (new_group_nr == 0)
9164     return;
9165
9166   for (i = 0; i < NUM_DIRECTIONS; i++)
9167   {
9168     x = ax + xy[i].x;
9169     y = ay + xy[i].y;
9170
9171     if (!IN_LEV_FIELD(x, y))
9172       continue;
9173
9174     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9175          Tile[x][y] == EL_BD_AMOEBA ||
9176          Tile[x][y] == EL_AMOEBA_DEAD) &&
9177         AmoebaNr[x][y] != new_group_nr)
9178     {
9179       int old_group_nr = AmoebaNr[x][y];
9180
9181       if (old_group_nr == 0)
9182         return;
9183
9184       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9185       AmoebaCnt[old_group_nr] = 0;
9186       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9187       AmoebaCnt2[old_group_nr] = 0;
9188
9189       SCAN_PLAYFIELD(xx, yy)
9190       {
9191         if (AmoebaNr[xx][yy] == old_group_nr)
9192           AmoebaNr[xx][yy] = new_group_nr;
9193       }
9194     }
9195   }
9196 }
9197
9198 void AmoebaToDiamond(int ax, int ay)
9199 {
9200   int i, x, y;
9201
9202   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9203   {
9204     int group_nr = AmoebaNr[ax][ay];
9205
9206 #ifdef DEBUG
9207     if (group_nr == 0)
9208     {
9209       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9210       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9211
9212       return;
9213     }
9214 #endif
9215
9216     SCAN_PLAYFIELD(x, y)
9217     {
9218       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9219       {
9220         AmoebaNr[x][y] = 0;
9221         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9222       }
9223     }
9224
9225     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9226                             SND_AMOEBA_TURNING_TO_GEM :
9227                             SND_AMOEBA_TURNING_TO_ROCK));
9228     Bang(ax, ay);
9229   }
9230   else
9231   {
9232     struct XY *xy = xy_topdown;
9233
9234     for (i = 0; i < NUM_DIRECTIONS; i++)
9235     {
9236       x = ax + xy[i].x;
9237       y = ay + xy[i].y;
9238
9239       if (!IN_LEV_FIELD(x, y))
9240         continue;
9241
9242       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9243       {
9244         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9245                               SND_AMOEBA_TURNING_TO_GEM :
9246                               SND_AMOEBA_TURNING_TO_ROCK));
9247         Bang(x, y);
9248       }
9249     }
9250   }
9251 }
9252
9253 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9254 {
9255   int x, y;
9256   int group_nr = AmoebaNr[ax][ay];
9257   boolean done = FALSE;
9258
9259 #ifdef DEBUG
9260   if (group_nr == 0)
9261   {
9262     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9263     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9264
9265     return;
9266   }
9267 #endif
9268
9269   SCAN_PLAYFIELD(x, y)
9270   {
9271     if (AmoebaNr[x][y] == group_nr &&
9272         (Tile[x][y] == EL_AMOEBA_DEAD ||
9273          Tile[x][y] == EL_BD_AMOEBA ||
9274          Tile[x][y] == EL_AMOEBA_GROWING))
9275     {
9276       AmoebaNr[x][y] = 0;
9277       Tile[x][y] = new_element;
9278       InitField(x, y, FALSE);
9279       TEST_DrawLevelField(x, y);
9280       done = TRUE;
9281     }
9282   }
9283
9284   if (done)
9285     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9286                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9287                             SND_BD_AMOEBA_TURNING_TO_GEM));
9288 }
9289
9290 static void AmoebaGrowing(int x, int y)
9291 {
9292   static DelayCounter sound_delay = { 0 };
9293
9294   if (!MovDelay[x][y])          // start new growing cycle
9295   {
9296     MovDelay[x][y] = 7;
9297
9298     if (DelayReached(&sound_delay))
9299     {
9300       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9301       sound_delay.value = 30;
9302     }
9303   }
9304
9305   if (MovDelay[x][y])           // wait some time before growing bigger
9306   {
9307     MovDelay[x][y]--;
9308     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9309     {
9310       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9311                                            6 - MovDelay[x][y]);
9312
9313       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9314     }
9315
9316     if (!MovDelay[x][y])
9317     {
9318       Tile[x][y] = Store[x][y];
9319       Store[x][y] = 0;
9320       TEST_DrawLevelField(x, y);
9321     }
9322   }
9323 }
9324
9325 static void AmoebaShrinking(int x, int y)
9326 {
9327   static DelayCounter sound_delay = { 0 };
9328
9329   if (!MovDelay[x][y])          // start new shrinking cycle
9330   {
9331     MovDelay[x][y] = 7;
9332
9333     if (DelayReached(&sound_delay))
9334       sound_delay.value = 30;
9335   }
9336
9337   if (MovDelay[x][y])           // wait some time before shrinking
9338   {
9339     MovDelay[x][y]--;
9340     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9341     {
9342       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9343                                            6 - MovDelay[x][y]);
9344
9345       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9346     }
9347
9348     if (!MovDelay[x][y])
9349     {
9350       Tile[x][y] = EL_EMPTY;
9351       TEST_DrawLevelField(x, y);
9352
9353       // don't let mole enter this field in this cycle;
9354       // (give priority to objects falling to this field from above)
9355       Stop[x][y] = TRUE;
9356     }
9357   }
9358 }
9359
9360 static void AmoebaReproduce(int ax, int ay)
9361 {
9362   int i;
9363   int element = Tile[ax][ay];
9364   int graphic = el2img(element);
9365   int newax = ax, neway = ay;
9366   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9367   struct XY *xy = xy_topdown;
9368
9369   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9370   {
9371     Tile[ax][ay] = EL_AMOEBA_DEAD;
9372     TEST_DrawLevelField(ax, ay);
9373     return;
9374   }
9375
9376   if (IS_ANIMATED(graphic))
9377     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9378
9379   if (!MovDelay[ax][ay])        // start making new amoeba field
9380     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9381
9382   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9383   {
9384     MovDelay[ax][ay]--;
9385     if (MovDelay[ax][ay])
9386       return;
9387   }
9388
9389   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9390   {
9391     int start = RND(4);
9392     int x = ax + xy[start].x;
9393     int y = ay + xy[start].y;
9394
9395     if (!IN_LEV_FIELD(x, y))
9396       return;
9397
9398     if (IS_FREE(x, y) ||
9399         CAN_GROW_INTO(Tile[x][y]) ||
9400         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9401         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9402     {
9403       newax = x;
9404       neway = y;
9405     }
9406
9407     if (newax == ax && neway == ay)
9408       return;
9409   }
9410   else                          // normal or "filled" (BD style) amoeba
9411   {
9412     int start = RND(4);
9413     boolean waiting_for_player = FALSE;
9414
9415     for (i = 0; i < NUM_DIRECTIONS; i++)
9416     {
9417       int j = (start + i) % 4;
9418       int x = ax + xy[j].x;
9419       int y = ay + xy[j].y;
9420
9421       if (!IN_LEV_FIELD(x, y))
9422         continue;
9423
9424       if (IS_FREE(x, y) ||
9425           CAN_GROW_INTO(Tile[x][y]) ||
9426           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9427           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9428       {
9429         newax = x;
9430         neway = y;
9431         break;
9432       }
9433       else if (IS_PLAYER(x, y))
9434         waiting_for_player = TRUE;
9435     }
9436
9437     if (newax == ax && neway == ay)             // amoeba cannot grow
9438     {
9439       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9440       {
9441         Tile[ax][ay] = EL_AMOEBA_DEAD;
9442         TEST_DrawLevelField(ax, ay);
9443         AmoebaCnt[AmoebaNr[ax][ay]]--;
9444
9445         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9446         {
9447           if (element == EL_AMOEBA_FULL)
9448             AmoebaToDiamond(ax, ay);
9449           else if (element == EL_BD_AMOEBA)
9450             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9451         }
9452       }
9453       return;
9454     }
9455     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9456     {
9457       // amoeba gets larger by growing in some direction
9458
9459       int new_group_nr = AmoebaNr[ax][ay];
9460
9461 #ifdef DEBUG
9462   if (new_group_nr == 0)
9463   {
9464     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9465           newax, neway);
9466     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9467
9468     return;
9469   }
9470 #endif
9471
9472       AmoebaNr[newax][neway] = new_group_nr;
9473       AmoebaCnt[new_group_nr]++;
9474       AmoebaCnt2[new_group_nr]++;
9475
9476       // if amoeba touches other amoeba(s) after growing, unify them
9477       AmoebaMerge(newax, neway);
9478
9479       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9480       {
9481         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9482         return;
9483       }
9484     }
9485   }
9486
9487   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9488       (neway == lev_fieldy - 1 && newax != ax))
9489   {
9490     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9491     Store[newax][neway] = element;
9492   }
9493   else if (neway == ay || element == EL_EMC_DRIPPER)
9494   {
9495     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9496
9497     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9498   }
9499   else
9500   {
9501     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9502     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9503     Store[ax][ay] = EL_AMOEBA_DROP;
9504     ContinueMoving(ax, ay);
9505     return;
9506   }
9507
9508   TEST_DrawLevelField(newax, neway);
9509 }
9510
9511 static void Life(int ax, int ay)
9512 {
9513   int x1, y1, x2, y2;
9514   int life_time = 40;
9515   int element = Tile[ax][ay];
9516   int graphic = el2img(element);
9517   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9518                          level.biomaze);
9519   boolean changed = FALSE;
9520
9521   if (IS_ANIMATED(graphic))
9522     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9523
9524   if (Stop[ax][ay])
9525     return;
9526
9527   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9528     MovDelay[ax][ay] = life_time;
9529
9530   if (MovDelay[ax][ay])         // wait some time before next cycle
9531   {
9532     MovDelay[ax][ay]--;
9533     if (MovDelay[ax][ay])
9534       return;
9535   }
9536
9537   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9538   {
9539     int xx = ax + x1, yy = ay + y1;
9540     int old_element = Tile[xx][yy];
9541     int num_neighbours = 0;
9542
9543     if (!IN_LEV_FIELD(xx, yy))
9544       continue;
9545
9546     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9547     {
9548       int x = xx + x2, y = yy + y2;
9549
9550       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9551         continue;
9552
9553       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9554       boolean is_neighbour = FALSE;
9555
9556       if (level.use_life_bugs)
9557         is_neighbour =
9558           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9559            (IS_FREE(x, y)                             &&  Stop[x][y]));
9560       else
9561         is_neighbour =
9562           (Last[x][y] == element || is_player_cell);
9563
9564       if (is_neighbour)
9565         num_neighbours++;
9566     }
9567
9568     boolean is_free = FALSE;
9569
9570     if (level.use_life_bugs)
9571       is_free = (IS_FREE(xx, yy));
9572     else
9573       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9574
9575     if (xx == ax && yy == ay)           // field in the middle
9576     {
9577       if (num_neighbours < life_parameter[0] ||
9578           num_neighbours > life_parameter[1])
9579       {
9580         Tile[xx][yy] = EL_EMPTY;
9581         if (Tile[xx][yy] != old_element)
9582           TEST_DrawLevelField(xx, yy);
9583         Stop[xx][yy] = TRUE;
9584         changed = TRUE;
9585       }
9586     }
9587     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9588     {                                   // free border field
9589       if (num_neighbours >= life_parameter[2] &&
9590           num_neighbours <= life_parameter[3])
9591       {
9592         Tile[xx][yy] = element;
9593         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9594         if (Tile[xx][yy] != old_element)
9595           TEST_DrawLevelField(xx, yy);
9596         Stop[xx][yy] = TRUE;
9597         changed = TRUE;
9598       }
9599     }
9600   }
9601
9602   if (changed)
9603     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9604                    SND_GAME_OF_LIFE_GROWING);
9605 }
9606
9607 static void InitRobotWheel(int x, int y)
9608 {
9609   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9610 }
9611
9612 static void RunRobotWheel(int x, int y)
9613 {
9614   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9615 }
9616
9617 static void StopRobotWheel(int x, int y)
9618 {
9619   if (game.robot_wheel_x == x &&
9620       game.robot_wheel_y == y)
9621   {
9622     game.robot_wheel_x = -1;
9623     game.robot_wheel_y = -1;
9624     game.robot_wheel_active = FALSE;
9625   }
9626 }
9627
9628 static void InitTimegateWheel(int x, int y)
9629 {
9630   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9631 }
9632
9633 static void RunTimegateWheel(int x, int y)
9634 {
9635   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9636 }
9637
9638 static void InitMagicBallDelay(int x, int y)
9639 {
9640   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9641 }
9642
9643 static void ActivateMagicBall(int bx, int by)
9644 {
9645   int x, y;
9646
9647   if (level.ball_random)
9648   {
9649     int pos_border = RND(8);    // select one of the eight border elements
9650     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9651     int xx = pos_content % 3;
9652     int yy = pos_content / 3;
9653
9654     x = bx - 1 + xx;
9655     y = by - 1 + yy;
9656
9657     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9658       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9659   }
9660   else
9661   {
9662     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9663     {
9664       int xx = x - bx + 1;
9665       int yy = y - by + 1;
9666
9667       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9668         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9669     }
9670   }
9671
9672   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9673 }
9674
9675 static void CheckExit(int x, int y)
9676 {
9677   if (game.gems_still_needed > 0 ||
9678       game.sokoban_fields_still_needed > 0 ||
9679       game.sokoban_objects_still_needed > 0 ||
9680       game.lights_still_needed > 0)
9681   {
9682     int element = Tile[x][y];
9683     int graphic = el2img(element);
9684
9685     if (IS_ANIMATED(graphic))
9686       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9687
9688     return;
9689   }
9690
9691   // do not re-open exit door closed after last player
9692   if (game.all_players_gone)
9693     return;
9694
9695   Tile[x][y] = EL_EXIT_OPENING;
9696
9697   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9698 }
9699
9700 static void CheckExitEM(int x, int y)
9701 {
9702   if (game.gems_still_needed > 0 ||
9703       game.sokoban_fields_still_needed > 0 ||
9704       game.sokoban_objects_still_needed > 0 ||
9705       game.lights_still_needed > 0)
9706   {
9707     int element = Tile[x][y];
9708     int graphic = el2img(element);
9709
9710     if (IS_ANIMATED(graphic))
9711       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9712
9713     return;
9714   }
9715
9716   // do not re-open exit door closed after last player
9717   if (game.all_players_gone)
9718     return;
9719
9720   Tile[x][y] = EL_EM_EXIT_OPENING;
9721
9722   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9723 }
9724
9725 static void CheckExitSteel(int x, int y)
9726 {
9727   if (game.gems_still_needed > 0 ||
9728       game.sokoban_fields_still_needed > 0 ||
9729       game.sokoban_objects_still_needed > 0 ||
9730       game.lights_still_needed > 0)
9731   {
9732     int element = Tile[x][y];
9733     int graphic = el2img(element);
9734
9735     if (IS_ANIMATED(graphic))
9736       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9737
9738     return;
9739   }
9740
9741   // do not re-open exit door closed after last player
9742   if (game.all_players_gone)
9743     return;
9744
9745   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9746
9747   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9748 }
9749
9750 static void CheckExitSteelEM(int x, int y)
9751 {
9752   if (game.gems_still_needed > 0 ||
9753       game.sokoban_fields_still_needed > 0 ||
9754       game.sokoban_objects_still_needed > 0 ||
9755       game.lights_still_needed > 0)
9756   {
9757     int element = Tile[x][y];
9758     int graphic = el2img(element);
9759
9760     if (IS_ANIMATED(graphic))
9761       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9762
9763     return;
9764   }
9765
9766   // do not re-open exit door closed after last player
9767   if (game.all_players_gone)
9768     return;
9769
9770   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9771
9772   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9773 }
9774
9775 static void CheckExitSP(int x, int y)
9776 {
9777   if (game.gems_still_needed > 0)
9778   {
9779     int element = Tile[x][y];
9780     int graphic = el2img(element);
9781
9782     if (IS_ANIMATED(graphic))
9783       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9784
9785     return;
9786   }
9787
9788   // do not re-open exit door closed after last player
9789   if (game.all_players_gone)
9790     return;
9791
9792   Tile[x][y] = EL_SP_EXIT_OPENING;
9793
9794   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9795 }
9796
9797 static void CloseAllOpenTimegates(void)
9798 {
9799   int x, y;
9800
9801   SCAN_PLAYFIELD(x, y)
9802   {
9803     int element = Tile[x][y];
9804
9805     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9806     {
9807       Tile[x][y] = EL_TIMEGATE_CLOSING;
9808
9809       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9810     }
9811   }
9812 }
9813
9814 static void DrawTwinkleOnField(int x, int y)
9815 {
9816   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9817     return;
9818
9819   if (Tile[x][y] == EL_BD_DIAMOND)
9820     return;
9821
9822   if (MovDelay[x][y] == 0)      // next animation frame
9823     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9824
9825   if (MovDelay[x][y] != 0)      // wait some time before next frame
9826   {
9827     MovDelay[x][y]--;
9828
9829     DrawLevelElementAnimation(x, y, Tile[x][y]);
9830
9831     if (MovDelay[x][y] != 0)
9832     {
9833       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9834                                            10 - MovDelay[x][y]);
9835
9836       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9837     }
9838   }
9839 }
9840
9841 static void WallGrowing(int x, int y)
9842 {
9843   int delay = 6;
9844
9845   if (!MovDelay[x][y])          // next animation frame
9846     MovDelay[x][y] = 3 * delay;
9847
9848   if (MovDelay[x][y])           // wait some time before next frame
9849   {
9850     MovDelay[x][y]--;
9851
9852     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9853     {
9854       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9855       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9856
9857       DrawLevelGraphic(x, y, graphic, frame);
9858     }
9859
9860     if (!MovDelay[x][y])
9861     {
9862       if (MovDir[x][y] == MV_LEFT)
9863       {
9864         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9865           TEST_DrawLevelField(x - 1, y);
9866       }
9867       else if (MovDir[x][y] == MV_RIGHT)
9868       {
9869         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9870           TEST_DrawLevelField(x + 1, y);
9871       }
9872       else if (MovDir[x][y] == MV_UP)
9873       {
9874         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9875           TEST_DrawLevelField(x, y - 1);
9876       }
9877       else
9878       {
9879         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9880           TEST_DrawLevelField(x, y + 1);
9881       }
9882
9883       Tile[x][y] = Store[x][y];
9884       Store[x][y] = 0;
9885       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9886       TEST_DrawLevelField(x, y);
9887     }
9888   }
9889 }
9890
9891 static void CheckWallGrowing(int ax, int ay)
9892 {
9893   int element = Tile[ax][ay];
9894   int graphic = el2img(element);
9895   boolean free_top    = FALSE;
9896   boolean free_bottom = FALSE;
9897   boolean free_left   = FALSE;
9898   boolean free_right  = FALSE;
9899   boolean stop_top    = FALSE;
9900   boolean stop_bottom = FALSE;
9901   boolean stop_left   = FALSE;
9902   boolean stop_right  = FALSE;
9903   boolean new_wall    = FALSE;
9904
9905   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9906                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9907                            element == EL_EXPANDABLE_STEELWALL_ANY);
9908
9909   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9910                              element == EL_EXPANDABLE_WALL_ANY ||
9911                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9912                              element == EL_EXPANDABLE_STEELWALL_ANY);
9913
9914   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9915                              element == EL_EXPANDABLE_WALL_ANY ||
9916                              element == EL_EXPANDABLE_WALL ||
9917                              element == EL_BD_EXPANDABLE_WALL ||
9918                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9919                              element == EL_EXPANDABLE_STEELWALL_ANY);
9920
9921   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9922                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9923
9924   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9925                              element == EL_EXPANDABLE_WALL ||
9926                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9927
9928   int wall_growing = (is_steelwall ?
9929                       EL_EXPANDABLE_STEELWALL_GROWING :
9930                       EL_EXPANDABLE_WALL_GROWING);
9931
9932   int gfx_wall_growing_up    = (is_steelwall ?
9933                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9934                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9935   int gfx_wall_growing_down  = (is_steelwall ?
9936                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9937                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9938   int gfx_wall_growing_left  = (is_steelwall ?
9939                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9940                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9941   int gfx_wall_growing_right = (is_steelwall ?
9942                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9943                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9944
9945   if (IS_ANIMATED(graphic))
9946     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9947
9948   if (!MovDelay[ax][ay])        // start building new wall
9949     MovDelay[ax][ay] = 6;
9950
9951   if (MovDelay[ax][ay])         // wait some time before building new wall
9952   {
9953     MovDelay[ax][ay]--;
9954     if (MovDelay[ax][ay])
9955       return;
9956   }
9957
9958   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9959     free_top = TRUE;
9960   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9961     free_bottom = TRUE;
9962   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9963     free_left = TRUE;
9964   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9965     free_right = TRUE;
9966
9967   if (grow_vertical)
9968   {
9969     if (free_top)
9970     {
9971       Tile[ax][ay - 1] = wall_growing;
9972       Store[ax][ay - 1] = element;
9973       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9974
9975       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9976         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9977
9978       new_wall = TRUE;
9979     }
9980
9981     if (free_bottom)
9982     {
9983       Tile[ax][ay + 1] = wall_growing;
9984       Store[ax][ay + 1] = element;
9985       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9986
9987       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9988         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9989
9990       new_wall = TRUE;
9991     }
9992   }
9993
9994   if (grow_horizontal)
9995   {
9996     if (free_left)
9997     {
9998       Tile[ax - 1][ay] = wall_growing;
9999       Store[ax - 1][ay] = element;
10000       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
10001
10002       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
10003         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
10004
10005       new_wall = TRUE;
10006     }
10007
10008     if (free_right)
10009     {
10010       Tile[ax + 1][ay] = wall_growing;
10011       Store[ax + 1][ay] = element;
10012       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
10013
10014       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
10015         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
10016
10017       new_wall = TRUE;
10018     }
10019   }
10020
10021   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
10022     TEST_DrawLevelField(ax, ay);
10023
10024   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
10025     stop_top = TRUE;
10026   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
10027     stop_bottom = TRUE;
10028   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
10029     stop_left = TRUE;
10030   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
10031     stop_right = TRUE;
10032
10033   if (((stop_top && stop_bottom) || stop_horizontal) &&
10034       ((stop_left && stop_right) || stop_vertical))
10035     Tile[ax][ay] = (is_steelwall ? EL_STEELWALL : EL_WALL);
10036
10037   if (new_wall)
10038     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
10039 }
10040
10041 static void CheckForDragon(int x, int y)
10042 {
10043   int i, j;
10044   boolean dragon_found = FALSE;
10045   struct XY *xy = xy_topdown;
10046
10047   for (i = 0; i < NUM_DIRECTIONS; i++)
10048   {
10049     for (j = 0; j < 4; j++)
10050     {
10051       int xx = x + j * xy[i].x;
10052       int yy = y + j * xy[i].y;
10053
10054       if (IN_LEV_FIELD(xx, yy) &&
10055           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
10056       {
10057         if (Tile[xx][yy] == EL_DRAGON)
10058           dragon_found = TRUE;
10059       }
10060       else
10061         break;
10062     }
10063   }
10064
10065   if (!dragon_found)
10066   {
10067     for (i = 0; i < NUM_DIRECTIONS; i++)
10068     {
10069       for (j = 0; j < 3; j++)
10070       {
10071         int xx = x + j * xy[i].x;
10072         int yy = y + j * xy[i].y;
10073
10074         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
10075         {
10076           Tile[xx][yy] = EL_EMPTY;
10077           TEST_DrawLevelField(xx, yy);
10078         }
10079         else
10080           break;
10081       }
10082     }
10083   }
10084 }
10085
10086 static void InitBuggyBase(int x, int y)
10087 {
10088   int element = Tile[x][y];
10089   int activating_delay = FRAMES_PER_SECOND / 4;
10090
10091   ChangeDelay[x][y] =
10092     (element == EL_SP_BUGGY_BASE ?
10093      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
10094      element == EL_SP_BUGGY_BASE_ACTIVATING ?
10095      activating_delay :
10096      element == EL_SP_BUGGY_BASE_ACTIVE ?
10097      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
10098 }
10099
10100 static void WarnBuggyBase(int x, int y)
10101 {
10102   int i;
10103   struct XY *xy = xy_topdown;
10104
10105   for (i = 0; i < NUM_DIRECTIONS; i++)
10106   {
10107     int xx = x + xy[i].x;
10108     int yy = y + xy[i].y;
10109
10110     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
10111     {
10112       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
10113
10114       break;
10115     }
10116   }
10117 }
10118
10119 static void InitTrap(int x, int y)
10120 {
10121   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
10122 }
10123
10124 static void ActivateTrap(int x, int y)
10125 {
10126   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
10127 }
10128
10129 static void ChangeActiveTrap(int x, int y)
10130 {
10131   int graphic = IMG_TRAP_ACTIVE;
10132
10133   // if new animation frame was drawn, correct crumbled sand border
10134   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
10135     TEST_DrawLevelFieldCrumbled(x, y);
10136 }
10137
10138 static int getSpecialActionElement(int element, int number, int base_element)
10139 {
10140   return (element != EL_EMPTY ? element :
10141           number != -1 ? base_element + number - 1 :
10142           EL_EMPTY);
10143 }
10144
10145 static int getModifiedActionNumber(int value_old, int operator, int operand,
10146                                    int value_min, int value_max)
10147 {
10148   int value_new = (operator == CA_MODE_SET      ? operand :
10149                    operator == CA_MODE_ADD      ? value_old + operand :
10150                    operator == CA_MODE_SUBTRACT ? value_old - operand :
10151                    operator == CA_MODE_MULTIPLY ? value_old * operand :
10152                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
10153                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
10154                    value_old);
10155
10156   return (value_new < value_min ? value_min :
10157           value_new > value_max ? value_max :
10158           value_new);
10159 }
10160
10161 static void ExecuteCustomElementAction(int x, int y, int element, int page)
10162 {
10163   struct ElementInfo *ei = &element_info[element];
10164   struct ElementChangeInfo *change = &ei->change_page[page];
10165   int target_element = change->target_element;
10166   int action_type = change->action_type;
10167   int action_mode = change->action_mode;
10168   int action_arg = change->action_arg;
10169   int action_element = change->action_element;
10170   int i;
10171
10172   if (!change->has_action)
10173     return;
10174
10175   // ---------- determine action paramater values -----------------------------
10176
10177   int level_time_value =
10178     (level.time > 0 ? TimeLeft :
10179      TimePlayed);
10180
10181   int action_arg_element_raw =
10182     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10183      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10184      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10185      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10186      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10187      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10188      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10189      EL_EMPTY);
10190   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10191
10192   int action_arg_direction =
10193     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10194      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10195      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10196      change->actual_trigger_side :
10197      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10198      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10199      MV_NONE);
10200
10201   int action_arg_number_min =
10202     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10203      CA_ARG_MIN);
10204
10205   int action_arg_number_max =
10206     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10207      action_type == CA_SET_LEVEL_GEMS ? 999 :
10208      action_type == CA_SET_LEVEL_TIME ? 9999 :
10209      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10210      action_type == CA_SET_CE_VALUE ? 9999 :
10211      action_type == CA_SET_CE_SCORE ? 9999 :
10212      CA_ARG_MAX);
10213
10214   int action_arg_number_reset =
10215     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10216      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10217      action_type == CA_SET_LEVEL_TIME ? level.time :
10218      action_type == CA_SET_LEVEL_SCORE ? 0 :
10219      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10220      action_type == CA_SET_CE_SCORE ? 0 :
10221      0);
10222
10223   int action_arg_number =
10224     (action_arg <= CA_ARG_MAX ? action_arg :
10225      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10226      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10227      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10228      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10229      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10230      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10231      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10232      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10233      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10234      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10235      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10236      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10237      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10238      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10239      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10240      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10241      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10242      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10243      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10244      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10245      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10246      -1);
10247
10248   int action_arg_number_old =
10249     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10250      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10251      action_type == CA_SET_LEVEL_SCORE ? game.score :
10252      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10253      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10254      0);
10255
10256   int action_arg_number_new =
10257     getModifiedActionNumber(action_arg_number_old,
10258                             action_mode, action_arg_number,
10259                             action_arg_number_min, action_arg_number_max);
10260
10261   int trigger_player_bits =
10262     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10263      change->actual_trigger_player_bits : change->trigger_player);
10264
10265   int action_arg_player_bits =
10266     (action_arg >= CA_ARG_PLAYER_1 &&
10267      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10268      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10269      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10270      PLAYER_BITS_ANY);
10271
10272   // ---------- execute action  -----------------------------------------------
10273
10274   switch (action_type)
10275   {
10276     case CA_NO_ACTION:
10277     {
10278       return;
10279     }
10280
10281     // ---------- level actions  ----------------------------------------------
10282
10283     case CA_RESTART_LEVEL:
10284     {
10285       game.restart_level = TRUE;
10286
10287       break;
10288     }
10289
10290     case CA_SHOW_ENVELOPE:
10291     {
10292       int element = getSpecialActionElement(action_arg_element,
10293                                             action_arg_number, EL_ENVELOPE_1);
10294
10295       if (IS_ENVELOPE(element))
10296         local_player->show_envelope = element;
10297
10298       break;
10299     }
10300
10301     case CA_SET_LEVEL_TIME:
10302     {
10303       if (level.time > 0)       // only modify limited time value
10304       {
10305         TimeLeft = action_arg_number_new;
10306
10307         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10308
10309         DisplayGameControlValues();
10310
10311         if (!TimeLeft && game.time_limit)
10312           for (i = 0; i < MAX_PLAYERS; i++)
10313             KillPlayer(&stored_player[i]);
10314       }
10315
10316       break;
10317     }
10318
10319     case CA_SET_LEVEL_SCORE:
10320     {
10321       game.score = action_arg_number_new;
10322
10323       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10324
10325       DisplayGameControlValues();
10326
10327       break;
10328     }
10329
10330     case CA_SET_LEVEL_GEMS:
10331     {
10332       game.gems_still_needed = action_arg_number_new;
10333
10334       game.snapshot.collected_item = TRUE;
10335
10336       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10337
10338       DisplayGameControlValues();
10339
10340       break;
10341     }
10342
10343     case CA_SET_LEVEL_WIND:
10344     {
10345       game.wind_direction = action_arg_direction;
10346
10347       break;
10348     }
10349
10350     case CA_SET_LEVEL_RANDOM_SEED:
10351     {
10352       // ensure that setting a new random seed while playing is predictable
10353       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10354
10355       break;
10356     }
10357
10358     // ---------- player actions  ---------------------------------------------
10359
10360     case CA_MOVE_PLAYER:
10361     case CA_MOVE_PLAYER_NEW:
10362     {
10363       // automatically move to the next field in specified direction
10364       for (i = 0; i < MAX_PLAYERS; i++)
10365         if (trigger_player_bits & (1 << i))
10366           if (action_type == CA_MOVE_PLAYER ||
10367               stored_player[i].MovPos == 0)
10368             stored_player[i].programmed_action = action_arg_direction;
10369
10370       break;
10371     }
10372
10373     case CA_EXIT_PLAYER:
10374     {
10375       for (i = 0; i < MAX_PLAYERS; i++)
10376         if (action_arg_player_bits & (1 << i))
10377           ExitPlayer(&stored_player[i]);
10378
10379       if (game.players_still_needed == 0)
10380         LevelSolved();
10381
10382       break;
10383     }
10384
10385     case CA_KILL_PLAYER:
10386     {
10387       for (i = 0; i < MAX_PLAYERS; i++)
10388         if (action_arg_player_bits & (1 << i))
10389           KillPlayer(&stored_player[i]);
10390
10391       break;
10392     }
10393
10394     case CA_SET_PLAYER_KEYS:
10395     {
10396       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10397       int element = getSpecialActionElement(action_arg_element,
10398                                             action_arg_number, EL_KEY_1);
10399
10400       if (IS_KEY(element))
10401       {
10402         for (i = 0; i < MAX_PLAYERS; i++)
10403         {
10404           if (trigger_player_bits & (1 << i))
10405           {
10406             stored_player[i].key[KEY_NR(element)] = key_state;
10407
10408             DrawGameDoorValues();
10409           }
10410         }
10411       }
10412
10413       break;
10414     }
10415
10416     case CA_SET_PLAYER_SPEED:
10417     {
10418       for (i = 0; i < MAX_PLAYERS; i++)
10419       {
10420         if (trigger_player_bits & (1 << i))
10421         {
10422           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10423
10424           if (action_arg == CA_ARG_SPEED_FASTER &&
10425               stored_player[i].cannot_move)
10426           {
10427             action_arg_number = STEPSIZE_VERY_SLOW;
10428           }
10429           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10430                    action_arg == CA_ARG_SPEED_FASTER)
10431           {
10432             action_arg_number = 2;
10433             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10434                            CA_MODE_MULTIPLY);
10435           }
10436           else if (action_arg == CA_ARG_NUMBER_RESET)
10437           {
10438             action_arg_number = level.initial_player_stepsize[i];
10439           }
10440
10441           move_stepsize =
10442             getModifiedActionNumber(move_stepsize,
10443                                     action_mode,
10444                                     action_arg_number,
10445                                     action_arg_number_min,
10446                                     action_arg_number_max);
10447
10448           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10449         }
10450       }
10451
10452       break;
10453     }
10454
10455     case CA_SET_PLAYER_SHIELD:
10456     {
10457       for (i = 0; i < MAX_PLAYERS; i++)
10458       {
10459         if (trigger_player_bits & (1 << i))
10460         {
10461           if (action_arg == CA_ARG_SHIELD_OFF)
10462           {
10463             stored_player[i].shield_normal_time_left = 0;
10464             stored_player[i].shield_deadly_time_left = 0;
10465           }
10466           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10467           {
10468             stored_player[i].shield_normal_time_left = 999999;
10469           }
10470           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10471           {
10472             stored_player[i].shield_normal_time_left = 999999;
10473             stored_player[i].shield_deadly_time_left = 999999;
10474           }
10475         }
10476       }
10477
10478       break;
10479     }
10480
10481     case CA_SET_PLAYER_GRAVITY:
10482     {
10483       for (i = 0; i < MAX_PLAYERS; i++)
10484       {
10485         if (trigger_player_bits & (1 << i))
10486         {
10487           stored_player[i].gravity =
10488             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10489              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10490              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10491              stored_player[i].gravity);
10492         }
10493       }
10494
10495       break;
10496     }
10497
10498     case CA_SET_PLAYER_ARTWORK:
10499     {
10500       for (i = 0; i < MAX_PLAYERS; i++)
10501       {
10502         if (trigger_player_bits & (1 << i))
10503         {
10504           int artwork_element = action_arg_element;
10505
10506           if (action_arg == CA_ARG_ELEMENT_RESET)
10507             artwork_element =
10508               (level.use_artwork_element[i] ? level.artwork_element[i] :
10509                stored_player[i].element_nr);
10510
10511           if (stored_player[i].artwork_element != artwork_element)
10512             stored_player[i].Frame = 0;
10513
10514           stored_player[i].artwork_element = artwork_element;
10515
10516           SetPlayerWaiting(&stored_player[i], FALSE);
10517
10518           // set number of special actions for bored and sleeping animation
10519           stored_player[i].num_special_action_bored =
10520             get_num_special_action(artwork_element,
10521                                    ACTION_BORING_1, ACTION_BORING_LAST);
10522           stored_player[i].num_special_action_sleeping =
10523             get_num_special_action(artwork_element,
10524                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10525         }
10526       }
10527
10528       break;
10529     }
10530
10531     case CA_SET_PLAYER_INVENTORY:
10532     {
10533       for (i = 0; i < MAX_PLAYERS; i++)
10534       {
10535         struct PlayerInfo *player = &stored_player[i];
10536         int j, k;
10537
10538         if (trigger_player_bits & (1 << i))
10539         {
10540           int inventory_element = action_arg_element;
10541
10542           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10543               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10544               action_arg == CA_ARG_ELEMENT_ACTION)
10545           {
10546             int element = inventory_element;
10547             int collect_count = element_info[element].collect_count_initial;
10548
10549             if (!IS_CUSTOM_ELEMENT(element))
10550               collect_count = 1;
10551
10552             if (collect_count == 0)
10553               player->inventory_infinite_element = element;
10554             else
10555               for (k = 0; k < collect_count; k++)
10556                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10557                   player->inventory_element[player->inventory_size++] =
10558                     element;
10559           }
10560           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10561                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10562                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10563           {
10564             if (player->inventory_infinite_element != EL_UNDEFINED &&
10565                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10566                                      action_arg_element_raw))
10567               player->inventory_infinite_element = EL_UNDEFINED;
10568
10569             for (k = 0, j = 0; j < player->inventory_size; j++)
10570             {
10571               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10572                                         action_arg_element_raw))
10573                 player->inventory_element[k++] = player->inventory_element[j];
10574             }
10575
10576             player->inventory_size = k;
10577           }
10578           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10579           {
10580             if (player->inventory_size > 0)
10581             {
10582               for (j = 0; j < player->inventory_size - 1; j++)
10583                 player->inventory_element[j] = player->inventory_element[j + 1];
10584
10585               player->inventory_size--;
10586             }
10587           }
10588           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10589           {
10590             if (player->inventory_size > 0)
10591               player->inventory_size--;
10592           }
10593           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10594           {
10595             player->inventory_infinite_element = EL_UNDEFINED;
10596             player->inventory_size = 0;
10597           }
10598           else if (action_arg == CA_ARG_INVENTORY_RESET)
10599           {
10600             player->inventory_infinite_element = EL_UNDEFINED;
10601             player->inventory_size = 0;
10602
10603             if (level.use_initial_inventory[i])
10604             {
10605               for (j = 0; j < level.initial_inventory_size[i]; j++)
10606               {
10607                 int element = level.initial_inventory_content[i][j];
10608                 int collect_count = element_info[element].collect_count_initial;
10609
10610                 if (!IS_CUSTOM_ELEMENT(element))
10611                   collect_count = 1;
10612
10613                 if (collect_count == 0)
10614                   player->inventory_infinite_element = element;
10615                 else
10616                   for (k = 0; k < collect_count; k++)
10617                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10618                       player->inventory_element[player->inventory_size++] =
10619                         element;
10620               }
10621             }
10622           }
10623         }
10624       }
10625
10626       break;
10627     }
10628
10629     // ---------- CE actions  -------------------------------------------------
10630
10631     case CA_SET_CE_VALUE:
10632     {
10633       int last_ce_value = CustomValue[x][y];
10634
10635       CustomValue[x][y] = action_arg_number_new;
10636
10637       if (CustomValue[x][y] != last_ce_value)
10638       {
10639         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10640         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10641
10642         if (CustomValue[x][y] == 0)
10643         {
10644           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10645           ChangeCount[x][y] = 0;        // allow at least one more change
10646
10647           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10648           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10649         }
10650       }
10651
10652       break;
10653     }
10654
10655     case CA_SET_CE_SCORE:
10656     {
10657       int last_ce_score = ei->collect_score;
10658
10659       ei->collect_score = action_arg_number_new;
10660
10661       if (ei->collect_score != last_ce_score)
10662       {
10663         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10664         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10665
10666         if (ei->collect_score == 0)
10667         {
10668           int xx, yy;
10669
10670           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10671           ChangeCount[x][y] = 0;        // allow at least one more change
10672
10673           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10674           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10675
10676           /*
10677             This is a very special case that seems to be a mixture between
10678             CheckElementChange() and CheckTriggeredElementChange(): while
10679             the first one only affects single elements that are triggered
10680             directly, the second one affects multiple elements in the playfield
10681             that are triggered indirectly by another element. This is a third
10682             case: Changing the CE score always affects multiple identical CEs,
10683             so every affected CE must be checked, not only the single CE for
10684             which the CE score was changed in the first place (as every instance
10685             of that CE shares the same CE score, and therefore also can change)!
10686           */
10687           SCAN_PLAYFIELD(xx, yy)
10688           {
10689             if (Tile[xx][yy] == element)
10690               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10691                                  CE_SCORE_GETS_ZERO);
10692           }
10693         }
10694       }
10695
10696       break;
10697     }
10698
10699     case CA_SET_CE_ARTWORK:
10700     {
10701       int artwork_element = action_arg_element;
10702       boolean reset_frame = FALSE;
10703       int xx, yy;
10704
10705       if (action_arg == CA_ARG_ELEMENT_RESET)
10706         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10707                            element);
10708
10709       if (ei->gfx_element != artwork_element)
10710         reset_frame = TRUE;
10711
10712       ei->gfx_element = artwork_element;
10713
10714       SCAN_PLAYFIELD(xx, yy)
10715       {
10716         if (Tile[xx][yy] == element)
10717         {
10718           if (reset_frame)
10719           {
10720             ResetGfxAnimation(xx, yy);
10721             ResetRandomAnimationValue(xx, yy);
10722           }
10723
10724           TEST_DrawLevelField(xx, yy);
10725         }
10726       }
10727
10728       break;
10729     }
10730
10731     // ---------- engine actions  ---------------------------------------------
10732
10733     case CA_SET_ENGINE_SCAN_MODE:
10734     {
10735       InitPlayfieldScanMode(action_arg);
10736
10737       break;
10738     }
10739
10740     default:
10741       break;
10742   }
10743 }
10744
10745 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10746 {
10747   int old_element = Tile[x][y];
10748   int new_element = GetElementFromGroupElement(element);
10749   int previous_move_direction = MovDir[x][y];
10750   int last_ce_value = CustomValue[x][y];
10751   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10752   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10753   boolean add_player_onto_element = (new_element_is_player &&
10754                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10755                                      IS_WALKABLE(old_element));
10756
10757   if (!add_player_onto_element)
10758   {
10759     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10760       RemoveMovingField(x, y);
10761     else
10762       RemoveField(x, y);
10763
10764     Tile[x][y] = new_element;
10765
10766     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10767       MovDir[x][y] = previous_move_direction;
10768
10769     if (element_info[new_element].use_last_ce_value)
10770       CustomValue[x][y] = last_ce_value;
10771
10772     InitField_WithBug1(x, y, FALSE);
10773
10774     new_element = Tile[x][y];   // element may have changed
10775
10776     ResetGfxAnimation(x, y);
10777     ResetRandomAnimationValue(x, y);
10778
10779     TEST_DrawLevelField(x, y);
10780
10781     if (GFX_CRUMBLED(new_element))
10782       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10783
10784     if (old_element == EL_EXPLOSION)
10785     {
10786       Store[x][y] = Store2[x][y] = 0;
10787
10788       // check if new element replaces an exploding player, requiring cleanup
10789       if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
10790         StorePlayer[x][y] = 0;
10791     }
10792
10793     // check if element under the player changes from accessible to unaccessible
10794     // (needed for special case of dropping element which then changes)
10795     // (must be checked after creating new element for walkable group elements)
10796     if (IS_PLAYER(x, y) && !player_explosion_protected &&
10797         IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10798     {
10799       KillPlayer(PLAYERINFO(x, y));
10800
10801       return;
10802     }
10803   }
10804
10805   // "ChangeCount" not set yet to allow "entered by player" change one time
10806   if (new_element_is_player)
10807     RelocatePlayer(x, y, new_element);
10808
10809   if (is_change)
10810     ChangeCount[x][y]++;        // count number of changes in the same frame
10811
10812   TestIfBadThingTouchesPlayer(x, y);
10813   TestIfPlayerTouchesCustomElement(x, y);
10814   TestIfElementTouchesCustomElement(x, y);
10815 }
10816
10817 static void CreateField(int x, int y, int element)
10818 {
10819   CreateFieldExt(x, y, element, FALSE);
10820 }
10821
10822 static void CreateElementFromChange(int x, int y, int element)
10823 {
10824   element = GET_VALID_RUNTIME_ELEMENT(element);
10825
10826   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10827   {
10828     int old_element = Tile[x][y];
10829
10830     // prevent changed element from moving in same engine frame
10831     // unless both old and new element can either fall or move
10832     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10833         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10834       Stop[x][y] = TRUE;
10835   }
10836
10837   CreateFieldExt(x, y, element, TRUE);
10838 }
10839
10840 static boolean ChangeElement(int x, int y, int element, int page)
10841 {
10842   struct ElementInfo *ei = &element_info[element];
10843   struct ElementChangeInfo *change = &ei->change_page[page];
10844   int ce_value = CustomValue[x][y];
10845   int ce_score = ei->collect_score;
10846   int target_element;
10847   int old_element = Tile[x][y];
10848
10849   // always use default change event to prevent running into a loop
10850   if (ChangeEvent[x][y] == -1)
10851     ChangeEvent[x][y] = CE_DELAY;
10852
10853   if (ChangeEvent[x][y] == CE_DELAY)
10854   {
10855     // reset actual trigger element, trigger player and action element
10856     change->actual_trigger_element = EL_EMPTY;
10857     change->actual_trigger_player = EL_EMPTY;
10858     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10859     change->actual_trigger_side = CH_SIDE_NONE;
10860     change->actual_trigger_ce_value = 0;
10861     change->actual_trigger_ce_score = 0;
10862     change->actual_trigger_x = -1;
10863     change->actual_trigger_y = -1;
10864   }
10865
10866   // do not change elements more than a specified maximum number of changes
10867   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10868     return FALSE;
10869
10870   ChangeCount[x][y]++;          // count number of changes in the same frame
10871
10872   if (ei->has_anim_event)
10873     HandleGlobalAnimEventByElementChange(element, page, x, y,
10874                                          change->actual_trigger_x,
10875                                          change->actual_trigger_y);
10876
10877   if (change->explode)
10878   {
10879     Bang(x, y);
10880
10881     return TRUE;
10882   }
10883
10884   if (change->use_target_content)
10885   {
10886     boolean complete_replace = TRUE;
10887     boolean can_replace[3][3];
10888     int xx, yy;
10889
10890     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10891     {
10892       boolean is_empty;
10893       boolean is_walkable;
10894       boolean is_diggable;
10895       boolean is_collectible;
10896       boolean is_removable;
10897       boolean is_destructible;
10898       int ex = x + xx - 1;
10899       int ey = y + yy - 1;
10900       int content_element = change->target_content.e[xx][yy];
10901       int e;
10902
10903       can_replace[xx][yy] = TRUE;
10904
10905       if (ex == x && ey == y)   // do not check changing element itself
10906         continue;
10907
10908       if (content_element == EL_EMPTY_SPACE)
10909       {
10910         can_replace[xx][yy] = FALSE;    // do not replace border with space
10911
10912         continue;
10913       }
10914
10915       if (!IN_LEV_FIELD(ex, ey))
10916       {
10917         can_replace[xx][yy] = FALSE;
10918         complete_replace = FALSE;
10919
10920         continue;
10921       }
10922
10923       e = Tile[ex][ey];
10924
10925       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10926         e = MovingOrBlocked2Element(ex, ey);
10927
10928       is_empty = (IS_FREE(ex, ey) ||
10929                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10930
10931       is_walkable     = (is_empty || IS_WALKABLE(e));
10932       is_diggable     = (is_empty || IS_DIGGABLE(e));
10933       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10934       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10935       is_removable    = (is_diggable || is_collectible);
10936
10937       can_replace[xx][yy] =
10938         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10939           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10940           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10941           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10942           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10943           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10944          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10945
10946       if (!can_replace[xx][yy])
10947         complete_replace = FALSE;
10948     }
10949
10950     if (!change->only_if_complete || complete_replace)
10951     {
10952       boolean something_has_changed = FALSE;
10953
10954       if (change->only_if_complete && change->use_random_replace &&
10955           RND(100) < change->random_percentage)
10956         return FALSE;
10957
10958       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10959       {
10960         int ex = x + xx - 1;
10961         int ey = y + yy - 1;
10962         int content_element;
10963
10964         if (can_replace[xx][yy] && (!change->use_random_replace ||
10965                                     RND(100) < change->random_percentage))
10966         {
10967           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10968             RemoveMovingField(ex, ey);
10969
10970           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10971
10972           content_element = change->target_content.e[xx][yy];
10973           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10974                                               ce_value, ce_score);
10975
10976           CreateElementFromChange(ex, ey, target_element);
10977
10978           something_has_changed = TRUE;
10979
10980           // for symmetry reasons, freeze newly created border elements
10981           if (ex != x || ey != y)
10982             Stop[ex][ey] = TRUE;        // no more moving in this frame
10983         }
10984       }
10985
10986       if (something_has_changed)
10987       {
10988         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10989         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10990       }
10991     }
10992   }
10993   else
10994   {
10995     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10996                                         ce_value, ce_score);
10997
10998     if (element == EL_DIAGONAL_GROWING ||
10999         element == EL_DIAGONAL_SHRINKING)
11000     {
11001       target_element = Store[x][y];
11002
11003       Store[x][y] = EL_EMPTY;
11004     }
11005
11006     // special case: element changes to player (and may be kept if walkable)
11007     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
11008       CreateElementFromChange(x, y, EL_EMPTY);
11009
11010     CreateElementFromChange(x, y, target_element);
11011
11012     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
11013     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
11014   }
11015
11016   // this uses direct change before indirect change
11017   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
11018
11019   return TRUE;
11020 }
11021
11022 static void HandleElementChange(int x, int y, int page)
11023 {
11024   int element = MovingOrBlocked2Element(x, y);
11025   struct ElementInfo *ei = &element_info[element];
11026   struct ElementChangeInfo *change = &ei->change_page[page];
11027   boolean handle_action_before_change = FALSE;
11028
11029 #ifdef DEBUG
11030   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
11031       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
11032   {
11033     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
11034           x, y, element, element_info[element].token_name);
11035     Debug("game:playing:HandleElementChange", "This should never happen!");
11036   }
11037 #endif
11038
11039   // this can happen with classic bombs on walkable, changing elements
11040   if (!CAN_CHANGE_OR_HAS_ACTION(element))
11041   {
11042     return;
11043   }
11044
11045   if (ChangeDelay[x][y] == 0)           // initialize element change
11046   {
11047     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
11048
11049     if (change->can_change)
11050     {
11051       // !!! not clear why graphic animation should be reset at all here !!!
11052       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
11053       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
11054
11055       /*
11056         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
11057
11058         When using an animation frame delay of 1 (this only happens with
11059         "sp_zonk.moving.left/right" in the classic graphics), the default
11060         (non-moving) animation shows wrong animation frames (while the
11061         moving animation, like "sp_zonk.moving.left/right", is correct,
11062         so this graphical bug never shows up with the classic graphics).
11063         For an animation with 4 frames, this causes wrong frames 0,0,1,2
11064         be drawn instead of the correct frames 0,1,2,3. This is caused by
11065         "GfxFrame[][]" being reset *twice* (in two successive frames) after
11066         an element change: First when the change delay ("ChangeDelay[][]")
11067         counter has reached zero after decrementing, then a second time in
11068         the next frame (after "GfxFrame[][]" was already incremented) when
11069         "ChangeDelay[][]" is reset to the initial delay value again.
11070
11071         This causes frame 0 to be drawn twice, while the last frame won't
11072         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
11073
11074         As some animations may already be cleverly designed around this bug
11075         (at least the "Snake Bite" snake tail animation does this), it cannot
11076         simply be fixed here without breaking such existing animations.
11077         Unfortunately, it cannot easily be detected if a graphics set was
11078         designed "before" or "after" the bug was fixed. As a workaround,
11079         a new graphics set option "game.graphics_engine_version" was added
11080         to be able to specify the game's major release version for which the
11081         graphics set was designed, which can then be used to decide if the
11082         bugfix should be used (version 4 and above) or not (version 3 or
11083         below, or if no version was specified at all, as with old sets).
11084
11085         (The wrong/fixed animation frames can be tested with the test level set
11086         "test_gfxframe" and level "000", which contains a specially prepared
11087         custom element at level position (x/y) == (11/9) which uses the zonk
11088         animation mentioned above. Using "game.graphics_engine_version: 4"
11089         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
11090         This can also be seen from the debug output for this test element.)
11091       */
11092
11093       // when a custom element is about to change (for example by change delay),
11094       // do not reset graphic animation when the custom element is moving
11095       if (game.graphics_engine_version < 4 &&
11096           !IS_MOVING(x, y))
11097       {
11098         ResetGfxAnimation(x, y);
11099         ResetRandomAnimationValue(x, y);
11100       }
11101
11102       if (change->pre_change_function)
11103         change->pre_change_function(x, y);
11104     }
11105   }
11106
11107   ChangeDelay[x][y]--;
11108
11109   if (ChangeDelay[x][y] != 0)           // continue element change
11110   {
11111     int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11112
11113     // also needed if CE can not change, but has CE delay with CE action
11114     if (IS_ANIMATED(graphic))
11115       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11116
11117     if (change->can_change)
11118     {
11119       if (change->change_function)
11120         change->change_function(x, y);
11121     }
11122   }
11123   else                                  // finish element change
11124   {
11125     if (ChangePage[x][y] != -1)         // remember page from delayed change
11126     {
11127       page = ChangePage[x][y];
11128       ChangePage[x][y] = -1;
11129
11130       change = &ei->change_page[page];
11131     }
11132
11133     if (IS_MOVING(x, y))                // never change a running system ;-)
11134     {
11135       ChangeDelay[x][y] = 1;            // try change after next move step
11136       ChangePage[x][y] = page;          // remember page to use for change
11137
11138       return;
11139     }
11140
11141     // special case: set new level random seed before changing element
11142     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
11143       handle_action_before_change = TRUE;
11144
11145     if (change->has_action && handle_action_before_change)
11146       ExecuteCustomElementAction(x, y, element, page);
11147
11148     if (change->can_change)
11149     {
11150       if (ChangeElement(x, y, element, page))
11151       {
11152         if (change->post_change_function)
11153           change->post_change_function(x, y);
11154       }
11155     }
11156
11157     if (change->has_action && !handle_action_before_change)
11158       ExecuteCustomElementAction(x, y, element, page);
11159   }
11160 }
11161
11162 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
11163                                               int trigger_element,
11164                                               int trigger_event,
11165                                               int trigger_player,
11166                                               int trigger_side,
11167                                               int trigger_page)
11168 {
11169   boolean change_done_any = FALSE;
11170   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
11171   int i;
11172
11173   if (!(trigger_events[trigger_element][trigger_event]))
11174     return FALSE;
11175
11176   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11177
11178   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
11179   {
11180     int element = EL_CUSTOM_START + i;
11181     boolean change_done = FALSE;
11182     int p;
11183
11184     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11185         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11186       continue;
11187
11188     for (p = 0; p < element_info[element].num_change_pages; p++)
11189     {
11190       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11191
11192       if (change->can_change_or_has_action &&
11193           change->has_event[trigger_event] &&
11194           change->trigger_side & trigger_side &&
11195           change->trigger_player & trigger_player &&
11196           change->trigger_page & trigger_page_bits &&
11197           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11198       {
11199         change->actual_trigger_element = trigger_element;
11200         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11201         change->actual_trigger_player_bits = trigger_player;
11202         change->actual_trigger_side = trigger_side;
11203         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11204         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11205         change->actual_trigger_x = trigger_x;
11206         change->actual_trigger_y = trigger_y;
11207
11208         if ((change->can_change && !change_done) || change->has_action)
11209         {
11210           int x, y;
11211
11212           SCAN_PLAYFIELD(x, y)
11213           {
11214             if (Tile[x][y] == element)
11215             {
11216               if (change->can_change && !change_done)
11217               {
11218                 // if element already changed in this frame, not only prevent
11219                 // another element change (checked in ChangeElement()), but
11220                 // also prevent additional element actions for this element
11221
11222                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11223                     !level.use_action_after_change_bug)
11224                   continue;
11225
11226                 ChangeDelay[x][y] = 1;
11227                 ChangeEvent[x][y] = trigger_event;
11228
11229                 HandleElementChange(x, y, p);
11230               }
11231               else if (change->has_action)
11232               {
11233                 // if element already changed in this frame, not only prevent
11234                 // another element change (checked in ChangeElement()), but
11235                 // also prevent additional element actions for this element
11236
11237                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11238                     !level.use_action_after_change_bug)
11239                   continue;
11240
11241                 ExecuteCustomElementAction(x, y, element, p);
11242                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11243               }
11244             }
11245           }
11246
11247           if (change->can_change)
11248           {
11249             change_done = TRUE;
11250             change_done_any = TRUE;
11251           }
11252         }
11253       }
11254     }
11255   }
11256
11257   RECURSION_LOOP_DETECTION_END();
11258
11259   return change_done_any;
11260 }
11261
11262 static boolean CheckElementChangeExt(int x, int y,
11263                                      int element,
11264                                      int trigger_element,
11265                                      int trigger_event,
11266                                      int trigger_player,
11267                                      int trigger_side)
11268 {
11269   boolean change_done = FALSE;
11270   int p;
11271
11272   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11273       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11274     return FALSE;
11275
11276   if (Tile[x][y] == EL_BLOCKED)
11277   {
11278     Blocked2Moving(x, y, &x, &y);
11279     element = Tile[x][y];
11280   }
11281
11282   // check if element has already changed or is about to change after moving
11283   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11284        Tile[x][y] != element) ||
11285
11286       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11287        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11288         ChangePage[x][y] != -1)))
11289     return FALSE;
11290
11291   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11292
11293   for (p = 0; p < element_info[element].num_change_pages; p++)
11294   {
11295     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11296
11297     /* check trigger element for all events where the element that is checked
11298        for changing interacts with a directly adjacent element -- this is
11299        different to element changes that affect other elements to change on the
11300        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11301     boolean check_trigger_element =
11302       (trigger_event == CE_NEXT_TO_X ||
11303        trigger_event == CE_TOUCHING_X ||
11304        trigger_event == CE_HITTING_X ||
11305        trigger_event == CE_HIT_BY_X ||
11306        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11307
11308     if (change->can_change_or_has_action &&
11309         change->has_event[trigger_event] &&
11310         change->trigger_side & trigger_side &&
11311         change->trigger_player & trigger_player &&
11312         (!check_trigger_element ||
11313          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11314     {
11315       change->actual_trigger_element = trigger_element;
11316       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11317       change->actual_trigger_player_bits = trigger_player;
11318       change->actual_trigger_side = trigger_side;
11319       change->actual_trigger_ce_value = CustomValue[x][y];
11320       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11321       change->actual_trigger_x = x;
11322       change->actual_trigger_y = y;
11323
11324       // special case: trigger element not at (x,y) position for some events
11325       if (check_trigger_element)
11326       {
11327         static struct
11328         {
11329           int dx, dy;
11330         } move_xy[] =
11331           {
11332             {  0,  0 },
11333             { -1,  0 },
11334             { +1,  0 },
11335             {  0,  0 },
11336             {  0, -1 },
11337             {  0,  0 }, { 0, 0 }, { 0, 0 },
11338             {  0, +1 }
11339           };
11340
11341         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11342         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11343
11344         change->actual_trigger_ce_value = CustomValue[xx][yy];
11345         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11346         change->actual_trigger_x = xx;
11347         change->actual_trigger_y = yy;
11348       }
11349
11350       if (change->can_change && !change_done)
11351       {
11352         ChangeDelay[x][y] = 1;
11353         ChangeEvent[x][y] = trigger_event;
11354
11355         HandleElementChange(x, y, p);
11356
11357         change_done = TRUE;
11358       }
11359       else if (change->has_action)
11360       {
11361         ExecuteCustomElementAction(x, y, element, p);
11362         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11363       }
11364     }
11365   }
11366
11367   RECURSION_LOOP_DETECTION_END();
11368
11369   return change_done;
11370 }
11371
11372 static void PlayPlayerSound(struct PlayerInfo *player)
11373 {
11374   int jx = player->jx, jy = player->jy;
11375   int sound_element = player->artwork_element;
11376   int last_action = player->last_action_waiting;
11377   int action = player->action_waiting;
11378
11379   if (player->is_waiting)
11380   {
11381     if (action != last_action)
11382       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11383     else
11384       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11385   }
11386   else
11387   {
11388     if (action != last_action)
11389       StopSound(element_info[sound_element].sound[last_action]);
11390
11391     if (last_action == ACTION_SLEEPING)
11392       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11393   }
11394 }
11395
11396 static void PlayAllPlayersSound(void)
11397 {
11398   int i;
11399
11400   for (i = 0; i < MAX_PLAYERS; i++)
11401     if (stored_player[i].active)
11402       PlayPlayerSound(&stored_player[i]);
11403 }
11404
11405 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11406 {
11407   boolean last_waiting = player->is_waiting;
11408   int move_dir = player->MovDir;
11409
11410   player->dir_waiting = move_dir;
11411   player->last_action_waiting = player->action_waiting;
11412
11413   if (is_waiting)
11414   {
11415     if (!last_waiting)          // not waiting -> waiting
11416     {
11417       player->is_waiting = TRUE;
11418
11419       player->frame_counter_bored =
11420         FrameCounter +
11421         game.player_boring_delay_fixed +
11422         GetSimpleRandom(game.player_boring_delay_random);
11423       player->frame_counter_sleeping =
11424         FrameCounter +
11425         game.player_sleeping_delay_fixed +
11426         GetSimpleRandom(game.player_sleeping_delay_random);
11427
11428       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11429     }
11430
11431     if (game.player_sleeping_delay_fixed +
11432         game.player_sleeping_delay_random > 0 &&
11433         player->anim_delay_counter == 0 &&
11434         player->post_delay_counter == 0 &&
11435         FrameCounter >= player->frame_counter_sleeping)
11436       player->is_sleeping = TRUE;
11437     else if (game.player_boring_delay_fixed +
11438              game.player_boring_delay_random > 0 &&
11439              FrameCounter >= player->frame_counter_bored)
11440       player->is_bored = TRUE;
11441
11442     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11443                               player->is_bored ? ACTION_BORING :
11444                               ACTION_WAITING);
11445
11446     if (player->is_sleeping && player->use_murphy)
11447     {
11448       // special case for sleeping Murphy when leaning against non-free tile
11449
11450       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11451           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11452            !IS_MOVING(player->jx - 1, player->jy)))
11453         move_dir = MV_LEFT;
11454       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11455                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11456                 !IS_MOVING(player->jx + 1, player->jy)))
11457         move_dir = MV_RIGHT;
11458       else
11459         player->is_sleeping = FALSE;
11460
11461       player->dir_waiting = move_dir;
11462     }
11463
11464     if (player->is_sleeping)
11465     {
11466       if (player->num_special_action_sleeping > 0)
11467       {
11468         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11469         {
11470           int last_special_action = player->special_action_sleeping;
11471           int num_special_action = player->num_special_action_sleeping;
11472           int special_action =
11473             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11474              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11475              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11476              last_special_action + 1 : ACTION_SLEEPING);
11477           int special_graphic =
11478             el_act_dir2img(player->artwork_element, special_action, move_dir);
11479
11480           player->anim_delay_counter =
11481             graphic_info[special_graphic].anim_delay_fixed +
11482             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11483           player->post_delay_counter =
11484             graphic_info[special_graphic].post_delay_fixed +
11485             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11486
11487           player->special_action_sleeping = special_action;
11488         }
11489
11490         if (player->anim_delay_counter > 0)
11491         {
11492           player->action_waiting = player->special_action_sleeping;
11493           player->anim_delay_counter--;
11494         }
11495         else if (player->post_delay_counter > 0)
11496         {
11497           player->post_delay_counter--;
11498         }
11499       }
11500     }
11501     else if (player->is_bored)
11502     {
11503       if (player->num_special_action_bored > 0)
11504       {
11505         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11506         {
11507           int special_action =
11508             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11509           int special_graphic =
11510             el_act_dir2img(player->artwork_element, special_action, move_dir);
11511
11512           player->anim_delay_counter =
11513             graphic_info[special_graphic].anim_delay_fixed +
11514             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11515           player->post_delay_counter =
11516             graphic_info[special_graphic].post_delay_fixed +
11517             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11518
11519           player->special_action_bored = special_action;
11520         }
11521
11522         if (player->anim_delay_counter > 0)
11523         {
11524           player->action_waiting = player->special_action_bored;
11525           player->anim_delay_counter--;
11526         }
11527         else if (player->post_delay_counter > 0)
11528         {
11529           player->post_delay_counter--;
11530         }
11531       }
11532     }
11533   }
11534   else if (last_waiting)        // waiting -> not waiting
11535   {
11536     player->is_waiting = FALSE;
11537     player->is_bored = FALSE;
11538     player->is_sleeping = FALSE;
11539
11540     player->frame_counter_bored = -1;
11541     player->frame_counter_sleeping = -1;
11542
11543     player->anim_delay_counter = 0;
11544     player->post_delay_counter = 0;
11545
11546     player->dir_waiting = player->MovDir;
11547     player->action_waiting = ACTION_DEFAULT;
11548
11549     player->special_action_bored = ACTION_DEFAULT;
11550     player->special_action_sleeping = ACTION_DEFAULT;
11551   }
11552 }
11553
11554 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11555 {
11556   if ((!player->is_moving  && player->was_moving) ||
11557       (player->MovPos == 0 && player->was_moving) ||
11558       (player->is_snapping && !player->was_snapping) ||
11559       (player->is_dropping && !player->was_dropping))
11560   {
11561     if (!CheckSaveEngineSnapshotToList())
11562       return;
11563
11564     player->was_moving = FALSE;
11565     player->was_snapping = TRUE;
11566     player->was_dropping = TRUE;
11567   }
11568   else
11569   {
11570     if (player->is_moving)
11571       player->was_moving = TRUE;
11572
11573     if (!player->is_snapping)
11574       player->was_snapping = FALSE;
11575
11576     if (!player->is_dropping)
11577       player->was_dropping = FALSE;
11578   }
11579
11580   static struct MouseActionInfo mouse_action_last = { 0 };
11581   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11582   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11583
11584   if (new_released)
11585     CheckSaveEngineSnapshotToList();
11586
11587   mouse_action_last = mouse_action;
11588 }
11589
11590 static void CheckSingleStepMode(struct PlayerInfo *player)
11591 {
11592   if (tape.single_step && tape.recording && !tape.pausing)
11593   {
11594     // as it is called "single step mode", just return to pause mode when the
11595     // player stopped moving after one tile (or never starts moving at all)
11596     // (reverse logic needed here in case single step mode used in team mode)
11597     if (player->is_moving ||
11598         player->is_pushing ||
11599         player->is_dropping_pressed ||
11600         player->effective_mouse_action.button)
11601       game.enter_single_step_mode = FALSE;
11602   }
11603
11604   CheckSaveEngineSnapshot(player);
11605 }
11606
11607 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11608 {
11609   int left      = player_action & JOY_LEFT;
11610   int right     = player_action & JOY_RIGHT;
11611   int up        = player_action & JOY_UP;
11612   int down      = player_action & JOY_DOWN;
11613   int button1   = player_action & JOY_BUTTON_1;
11614   int button2   = player_action & JOY_BUTTON_2;
11615   int dx        = (left ? -1 : right ? 1 : 0);
11616   int dy        = (up   ? -1 : down  ? 1 : 0);
11617
11618   if (!player->active || tape.pausing)
11619     return 0;
11620
11621   if (player_action)
11622   {
11623     if (button1)
11624       SnapField(player, dx, dy);
11625     else
11626     {
11627       if (button2)
11628         DropElement(player);
11629
11630       MovePlayer(player, dx, dy);
11631     }
11632
11633     CheckSingleStepMode(player);
11634
11635     SetPlayerWaiting(player, FALSE);
11636
11637     return player_action;
11638   }
11639   else
11640   {
11641     // no actions for this player (no input at player's configured device)
11642
11643     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11644     SnapField(player, 0, 0);
11645     CheckGravityMovementWhenNotMoving(player);
11646
11647     if (player->MovPos == 0)
11648       SetPlayerWaiting(player, TRUE);
11649
11650     if (player->MovPos == 0)    // needed for tape.playing
11651       player->is_moving = FALSE;
11652
11653     player->is_dropping = FALSE;
11654     player->is_dropping_pressed = FALSE;
11655     player->drop_pressed_delay = 0;
11656
11657     CheckSingleStepMode(player);
11658
11659     return 0;
11660   }
11661 }
11662
11663 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11664                                          byte *tape_action)
11665 {
11666   if (!tape.use_mouse_actions)
11667     return;
11668
11669   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11670   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11671   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11672 }
11673
11674 static void SetTapeActionFromMouseAction(byte *tape_action,
11675                                          struct MouseActionInfo *mouse_action)
11676 {
11677   if (!tape.use_mouse_actions)
11678     return;
11679
11680   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11681   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11682   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11683 }
11684
11685 static void CheckLevelSolved(void)
11686 {
11687   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11688   {
11689     if (game_bd.level_solved &&
11690         !game_bd.game_over)                             // game won
11691     {
11692       LevelSolved();
11693
11694       game_bd.game_over = TRUE;
11695
11696       game.all_players_gone = TRUE;
11697     }
11698
11699     if (game_bd.game_over)                              // game lost
11700       game.all_players_gone = TRUE;
11701   }
11702   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11703   {
11704     if (game_em.level_solved &&
11705         !game_em.game_over)                             // game won
11706     {
11707       LevelSolved();
11708
11709       game_em.game_over = TRUE;
11710
11711       game.all_players_gone = TRUE;
11712     }
11713
11714     if (game_em.game_over)                              // game lost
11715       game.all_players_gone = TRUE;
11716   }
11717   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11718   {
11719     if (game_sp.level_solved &&
11720         !game_sp.game_over)                             // game won
11721     {
11722       LevelSolved();
11723
11724       game_sp.game_over = TRUE;
11725
11726       game.all_players_gone = TRUE;
11727     }
11728
11729     if (game_sp.game_over)                              // game lost
11730       game.all_players_gone = TRUE;
11731   }
11732   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11733   {
11734     if (game_mm.level_solved &&
11735         !game_mm.game_over)                             // game won
11736     {
11737       LevelSolved();
11738
11739       game_mm.game_over = TRUE;
11740
11741       game.all_players_gone = TRUE;
11742     }
11743
11744     if (game_mm.game_over)                              // game lost
11745       game.all_players_gone = TRUE;
11746   }
11747 }
11748
11749 static void PlayTimeoutSound(int seconds_left)
11750 {
11751   // will be played directly by BD engine (for classic bonus time sounds)
11752   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && checkBonusTime_BD())
11753     return;
11754
11755   // try to use individual "running out of time" sound for each second left
11756   int sound = SND_GAME_RUNNING_OUT_OF_TIME_0 - seconds_left;
11757
11758   // if special sound per second not defined, use default sound
11759   if (getSoundInfoEntryFilename(sound) == NULL)
11760     sound = SND_GAME_RUNNING_OUT_OF_TIME;
11761
11762   // if out of time, but player still alive, play special "timeout" sound, if defined
11763   if (seconds_left == 0 && !checkGameFailed())
11764     if (getSoundInfoEntryFilename(SND_GAME_TIMEOUT) != NULL)
11765       sound = SND_GAME_TIMEOUT;
11766
11767   PlaySound(sound);
11768 }
11769
11770 static void CheckLevelTime_StepCounter(void)
11771 {
11772   int i;
11773
11774   TimePlayed++;
11775
11776   if (TimeLeft > 0)
11777   {
11778     TimeLeft--;
11779
11780     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11781       PlayTimeoutSound(TimeLeft);
11782
11783     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11784
11785     DisplayGameControlValues();
11786
11787     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11788       for (i = 0; i < MAX_PLAYERS; i++)
11789         KillPlayer(&stored_player[i]);
11790   }
11791   else if (game.no_level_time_limit && !game.all_players_gone)
11792   {
11793     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11794
11795     DisplayGameControlValues();
11796   }
11797 }
11798
11799 static void CheckLevelTime(void)
11800 {
11801   int frames_per_second = FRAMES_PER_SECOND;
11802   int i;
11803
11804   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11805   {
11806     // level time may be running slower in native BD engine
11807     frames_per_second = getFramesPerSecond_BD();
11808
11809     // if native engine time changed, force main engine time change
11810     if (getTimeLeft_BD() < TimeLeft)
11811       TimeFrames = frames_per_second;
11812
11813     // if last second running, wait for native engine time to exactly reach zero
11814     if (getTimeLeft_BD() == 1 && TimeLeft == 1)
11815       TimeFrames = frames_per_second - 1;
11816   }
11817
11818   if (TimeFrames >= frames_per_second)
11819   {
11820     TimeFrames = 0;
11821
11822     for (i = 0; i < MAX_PLAYERS; i++)
11823     {
11824       struct PlayerInfo *player = &stored_player[i];
11825
11826       if (SHIELD_ON(player))
11827       {
11828         player->shield_normal_time_left--;
11829
11830         if (player->shield_deadly_time_left > 0)
11831           player->shield_deadly_time_left--;
11832       }
11833     }
11834
11835     if (!game.LevelSolved && !level.use_step_counter)
11836     {
11837       TimePlayed++;
11838
11839       if (TimeLeft > 0)
11840       {
11841         TimeLeft--;
11842
11843         if (TimeLeft <= 10 && game.time_limit)
11844           PlayTimeoutSound(TimeLeft);
11845
11846         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11847            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11848
11849         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11850
11851         if (!TimeLeft && game.time_limit)
11852         {
11853           if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
11854           {
11855             if (game_bd.game->cave->player_state == GD_PL_LIVING)
11856               game_bd.game->cave->player_state = GD_PL_TIMEOUT;
11857           }
11858           else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11859           {
11860             game_em.lev->killed_out_of_time = TRUE;
11861           }
11862           else
11863           {
11864             for (i = 0; i < MAX_PLAYERS; i++)
11865               KillPlayer(&stored_player[i]);
11866           }
11867         }
11868       }
11869       else if (game.no_level_time_limit && !game.all_players_gone)
11870       {
11871         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11872       }
11873
11874       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11875     }
11876   }
11877
11878   if (TapeTimeFrames >= FRAMES_PER_SECOND)
11879   {
11880     TapeTimeFrames = 0;
11881     TapeTime++;
11882
11883     if (tape.recording || tape.playing)
11884       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11885   }
11886
11887   if (tape.recording || tape.playing)
11888     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11889
11890   UpdateAndDisplayGameControlValues();
11891 }
11892
11893 void AdvanceFrameAndPlayerCounters(int player_nr)
11894 {
11895   int i;
11896
11897   // handle game and tape time differently for native BD game engine
11898
11899   // tape time is running in native BD engine even if player is not hatched yet
11900   if (!checkGameRunning())
11901     return;
11902
11903   // advance frame counters (global frame counter and tape time frame counter)
11904   FrameCounter++;
11905   TapeTimeFrames++;
11906
11907   // level time is running in native BD engine after player is being hatched
11908   if (!checkGamePlaying())
11909     return;
11910
11911   // advance time frame counter (used to control available time to solve level)
11912   TimeFrames++;
11913
11914   // advance player counters (counters for move delay, move animation etc.)
11915   for (i = 0; i < MAX_PLAYERS; i++)
11916   {
11917     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11918     int move_delay_value = stored_player[i].move_delay_value;
11919     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11920
11921     if (!advance_player_counters)       // not all players may be affected
11922       continue;
11923
11924     if (move_frames == 0)       // less than one move per game frame
11925     {
11926       int stepsize = TILEX / move_delay_value;
11927       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11928       int count = (stored_player[i].is_moving ?
11929                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11930
11931       if (count % delay == 0)
11932         move_frames = 1;
11933     }
11934
11935     stored_player[i].Frame += move_frames;
11936
11937     if (stored_player[i].MovPos != 0)
11938       stored_player[i].StepFrame += move_frames;
11939
11940     if (stored_player[i].move_delay > 0)
11941       stored_player[i].move_delay--;
11942
11943     // due to bugs in previous versions, counter must count up, not down
11944     if (stored_player[i].push_delay != -1)
11945       stored_player[i].push_delay++;
11946
11947     if (stored_player[i].drop_delay > 0)
11948       stored_player[i].drop_delay--;
11949
11950     if (stored_player[i].is_dropping_pressed)
11951       stored_player[i].drop_pressed_delay++;
11952   }
11953 }
11954
11955 void AdvanceFrameCounter(void)
11956 {
11957   FrameCounter++;
11958 }
11959
11960 void AdvanceGfxFrame(void)
11961 {
11962   int x, y;
11963
11964   SCAN_PLAYFIELD(x, y)
11965   {
11966     GfxFrame[x][y]++;
11967   }
11968 }
11969
11970 static void HandleMouseAction(struct MouseActionInfo *mouse_action,
11971                               struct MouseActionInfo *mouse_action_last)
11972 {
11973   if (mouse_action->button)
11974   {
11975     int new_button = (mouse_action->button && mouse_action_last->button == 0);
11976     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action->button);
11977     int x = mouse_action->lx;
11978     int y = mouse_action->ly;
11979     int element = Tile[x][y];
11980
11981     if (new_button)
11982     {
11983       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
11984       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
11985                                          ch_button);
11986     }
11987
11988     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
11989     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
11990                                        ch_button);
11991
11992     if (level.use_step_counter)
11993     {
11994       boolean counted_click = FALSE;
11995
11996       // element clicked that can change when clicked/pressed
11997       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
11998           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
11999            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12000         counted_click = TRUE;
12001
12002       // element clicked that can trigger change when clicked/pressed
12003       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12004           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12005         counted_click = TRUE;
12006
12007       if (new_button && counted_click)
12008         CheckLevelTime_StepCounter();
12009     }
12010   }
12011 }
12012
12013 void StartGameActions(boolean init_network_game, boolean record_tape,
12014                       int random_seed)
12015 {
12016   unsigned int new_random_seed = InitRND(random_seed);
12017
12018   if (record_tape)
12019     TapeStartRecording(new_random_seed);
12020
12021   if (setup.auto_pause_on_start && !tape.pausing)
12022     TapeTogglePause(TAPE_TOGGLE_MANUAL);
12023
12024   if (init_network_game)
12025   {
12026     SendToServer_LevelFile();
12027     SendToServer_StartPlaying();
12028
12029     return;
12030   }
12031
12032   InitGame();
12033 }
12034
12035 static void GameActionsExt(void)
12036 {
12037 #if 0
12038   static unsigned int game_frame_delay = 0;
12039 #endif
12040   unsigned int game_frame_delay_value;
12041   byte *recorded_player_action;
12042   byte summarized_player_action = 0;
12043   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
12044   int i;
12045
12046   // detect endless loops, caused by custom element programming
12047   if (recursion_loop_detected && recursion_loop_depth == 0)
12048   {
12049     char *message = getStringCat3("Internal Error! Element ",
12050                                   EL_NAME(recursion_loop_element),
12051                                   " caused endless loop! Quit the game?");
12052
12053     Warn("element '%s' caused endless loop in game engine",
12054          EL_NAME(recursion_loop_element));
12055
12056     RequestQuitGameExt(program.headless, level_editor_test_game, message);
12057
12058     recursion_loop_detected = FALSE;    // if game should be continued
12059
12060     free(message);
12061
12062     return;
12063   }
12064
12065   if (game.restart_level)
12066     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
12067
12068   CheckLevelSolved();
12069
12070   if (game.LevelSolved && !game.LevelSolved_GameEnd)
12071     GameWon();
12072
12073   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
12074     TapeStop();
12075
12076   if (game_status != GAME_MODE_PLAYING)         // status might have changed
12077     return;
12078
12079   game_frame_delay_value =
12080     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
12081
12082   if (tape.playing && tape.warp_forward && !tape.pausing)
12083     game_frame_delay_value = 0;
12084
12085   SetVideoFrameDelay(game_frame_delay_value);
12086
12087   // (de)activate virtual buttons depending on current game status
12088   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
12089   {
12090     if (game.all_players_gone)  // if no players there to be controlled anymore
12091       SetOverlayActive(FALSE);
12092     else if (!tape.playing)     // if game continues after tape stopped playing
12093       SetOverlayActive(TRUE);
12094   }
12095
12096 #if 0
12097 #if 0
12098   // ---------- main game synchronization point ----------
12099
12100   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12101
12102   Debug("game:playing:skip", "skip == %d", skip);
12103
12104 #else
12105   // ---------- main game synchronization point ----------
12106
12107   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
12108 #endif
12109 #endif
12110
12111   if (network_playing && !network_player_action_received)
12112   {
12113     // try to get network player actions in time
12114
12115     // last chance to get network player actions without main loop delay
12116     HandleNetworking();
12117
12118     // game was quit by network peer
12119     if (game_status != GAME_MODE_PLAYING)
12120       return;
12121
12122     // check if network player actions still missing and game still running
12123     if (!network_player_action_received && !checkGameEnded())
12124       return;           // failed to get network player actions in time
12125
12126     // do not yet reset "network_player_action_received" (for tape.pausing)
12127   }
12128
12129   if (tape.pausing)
12130     return;
12131
12132   // at this point we know that we really continue executing the game
12133
12134   network_player_action_received = FALSE;
12135
12136   // when playing tape, read previously recorded player input from tape data
12137   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
12138
12139   local_player->effective_mouse_action = local_player->mouse_action;
12140
12141   if (recorded_player_action != NULL)
12142     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
12143                                  recorded_player_action);
12144
12145   // TapePlayAction() may return NULL when toggling to "pause before death"
12146   if (tape.pausing)
12147     return;
12148
12149   if (tape.set_centered_player)
12150   {
12151     game.centered_player_nr_next = tape.centered_player_nr_next;
12152     game.set_centered_player = TRUE;
12153   }
12154
12155   for (i = 0; i < MAX_PLAYERS; i++)
12156   {
12157     summarized_player_action |= stored_player[i].action;
12158
12159     if (!network_playing && (game.team_mode || tape.playing))
12160       stored_player[i].effective_action = stored_player[i].action;
12161   }
12162
12163   if (network_playing && !checkGameEnded())
12164     SendToServer_MovePlayer(summarized_player_action);
12165
12166   // summarize all actions at local players mapped input device position
12167   // (this allows using different input devices in single player mode)
12168   if (!network.enabled && !game.team_mode)
12169     stored_player[map_player_action[local_player->index_nr]].effective_action =
12170       summarized_player_action;
12171
12172   // summarize all actions at centered player in local team mode
12173   if (tape.recording &&
12174       setup.team_mode && !network.enabled &&
12175       setup.input_on_focus &&
12176       game.centered_player_nr != -1)
12177   {
12178     for (i = 0; i < MAX_PLAYERS; i++)
12179       stored_player[map_player_action[i]].effective_action =
12180         (i == game.centered_player_nr ? summarized_player_action : 0);
12181   }
12182
12183   if (recorded_player_action != NULL)
12184     for (i = 0; i < MAX_PLAYERS; i++)
12185       stored_player[i].effective_action = recorded_player_action[i];
12186
12187   for (i = 0; i < MAX_PLAYERS; i++)
12188   {
12189     tape_action[i] = stored_player[i].effective_action;
12190
12191     /* (this may happen in the RND game engine if a player was not present on
12192        the playfield on level start, but appeared later from a custom element */
12193     if (setup.team_mode &&
12194         tape.recording &&
12195         tape_action[i] &&
12196         !tape.player_participates[i])
12197       tape.player_participates[i] = TRUE;
12198   }
12199
12200   SetTapeActionFromMouseAction(tape_action,
12201                                &local_player->effective_mouse_action);
12202
12203   // only record actions from input devices, but not programmed actions
12204   if (tape.recording)
12205     TapeRecordAction(tape_action);
12206
12207   // remember if game was played (especially after tape stopped playing)
12208   if (!tape.playing && summarized_player_action && !checkGameFailed())
12209     game.GamePlayed = TRUE;
12210
12211 #if USE_NEW_PLAYER_ASSIGNMENTS
12212   // !!! also map player actions in single player mode !!!
12213   // if (game.team_mode)
12214   if (1)
12215   {
12216     byte mapped_action[MAX_PLAYERS];
12217
12218 #if DEBUG_PLAYER_ACTIONS
12219     for (i = 0; i < MAX_PLAYERS; i++)
12220       DebugContinued("", "%d, ", stored_player[i].effective_action);
12221 #endif
12222
12223     for (i = 0; i < MAX_PLAYERS; i++)
12224       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
12225
12226     for (i = 0; i < MAX_PLAYERS; i++)
12227       stored_player[i].effective_action = mapped_action[i];
12228
12229 #if DEBUG_PLAYER_ACTIONS
12230     DebugContinued("", "=> ");
12231     for (i = 0; i < MAX_PLAYERS; i++)
12232       DebugContinued("", "%d, ", stored_player[i].effective_action);
12233     DebugContinued("game:playing:player", "\n");
12234 #endif
12235   }
12236 #if DEBUG_PLAYER_ACTIONS
12237   else
12238   {
12239     for (i = 0; i < MAX_PLAYERS; i++)
12240       DebugContinued("", "%d, ", stored_player[i].effective_action);
12241     DebugContinued("game:playing:player", "\n");
12242   }
12243 #endif
12244 #endif
12245
12246   for (i = 0; i < MAX_PLAYERS; i++)
12247   {
12248     // allow engine snapshot in case of changed movement attempt
12249     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
12250         (stored_player[i].effective_action & KEY_MOTION))
12251       game.snapshot.changed_action = TRUE;
12252
12253     // allow engine snapshot in case of snapping/dropping attempt
12254     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
12255         (stored_player[i].effective_action & KEY_BUTTON) != 0)
12256       game.snapshot.changed_action = TRUE;
12257
12258     game.snapshot.last_action[i] = stored_player[i].effective_action;
12259   }
12260
12261   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
12262   {
12263     GameActions_BD_Main();
12264   }
12265   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
12266   {
12267     GameActions_EM_Main();
12268   }
12269   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
12270   {
12271     GameActions_SP_Main();
12272   }
12273   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
12274   {
12275     GameActions_MM_Main();
12276   }
12277   else
12278   {
12279     GameActions_RND_Main();
12280   }
12281
12282   BlitScreenToBitmap(backbuffer);
12283
12284   CheckLevelSolved();
12285   CheckLevelTime();
12286
12287   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
12288
12289   if (global.show_frames_per_second)
12290   {
12291     static unsigned int fps_counter = 0;
12292     static int fps_frames = 0;
12293     unsigned int fps_delay_ms = Counter() - fps_counter;
12294
12295     fps_frames++;
12296
12297     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
12298     {
12299       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
12300
12301       fps_frames = 0;
12302       fps_counter = Counter();
12303
12304       // always draw FPS to screen after FPS value was updated
12305       redraw_mask |= REDRAW_FPS;
12306     }
12307
12308     // only draw FPS if no screen areas are deactivated (invisible warp mode)
12309     if (GetDrawDeactivationMask() == REDRAW_NONE)
12310       redraw_mask |= REDRAW_FPS;
12311   }
12312 }
12313
12314 static void GameActions_CheckSaveEngineSnapshot(void)
12315 {
12316   if (!game.snapshot.save_snapshot)
12317     return;
12318
12319   // clear flag for saving snapshot _before_ saving snapshot
12320   game.snapshot.save_snapshot = FALSE;
12321
12322   SaveEngineSnapshotToList();
12323 }
12324
12325 void GameActions(void)
12326 {
12327   GameActionsExt();
12328
12329   GameActions_CheckSaveEngineSnapshot();
12330 }
12331
12332 void GameActions_BD_Main(void)
12333 {
12334   byte effective_action[MAX_PLAYERS];
12335   int i;
12336
12337   for (i = 0; i < MAX_PLAYERS; i++)
12338     effective_action[i] = stored_player[i].effective_action;
12339
12340   GameActions_BD(effective_action);
12341 }
12342
12343 void GameActions_EM_Main(void)
12344 {
12345   byte effective_action[MAX_PLAYERS];
12346   int i;
12347
12348   for (i = 0; i < MAX_PLAYERS; i++)
12349     effective_action[i] = stored_player[i].effective_action;
12350
12351   GameActions_EM(effective_action);
12352 }
12353
12354 void GameActions_SP_Main(void)
12355 {
12356   byte effective_action[MAX_PLAYERS];
12357   int i;
12358
12359   for (i = 0; i < MAX_PLAYERS; i++)
12360     effective_action[i] = stored_player[i].effective_action;
12361
12362   GameActions_SP(effective_action);
12363
12364   for (i = 0; i < MAX_PLAYERS; i++)
12365   {
12366     if (stored_player[i].force_dropping)
12367       stored_player[i].action |= KEY_BUTTON_DROP;
12368
12369     stored_player[i].force_dropping = FALSE;
12370   }
12371 }
12372
12373 void GameActions_MM_Main(void)
12374 {
12375   AdvanceGfxFrame();
12376
12377   GameActions_MM(local_player->effective_mouse_action);
12378 }
12379
12380 void GameActions_RND_Main(void)
12381 {
12382   GameActions_RND();
12383 }
12384
12385 void GameActions_RND(void)
12386 {
12387   static struct MouseActionInfo mouse_action_last = { 0 };
12388   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12389   int magic_wall_x = 0, magic_wall_y = 0;
12390   int i, x, y, element, graphic, last_gfx_frame;
12391
12392   InitPlayfieldScanModeVars();
12393
12394   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12395   {
12396     SCAN_PLAYFIELD(x, y)
12397     {
12398       ChangeCount[x][y] = 0;
12399       ChangeEvent[x][y] = -1;
12400     }
12401   }
12402
12403   if (game.set_centered_player)
12404   {
12405     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12406
12407     // switching to "all players" only possible if all players fit to screen
12408     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12409     {
12410       game.centered_player_nr_next = game.centered_player_nr;
12411       game.set_centered_player = FALSE;
12412     }
12413
12414     // do not switch focus to non-existing (or non-active) player
12415     if (game.centered_player_nr_next >= 0 &&
12416         !stored_player[game.centered_player_nr_next].active)
12417     {
12418       game.centered_player_nr_next = game.centered_player_nr;
12419       game.set_centered_player = FALSE;
12420     }
12421   }
12422
12423   if (game.set_centered_player &&
12424       ScreenMovPos == 0)        // screen currently aligned at tile position
12425   {
12426     int sx, sy;
12427
12428     if (game.centered_player_nr_next == -1)
12429     {
12430       setScreenCenteredToAllPlayers(&sx, &sy);
12431     }
12432     else
12433     {
12434       sx = stored_player[game.centered_player_nr_next].jx;
12435       sy = stored_player[game.centered_player_nr_next].jy;
12436     }
12437
12438     game.centered_player_nr = game.centered_player_nr_next;
12439     game.set_centered_player = FALSE;
12440
12441     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12442     DrawGameDoorValues();
12443   }
12444
12445   // check single step mode (set flag and clear again if any player is active)
12446   game.enter_single_step_mode =
12447     (tape.single_step && tape.recording && !tape.pausing);
12448
12449   for (i = 0; i < MAX_PLAYERS; i++)
12450   {
12451     int actual_player_action = stored_player[i].effective_action;
12452
12453 #if 1
12454     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12455        - rnd_equinox_tetrachloride 048
12456        - rnd_equinox_tetrachloride_ii 096
12457        - rnd_emanuel_schmieg 002
12458        - doctor_sloan_ww 001, 020
12459     */
12460     if (stored_player[i].MovPos == 0)
12461       CheckGravityMovement(&stored_player[i]);
12462 #endif
12463
12464     // overwrite programmed action with tape action
12465     if (stored_player[i].programmed_action)
12466       actual_player_action = stored_player[i].programmed_action;
12467
12468     PlayerActions(&stored_player[i], actual_player_action);
12469
12470     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12471   }
12472
12473   // single step pause mode may already have been toggled by "ScrollPlayer()"
12474   if (game.enter_single_step_mode && !tape.pausing)
12475     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12476
12477   ScrollScreen(NULL, SCROLL_GO_ON);
12478
12479   /* for backwards compatibility, the following code emulates a fixed bug that
12480      occured when pushing elements (causing elements that just made their last
12481      pushing step to already (if possible) make their first falling step in the
12482      same game frame, which is bad); this code is also needed to use the famous
12483      "spring push bug" which is used in older levels and might be wanted to be
12484      used also in newer levels, but in this case the buggy pushing code is only
12485      affecting the "spring" element and no other elements */
12486
12487   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12488   {
12489     for (i = 0; i < MAX_PLAYERS; i++)
12490     {
12491       struct PlayerInfo *player = &stored_player[i];
12492       int x = player->jx;
12493       int y = player->jy;
12494
12495       if (player->active && player->is_pushing && player->is_moving &&
12496           IS_MOVING(x, y) &&
12497           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12498            Tile[x][y] == EL_SPRING))
12499       {
12500         ContinueMoving(x, y);
12501
12502         // continue moving after pushing (this is actually a bug)
12503         if (!IS_MOVING(x, y))
12504           Stop[x][y] = FALSE;
12505       }
12506     }
12507   }
12508
12509   SCAN_PLAYFIELD(x, y)
12510   {
12511     Last[x][y] = Tile[x][y];
12512
12513     ChangeCount[x][y] = 0;
12514     ChangeEvent[x][y] = -1;
12515
12516     // this must be handled before main playfield loop
12517     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12518     {
12519       MovDelay[x][y]--;
12520       if (MovDelay[x][y] <= 0)
12521         RemoveField(x, y);
12522     }
12523
12524     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12525     {
12526       MovDelay[x][y]--;
12527       if (MovDelay[x][y] <= 0)
12528       {
12529         int element = Store[x][y];
12530         int move_direction = MovDir[x][y];
12531         int player_index_bit = Store2[x][y];
12532
12533         Store[x][y] = 0;
12534         Store2[x][y] = 0;
12535
12536         RemoveField(x, y);
12537         TEST_DrawLevelField(x, y);
12538
12539         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12540
12541         if (IS_ENVELOPE(element))
12542           local_player->show_envelope = element;
12543       }
12544     }
12545
12546 #if DEBUG
12547     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12548     {
12549       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12550             x, y);
12551       Debug("game:playing:GameActions_RND", "This should never happen!");
12552
12553       ChangePage[x][y] = -1;
12554     }
12555 #endif
12556
12557     Stop[x][y] = FALSE;
12558     if (WasJustMoving[x][y] > 0)
12559       WasJustMoving[x][y]--;
12560     if (WasJustFalling[x][y] > 0)
12561       WasJustFalling[x][y]--;
12562     if (CheckCollision[x][y] > 0)
12563       CheckCollision[x][y]--;
12564     if (CheckImpact[x][y] > 0)
12565       CheckImpact[x][y]--;
12566
12567     GfxFrame[x][y]++;
12568
12569     /* reset finished pushing action (not done in ContinueMoving() to allow
12570        continuous pushing animation for elements with zero push delay) */
12571     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12572     {
12573       ResetGfxAnimation(x, y);
12574       TEST_DrawLevelField(x, y);
12575     }
12576
12577 #if DEBUG
12578     if (IS_BLOCKED(x, y))
12579     {
12580       int oldx, oldy;
12581
12582       Blocked2Moving(x, y, &oldx, &oldy);
12583       if (!IS_MOVING(oldx, oldy))
12584       {
12585         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12586         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12587         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12588         Debug("game:playing:GameActions_RND", "This should never happen!");
12589       }
12590     }
12591 #endif
12592   }
12593
12594   HandleMouseAction(&mouse_action, &mouse_action_last);
12595
12596   SCAN_PLAYFIELD(x, y)
12597   {
12598     element = Tile[x][y];
12599     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12600     last_gfx_frame = GfxFrame[x][y];
12601
12602     if (element == EL_EMPTY)
12603       graphic = el2img(GfxElementEmpty[x][y]);
12604
12605     ResetGfxFrame(x, y);
12606
12607     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12608       DrawLevelGraphicAnimation(x, y, graphic);
12609
12610     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12611         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12612       ResetRandomAnimationValue(x, y);
12613
12614     SetRandomAnimationValue(x, y);
12615
12616     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12617
12618     if (IS_INACTIVE(element))
12619     {
12620       if (IS_ANIMATED(graphic))
12621         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12622
12623       continue;
12624     }
12625
12626     // this may take place after moving, so 'element' may have changed
12627     if (IS_CHANGING(x, y) &&
12628         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12629     {
12630       int page = element_info[element].event_page_nr[CE_DELAY];
12631
12632       HandleElementChange(x, y, page);
12633
12634       element = Tile[x][y];
12635       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12636     }
12637
12638     CheckNextToConditions(x, y);
12639
12640     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12641     {
12642       StartMoving(x, y);
12643
12644       element = Tile[x][y];
12645       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12646
12647       if (IS_ANIMATED(graphic) &&
12648           !IS_MOVING(x, y) &&
12649           !Stop[x][y])
12650         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12651
12652       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12653         TEST_DrawTwinkleOnField(x, y);
12654     }
12655     else if (element == EL_ACID)
12656     {
12657       if (!Stop[x][y])
12658         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12659     }
12660     else if ((element == EL_EXIT_OPEN ||
12661               element == EL_EM_EXIT_OPEN ||
12662               element == EL_SP_EXIT_OPEN ||
12663               element == EL_STEEL_EXIT_OPEN ||
12664               element == EL_EM_STEEL_EXIT_OPEN ||
12665               element == EL_SP_TERMINAL ||
12666               element == EL_SP_TERMINAL_ACTIVE ||
12667               element == EL_EXTRA_TIME ||
12668               element == EL_SHIELD_NORMAL ||
12669               element == EL_SHIELD_DEADLY) &&
12670              IS_ANIMATED(graphic))
12671       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12672     else if (IS_MOVING(x, y))
12673       ContinueMoving(x, y);
12674     else if (IS_ACTIVE_BOMB(element))
12675       CheckDynamite(x, y);
12676     else if (element == EL_AMOEBA_GROWING)
12677       AmoebaGrowing(x, y);
12678     else if (element == EL_AMOEBA_SHRINKING)
12679       AmoebaShrinking(x, y);
12680
12681 #if !USE_NEW_AMOEBA_CODE
12682     else if (IS_AMOEBALIVE(element))
12683       AmoebaReproduce(x, y);
12684 #endif
12685
12686     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12687       Life(x, y);
12688     else if (element == EL_EXIT_CLOSED)
12689       CheckExit(x, y);
12690     else if (element == EL_EM_EXIT_CLOSED)
12691       CheckExitEM(x, y);
12692     else if (element == EL_STEEL_EXIT_CLOSED)
12693       CheckExitSteel(x, y);
12694     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12695       CheckExitSteelEM(x, y);
12696     else if (element == EL_SP_EXIT_CLOSED)
12697       CheckExitSP(x, y);
12698     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12699              element == EL_EXPANDABLE_STEELWALL_GROWING)
12700       WallGrowing(x, y);
12701     else if (element == EL_EXPANDABLE_WALL ||
12702              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12703              element == EL_EXPANDABLE_WALL_VERTICAL ||
12704              element == EL_EXPANDABLE_WALL_ANY ||
12705              element == EL_BD_EXPANDABLE_WALL ||
12706              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12707              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12708              element == EL_EXPANDABLE_STEELWALL_ANY)
12709       CheckWallGrowing(x, y);
12710     else if (element == EL_FLAMES)
12711       CheckForDragon(x, y);
12712     else if (element == EL_EXPLOSION)
12713       ; // drawing of correct explosion animation is handled separately
12714     else if (element == EL_ELEMENT_SNAPPING ||
12715              element == EL_DIAGONAL_SHRINKING ||
12716              element == EL_DIAGONAL_GROWING)
12717     {
12718       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y], GfxDir[x][y]);
12719
12720       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12721     }
12722     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12723       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12724
12725     if (IS_BELT_ACTIVE(element))
12726       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12727
12728     if (game.magic_wall_active)
12729     {
12730       int jx = local_player->jx, jy = local_player->jy;
12731
12732       // play the element sound at the position nearest to the player
12733       if ((element == EL_MAGIC_WALL_FULL ||
12734            element == EL_MAGIC_WALL_ACTIVE ||
12735            element == EL_MAGIC_WALL_EMPTYING ||
12736            element == EL_BD_MAGIC_WALL_FULL ||
12737            element == EL_BD_MAGIC_WALL_ACTIVE ||
12738            element == EL_BD_MAGIC_WALL_EMPTYING ||
12739            element == EL_DC_MAGIC_WALL_FULL ||
12740            element == EL_DC_MAGIC_WALL_ACTIVE ||
12741            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12742           ABS(x - jx) + ABS(y - jy) <
12743           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12744       {
12745         magic_wall_x = x;
12746         magic_wall_y = y;
12747       }
12748     }
12749   }
12750
12751 #if USE_NEW_AMOEBA_CODE
12752   // new experimental amoeba growth stuff
12753   if (!(FrameCounter % 8))
12754   {
12755     static unsigned int random = 1684108901;
12756
12757     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12758     {
12759       x = RND(lev_fieldx);
12760       y = RND(lev_fieldy);
12761       element = Tile[x][y];
12762
12763       if (!IS_PLAYER(x, y) &&
12764           (element == EL_EMPTY ||
12765            CAN_GROW_INTO(element) ||
12766            element == EL_QUICKSAND_EMPTY ||
12767            element == EL_QUICKSAND_FAST_EMPTY ||
12768            element == EL_ACID_SPLASH_LEFT ||
12769            element == EL_ACID_SPLASH_RIGHT))
12770       {
12771         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12772             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12773             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12774             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12775           Tile[x][y] = EL_AMOEBA_DROP;
12776       }
12777
12778       random = random * 129 + 1;
12779     }
12780   }
12781 #endif
12782
12783   game.explosions_delayed = FALSE;
12784
12785   SCAN_PLAYFIELD(x, y)
12786   {
12787     element = Tile[x][y];
12788
12789     if (ExplodeField[x][y])
12790       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12791     else if (element == EL_EXPLOSION)
12792       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12793
12794     ExplodeField[x][y] = EX_TYPE_NONE;
12795   }
12796
12797   game.explosions_delayed = TRUE;
12798
12799   if (game.magic_wall_active)
12800   {
12801     if (!(game.magic_wall_time_left % 4))
12802     {
12803       int element = Tile[magic_wall_x][magic_wall_y];
12804
12805       if (element == EL_BD_MAGIC_WALL_FULL ||
12806           element == EL_BD_MAGIC_WALL_ACTIVE ||
12807           element == EL_BD_MAGIC_WALL_EMPTYING)
12808         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12809       else if (element == EL_DC_MAGIC_WALL_FULL ||
12810                element == EL_DC_MAGIC_WALL_ACTIVE ||
12811                element == EL_DC_MAGIC_WALL_EMPTYING)
12812         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12813       else
12814         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12815     }
12816
12817     if (game.magic_wall_time_left > 0)
12818     {
12819       game.magic_wall_time_left--;
12820
12821       if (!game.magic_wall_time_left)
12822       {
12823         SCAN_PLAYFIELD(x, y)
12824         {
12825           element = Tile[x][y];
12826
12827           if (element == EL_MAGIC_WALL_ACTIVE ||
12828               element == EL_MAGIC_WALL_FULL)
12829           {
12830             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12831             TEST_DrawLevelField(x, y);
12832           }
12833           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12834                    element == EL_BD_MAGIC_WALL_FULL)
12835           {
12836             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12837             TEST_DrawLevelField(x, y);
12838           }
12839           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12840                    element == EL_DC_MAGIC_WALL_FULL)
12841           {
12842             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12843             TEST_DrawLevelField(x, y);
12844           }
12845         }
12846
12847         game.magic_wall_active = FALSE;
12848       }
12849     }
12850   }
12851
12852   if (game.light_time_left > 0)
12853   {
12854     game.light_time_left--;
12855
12856     if (game.light_time_left == 0)
12857       RedrawAllLightSwitchesAndInvisibleElements();
12858   }
12859
12860   if (game.timegate_time_left > 0)
12861   {
12862     game.timegate_time_left--;
12863
12864     if (game.timegate_time_left == 0)
12865       CloseAllOpenTimegates();
12866   }
12867
12868   if (game.lenses_time_left > 0)
12869   {
12870     game.lenses_time_left--;
12871
12872     if (game.lenses_time_left == 0)
12873       RedrawAllInvisibleElementsForLenses();
12874   }
12875
12876   if (game.magnify_time_left > 0)
12877   {
12878     game.magnify_time_left--;
12879
12880     if (game.magnify_time_left == 0)
12881       RedrawAllInvisibleElementsForMagnifier();
12882   }
12883
12884   for (i = 0; i < MAX_PLAYERS; i++)
12885   {
12886     struct PlayerInfo *player = &stored_player[i];
12887
12888     if (SHIELD_ON(player))
12889     {
12890       if (player->shield_deadly_time_left)
12891         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12892       else if (player->shield_normal_time_left)
12893         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12894     }
12895   }
12896
12897 #if USE_DELAYED_GFX_REDRAW
12898   SCAN_PLAYFIELD(x, y)
12899   {
12900     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12901     {
12902       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12903          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12904
12905       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12906         DrawLevelField(x, y);
12907
12908       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12909         DrawLevelFieldCrumbled(x, y);
12910
12911       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12912         DrawLevelFieldCrumbledNeighbours(x, y);
12913
12914       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12915         DrawTwinkleOnField(x, y);
12916     }
12917
12918     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12919   }
12920 #endif
12921
12922   DrawAllPlayers();
12923   PlayAllPlayersSound();
12924
12925   for (i = 0; i < MAX_PLAYERS; i++)
12926   {
12927     struct PlayerInfo *player = &stored_player[i];
12928
12929     if (player->show_envelope != 0 && (!player->active ||
12930                                        player->MovPos == 0))
12931     {
12932       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12933
12934       player->show_envelope = 0;
12935     }
12936   }
12937
12938   // use random number generator in every frame to make it less predictable
12939   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12940     RND(1);
12941
12942   mouse_action_last = mouse_action;
12943 }
12944
12945 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12946 {
12947   int min_x = x, min_y = y, max_x = x, max_y = y;
12948   int scr_fieldx = getScreenFieldSizeX();
12949   int scr_fieldy = getScreenFieldSizeY();
12950   int i;
12951
12952   for (i = 0; i < MAX_PLAYERS; i++)
12953   {
12954     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12955
12956     if (!stored_player[i].active || &stored_player[i] == player)
12957       continue;
12958
12959     min_x = MIN(min_x, jx);
12960     min_y = MIN(min_y, jy);
12961     max_x = MAX(max_x, jx);
12962     max_y = MAX(max_y, jy);
12963   }
12964
12965   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12966 }
12967
12968 static boolean AllPlayersInVisibleScreen(void)
12969 {
12970   int i;
12971
12972   for (i = 0; i < MAX_PLAYERS; i++)
12973   {
12974     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12975
12976     if (!stored_player[i].active)
12977       continue;
12978
12979     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12980       return FALSE;
12981   }
12982
12983   return TRUE;
12984 }
12985
12986 void ScrollLevel(int dx, int dy)
12987 {
12988   int scroll_offset = 2 * TILEX_VAR;
12989   int x, y;
12990
12991   BlitBitmap(drawto_field, drawto_field,
12992              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12993              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12994              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12995              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12996              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12997              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12998
12999   if (dx != 0)
13000   {
13001     x = (dx == 1 ? BX1 : BX2);
13002     for (y = BY1; y <= BY2; y++)
13003       DrawScreenField(x, y);
13004   }
13005
13006   if (dy != 0)
13007   {
13008     y = (dy == 1 ? BY1 : BY2);
13009     for (x = BX1; x <= BX2; x++)
13010       DrawScreenField(x, y);
13011   }
13012
13013   redraw_mask |= REDRAW_FIELD;
13014 }
13015
13016 static boolean canFallDown(struct PlayerInfo *player)
13017 {
13018   int jx = player->jx, jy = player->jy;
13019
13020   return (IN_LEV_FIELD(jx, jy + 1) &&
13021           (IS_FREE(jx, jy + 1) ||
13022            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
13023           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
13024           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
13025 }
13026
13027 static boolean canPassField(int x, int y, int move_dir)
13028 {
13029   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13030   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13031   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13032   int nextx = x + dx;
13033   int nexty = y + dy;
13034   int element = Tile[x][y];
13035
13036   return (IS_PASSABLE_FROM(element, opposite_dir) &&
13037           !CAN_MOVE(element) &&
13038           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
13039           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
13040           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
13041 }
13042
13043 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
13044 {
13045   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
13046   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
13047   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
13048   int newx = x + dx;
13049   int newy = y + dy;
13050
13051   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
13052           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
13053           (IS_DIGGABLE(Tile[newx][newy]) ||
13054            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
13055            canPassField(newx, newy, move_dir)));
13056 }
13057
13058 static void CheckGravityMovement(struct PlayerInfo *player)
13059 {
13060   if (player->gravity && !player->programmed_action)
13061   {
13062     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
13063     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
13064     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
13065     int jx = player->jx, jy = player->jy;
13066     boolean player_is_moving_to_valid_field =
13067       (!player_is_snapping &&
13068        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
13069         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
13070     boolean player_can_fall_down = canFallDown(player);
13071
13072     if (player_can_fall_down &&
13073         !player_is_moving_to_valid_field)
13074       player->programmed_action = MV_DOWN;
13075   }
13076 }
13077
13078 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
13079 {
13080   return CheckGravityMovement(player);
13081
13082   if (player->gravity && !player->programmed_action)
13083   {
13084     int jx = player->jx, jy = player->jy;
13085     boolean field_under_player_is_free =
13086       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
13087     boolean player_is_standing_on_valid_field =
13088       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
13089        (IS_WALKABLE(Tile[jx][jy]) &&
13090         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
13091
13092     if (field_under_player_is_free && !player_is_standing_on_valid_field)
13093       player->programmed_action = MV_DOWN;
13094   }
13095 }
13096
13097 /*
13098   MovePlayerOneStep()
13099   -----------------------------------------------------------------------------
13100   dx, dy:               direction (non-diagonal) to try to move the player to
13101   real_dx, real_dy:     direction as read from input device (can be diagonal)
13102 */
13103
13104 boolean MovePlayerOneStep(struct PlayerInfo *player,
13105                           int dx, int dy, int real_dx, int real_dy)
13106 {
13107   int jx = player->jx, jy = player->jy;
13108   int new_jx = jx + dx, new_jy = jy + dy;
13109   int can_move;
13110   boolean player_can_move = !player->cannot_move;
13111
13112   if (!player->active || (!dx && !dy))
13113     return MP_NO_ACTION;
13114
13115   player->MovDir = (dx < 0 ? MV_LEFT :
13116                     dx > 0 ? MV_RIGHT :
13117                     dy < 0 ? MV_UP :
13118                     dy > 0 ? MV_DOWN :  MV_NONE);
13119
13120   if (!IN_LEV_FIELD(new_jx, new_jy))
13121     return MP_NO_ACTION;
13122
13123   if (!player_can_move)
13124   {
13125     if (player->MovPos == 0)
13126     {
13127       player->is_moving = FALSE;
13128       player->is_digging = FALSE;
13129       player->is_collecting = FALSE;
13130       player->is_snapping = FALSE;
13131       player->is_pushing = FALSE;
13132     }
13133   }
13134
13135   if (!network.enabled && game.centered_player_nr == -1 &&
13136       !AllPlayersInSight(player, new_jx, new_jy))
13137     return MP_NO_ACTION;
13138
13139   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx, real_dy, DF_DIG);
13140   if (can_move != MP_MOVING)
13141     return can_move;
13142
13143   // check if DigField() has caused relocation of the player
13144   if (player->jx != jx || player->jy != jy)
13145     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
13146
13147   StorePlayer[jx][jy] = 0;
13148   player->last_jx = jx;
13149   player->last_jy = jy;
13150   player->jx = new_jx;
13151   player->jy = new_jy;
13152   StorePlayer[new_jx][new_jy] = player->element_nr;
13153
13154   if (player->move_delay_value_next != -1)
13155   {
13156     player->move_delay_value = player->move_delay_value_next;
13157     player->move_delay_value_next = -1;
13158   }
13159
13160   player->MovPos =
13161     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
13162
13163   player->step_counter++;
13164
13165   PlayerVisit[jx][jy] = FrameCounter;
13166
13167   player->is_moving = TRUE;
13168
13169 #if 1
13170   // should better be called in MovePlayer(), but this breaks some tapes
13171   ScrollPlayer(player, SCROLL_INIT);
13172 #endif
13173
13174   return MP_MOVING;
13175 }
13176
13177 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
13178 {
13179   int jx = player->jx, jy = player->jy;
13180   int old_jx = jx, old_jy = jy;
13181   int moved = MP_NO_ACTION;
13182
13183   if (!player->active)
13184     return FALSE;
13185
13186   if (!dx && !dy)
13187   {
13188     if (player->MovPos == 0)
13189     {
13190       player->is_moving = FALSE;
13191       player->is_digging = FALSE;
13192       player->is_collecting = FALSE;
13193       player->is_snapping = FALSE;
13194       player->is_pushing = FALSE;
13195     }
13196
13197     return FALSE;
13198   }
13199
13200   if (player->move_delay > 0)
13201     return FALSE;
13202
13203   player->move_delay = -1;              // set to "uninitialized" value
13204
13205   // store if player is automatically moved to next field
13206   player->is_auto_moving = (player->programmed_action != MV_NONE);
13207
13208   // remove the last programmed player action
13209   player->programmed_action = 0;
13210
13211   if (player->MovPos)
13212   {
13213     // should only happen if pre-1.2 tape recordings are played
13214     // this is only for backward compatibility
13215
13216     int original_move_delay_value = player->move_delay_value;
13217
13218 #if DEBUG
13219     Debug("game:playing:MovePlayer",
13220           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
13221           tape.counter);
13222 #endif
13223
13224     // scroll remaining steps with finest movement resolution
13225     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
13226
13227     while (player->MovPos)
13228     {
13229       ScrollPlayer(player, SCROLL_GO_ON);
13230       ScrollScreen(NULL, SCROLL_GO_ON);
13231
13232       AdvanceFrameAndPlayerCounters(player->index_nr);
13233
13234       DrawAllPlayers();
13235       BackToFront_WithFrameDelay(0);
13236     }
13237
13238     player->move_delay_value = original_move_delay_value;
13239   }
13240
13241   player->is_active = FALSE;
13242
13243   if (player->last_move_dir & MV_HORIZONTAL)
13244   {
13245     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
13246       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
13247   }
13248   else
13249   {
13250     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
13251       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
13252   }
13253
13254   if (!moved && !player->is_active)
13255   {
13256     player->is_moving = FALSE;
13257     player->is_digging = FALSE;
13258     player->is_collecting = FALSE;
13259     player->is_snapping = FALSE;
13260     player->is_pushing = FALSE;
13261   }
13262
13263   jx = player->jx;
13264   jy = player->jy;
13265
13266   if (moved & MP_MOVING && !ScreenMovPos &&
13267       (player->index_nr == game.centered_player_nr ||
13268        game.centered_player_nr == -1))
13269   {
13270     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
13271
13272     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
13273     {
13274       // actual player has left the screen -- scroll in that direction
13275       if (jx != old_jx)         // player has moved horizontally
13276         scroll_x += (jx - old_jx);
13277       else                      // player has moved vertically
13278         scroll_y += (jy - old_jy);
13279     }
13280     else
13281     {
13282       int offset_raw = game.scroll_delay_value;
13283
13284       if (jx != old_jx)         // player has moved horizontally
13285       {
13286         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
13287         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
13288         int new_scroll_x = jx - MIDPOSX + offset_x;
13289
13290         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
13291             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
13292           scroll_x = new_scroll_x;
13293
13294         // don't scroll over playfield boundaries
13295         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
13296
13297         // don't scroll more than one field at a time
13298         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
13299
13300         // don't scroll against the player's moving direction
13301         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
13302             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
13303           scroll_x = old_scroll_x;
13304       }
13305       else                      // player has moved vertically
13306       {
13307         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
13308         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
13309         int new_scroll_y = jy - MIDPOSY + offset_y;
13310
13311         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13312             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13313           scroll_y = new_scroll_y;
13314
13315         // don't scroll over playfield boundaries
13316         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13317
13318         // don't scroll more than one field at a time
13319         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13320
13321         // don't scroll against the player's moving direction
13322         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13323             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13324           scroll_y = old_scroll_y;
13325       }
13326     }
13327
13328     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13329     {
13330       if (!network.enabled && game.centered_player_nr == -1 &&
13331           !AllPlayersInVisibleScreen())
13332       {
13333         scroll_x = old_scroll_x;
13334         scroll_y = old_scroll_y;
13335       }
13336       else
13337       {
13338         ScrollScreen(player, SCROLL_INIT);
13339         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13340       }
13341     }
13342   }
13343
13344   player->StepFrame = 0;
13345
13346   if (moved & MP_MOVING)
13347   {
13348     if (old_jx != jx && old_jy == jy)
13349       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13350     else if (old_jx == jx && old_jy != jy)
13351       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13352
13353     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13354
13355     player->last_move_dir = player->MovDir;
13356     player->is_moving = TRUE;
13357     player->is_snapping = FALSE;
13358     player->is_switching = FALSE;
13359     player->is_dropping = FALSE;
13360     player->is_dropping_pressed = FALSE;
13361     player->drop_pressed_delay = 0;
13362
13363 #if 0
13364     // should better be called here than above, but this breaks some tapes
13365     ScrollPlayer(player, SCROLL_INIT);
13366 #endif
13367   }
13368   else
13369   {
13370     CheckGravityMovementWhenNotMoving(player);
13371
13372     player->is_moving = FALSE;
13373
13374     /* at this point, the player is allowed to move, but cannot move right now
13375        (e.g. because of something blocking the way) -- ensure that the player
13376        is also allowed to move in the next frame (in old versions before 3.1.1,
13377        the player was forced to wait again for eight frames before next try) */
13378
13379     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13380       player->move_delay = 0;   // allow direct movement in the next frame
13381   }
13382
13383   if (player->move_delay == -1)         // not yet initialized by DigField()
13384     player->move_delay = player->move_delay_value;
13385
13386   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13387   {
13388     TestIfPlayerTouchesBadThing(jx, jy);
13389     TestIfPlayerTouchesCustomElement(jx, jy);
13390   }
13391
13392   if (!player->active)
13393     RemovePlayer(player);
13394
13395   return moved;
13396 }
13397
13398 void ScrollPlayer(struct PlayerInfo *player, int mode)
13399 {
13400   int jx = player->jx, jy = player->jy;
13401   int last_jx = player->last_jx, last_jy = player->last_jy;
13402   int move_stepsize = TILEX / player->move_delay_value;
13403
13404   if (!player->active)
13405     return;
13406
13407   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13408     return;
13409
13410   if (mode == SCROLL_INIT)
13411   {
13412     player->actual_frame_counter.count = FrameCounter;
13413     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13414
13415     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13416         Tile[last_jx][last_jy] == EL_EMPTY)
13417     {
13418       int last_field_block_delay = 0;   // start with no blocking at all
13419       int block_delay_adjustment = player->block_delay_adjustment;
13420
13421       // if player blocks last field, add delay for exactly one move
13422       if (player->block_last_field)
13423       {
13424         last_field_block_delay += player->move_delay_value;
13425
13426         // when blocking enabled, prevent moving up despite gravity
13427         if (player->gravity && player->MovDir == MV_UP)
13428           block_delay_adjustment = -1;
13429       }
13430
13431       // add block delay adjustment (also possible when not blocking)
13432       last_field_block_delay += block_delay_adjustment;
13433
13434       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13435       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13436     }
13437
13438     if (player->MovPos != 0)    // player has not yet reached destination
13439       return;
13440   }
13441   else if (!FrameReached(&player->actual_frame_counter))
13442     return;
13443
13444   if (player->MovPos != 0)
13445   {
13446     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13447     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13448
13449     // before DrawPlayer() to draw correct player graphic for this case
13450     if (player->MovPos == 0)
13451       CheckGravityMovement(player);
13452   }
13453
13454   if (player->MovPos == 0)      // player reached destination field
13455   {
13456     if (player->move_delay_reset_counter > 0)
13457     {
13458       player->move_delay_reset_counter--;
13459
13460       if (player->move_delay_reset_counter == 0)
13461       {
13462         // continue with normal speed after quickly moving through gate
13463         HALVE_PLAYER_SPEED(player);
13464
13465         // be able to make the next move without delay
13466         player->move_delay = 0;
13467       }
13468     }
13469
13470     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13471         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13472         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13473         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13474         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13475         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13476         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13477         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13478     {
13479       ExitPlayer(player);
13480
13481       if (game.players_still_needed == 0 &&
13482           (game.friends_still_needed == 0 ||
13483            IS_SP_ELEMENT(Tile[jx][jy])))
13484         LevelSolved();
13485     }
13486
13487     player->last_jx = jx;
13488     player->last_jy = jy;
13489
13490     // this breaks one level: "machine", level 000
13491     {
13492       int move_direction = player->MovDir;
13493       int enter_side = MV_DIR_OPPOSITE(move_direction);
13494       int leave_side = move_direction;
13495       int old_jx = last_jx;
13496       int old_jy = last_jy;
13497       int old_element = Tile[old_jx][old_jy];
13498       int new_element = Tile[jx][jy];
13499
13500       if (IS_CUSTOM_ELEMENT(old_element))
13501         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13502                                    CE_LEFT_BY_PLAYER,
13503                                    player->index_bit, leave_side);
13504
13505       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13506                                           CE_PLAYER_LEAVES_X,
13507                                           player->index_bit, leave_side);
13508
13509       // needed because pushed element has not yet reached its destination,
13510       // so it would trigger a change event at its previous field location
13511       if (!player->is_pushing)
13512       {
13513         if (IS_CUSTOM_ELEMENT(new_element))
13514           CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13515                                      player->index_bit, enter_side);
13516
13517         CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13518                                             CE_PLAYER_ENTERS_X,
13519                                             player->index_bit, enter_side);
13520       }
13521
13522       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13523                                         CE_MOVE_OF_X, move_direction);
13524     }
13525
13526     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13527     {
13528       TestIfPlayerTouchesBadThing(jx, jy);
13529       TestIfPlayerTouchesCustomElement(jx, jy);
13530
13531       // needed because pushed element has not yet reached its destination,
13532       // so it would trigger a change event at its previous field location
13533       if (!player->is_pushing)
13534         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13535
13536       if (level.finish_dig_collect &&
13537           (player->is_digging || player->is_collecting))
13538       {
13539         int last_element = player->last_removed_element;
13540         int move_direction = player->MovDir;
13541         int enter_side = MV_DIR_OPPOSITE(move_direction);
13542         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13543                             CE_PLAYER_COLLECTS_X);
13544
13545         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13546                                             player->index_bit, enter_side);
13547
13548         player->last_removed_element = EL_UNDEFINED;
13549       }
13550
13551       if (!player->active)
13552         RemovePlayer(player);
13553     }
13554
13555     if (level.use_step_counter)
13556       CheckLevelTime_StepCounter();
13557
13558     if (tape.single_step && tape.recording && !tape.pausing &&
13559         !player->programmed_action)
13560       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13561
13562     if (!player->programmed_action)
13563       CheckSaveEngineSnapshot(player);
13564   }
13565 }
13566
13567 void ScrollScreen(struct PlayerInfo *player, int mode)
13568 {
13569   static DelayCounter screen_frame_counter = { 0 };
13570
13571   if (mode == SCROLL_INIT)
13572   {
13573     // set scrolling step size according to actual player's moving speed
13574     ScrollStepSize = TILEX / player->move_delay_value;
13575
13576     screen_frame_counter.count = FrameCounter;
13577     screen_frame_counter.value = 1;
13578
13579     ScreenMovDir = player->MovDir;
13580     ScreenMovPos = player->MovPos;
13581     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13582     return;
13583   }
13584   else if (!FrameReached(&screen_frame_counter))
13585     return;
13586
13587   if (ScreenMovPos)
13588   {
13589     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13590     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13591     redraw_mask |= REDRAW_FIELD;
13592   }
13593   else
13594     ScreenMovDir = MV_NONE;
13595 }
13596
13597 void CheckNextToConditions(int x, int y)
13598 {
13599   int element = Tile[x][y];
13600
13601   if (IS_PLAYER(x, y))
13602     TestIfPlayerNextToCustomElement(x, y);
13603
13604   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13605       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13606     TestIfElementNextToCustomElement(x, y);
13607 }
13608
13609 void TestIfPlayerNextToCustomElement(int x, int y)
13610 {
13611   struct XY *xy = xy_topdown;
13612   static int trigger_sides[4][2] =
13613   {
13614     // center side       border side
13615     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13616     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13617     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13618     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13619   };
13620   int i;
13621
13622   if (!IS_PLAYER(x, y))
13623     return;
13624
13625   struct PlayerInfo *player = PLAYERINFO(x, y);
13626
13627   if (player->is_moving)
13628     return;
13629
13630   for (i = 0; i < NUM_DIRECTIONS; i++)
13631   {
13632     int xx = x + xy[i].x;
13633     int yy = y + xy[i].y;
13634     int border_side = trigger_sides[i][1];
13635     int border_element;
13636
13637     if (!IN_LEV_FIELD(xx, yy))
13638       continue;
13639
13640     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13641       continue;         // center and border element not connected
13642
13643     border_element = Tile[xx][yy];
13644
13645     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13646                                player->index_bit, border_side);
13647     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13648                                         CE_PLAYER_NEXT_TO_X,
13649                                         player->index_bit, border_side);
13650
13651     /* use player element that is initially defined in the level playfield,
13652        not the player element that corresponds to the runtime player number
13653        (example: a level that contains EL_PLAYER_3 as the only player would
13654        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13655
13656     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13657                              CE_NEXT_TO_X, border_side);
13658   }
13659 }
13660
13661 void TestIfPlayerTouchesCustomElement(int x, int y)
13662 {
13663   struct XY *xy = xy_topdown;
13664   static int trigger_sides[4][2] =
13665   {
13666     // center side       border side
13667     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13668     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13669     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13670     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13671   };
13672   static int touch_dir[4] =
13673   {
13674     MV_LEFT | MV_RIGHT,
13675     MV_UP   | MV_DOWN,
13676     MV_UP   | MV_DOWN,
13677     MV_LEFT | MV_RIGHT
13678   };
13679   int center_element = Tile[x][y];      // should always be non-moving!
13680   int i;
13681
13682   for (i = 0; i < NUM_DIRECTIONS; i++)
13683   {
13684     int xx = x + xy[i].x;
13685     int yy = y + xy[i].y;
13686     int center_side = trigger_sides[i][0];
13687     int border_side = trigger_sides[i][1];
13688     int border_element;
13689
13690     if (!IN_LEV_FIELD(xx, yy))
13691       continue;
13692
13693     if (IS_PLAYER(x, y))                // player found at center element
13694     {
13695       struct PlayerInfo *player = PLAYERINFO(x, y);
13696
13697       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13698         border_element = Tile[xx][yy];          // may be moving!
13699       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13700         border_element = Tile[xx][yy];
13701       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13702         border_element = MovingOrBlocked2Element(xx, yy);
13703       else
13704         continue;               // center and border element do not touch
13705
13706       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13707                                  player->index_bit, border_side);
13708       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13709                                           CE_PLAYER_TOUCHES_X,
13710                                           player->index_bit, border_side);
13711
13712       {
13713         /* use player element that is initially defined in the level playfield,
13714            not the player element that corresponds to the runtime player number
13715            (example: a level that contains EL_PLAYER_3 as the only player would
13716            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13717         int player_element = PLAYERINFO(x, y)->initial_element;
13718
13719         // as element "X" is the player here, check opposite (center) side
13720         CheckElementChangeBySide(xx, yy, border_element, player_element,
13721                                  CE_TOUCHING_X, center_side);
13722       }
13723     }
13724     else if (IS_PLAYER(xx, yy))         // player found at border element
13725     {
13726       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13727
13728       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13729       {
13730         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13731           continue;             // center and border element do not touch
13732       }
13733
13734       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13735                                  player->index_bit, center_side);
13736       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13737                                           CE_PLAYER_TOUCHES_X,
13738                                           player->index_bit, center_side);
13739
13740       {
13741         /* use player element that is initially defined in the level playfield,
13742            not the player element that corresponds to the runtime player number
13743            (example: a level that contains EL_PLAYER_3 as the only player would
13744            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13745         int player_element = PLAYERINFO(xx, yy)->initial_element;
13746
13747         // as element "X" is the player here, check opposite (border) side
13748         CheckElementChangeBySide(x, y, center_element, player_element,
13749                                  CE_TOUCHING_X, border_side);
13750       }
13751
13752       break;
13753     }
13754   }
13755 }
13756
13757 void TestIfElementNextToCustomElement(int x, int y)
13758 {
13759   struct XY *xy = xy_topdown;
13760   static int trigger_sides[4][2] =
13761   {
13762     // center side      border side
13763     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13764     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13765     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13766     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13767   };
13768   int center_element = Tile[x][y];      // should always be non-moving!
13769   int i;
13770
13771   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13772     return;
13773
13774   for (i = 0; i < NUM_DIRECTIONS; i++)
13775   {
13776     int xx = x + xy[i].x;
13777     int yy = y + xy[i].y;
13778     int border_side = trigger_sides[i][1];
13779     int border_element;
13780
13781     if (!IN_LEV_FIELD(xx, yy))
13782       continue;
13783
13784     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13785       continue;                 // center and border element not connected
13786
13787     border_element = Tile[xx][yy];
13788
13789     // check for change of center element (but change it only once)
13790     if (CheckElementChangeBySide(x, y, center_element, border_element,
13791                                  CE_NEXT_TO_X, border_side))
13792       break;
13793   }
13794 }
13795
13796 void TestIfElementTouchesCustomElement(int x, int y)
13797 {
13798   struct XY *xy = xy_topdown;
13799   static int trigger_sides[4][2] =
13800   {
13801     // center side      border side
13802     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13803     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13804     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13805     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13806   };
13807   static int touch_dir[4] =
13808   {
13809     MV_LEFT | MV_RIGHT,
13810     MV_UP   | MV_DOWN,
13811     MV_UP   | MV_DOWN,
13812     MV_LEFT | MV_RIGHT
13813   };
13814   boolean change_center_element = FALSE;
13815   int center_element = Tile[x][y];      // should always be non-moving!
13816   int border_element_old[NUM_DIRECTIONS];
13817   int i;
13818
13819   for (i = 0; i < NUM_DIRECTIONS; i++)
13820   {
13821     int xx = x + xy[i].x;
13822     int yy = y + xy[i].y;
13823     int border_element;
13824
13825     border_element_old[i] = -1;
13826
13827     if (!IN_LEV_FIELD(xx, yy))
13828       continue;
13829
13830     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13831       border_element = Tile[xx][yy];    // may be moving!
13832     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13833       border_element = Tile[xx][yy];
13834     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13835       border_element = MovingOrBlocked2Element(xx, yy);
13836     else
13837       continue;                 // center and border element do not touch
13838
13839     border_element_old[i] = border_element;
13840   }
13841
13842   for (i = 0; i < NUM_DIRECTIONS; i++)
13843   {
13844     int xx = x + xy[i].x;
13845     int yy = y + xy[i].y;
13846     int center_side = trigger_sides[i][0];
13847     int border_element = border_element_old[i];
13848
13849     if (border_element == -1)
13850       continue;
13851
13852     // check for change of border element
13853     CheckElementChangeBySide(xx, yy, border_element, center_element,
13854                              CE_TOUCHING_X, center_side);
13855
13856     // (center element cannot be player, so we don't have to check this here)
13857   }
13858
13859   for (i = 0; i < NUM_DIRECTIONS; i++)
13860   {
13861     int xx = x + xy[i].x;
13862     int yy = y + xy[i].y;
13863     int border_side = trigger_sides[i][1];
13864     int border_element = border_element_old[i];
13865
13866     if (border_element == -1)
13867       continue;
13868
13869     // check for change of center element (but change it only once)
13870     if (!change_center_element)
13871       change_center_element =
13872         CheckElementChangeBySide(x, y, center_element, border_element,
13873                                  CE_TOUCHING_X, border_side);
13874
13875     if (IS_PLAYER(xx, yy))
13876     {
13877       /* use player element that is initially defined in the level playfield,
13878          not the player element that corresponds to the runtime player number
13879          (example: a level that contains EL_PLAYER_3 as the only player would
13880          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13881       int player_element = PLAYERINFO(xx, yy)->initial_element;
13882
13883       // as element "X" is the player here, check opposite (border) side
13884       CheckElementChangeBySide(x, y, center_element, player_element,
13885                                CE_TOUCHING_X, border_side);
13886     }
13887   }
13888 }
13889
13890 void TestIfElementHitsCustomElement(int x, int y, int direction)
13891 {
13892   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13893   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13894   int hitx = x + dx, hity = y + dy;
13895   int hitting_element = Tile[x][y];
13896   int touched_element;
13897
13898   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13899     return;
13900
13901   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13902                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13903
13904   if (IN_LEV_FIELD(hitx, hity))
13905   {
13906     int opposite_direction = MV_DIR_OPPOSITE(direction);
13907     int hitting_side = direction;
13908     int touched_side = opposite_direction;
13909     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13910                           MovDir[hitx][hity] != direction ||
13911                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13912
13913     object_hit = TRUE;
13914
13915     if (object_hit)
13916     {
13917       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13918                                CE_HITTING_X, touched_side);
13919
13920       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13921                                CE_HIT_BY_X, hitting_side);
13922
13923       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13924                                CE_HIT_BY_SOMETHING, opposite_direction);
13925
13926       if (IS_PLAYER(hitx, hity))
13927       {
13928         /* use player element that is initially defined in the level playfield,
13929            not the player element that corresponds to the runtime player number
13930            (example: a level that contains EL_PLAYER_3 as the only player would
13931            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13932         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13933
13934         CheckElementChangeBySide(x, y, hitting_element, player_element,
13935                                  CE_HITTING_X, touched_side);
13936       }
13937     }
13938   }
13939
13940   // "hitting something" is also true when hitting the playfield border
13941   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13942                            CE_HITTING_SOMETHING, direction);
13943 }
13944
13945 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13946 {
13947   int i, kill_x = -1, kill_y = -1;
13948
13949   int bad_element = -1;
13950   struct XY *test_xy = xy_topdown;
13951   static int test_dir[4] =
13952   {
13953     MV_UP,
13954     MV_LEFT,
13955     MV_RIGHT,
13956     MV_DOWN
13957   };
13958
13959   for (i = 0; i < NUM_DIRECTIONS; i++)
13960   {
13961     int test_x, test_y, test_move_dir, test_element;
13962
13963     test_x = good_x + test_xy[i].x;
13964     test_y = good_y + test_xy[i].y;
13965
13966     if (!IN_LEV_FIELD(test_x, test_y))
13967       continue;
13968
13969     test_move_dir =
13970       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13971
13972     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13973
13974     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13975        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13976     */
13977     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13978         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13979     {
13980       kill_x = test_x;
13981       kill_y = test_y;
13982       bad_element = test_element;
13983
13984       break;
13985     }
13986   }
13987
13988   if (kill_x != -1 || kill_y != -1)
13989   {
13990     if (IS_PLAYER(good_x, good_y))
13991     {
13992       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13993
13994       if (player->shield_deadly_time_left > 0 &&
13995           !IS_INDESTRUCTIBLE(bad_element))
13996         Bang(kill_x, kill_y);
13997       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13998         KillPlayer(player);
13999     }
14000     else
14001       Bang(good_x, good_y);
14002   }
14003 }
14004
14005 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
14006 {
14007   int i, kill_x = -1, kill_y = -1;
14008   int bad_element = Tile[bad_x][bad_y];
14009   struct XY *test_xy = xy_topdown;
14010   static int touch_dir[4] =
14011   {
14012     MV_LEFT | MV_RIGHT,
14013     MV_UP   | MV_DOWN,
14014     MV_UP   | MV_DOWN,
14015     MV_LEFT | MV_RIGHT
14016   };
14017   static int test_dir[4] =
14018   {
14019     MV_UP,
14020     MV_LEFT,
14021     MV_RIGHT,
14022     MV_DOWN
14023   };
14024
14025   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
14026     return;
14027
14028   for (i = 0; i < NUM_DIRECTIONS; i++)
14029   {
14030     int test_x, test_y, test_move_dir, test_element;
14031
14032     test_x = bad_x + test_xy[i].x;
14033     test_y = bad_y + test_xy[i].y;
14034
14035     if (!IN_LEV_FIELD(test_x, test_y))
14036       continue;
14037
14038     test_move_dir =
14039       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14040
14041     test_element = Tile[test_x][test_y];
14042
14043     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
14044        2nd case: DONT_TOUCH style bad thing does not move away from good thing
14045     */
14046     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
14047         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
14048     {
14049       // good thing is player or penguin that does not move away
14050       if (IS_PLAYER(test_x, test_y))
14051       {
14052         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14053
14054         if (bad_element == EL_ROBOT && player->is_moving)
14055           continue;     // robot does not kill player if he is moving
14056
14057         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
14058         {
14059           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
14060             continue;           // center and border element do not touch
14061         }
14062
14063         kill_x = test_x;
14064         kill_y = test_y;
14065
14066         break;
14067       }
14068       else if (test_element == EL_PENGUIN)
14069       {
14070         kill_x = test_x;
14071         kill_y = test_y;
14072
14073         break;
14074       }
14075     }
14076   }
14077
14078   if (kill_x != -1 || kill_y != -1)
14079   {
14080     if (IS_PLAYER(kill_x, kill_y))
14081     {
14082       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14083
14084       if (player->shield_deadly_time_left > 0 &&
14085           !IS_INDESTRUCTIBLE(bad_element))
14086         Bang(bad_x, bad_y);
14087       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14088         KillPlayer(player);
14089     }
14090     else
14091       Bang(kill_x, kill_y);
14092   }
14093 }
14094
14095 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
14096 {
14097   int bad_element = Tile[bad_x][bad_y];
14098   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
14099   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
14100   int test_x = bad_x + dx, test_y = bad_y + dy;
14101   int test_move_dir, test_element;
14102   int kill_x = -1, kill_y = -1;
14103
14104   if (!IN_LEV_FIELD(test_x, test_y))
14105     return;
14106
14107   test_move_dir =
14108     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
14109
14110   test_element = Tile[test_x][test_y];
14111
14112   if (test_move_dir != bad_move_dir)
14113   {
14114     // good thing can be player or penguin that does not move away
14115     if (IS_PLAYER(test_x, test_y))
14116     {
14117       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
14118
14119       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
14120          player as being hit when he is moving towards the bad thing, because
14121          the "get hit by" condition would be lost after the player stops) */
14122       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
14123         return;         // player moves away from bad thing
14124
14125       kill_x = test_x;
14126       kill_y = test_y;
14127     }
14128     else if (test_element == EL_PENGUIN)
14129     {
14130       kill_x = test_x;
14131       kill_y = test_y;
14132     }
14133   }
14134
14135   if (kill_x != -1 || kill_y != -1)
14136   {
14137     if (IS_PLAYER(kill_x, kill_y))
14138     {
14139       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
14140
14141       if (player->shield_deadly_time_left > 0 &&
14142           !IS_INDESTRUCTIBLE(bad_element))
14143         Bang(bad_x, bad_y);
14144       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
14145         KillPlayer(player);
14146     }
14147     else
14148       Bang(kill_x, kill_y);
14149   }
14150 }
14151
14152 void TestIfPlayerTouchesBadThing(int x, int y)
14153 {
14154   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14155 }
14156
14157 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
14158 {
14159   TestIfGoodThingHitsBadThing(x, y, move_dir);
14160 }
14161
14162 void TestIfBadThingTouchesPlayer(int x, int y)
14163 {
14164   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14165 }
14166
14167 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
14168 {
14169   TestIfBadThingHitsGoodThing(x, y, move_dir);
14170 }
14171
14172 void TestIfFriendTouchesBadThing(int x, int y)
14173 {
14174   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
14175 }
14176
14177 void TestIfBadThingTouchesFriend(int x, int y)
14178 {
14179   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
14180 }
14181
14182 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
14183 {
14184   int i, kill_x = bad_x, kill_y = bad_y;
14185   struct XY *xy = xy_topdown;
14186
14187   for (i = 0; i < NUM_DIRECTIONS; i++)
14188   {
14189     int x, y, element;
14190
14191     x = bad_x + xy[i].x;
14192     y = bad_y + xy[i].y;
14193     if (!IN_LEV_FIELD(x, y))
14194       continue;
14195
14196     element = Tile[x][y];
14197     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
14198         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
14199     {
14200       kill_x = x;
14201       kill_y = y;
14202       break;
14203     }
14204   }
14205
14206   if (kill_x != bad_x || kill_y != bad_y)
14207     Bang(bad_x, bad_y);
14208 }
14209
14210 void KillPlayer(struct PlayerInfo *player)
14211 {
14212   int jx = player->jx, jy = player->jy;
14213
14214   if (!player->active)
14215     return;
14216
14217 #if 0
14218   Debug("game:playing:KillPlayer",
14219         "0: killed == %d, active == %d, reanimated == %d",
14220         player->killed, player->active, player->reanimated);
14221 #endif
14222
14223   /* the following code was introduced to prevent an infinite loop when calling
14224      -> Bang()
14225      -> CheckTriggeredElementChangeExt()
14226      -> ExecuteCustomElementAction()
14227      -> KillPlayer()
14228      -> (infinitely repeating the above sequence of function calls)
14229      which occurs when killing the player while having a CE with the setting
14230      "kill player X when explosion of <player X>"; the solution using a new
14231      field "player->killed" was chosen for backwards compatibility, although
14232      clever use of the fields "player->active" etc. would probably also work */
14233 #if 1
14234   if (player->killed)
14235     return;
14236 #endif
14237
14238   player->killed = TRUE;
14239
14240   // remove accessible field at the player's position
14241   RemoveField(jx, jy);
14242
14243   // deactivate shield (else Bang()/Explode() would not work right)
14244   player->shield_normal_time_left = 0;
14245   player->shield_deadly_time_left = 0;
14246
14247 #if 0
14248   Debug("game:playing:KillPlayer",
14249         "1: killed == %d, active == %d, reanimated == %d",
14250         player->killed, player->active, player->reanimated);
14251 #endif
14252
14253   Bang(jx, jy);
14254
14255 #if 0
14256   Debug("game:playing:KillPlayer",
14257         "2: killed == %d, active == %d, reanimated == %d",
14258         player->killed, player->active, player->reanimated);
14259 #endif
14260
14261   if (player->reanimated)       // killed player may have been reanimated
14262     player->killed = player->reanimated = FALSE;
14263   else
14264     BuryPlayer(player);
14265 }
14266
14267 static void KillPlayerUnlessEnemyProtected(int x, int y)
14268 {
14269   if (!PLAYER_ENEMY_PROTECTED(x, y))
14270     KillPlayer(PLAYERINFO(x, y));
14271 }
14272
14273 static void KillPlayerUnlessExplosionProtected(int x, int y)
14274 {
14275   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
14276     KillPlayer(PLAYERINFO(x, y));
14277 }
14278
14279 void BuryPlayer(struct PlayerInfo *player)
14280 {
14281   int jx = player->jx, jy = player->jy;
14282
14283   if (!player->active)
14284     return;
14285
14286   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
14287
14288   RemovePlayer(player);
14289
14290   player->buried = TRUE;
14291
14292   if (game.all_players_gone)
14293     game.GameOver = TRUE;
14294 }
14295
14296 void RemovePlayer(struct PlayerInfo *player)
14297 {
14298   int jx = player->jx, jy = player->jy;
14299   int i, found = FALSE;
14300
14301   player->present = FALSE;
14302   player->active = FALSE;
14303
14304   // required for some CE actions (even if the player is not active anymore)
14305   player->MovPos = 0;
14306
14307   if (!ExplodeField[jx][jy])
14308     StorePlayer[jx][jy] = 0;
14309
14310   if (player->is_moving)
14311     TEST_DrawLevelField(player->last_jx, player->last_jy);
14312
14313   for (i = 0; i < MAX_PLAYERS; i++)
14314     if (stored_player[i].active)
14315       found = TRUE;
14316
14317   if (!found)
14318   {
14319     game.all_players_gone = TRUE;
14320     game.GameOver = TRUE;
14321   }
14322
14323   game.exit_x = game.robot_wheel_x = jx;
14324   game.exit_y = game.robot_wheel_y = jy;
14325 }
14326
14327 void ExitPlayer(struct PlayerInfo *player)
14328 {
14329   DrawPlayer(player);   // needed here only to cleanup last field
14330   RemovePlayer(player);
14331
14332   if (game.players_still_needed > 0)
14333     game.players_still_needed--;
14334 }
14335
14336 static void SetFieldForSnapping(int x, int y, int element, int direction,
14337                                 int player_index_bit)
14338 {
14339   struct ElementInfo *ei = &element_info[element];
14340   int direction_bit = MV_DIR_TO_BIT(direction);
14341   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14342   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14343                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14344
14345   Tile[x][y] = EL_ELEMENT_SNAPPING;
14346   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14347   MovDir[x][y] = direction;
14348   Store[x][y] = element;
14349   Store2[x][y] = player_index_bit;
14350
14351   ResetGfxAnimation(x, y);
14352
14353   GfxElement[x][y] = element;
14354   GfxAction[x][y] = action;
14355   GfxDir[x][y] = direction;
14356   GfxFrame[x][y] = -1;
14357 }
14358
14359 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14360                                    int player_index_bit)
14361 {
14362   TestIfElementTouchesCustomElement(x, y);      // for empty space
14363
14364   if (level.finish_dig_collect)
14365   {
14366     int dig_side = MV_DIR_OPPOSITE(direction);
14367     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14368                         CE_PLAYER_COLLECTS_X);
14369
14370     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14371                                         player_index_bit, dig_side);
14372     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14373                                         player_index_bit, dig_side);
14374   }
14375 }
14376
14377 /*
14378   =============================================================================
14379   checkDiagonalPushing()
14380   -----------------------------------------------------------------------------
14381   check if diagonal input device direction results in pushing of object
14382   (by checking if the alternative direction is walkable, diggable, ...)
14383   =============================================================================
14384 */
14385
14386 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14387                                     int x, int y, int real_dx, int real_dy)
14388 {
14389   int jx, jy, dx, dy, xx, yy;
14390
14391   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14392     return TRUE;
14393
14394   // diagonal direction: check alternative direction
14395   jx = player->jx;
14396   jy = player->jy;
14397   dx = x - jx;
14398   dy = y - jy;
14399   xx = jx + (dx == 0 ? real_dx : 0);
14400   yy = jy + (dy == 0 ? real_dy : 0);
14401
14402   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14403 }
14404
14405 /*
14406   =============================================================================
14407   DigField()
14408   -----------------------------------------------------------------------------
14409   x, y:                 field next to player (non-diagonal) to try to dig to
14410   real_dx, real_dy:     direction as read from input device (can be diagonal)
14411   =============================================================================
14412 */
14413
14414 static int DigField(struct PlayerInfo *player,
14415                     int oldx, int oldy, int x, int y,
14416                     int real_dx, int real_dy, int mode)
14417 {
14418   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14419   boolean player_was_pushing = player->is_pushing;
14420   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14421   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14422   int jx = oldx, jy = oldy;
14423   int dx = x - jx, dy = y - jy;
14424   int nextx = x + dx, nexty = y + dy;
14425   int move_direction = (dx == -1 ? MV_LEFT  :
14426                         dx == +1 ? MV_RIGHT :
14427                         dy == -1 ? MV_UP    :
14428                         dy == +1 ? MV_DOWN  : MV_NONE);
14429   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14430   int dig_side = MV_DIR_OPPOSITE(move_direction);
14431   int old_element = Tile[jx][jy];
14432   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14433   int collect_count;
14434
14435   if (is_player)                // function can also be called by EL_PENGUIN
14436   {
14437     if (player->MovPos == 0)
14438     {
14439       player->is_digging = FALSE;
14440       player->is_collecting = FALSE;
14441     }
14442
14443     if (player->MovPos == 0)    // last pushing move finished
14444       player->is_pushing = FALSE;
14445
14446     if (mode == DF_NO_PUSH)     // player just stopped pushing
14447     {
14448       player->is_switching = FALSE;
14449       player->push_delay = -1;
14450
14451       return MP_NO_ACTION;
14452     }
14453   }
14454   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14455     old_element = Back[jx][jy];
14456
14457   // in case of element dropped at player position, check background
14458   else if (Back[jx][jy] != EL_EMPTY &&
14459            game.engine_version >= VERSION_IDENT(2,2,0,0))
14460     old_element = Back[jx][jy];
14461
14462   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14463     return MP_NO_ACTION;        // field has no opening in this direction
14464
14465   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element, opposite_direction))
14466     return MP_NO_ACTION;        // field has no opening in this direction
14467
14468   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14469   {
14470     SplashAcid(x, y);
14471
14472     Tile[jx][jy] = player->artwork_element;
14473     InitMovingField(jx, jy, MV_DOWN);
14474     Store[jx][jy] = EL_ACID;
14475     ContinueMoving(jx, jy);
14476     BuryPlayer(player);
14477
14478     return MP_DONT_RUN_INTO;
14479   }
14480
14481   if (player_can_move && DONT_RUN_INTO(element))
14482   {
14483     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14484
14485     return MP_DONT_RUN_INTO;
14486   }
14487
14488   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14489     return MP_NO_ACTION;
14490
14491   collect_count = element_info[element].collect_count_initial;
14492
14493   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14494     return MP_NO_ACTION;
14495
14496   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14497     player_can_move = player_can_move_or_snap;
14498
14499   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14500       game.engine_version >= VERSION_IDENT(2,2,0,0))
14501   {
14502     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14503                                player->index_bit, dig_side);
14504     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14505                                         player->index_bit, dig_side);
14506
14507     if (element == EL_DC_LANDMINE)
14508       Bang(x, y);
14509
14510     if (Tile[x][y] != element)          // field changed by snapping
14511       return MP_ACTION;
14512
14513     return MP_NO_ACTION;
14514   }
14515
14516   if (player->gravity && is_player && !player->is_auto_moving &&
14517       canFallDown(player) && move_direction != MV_DOWN &&
14518       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14519     return MP_NO_ACTION;        // player cannot walk here due to gravity
14520
14521   if (player_can_move &&
14522       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14523   {
14524     int sound_element = SND_ELEMENT(element);
14525     int sound_action = ACTION_WALKING;
14526
14527     if (IS_RND_GATE(element))
14528     {
14529       if (!player->key[RND_GATE_NR(element)])
14530         return MP_NO_ACTION;
14531     }
14532     else if (IS_RND_GATE_GRAY(element))
14533     {
14534       if (!player->key[RND_GATE_GRAY_NR(element)])
14535         return MP_NO_ACTION;
14536     }
14537     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14538     {
14539       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14540         return MP_NO_ACTION;
14541     }
14542     else if (element == EL_EXIT_OPEN ||
14543              element == EL_EM_EXIT_OPEN ||
14544              element == EL_EM_EXIT_OPENING ||
14545              element == EL_STEEL_EXIT_OPEN ||
14546              element == EL_EM_STEEL_EXIT_OPEN ||
14547              element == EL_EM_STEEL_EXIT_OPENING ||
14548              element == EL_SP_EXIT_OPEN ||
14549              element == EL_SP_EXIT_OPENING)
14550     {
14551       sound_action = ACTION_PASSING;    // player is passing exit
14552     }
14553     else if (element == EL_EMPTY)
14554     {
14555       sound_action = ACTION_MOVING;             // nothing to walk on
14556     }
14557
14558     // play sound from background or player, whatever is available
14559     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14560       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14561     else
14562       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14563   }
14564   else if (player_can_move &&
14565            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14566   {
14567     if (!ACCESS_FROM(element, opposite_direction))
14568       return MP_NO_ACTION;      // field not accessible from this direction
14569
14570     if (CAN_MOVE(element))      // only fixed elements can be passed!
14571       return MP_NO_ACTION;
14572
14573     if (IS_EM_GATE(element))
14574     {
14575       if (!player->key[EM_GATE_NR(element)])
14576         return MP_NO_ACTION;
14577     }
14578     else if (IS_EM_GATE_GRAY(element))
14579     {
14580       if (!player->key[EM_GATE_GRAY_NR(element)])
14581         return MP_NO_ACTION;
14582     }
14583     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14584     {
14585       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14586         return MP_NO_ACTION;
14587     }
14588     else if (IS_EMC_GATE(element))
14589     {
14590       if (!player->key[EMC_GATE_NR(element)])
14591         return MP_NO_ACTION;
14592     }
14593     else if (IS_EMC_GATE_GRAY(element))
14594     {
14595       if (!player->key[EMC_GATE_GRAY_NR(element)])
14596         return MP_NO_ACTION;
14597     }
14598     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14599     {
14600       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14601         return MP_NO_ACTION;
14602     }
14603     else if (element == EL_DC_GATE_WHITE ||
14604              element == EL_DC_GATE_WHITE_GRAY ||
14605              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14606     {
14607       if (player->num_white_keys == 0)
14608         return MP_NO_ACTION;
14609
14610       player->num_white_keys--;
14611     }
14612     else if (IS_SP_PORT(element))
14613     {
14614       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14615           element == EL_SP_GRAVITY_PORT_RIGHT ||
14616           element == EL_SP_GRAVITY_PORT_UP ||
14617           element == EL_SP_GRAVITY_PORT_DOWN)
14618         player->gravity = !player->gravity;
14619       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14620                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14621                element == EL_SP_GRAVITY_ON_PORT_UP ||
14622                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14623         player->gravity = TRUE;
14624       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14625                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14626                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14627                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14628         player->gravity = FALSE;
14629     }
14630
14631     // automatically move to the next field with double speed
14632     player->programmed_action = move_direction;
14633
14634     if (player->move_delay_reset_counter == 0)
14635     {
14636       player->move_delay_reset_counter = 2;     // two double speed steps
14637
14638       DOUBLE_PLAYER_SPEED(player);
14639     }
14640
14641     PlayLevelSoundAction(x, y, ACTION_PASSING);
14642   }
14643   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14644   {
14645     RemoveField(x, y);
14646
14647     if (mode != DF_SNAP)
14648     {
14649       GfxElement[x][y] = GFX_ELEMENT(element);
14650       player->is_digging = TRUE;
14651     }
14652
14653     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14654
14655     // use old behaviour for old levels (digging)
14656     if (!level.finish_dig_collect)
14657     {
14658       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14659                                           player->index_bit, dig_side);
14660
14661       // if digging triggered player relocation, finish digging tile
14662       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14663         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14664     }
14665
14666     if (mode == DF_SNAP)
14667     {
14668       if (level.block_snap_field)
14669         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14670       else
14671         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14672
14673       // use old behaviour for old levels (snapping)
14674       if (!level.finish_dig_collect)
14675         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14676                                             player->index_bit, dig_side);
14677     }
14678   }
14679   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14680   {
14681     RemoveField(x, y);
14682
14683     if (is_player && mode != DF_SNAP)
14684     {
14685       GfxElement[x][y] = element;
14686       player->is_collecting = TRUE;
14687     }
14688
14689     if (element == EL_SPEED_PILL)
14690     {
14691       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14692     }
14693     else if (element == EL_EXTRA_TIME && level.time > 0)
14694     {
14695       TimeLeft += level.extra_time;
14696
14697       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14698
14699       DisplayGameControlValues();
14700     }
14701     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14702     {
14703       int shield_time = (element == EL_SHIELD_DEADLY ?
14704                          level.shield_deadly_time :
14705                          level.shield_normal_time);
14706
14707       player->shield_normal_time_left += shield_time;
14708       if (element == EL_SHIELD_DEADLY)
14709         player->shield_deadly_time_left += shield_time;
14710     }
14711     else if (element == EL_DYNAMITE ||
14712              element == EL_EM_DYNAMITE ||
14713              element == EL_SP_DISK_RED)
14714     {
14715       if (player->inventory_size < MAX_INVENTORY_SIZE)
14716         player->inventory_element[player->inventory_size++] = element;
14717
14718       DrawGameDoorValues();
14719     }
14720     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14721     {
14722       player->dynabomb_count++;
14723       player->dynabombs_left++;
14724     }
14725     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14726     {
14727       player->dynabomb_size++;
14728     }
14729     else if (element == EL_DYNABOMB_INCREASE_POWER)
14730     {
14731       player->dynabomb_xl = TRUE;
14732     }
14733     else if (IS_KEY(element))
14734     {
14735       player->key[KEY_NR(element)] = TRUE;
14736
14737       DrawGameDoorValues();
14738     }
14739     else if (element == EL_DC_KEY_WHITE)
14740     {
14741       player->num_white_keys++;
14742
14743       // display white keys?
14744       // DrawGameDoorValues();
14745     }
14746     else if (IS_ENVELOPE(element))
14747     {
14748       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14749
14750       if (!wait_for_snapping)
14751         player->show_envelope = element;
14752     }
14753     else if (element == EL_EMC_LENSES)
14754     {
14755       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14756
14757       RedrawAllInvisibleElementsForLenses();
14758     }
14759     else if (element == EL_EMC_MAGNIFIER)
14760     {
14761       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14762
14763       RedrawAllInvisibleElementsForMagnifier();
14764     }
14765     else if (IS_DROPPABLE(element) ||
14766              IS_THROWABLE(element))     // can be collected and dropped
14767     {
14768       int i;
14769
14770       if (collect_count == 0)
14771         player->inventory_infinite_element = element;
14772       else
14773         for (i = 0; i < collect_count; i++)
14774           if (player->inventory_size < MAX_INVENTORY_SIZE)
14775             player->inventory_element[player->inventory_size++] = element;
14776
14777       DrawGameDoorValues();
14778     }
14779     else if (collect_count > 0)
14780     {
14781       game.gems_still_needed -= collect_count;
14782       if (game.gems_still_needed < 0)
14783         game.gems_still_needed = 0;
14784
14785       game.snapshot.collected_item = TRUE;
14786
14787       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14788
14789       DisplayGameControlValues();
14790     }
14791
14792     RaiseScoreElement(element);
14793     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14794
14795     // use old behaviour for old levels (collecting)
14796     if (!level.finish_dig_collect && is_player)
14797     {
14798       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14799                                           player->index_bit, dig_side);
14800
14801       // if collecting triggered player relocation, finish collecting tile
14802       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14803         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14804     }
14805
14806     if (mode == DF_SNAP)
14807     {
14808       if (level.block_snap_field)
14809         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14810       else
14811         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14812
14813       // use old behaviour for old levels (snapping)
14814       if (!level.finish_dig_collect)
14815         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14816                                             player->index_bit, dig_side);
14817     }
14818   }
14819   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14820   {
14821     if (mode == DF_SNAP && element != EL_BD_ROCK)
14822       return MP_NO_ACTION;
14823
14824     if (CAN_FALL(element) && dy)
14825       return MP_NO_ACTION;
14826
14827     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14828         !(element == EL_SPRING && level.use_spring_bug))
14829       return MP_NO_ACTION;
14830
14831     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14832         ((move_direction & MV_VERTICAL &&
14833           ((element_info[element].move_pattern & MV_LEFT &&
14834             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14835            (element_info[element].move_pattern & MV_RIGHT &&
14836             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14837          (move_direction & MV_HORIZONTAL &&
14838           ((element_info[element].move_pattern & MV_UP &&
14839             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14840            (element_info[element].move_pattern & MV_DOWN &&
14841             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14842       return MP_NO_ACTION;
14843
14844     // do not push elements already moving away faster than player
14845     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14846         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14847       return MP_NO_ACTION;
14848
14849     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14850     {
14851       if (player->push_delay_value == -1 || !player_was_pushing)
14852         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14853     }
14854     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14855     {
14856       if (player->push_delay_value == -1)
14857         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14858     }
14859     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14860     {
14861       if (!player->is_pushing)
14862         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14863     }
14864
14865     player->is_pushing = TRUE;
14866     player->is_active = TRUE;
14867
14868     if (!(IN_LEV_FIELD(nextx, nexty) &&
14869           (IS_FREE(nextx, nexty) ||
14870            (IS_SB_ELEMENT(element) &&
14871             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14872            (IS_CUSTOM_ELEMENT(element) &&
14873             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14874       return MP_NO_ACTION;
14875
14876     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14877       return MP_NO_ACTION;
14878
14879     if (player->push_delay == -1)       // new pushing; restart delay
14880       player->push_delay = 0;
14881
14882     if (player->push_delay < player->push_delay_value &&
14883         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14884         element != EL_SPRING && element != EL_BALLOON)
14885     {
14886       // make sure that there is no move delay before next try to push
14887       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14888         player->move_delay = 0;
14889
14890       return MP_NO_ACTION;
14891     }
14892
14893     if (IS_CUSTOM_ELEMENT(element) &&
14894         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14895     {
14896       if (!DigFieldByCE(nextx, nexty, element))
14897         return MP_NO_ACTION;
14898     }
14899
14900     if (IS_SB_ELEMENT(element))
14901     {
14902       boolean sokoban_task_solved = FALSE;
14903
14904       if (element == EL_SOKOBAN_FIELD_FULL)
14905       {
14906         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14907
14908         IncrementSokobanFieldsNeeded();
14909         IncrementSokobanObjectsNeeded();
14910       }
14911
14912       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14913       {
14914         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14915
14916         DecrementSokobanFieldsNeeded();
14917         DecrementSokobanObjectsNeeded();
14918
14919         // sokoban object was pushed from empty field to sokoban field
14920         if (Back[x][y] == EL_EMPTY)
14921           sokoban_task_solved = TRUE;
14922       }
14923
14924       Tile[x][y] = EL_SOKOBAN_OBJECT;
14925
14926       if (Back[x][y] == Back[nextx][nexty])
14927         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14928       else if (Back[x][y] != 0)
14929         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14930                                     ACTION_EMPTYING);
14931       else
14932         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14933                                     ACTION_FILLING);
14934
14935       if (sokoban_task_solved &&
14936           game.sokoban_fields_still_needed == 0 &&
14937           game.sokoban_objects_still_needed == 0 &&
14938           level.auto_exit_sokoban)
14939       {
14940         game.players_still_needed = 0;
14941
14942         LevelSolved();
14943
14944         PlaySound(SND_GAME_SOKOBAN_SOLVING);
14945       }
14946     }
14947     else
14948       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14949
14950     InitMovingField(x, y, move_direction);
14951     GfxAction[x][y] = ACTION_PUSHING;
14952
14953     if (mode == DF_SNAP)
14954       ContinueMoving(x, y);
14955     else
14956       MovPos[x][y] = (dx != 0 ? dx : dy);
14957
14958     Pushed[x][y] = TRUE;
14959     Pushed[nextx][nexty] = TRUE;
14960
14961     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14962       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14963     else
14964       player->push_delay_value = -1;    // get new value later
14965
14966     // check for element change _after_ element has been pushed
14967     if (game.use_change_when_pushing_bug)
14968     {
14969       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14970                                  player->index_bit, dig_side);
14971       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14972                                           player->index_bit, dig_side);
14973     }
14974   }
14975   else if (IS_SWITCHABLE(element))
14976   {
14977     if (PLAYER_SWITCHING(player, x, y))
14978     {
14979       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14980                                           player->index_bit, dig_side);
14981
14982       return MP_ACTION;
14983     }
14984
14985     player->is_switching = TRUE;
14986     player->switch_x = x;
14987     player->switch_y = y;
14988
14989     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14990
14991     if (element == EL_ROBOT_WHEEL)
14992     {
14993       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14994
14995       game.robot_wheel_x = x;
14996       game.robot_wheel_y = y;
14997       game.robot_wheel_active = TRUE;
14998
14999       TEST_DrawLevelField(x, y);
15000     }
15001     else if (element == EL_SP_TERMINAL)
15002     {
15003       int xx, yy;
15004
15005       SCAN_PLAYFIELD(xx, yy)
15006       {
15007         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
15008         {
15009           Bang(xx, yy);
15010         }
15011         else if (Tile[xx][yy] == EL_SP_TERMINAL)
15012         {
15013           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
15014
15015           ResetGfxAnimation(xx, yy);
15016           TEST_DrawLevelField(xx, yy);
15017         }
15018       }
15019     }
15020     else if (IS_BELT_SWITCH(element))
15021     {
15022       ToggleBeltSwitch(x, y);
15023     }
15024     else if (element == EL_SWITCHGATE_SWITCH_UP ||
15025              element == EL_SWITCHGATE_SWITCH_DOWN ||
15026              element == EL_DC_SWITCHGATE_SWITCH_UP ||
15027              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
15028     {
15029       ToggleSwitchgateSwitch();
15030     }
15031     else if (element == EL_LIGHT_SWITCH ||
15032              element == EL_LIGHT_SWITCH_ACTIVE)
15033     {
15034       ToggleLightSwitch(x, y);
15035     }
15036     else if (element == EL_TIMEGATE_SWITCH ||
15037              element == EL_DC_TIMEGATE_SWITCH)
15038     {
15039       ActivateTimegateSwitch(x, y);
15040     }
15041     else if (element == EL_BALLOON_SWITCH_LEFT  ||
15042              element == EL_BALLOON_SWITCH_RIGHT ||
15043              element == EL_BALLOON_SWITCH_UP    ||
15044              element == EL_BALLOON_SWITCH_DOWN  ||
15045              element == EL_BALLOON_SWITCH_NONE  ||
15046              element == EL_BALLOON_SWITCH_ANY)
15047     {
15048       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
15049                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
15050                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
15051                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
15052                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
15053                              move_direction);
15054     }
15055     else if (element == EL_LAMP)
15056     {
15057       Tile[x][y] = EL_LAMP_ACTIVE;
15058       game.lights_still_needed--;
15059
15060       ResetGfxAnimation(x, y);
15061       TEST_DrawLevelField(x, y);
15062     }
15063     else if (element == EL_TIME_ORB_FULL)
15064     {
15065       Tile[x][y] = EL_TIME_ORB_EMPTY;
15066
15067       if (level.time > 0 || level.use_time_orb_bug)
15068       {
15069         TimeLeft += level.time_orb_time;
15070         game.no_level_time_limit = FALSE;
15071
15072         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
15073
15074         DisplayGameControlValues();
15075       }
15076
15077       ResetGfxAnimation(x, y);
15078       TEST_DrawLevelField(x, y);
15079     }
15080     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
15081              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15082     {
15083       int xx, yy;
15084
15085       game.ball_active = !game.ball_active;
15086
15087       SCAN_PLAYFIELD(xx, yy)
15088       {
15089         int e = Tile[xx][yy];
15090
15091         if (game.ball_active)
15092         {
15093           if (e == EL_EMC_MAGIC_BALL)
15094             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
15095           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
15096             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
15097         }
15098         else
15099         {
15100           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
15101             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
15102           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
15103             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
15104         }
15105       }
15106     }
15107
15108     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15109                                         player->index_bit, dig_side);
15110
15111     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15112                                         player->index_bit, dig_side);
15113
15114     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15115                                         player->index_bit, dig_side);
15116
15117     return MP_ACTION;
15118   }
15119   else
15120   {
15121     if (!PLAYER_SWITCHING(player, x, y))
15122     {
15123       player->is_switching = TRUE;
15124       player->switch_x = x;
15125       player->switch_y = y;
15126
15127       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
15128                                  player->index_bit, dig_side);
15129       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
15130                                           player->index_bit, dig_side);
15131
15132       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
15133                                  player->index_bit, dig_side);
15134       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
15135                                           player->index_bit, dig_side);
15136     }
15137
15138     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
15139                                player->index_bit, dig_side);
15140     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
15141                                         player->index_bit, dig_side);
15142
15143     return MP_NO_ACTION;
15144   }
15145
15146   player->push_delay = -1;
15147
15148   if (is_player)                // function can also be called by EL_PENGUIN
15149   {
15150     if (Tile[x][y] != element)          // really digged/collected something
15151     {
15152       player->is_collecting = !player->is_digging;
15153       player->is_active = TRUE;
15154
15155       player->last_removed_element = element;
15156     }
15157   }
15158
15159   return MP_MOVING;
15160 }
15161
15162 static boolean DigFieldByCE(int x, int y, int digging_element)
15163 {
15164   int element = Tile[x][y];
15165
15166   if (!IS_FREE(x, y))
15167   {
15168     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
15169                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
15170                   ACTION_BREAKING);
15171
15172     // no element can dig solid indestructible elements
15173     if (IS_INDESTRUCTIBLE(element) &&
15174         !IS_DIGGABLE(element) &&
15175         !IS_COLLECTIBLE(element))
15176       return FALSE;
15177
15178     if (AmoebaNr[x][y] &&
15179         (element == EL_AMOEBA_FULL ||
15180          element == EL_BD_AMOEBA ||
15181          element == EL_AMOEBA_GROWING))
15182     {
15183       AmoebaCnt[AmoebaNr[x][y]]--;
15184       AmoebaCnt2[AmoebaNr[x][y]]--;
15185     }
15186
15187     if (IS_MOVING(x, y))
15188       RemoveMovingField(x, y);
15189     else
15190     {
15191       RemoveField(x, y);
15192       TEST_DrawLevelField(x, y);
15193     }
15194
15195     // if digged element was about to explode, prevent the explosion
15196     ExplodeField[x][y] = EX_TYPE_NONE;
15197
15198     PlayLevelSoundAction(x, y, action);
15199   }
15200
15201   Store[x][y] = EL_EMPTY;
15202
15203   // this makes it possible to leave the removed element again
15204   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
15205     Store[x][y] = element;
15206
15207   return TRUE;
15208 }
15209
15210 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
15211 {
15212   int jx = player->jx, jy = player->jy;
15213   int x = jx + dx, y = jy + dy;
15214   int snap_direction = (dx == -1 ? MV_LEFT  :
15215                         dx == +1 ? MV_RIGHT :
15216                         dy == -1 ? MV_UP    :
15217                         dy == +1 ? MV_DOWN  : MV_NONE);
15218   boolean can_continue_snapping = (level.continuous_snapping &&
15219                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
15220
15221   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
15222     return FALSE;
15223
15224   if (!player->active || !IN_LEV_FIELD(x, y))
15225     return FALSE;
15226
15227   if (dx && dy)
15228     return FALSE;
15229
15230   if (!dx && !dy)
15231   {
15232     if (player->MovPos == 0)
15233       player->is_pushing = FALSE;
15234
15235     player->is_snapping = FALSE;
15236
15237     if (player->MovPos == 0)
15238     {
15239       player->is_moving = FALSE;
15240       player->is_digging = FALSE;
15241       player->is_collecting = FALSE;
15242     }
15243
15244     return FALSE;
15245   }
15246
15247   // prevent snapping with already pressed snap key when not allowed
15248   if (player->is_snapping && !can_continue_snapping)
15249     return FALSE;
15250
15251   player->MovDir = snap_direction;
15252
15253   if (player->MovPos == 0)
15254   {
15255     player->is_moving = FALSE;
15256     player->is_digging = FALSE;
15257     player->is_collecting = FALSE;
15258   }
15259
15260   player->is_dropping = FALSE;
15261   player->is_dropping_pressed = FALSE;
15262   player->drop_pressed_delay = 0;
15263
15264   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
15265     return FALSE;
15266
15267   player->is_snapping = TRUE;
15268   player->is_active = TRUE;
15269
15270   if (player->MovPos == 0)
15271   {
15272     player->is_moving = FALSE;
15273     player->is_digging = FALSE;
15274     player->is_collecting = FALSE;
15275   }
15276
15277   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
15278     TEST_DrawLevelField(player->last_jx, player->last_jy);
15279
15280   TEST_DrawLevelField(x, y);
15281
15282   return TRUE;
15283 }
15284
15285 static boolean DropElement(struct PlayerInfo *player)
15286 {
15287   int old_element, new_element;
15288   int dropx = player->jx, dropy = player->jy;
15289   int drop_direction = player->MovDir;
15290   int drop_side = drop_direction;
15291   int drop_element = get_next_dropped_element(player);
15292
15293   /* do not drop an element on top of another element; when holding drop key
15294      pressed without moving, dropped element must move away before the next
15295      element can be dropped (this is especially important if the next element
15296      is dynamite, which can be placed on background for historical reasons) */
15297   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
15298     return MP_ACTION;
15299
15300   if (IS_THROWABLE(drop_element))
15301   {
15302     dropx += GET_DX_FROM_DIR(drop_direction);
15303     dropy += GET_DY_FROM_DIR(drop_direction);
15304
15305     if (!IN_LEV_FIELD(dropx, dropy))
15306       return FALSE;
15307   }
15308
15309   old_element = Tile[dropx][dropy];     // old element at dropping position
15310   new_element = drop_element;           // default: no change when dropping
15311
15312   // check if player is active, not moving and ready to drop
15313   if (!player->active || player->MovPos || player->drop_delay > 0)
15314     return FALSE;
15315
15316   // check if player has anything that can be dropped
15317   if (new_element == EL_UNDEFINED)
15318     return FALSE;
15319
15320   // only set if player has anything that can be dropped
15321   player->is_dropping_pressed = TRUE;
15322
15323   // check if drop key was pressed long enough for EM style dynamite
15324   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15325     return FALSE;
15326
15327   // check if anything can be dropped at the current position
15328   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15329     return FALSE;
15330
15331   // collected custom elements can only be dropped on empty fields
15332   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15333     return FALSE;
15334
15335   if (old_element != EL_EMPTY)
15336     Back[dropx][dropy] = old_element;   // store old element on this field
15337
15338   ResetGfxAnimation(dropx, dropy);
15339   ResetRandomAnimationValue(dropx, dropy);
15340
15341   if (player->inventory_size > 0 ||
15342       player->inventory_infinite_element != EL_UNDEFINED)
15343   {
15344     if (player->inventory_size > 0)
15345     {
15346       player->inventory_size--;
15347
15348       DrawGameDoorValues();
15349
15350       if (new_element == EL_DYNAMITE)
15351         new_element = EL_DYNAMITE_ACTIVE;
15352       else if (new_element == EL_EM_DYNAMITE)
15353         new_element = EL_EM_DYNAMITE_ACTIVE;
15354       else if (new_element == EL_SP_DISK_RED)
15355         new_element = EL_SP_DISK_RED_ACTIVE;
15356     }
15357
15358     Tile[dropx][dropy] = new_element;
15359
15360     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15361       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15362                           el2img(Tile[dropx][dropy]), 0);
15363
15364     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15365
15366     // needed if previous element just changed to "empty" in the last frame
15367     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15368
15369     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15370                                player->index_bit, drop_side);
15371     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15372                                         CE_PLAYER_DROPS_X,
15373                                         player->index_bit, drop_side);
15374
15375     TestIfElementTouchesCustomElement(dropx, dropy);
15376   }
15377   else          // player is dropping a dyna bomb
15378   {
15379     player->dynabombs_left--;
15380
15381     Tile[dropx][dropy] = new_element;
15382
15383     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15384       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15385                           el2img(Tile[dropx][dropy]), 0);
15386
15387     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15388   }
15389
15390   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15391     InitField_WithBug1(dropx, dropy, FALSE);
15392
15393   new_element = Tile[dropx][dropy];     // element might have changed
15394
15395   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15396       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15397   {
15398     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15399       MovDir[dropx][dropy] = drop_direction;
15400
15401     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15402
15403     // do not cause impact style collision by dropping elements that can fall
15404     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15405   }
15406
15407   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15408   player->is_dropping = TRUE;
15409
15410   player->drop_pressed_delay = 0;
15411   player->is_dropping_pressed = FALSE;
15412
15413   player->drop_x = dropx;
15414   player->drop_y = dropy;
15415
15416   return TRUE;
15417 }
15418
15419 // ----------------------------------------------------------------------------
15420 // game sound playing functions
15421 // ----------------------------------------------------------------------------
15422
15423 static int *loop_sound_frame = NULL;
15424 static int *loop_sound_volume = NULL;
15425
15426 void InitPlayLevelSound(void)
15427 {
15428   int num_sounds = getSoundListSize();
15429
15430   checked_free(loop_sound_frame);
15431   checked_free(loop_sound_volume);
15432
15433   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15434   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15435 }
15436
15437 static void PlayLevelSoundExt(int x, int y, int nr, boolean is_loop_sound)
15438 {
15439   int sx = SCREENX(x), sy = SCREENY(y);
15440   int volume, stereo_position;
15441   int max_distance = 8;
15442   int type = (is_loop_sound ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15443
15444   if ((!setup.sound_simple && !is_loop_sound) ||
15445       (!setup.sound_loops && is_loop_sound))
15446     return;
15447
15448   if (!IN_LEV_FIELD(x, y) ||
15449       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15450       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15451     return;
15452
15453   volume = SOUND_MAX_VOLUME;
15454
15455   if (!IN_SCR_FIELD(sx, sy))
15456   {
15457     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15458     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15459
15460     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15461   }
15462
15463   stereo_position = (SOUND_MAX_LEFT +
15464                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15465                      (SCR_FIELDX + 2 * max_distance));
15466
15467   if (is_loop_sound)
15468   {
15469     /* This assures that quieter loop sounds do not overwrite louder ones,
15470        while restarting sound volume comparison with each new game frame. */
15471
15472     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15473       return;
15474
15475     loop_sound_volume[nr] = volume;
15476     loop_sound_frame[nr] = FrameCounter;
15477   }
15478
15479   PlaySoundExt(nr, volume, stereo_position, type);
15480 }
15481
15482 static void PlayLevelSound(int x, int y, int nr)
15483 {
15484   PlayLevelSoundExt(x, y, nr, IS_LOOP_SOUND(nr));
15485 }
15486
15487 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15488 {
15489   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15490                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15491                  y < LEVELY(BY1) ? LEVELY(BY1) :
15492                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15493                  sound_action);
15494 }
15495
15496 static void PlayLevelSoundAction(int x, int y, int action)
15497 {
15498   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15499 }
15500
15501 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15502 {
15503   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15504
15505   if (sound_effect != SND_UNDEFINED)
15506     PlayLevelSound(x, y, sound_effect);
15507 }
15508
15509 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15510                                               int action)
15511 {
15512   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15513
15514   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15515     PlayLevelSound(x, y, sound_effect);
15516 }
15517
15518 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15519 {
15520   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15521
15522   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15523     PlayLevelSound(x, y, sound_effect);
15524 }
15525
15526 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15527 {
15528   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15529
15530   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15531     StopSound(sound_effect);
15532 }
15533
15534 static int getLevelMusicNr(void)
15535 {
15536   int level_pos = level_nr - leveldir_current->first_level;
15537
15538   if (levelset.music[level_nr] != MUS_UNDEFINED)
15539     return levelset.music[level_nr];            // from config file
15540   else
15541     return MAP_NOCONF_MUSIC(level_pos);         // from music dir
15542 }
15543
15544 static void FadeLevelSounds(void)
15545 {
15546   FadeSounds();
15547 }
15548
15549 static void FadeLevelMusic(void)
15550 {
15551   int music_nr = getLevelMusicNr();
15552   char *curr_music = getCurrentlyPlayingMusicFilename();
15553   char *next_music = getMusicInfoEntryFilename(music_nr);
15554
15555   if (!strEqual(curr_music, next_music))
15556     FadeMusic();
15557 }
15558
15559 void FadeLevelSoundsAndMusic(void)
15560 {
15561   FadeLevelSounds();
15562   FadeLevelMusic();
15563 }
15564
15565 static void PlayLevelMusic(void)
15566 {
15567   int music_nr = getLevelMusicNr();
15568   char *curr_music = getCurrentlyPlayingMusicFilename();
15569   char *next_music = getMusicInfoEntryFilename(music_nr);
15570
15571   if (!strEqual(curr_music, next_music))
15572     PlayMusicLoop(music_nr);
15573 }
15574
15575 static int getSoundAction_BD(int sample)
15576 {
15577   switch (sample)
15578   {
15579     case GD_S_STONE_PUSHING:
15580     case GD_S_MEGA_STONE_PUSHING:
15581     case GD_S_FLYING_STONE_PUSHING:
15582     case GD_S_WAITING_STONE_PUSHING:
15583     case GD_S_CHASING_STONE_PUSHING:
15584     case GD_S_NUT_PUSHING:
15585     case GD_S_NITRO_PACK_PUSHING:
15586     case GD_S_BLADDER_PUSHING:
15587     case GD_S_BOX_PUSHING:
15588       return ACTION_PUSHING;
15589
15590     case GD_S_STONE_FALLING:
15591     case GD_S_MEGA_STONE_FALLING:
15592     case GD_S_FLYING_STONE_FALLING:
15593     case GD_S_NUT_FALLING:
15594     case GD_S_DIRT_BALL_FALLING:
15595     case GD_S_DIRT_LOOSE_FALLING:
15596     case GD_S_NITRO_PACK_FALLING:
15597     case GD_S_FALLING_WALL_FALLING:
15598       return ACTION_FALLING;
15599
15600     case GD_S_STONE_IMPACT:
15601     case GD_S_MEGA_STONE_IMPACT:
15602     case GD_S_FLYING_STONE_IMPACT:
15603     case GD_S_NUT_IMPACT:
15604     case GD_S_DIRT_BALL_IMPACT:
15605     case GD_S_DIRT_LOOSE_IMPACT:
15606     case GD_S_NITRO_PACK_IMPACT:
15607     case GD_S_FALLING_WALL_IMPACT:
15608       return ACTION_IMPACT;
15609
15610     case GD_S_NUT_CRACKING:
15611       return ACTION_BREAKING;
15612
15613     case GD_S_EXPANDING_WALL:
15614     case GD_S_WALL_REAPPEARING:
15615     case GD_S_SLIME:
15616     case GD_S_LAVA:
15617     case GD_S_ACID_SPREADING:
15618       return ACTION_GROWING;
15619
15620     case GD_S_DIAMOND_COLLECTING:
15621     case GD_S_FLYING_DIAMOND_COLLECTING:
15622     case GD_S_SKELETON_COLLECTING:
15623     case GD_S_PNEUMATIC_COLLECTING:
15624     case GD_S_BOMB_COLLECTING:
15625     case GD_S_CLOCK_COLLECTING:
15626     case GD_S_SWEET_COLLECTING:
15627     case GD_S_KEY_COLLECTING:
15628     case GD_S_DIAMOND_KEY_COLLECTING:
15629       return ACTION_COLLECTING;
15630
15631     case GD_S_BOMB_PLACING:
15632     case GD_S_REPLICATOR:
15633       return ACTION_DROPPING;
15634
15635     case GD_S_BLADDER_MOVING:
15636       return ACTION_MOVING;
15637
15638     case GD_S_BLADDER_SPENDER:
15639     case GD_S_BLADDER_CONVERTING:
15640     case GD_S_GRAVITY_CHANGING:
15641       return ACTION_CHANGING;
15642
15643     case GD_S_BITER_EATING:
15644       return ACTION_EATING;
15645
15646     case GD_S_DOOR_OPENING:
15647     case GD_S_CRACKING:
15648       return ACTION_OPENING;
15649
15650     case GD_S_DIRT_WALKING:
15651       return ACTION_DIGGING;
15652
15653     case GD_S_EMPTY_WALKING:
15654       return ACTION_WALKING;
15655
15656     case GD_S_SWITCH_BITER:
15657     case GD_S_SWITCH_CREATURES:
15658     case GD_S_SWITCH_GRAVITY:
15659     case GD_S_SWITCH_EXPANDING:
15660     case GD_S_SWITCH_CONVEYOR:
15661     case GD_S_SWITCH_REPLICATOR:
15662     case GD_S_STIRRING:
15663       return ACTION_ACTIVATING;
15664
15665     case GD_S_TELEPORTER:
15666       return ACTION_PASSING;
15667
15668     case GD_S_EXPLODING:
15669     case GD_S_BOMB_EXPLODING:
15670     case GD_S_GHOST_EXPLODING:
15671     case GD_S_VOODOO_EXPLODING:
15672     case GD_S_NITRO_PACK_EXPLODING:
15673       return ACTION_EXPLODING;
15674
15675     case GD_S_COVERING:
15676     case GD_S_AMOEBA:
15677     case GD_S_MAGIC_WALL:
15678     case GD_S_PNEUMATIC_HAMMER:
15679     case GD_S_WATER:
15680       return ACTION_ACTIVE;
15681
15682     case GD_S_DIAMOND_FALLING_RANDOM:
15683     case GD_S_DIAMOND_FALLING_1:
15684     case GD_S_DIAMOND_FALLING_2:
15685     case GD_S_DIAMOND_FALLING_3:
15686     case GD_S_DIAMOND_FALLING_4:
15687     case GD_S_DIAMOND_FALLING_5:
15688     case GD_S_DIAMOND_FALLING_6:
15689     case GD_S_DIAMOND_FALLING_7:
15690     case GD_S_DIAMOND_FALLING_8:
15691     case GD_S_DIAMOND_IMPACT_RANDOM:
15692     case GD_S_DIAMOND_IMPACT_1:
15693     case GD_S_DIAMOND_IMPACT_2:
15694     case GD_S_DIAMOND_IMPACT_3:
15695     case GD_S_DIAMOND_IMPACT_4:
15696     case GD_S_DIAMOND_IMPACT_5:
15697     case GD_S_DIAMOND_IMPACT_6:
15698     case GD_S_DIAMOND_IMPACT_7:
15699     case GD_S_DIAMOND_IMPACT_8:
15700     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15701     case GD_S_FLYING_DIAMOND_FALLING_1:
15702     case GD_S_FLYING_DIAMOND_FALLING_2:
15703     case GD_S_FLYING_DIAMOND_FALLING_3:
15704     case GD_S_FLYING_DIAMOND_FALLING_4:
15705     case GD_S_FLYING_DIAMOND_FALLING_5:
15706     case GD_S_FLYING_DIAMOND_FALLING_6:
15707     case GD_S_FLYING_DIAMOND_FALLING_7:
15708     case GD_S_FLYING_DIAMOND_FALLING_8:
15709     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15710     case GD_S_FLYING_DIAMOND_IMPACT_1:
15711     case GD_S_FLYING_DIAMOND_IMPACT_2:
15712     case GD_S_FLYING_DIAMOND_IMPACT_3:
15713     case GD_S_FLYING_DIAMOND_IMPACT_4:
15714     case GD_S_FLYING_DIAMOND_IMPACT_5:
15715     case GD_S_FLYING_DIAMOND_IMPACT_6:
15716     case GD_S_FLYING_DIAMOND_IMPACT_7:
15717     case GD_S_FLYING_DIAMOND_IMPACT_8:
15718     case GD_S_TIMEOUT_0:
15719     case GD_S_TIMEOUT_1:
15720     case GD_S_TIMEOUT_2:
15721     case GD_S_TIMEOUT_3:
15722     case GD_S_TIMEOUT_4:
15723     case GD_S_TIMEOUT_5:
15724     case GD_S_TIMEOUT_6:
15725     case GD_S_TIMEOUT_7:
15726     case GD_S_TIMEOUT_8:
15727     case GD_S_TIMEOUT_9:
15728     case GD_S_TIMEOUT_10:
15729     case GD_S_BONUS_LIFE:
15730       // trigger special post-processing (and force sound to be non-looping)
15731       return ACTION_OTHER;
15732
15733     case GD_S_AMOEBA_MAGIC:
15734     case GD_S_FINISHED:
15735       // trigger special post-processing (and force sound to be looping)
15736       return ACTION_DEFAULT;
15737
15738     default:
15739       return ACTION_DEFAULT;
15740   }
15741 }
15742
15743 static int getSoundEffect_BD(int element_bd, int sample)
15744 {
15745   int sound_action = getSoundAction_BD(sample);
15746   int sound_effect = element_info[SND_ELEMENT(element_bd)].sound[sound_action];
15747   int nr;
15748
15749   // standard sounds
15750   if (sound_action != ACTION_OTHER &&
15751       sound_action != ACTION_DEFAULT)
15752     return sound_effect;
15753
15754   // special post-processing for some sounds
15755   switch (sample)
15756   {
15757     case GD_S_DIAMOND_FALLING_RANDOM:
15758     case GD_S_DIAMOND_FALLING_1:
15759     case GD_S_DIAMOND_FALLING_2:
15760     case GD_S_DIAMOND_FALLING_3:
15761     case GD_S_DIAMOND_FALLING_4:
15762     case GD_S_DIAMOND_FALLING_5:
15763     case GD_S_DIAMOND_FALLING_6:
15764     case GD_S_DIAMOND_FALLING_7:
15765     case GD_S_DIAMOND_FALLING_8:
15766       nr = (sample == GD_S_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15767             sample - GD_S_DIAMOND_FALLING_1);
15768       sound_effect = SND_BD_DIAMOND_FALLING_RANDOM_1 + nr;
15769
15770       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15771         sound_effect = SND_BD_DIAMOND_FALLING;
15772       break;
15773
15774     case GD_S_DIAMOND_IMPACT_RANDOM:
15775     case GD_S_DIAMOND_IMPACT_1:
15776     case GD_S_DIAMOND_IMPACT_2:
15777     case GD_S_DIAMOND_IMPACT_3:
15778     case GD_S_DIAMOND_IMPACT_4:
15779     case GD_S_DIAMOND_IMPACT_5:
15780     case GD_S_DIAMOND_IMPACT_6:
15781     case GD_S_DIAMOND_IMPACT_7:
15782     case GD_S_DIAMOND_IMPACT_8:
15783       nr = (sample == GD_S_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15784             sample - GD_S_DIAMOND_IMPACT_1);
15785       sound_effect = SND_BD_DIAMOND_IMPACT_RANDOM_1 + nr;
15786
15787       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15788         sound_effect = SND_BD_DIAMOND_IMPACT;
15789       break;
15790
15791     case GD_S_FLYING_DIAMOND_FALLING_RANDOM:
15792     case GD_S_FLYING_DIAMOND_FALLING_1:
15793     case GD_S_FLYING_DIAMOND_FALLING_2:
15794     case GD_S_FLYING_DIAMOND_FALLING_3:
15795     case GD_S_FLYING_DIAMOND_FALLING_4:
15796     case GD_S_FLYING_DIAMOND_FALLING_5:
15797     case GD_S_FLYING_DIAMOND_FALLING_6:
15798     case GD_S_FLYING_DIAMOND_FALLING_7:
15799     case GD_S_FLYING_DIAMOND_FALLING_8:
15800       nr = (sample == GD_S_FLYING_DIAMOND_FALLING_RANDOM ? GetSimpleRandom(8) :
15801             sample - GD_S_FLYING_DIAMOND_FALLING_1);
15802       sound_effect = SND_BD_FLYING_DIAMOND_FALLING_RANDOM_1 + nr;
15803
15804       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15805         sound_effect = SND_BD_FLYING_DIAMOND_FALLING;
15806       break;
15807
15808     case GD_S_FLYING_DIAMOND_IMPACT_RANDOM:
15809     case GD_S_FLYING_DIAMOND_IMPACT_1:
15810     case GD_S_FLYING_DIAMOND_IMPACT_2:
15811     case GD_S_FLYING_DIAMOND_IMPACT_3:
15812     case GD_S_FLYING_DIAMOND_IMPACT_4:
15813     case GD_S_FLYING_DIAMOND_IMPACT_5:
15814     case GD_S_FLYING_DIAMOND_IMPACT_6:
15815     case GD_S_FLYING_DIAMOND_IMPACT_7:
15816     case GD_S_FLYING_DIAMOND_IMPACT_8:
15817       nr = (sample == GD_S_FLYING_DIAMOND_IMPACT_RANDOM ? GetSimpleRandom(8) :
15818             sample - GD_S_FLYING_DIAMOND_IMPACT_1);
15819       sound_effect = SND_BD_FLYING_DIAMOND_IMPACT_RANDOM_1 + nr;
15820
15821       if (getSoundInfoEntryFilename(sound_effect) == NULL)
15822         sound_effect = SND_BD_FLYING_DIAMOND_IMPACT;
15823       break;
15824
15825     case GD_S_TIMEOUT_0:
15826     case GD_S_TIMEOUT_1:
15827     case GD_S_TIMEOUT_2:
15828     case GD_S_TIMEOUT_3:
15829     case GD_S_TIMEOUT_4:
15830     case GD_S_TIMEOUT_5:
15831     case GD_S_TIMEOUT_6:
15832     case GD_S_TIMEOUT_7:
15833     case GD_S_TIMEOUT_8:
15834     case GD_S_TIMEOUT_9:
15835     case GD_S_TIMEOUT_10:
15836       nr = sample - GD_S_TIMEOUT_0;
15837       sound_effect = SND_GAME_RUNNING_OUT_OF_TIME_0 + nr;
15838
15839       if (getSoundInfoEntryFilename(sound_effect) == NULL && sample != GD_S_TIMEOUT_0)
15840         sound_effect = SND_GAME_RUNNING_OUT_OF_TIME;
15841       break;
15842
15843     case GD_S_BONUS_LIFE:
15844       sound_effect = SND_GAME_HEALTH_BONUS;
15845       break;
15846
15847     case GD_S_AMOEBA_MAGIC:
15848       sound_effect = SND_BD_AMOEBA_OTHER;
15849       break;
15850
15851     case GD_S_FINISHED:
15852       sound_effect = SND_GAME_LEVELTIME_BONUS;
15853       break;
15854
15855     default:
15856       sound_effect = SND_UNDEFINED;
15857       break;
15858   }
15859
15860   return sound_effect;
15861 }
15862
15863 void PlayLevelSound_BD(int xx, int yy, int element_bd, int sample)
15864 {
15865   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15866   int sound_effect = getSoundEffect_BD(element, sample);
15867   int sound_action = getSoundAction_BD(sample);
15868   boolean is_loop_sound = IS_LOOP_SOUND(sound_effect);
15869   int offset = 0;
15870   int x = xx - offset;
15871   int y = yy - offset;
15872
15873   // some sound actions are always looping in native BD game engine
15874   if (sound_action == ACTION_DEFAULT)
15875     is_loop_sound = TRUE;
15876
15877   // some sound actions are always non-looping in native BD game engine
15878   if (sound_action == ACTION_FALLING ||
15879       sound_action == ACTION_MOVING ||
15880       sound_action == ACTION_OTHER)
15881     is_loop_sound = FALSE;
15882
15883   if (sound_effect != SND_UNDEFINED)
15884     PlayLevelSoundExt(x, y, sound_effect, is_loop_sound);
15885 }
15886
15887 void StopSound_BD(int element_bd, int sample)
15888 {
15889   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15890   int sound_effect = getSoundEffect_BD(element, sample);
15891
15892   if (sound_effect != SND_UNDEFINED)
15893     StopSound(sound_effect);
15894 }
15895
15896 boolean isSoundPlaying_BD(int element_bd, int sample)
15897 {
15898   int element = (element_bd > -1 ? map_element_BD_to_RND_game(element_bd) : 0);
15899   int sound_effect = getSoundEffect_BD(element, sample);
15900
15901   if (sound_effect != SND_UNDEFINED)
15902     return isSoundPlaying(sound_effect);
15903
15904   return FALSE;
15905 }
15906
15907 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15908 {
15909   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15910   int offset = 0;
15911   int x = xx - offset;
15912   int y = yy - offset;
15913
15914   switch (sample)
15915   {
15916     case SOUND_blank:
15917       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15918       break;
15919
15920     case SOUND_roll:
15921       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15922       break;
15923
15924     case SOUND_stone:
15925       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15926       break;
15927
15928     case SOUND_nut:
15929       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15930       break;
15931
15932     case SOUND_crack:
15933       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15934       break;
15935
15936     case SOUND_bug:
15937       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15938       break;
15939
15940     case SOUND_tank:
15941       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15942       break;
15943
15944     case SOUND_android_clone:
15945       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15946       break;
15947
15948     case SOUND_android_move:
15949       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15950       break;
15951
15952     case SOUND_spring:
15953       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15954       break;
15955
15956     case SOUND_slurp:
15957       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15958       break;
15959
15960     case SOUND_eater:
15961       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15962       break;
15963
15964     case SOUND_eater_eat:
15965       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15966       break;
15967
15968     case SOUND_alien:
15969       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15970       break;
15971
15972     case SOUND_collect:
15973       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15974       break;
15975
15976     case SOUND_diamond:
15977       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15978       break;
15979
15980     case SOUND_squash:
15981       // !!! CHECK THIS !!!
15982 #if 1
15983       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15984 #else
15985       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15986 #endif
15987       break;
15988
15989     case SOUND_wonderfall:
15990       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15991       break;
15992
15993     case SOUND_drip:
15994       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15995       break;
15996
15997     case SOUND_push:
15998       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15999       break;
16000
16001     case SOUND_dirt:
16002       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
16003       break;
16004
16005     case SOUND_acid:
16006       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
16007       break;
16008
16009     case SOUND_ball:
16010       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16011       break;
16012
16013     case SOUND_slide:
16014       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
16015       break;
16016
16017     case SOUND_wonder:
16018       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16019       break;
16020
16021     case SOUND_door:
16022       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16023       break;
16024
16025     case SOUND_exit_open:
16026       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
16027       break;
16028
16029     case SOUND_exit_leave:
16030       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
16031       break;
16032
16033     case SOUND_dynamite:
16034       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
16035       break;
16036
16037     case SOUND_tick:
16038       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16039       break;
16040
16041     case SOUND_press:
16042       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
16043       break;
16044
16045     case SOUND_wheel:
16046       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
16047       break;
16048
16049     case SOUND_boom:
16050       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
16051       break;
16052
16053     case SOUND_die:
16054       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
16055       break;
16056
16057     case SOUND_time:
16058       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
16059       break;
16060
16061     default:
16062       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
16063       break;
16064   }
16065 }
16066
16067 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
16068 {
16069   int element = map_element_SP_to_RND(element_sp);
16070   int action = map_action_SP_to_RND(action_sp);
16071   int offset = (setup.sp_show_border_elements ? 0 : 1);
16072   int x = xx - offset;
16073   int y = yy - offset;
16074
16075   PlayLevelSoundElementAction(x, y, element, action);
16076 }
16077
16078 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
16079 {
16080   int element = map_element_MM_to_RND(element_mm);
16081   int action = map_action_MM_to_RND(action_mm);
16082   int offset = 0;
16083   int x = xx - offset;
16084   int y = yy - offset;
16085
16086   if (!IS_MM_ELEMENT(element))
16087     element = EL_MM_DEFAULT;
16088
16089   PlayLevelSoundElementAction(x, y, element, action);
16090 }
16091
16092 void PlaySound_MM(int sound_mm)
16093 {
16094   int sound = map_sound_MM_to_RND(sound_mm);
16095
16096   if (sound == SND_UNDEFINED)
16097     return;
16098
16099   PlaySound(sound);
16100 }
16101
16102 void PlaySoundLoop_MM(int sound_mm)
16103 {
16104   int sound = map_sound_MM_to_RND(sound_mm);
16105
16106   if (sound == SND_UNDEFINED)
16107     return;
16108
16109   PlaySoundLoop(sound);
16110 }
16111
16112 void StopSound_MM(int sound_mm)
16113 {
16114   int sound = map_sound_MM_to_RND(sound_mm);
16115
16116   if (sound == SND_UNDEFINED)
16117     return;
16118
16119   StopSound(sound);
16120 }
16121
16122 void RaiseScore(int value)
16123 {
16124   game.score += value;
16125
16126   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
16127
16128   DisplayGameControlValues();
16129 }
16130
16131 void RaiseScoreElement(int element)
16132 {
16133   switch (element)
16134   {
16135     case EL_EMERALD:
16136     case EL_BD_DIAMOND:
16137     case EL_EMERALD_YELLOW:
16138     case EL_EMERALD_RED:
16139     case EL_EMERALD_PURPLE:
16140     case EL_SP_INFOTRON:
16141       RaiseScore(level.score[SC_EMERALD]);
16142       break;
16143     case EL_DIAMOND:
16144       RaiseScore(level.score[SC_DIAMOND]);
16145       break;
16146     case EL_CRYSTAL:
16147       RaiseScore(level.score[SC_CRYSTAL]);
16148       break;
16149     case EL_PEARL:
16150       RaiseScore(level.score[SC_PEARL]);
16151       break;
16152     case EL_BUG:
16153     case EL_BD_BUTTERFLY:
16154     case EL_SP_ELECTRON:
16155       RaiseScore(level.score[SC_BUG]);
16156       break;
16157     case EL_SPACESHIP:
16158     case EL_BD_FIREFLY:
16159     case EL_SP_SNIKSNAK:
16160       RaiseScore(level.score[SC_SPACESHIP]);
16161       break;
16162     case EL_YAMYAM:
16163     case EL_DARK_YAMYAM:
16164       RaiseScore(level.score[SC_YAMYAM]);
16165       break;
16166     case EL_ROBOT:
16167       RaiseScore(level.score[SC_ROBOT]);
16168       break;
16169     case EL_PACMAN:
16170       RaiseScore(level.score[SC_PACMAN]);
16171       break;
16172     case EL_NUT:
16173       RaiseScore(level.score[SC_NUT]);
16174       break;
16175     case EL_DYNAMITE:
16176     case EL_EM_DYNAMITE:
16177     case EL_SP_DISK_RED:
16178     case EL_DYNABOMB_INCREASE_NUMBER:
16179     case EL_DYNABOMB_INCREASE_SIZE:
16180     case EL_DYNABOMB_INCREASE_POWER:
16181       RaiseScore(level.score[SC_DYNAMITE]);
16182       break;
16183     case EL_SHIELD_NORMAL:
16184     case EL_SHIELD_DEADLY:
16185       RaiseScore(level.score[SC_SHIELD]);
16186       break;
16187     case EL_EXTRA_TIME:
16188       RaiseScore(level.extra_time_score);
16189       break;
16190     case EL_KEY_1:
16191     case EL_KEY_2:
16192     case EL_KEY_3:
16193     case EL_KEY_4:
16194     case EL_EM_KEY_1:
16195     case EL_EM_KEY_2:
16196     case EL_EM_KEY_3:
16197     case EL_EM_KEY_4:
16198     case EL_EMC_KEY_5:
16199     case EL_EMC_KEY_6:
16200     case EL_EMC_KEY_7:
16201     case EL_EMC_KEY_8:
16202     case EL_DC_KEY_WHITE:
16203       RaiseScore(level.score[SC_KEY]);
16204       break;
16205     default:
16206       RaiseScore(element_info[element].collect_score);
16207       break;
16208   }
16209 }
16210
16211 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
16212 {
16213   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
16214   {
16215     if (!quick_quit)
16216     {
16217       // prevent short reactivation of overlay buttons while closing door
16218       SetOverlayActive(FALSE);
16219       UnmapGameButtons();
16220
16221       // door may still be open due to skipped or envelope style request
16222       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
16223     }
16224
16225     if (network.enabled)
16226     {
16227       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
16228     }
16229     else
16230     {
16231       // when using BD game engine, cover screen before fading out
16232       if (!quick_quit && level.game_engine_type == GAME_ENGINE_TYPE_BD)
16233         game_bd.cover_screen = TRUE;
16234
16235       if (quick_quit)
16236         FadeSkipNextFadeIn();
16237
16238       SetGameStatus(GAME_MODE_MAIN);
16239
16240       DrawMainMenu();
16241     }
16242   }
16243   else          // continue playing the game
16244   {
16245     if (tape.playing && tape.deactivate_display)
16246       TapeDeactivateDisplayOff(TRUE);
16247
16248     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
16249
16250     if (tape.playing && tape.deactivate_display)
16251       TapeDeactivateDisplayOn();
16252   }
16253 }
16254
16255 void RequestQuitGame(boolean escape_key_pressed)
16256 {
16257   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
16258   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
16259                         level_editor_test_game);
16260   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
16261                           quick_quit || score_info_tape_play);
16262
16263   RequestQuitGameExt(skip_request, quick_quit,
16264                      "Do you really want to quit the game?");
16265 }
16266
16267 static char *getRestartGameMessage(void)
16268 {
16269   boolean play_again = hasStartedNetworkGame();
16270   static char message[MAX_OUTPUT_LINESIZE];
16271   char *game_over_text = "Game over!";
16272   char *play_again_text = " Play it again?";
16273
16274   if (level.game_engine_type == GAME_ENGINE_TYPE_MM &&
16275       game_mm.game_over_message != NULL)
16276     game_over_text = game_mm.game_over_message;
16277
16278   snprintf(message, MAX_OUTPUT_LINESIZE, "%s%s", game_over_text,
16279            (play_again ? play_again_text : ""));
16280
16281   return message;
16282 }
16283
16284 static void RequestRestartGame(void)
16285 {
16286   char *message = getRestartGameMessage();
16287   boolean has_started_game = hasStartedNetworkGame();
16288   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
16289   int door_state = DOOR_CLOSE_1;
16290
16291   boolean restart_wanted = (Request(message, request_mode | REQ_STAY_OPEN) && has_started_game);
16292
16293   // if no restart wanted, continue with next level for BD style intermission levels
16294   if (!restart_wanted && !level_editor_test_game && level.bd_intermission)
16295   {
16296     boolean success = AdvanceToNextLevel();
16297
16298     restart_wanted = (success && setup.auto_play_next_level);
16299   }
16300
16301   if (restart_wanted)
16302   {
16303     CloseDoor(door_state);
16304
16305     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16306   }
16307   else
16308   {
16309     // if game was invoked from level editor, also close tape recorder door
16310     if (level_editor_test_game)
16311       door_state = DOOR_CLOSE_ALL;
16312
16313     CloseDoor(door_state);
16314
16315     SetGameStatus(GAME_MODE_MAIN);
16316
16317     DrawMainMenu();
16318   }
16319 }
16320
16321 boolean CheckRestartGame(void)
16322 {
16323   static int game_over_delay = 0;
16324   int game_over_delay_value = 50;
16325   boolean game_over = checkGameFailed();
16326
16327   if (!game_over)
16328   {
16329     game_over_delay = game_over_delay_value;
16330
16331     return FALSE;
16332   }
16333
16334   if (game_over_delay > 0)
16335   {
16336     if (game_over_delay == game_over_delay_value / 2)
16337       PlaySound(SND_GAME_LOSING);
16338
16339     game_over_delay--;
16340
16341     return FALSE;
16342   }
16343
16344   // do not ask to play again if request dialog is already active
16345   if (game.request_active)
16346     return FALSE;
16347
16348   // do not ask to play again if request dialog already handled
16349   if (game.RestartGameRequested)
16350     return FALSE;
16351
16352   // do not ask to play again if game was never actually played
16353   if (!game.GamePlayed)
16354     return FALSE;
16355
16356   // do not ask to play again if this was disabled in setup menu
16357   if (!setup.ask_on_game_over)
16358     return FALSE;
16359
16360   game.RestartGameRequested = TRUE;
16361
16362   RequestRestartGame();
16363
16364   return TRUE;
16365 }
16366
16367 boolean checkGameRunning(void)
16368 {
16369   if (game_status != GAME_MODE_PLAYING)
16370     return FALSE;
16371
16372   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGameRunning_BD())
16373     return FALSE;
16374
16375   return TRUE;
16376 }
16377
16378 boolean checkGamePlaying(void)
16379 {
16380   if (game_status != GAME_MODE_PLAYING)
16381     return FALSE;
16382
16383   if (level.game_engine_type == GAME_ENGINE_TYPE_BD && !checkGamePlaying_BD())
16384     return FALSE;
16385
16386   return TRUE;
16387 }
16388
16389 boolean checkGameSolved(void)
16390 {
16391   // set for all game engines if level was solved
16392   return game.LevelSolved_GameEnd;
16393 }
16394
16395 boolean checkGameFailed(void)
16396 {
16397   if (level.game_engine_type == GAME_ENGINE_TYPE_BD)
16398     return (game_bd.game_over && !game_bd.level_solved);
16399   else if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16400     return (game_em.game_over && !game_em.level_solved);
16401   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16402     return (game_sp.game_over && !game_sp.level_solved);
16403   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16404     return (game_mm.game_over && !game_mm.level_solved);
16405   else                          // GAME_ENGINE_TYPE_RND
16406     return (game.GameOver && !game.LevelSolved);
16407 }
16408
16409 boolean checkGameEnded(void)
16410 {
16411   return (checkGameSolved() || checkGameFailed());
16412 }
16413
16414
16415 // ----------------------------------------------------------------------------
16416 // random generator functions
16417 // ----------------------------------------------------------------------------
16418
16419 unsigned int InitEngineRandom_RND(int seed)
16420 {
16421   game.num_random_calls = 0;
16422
16423   return InitEngineRandom(seed);
16424 }
16425
16426 unsigned int RND(int max)
16427 {
16428   if (max > 0)
16429   {
16430     game.num_random_calls++;
16431
16432     return GetEngineRandom(max);
16433   }
16434
16435   return 0;
16436 }
16437
16438
16439 // ----------------------------------------------------------------------------
16440 // game engine snapshot handling functions
16441 // ----------------------------------------------------------------------------
16442
16443 struct EngineSnapshotInfo
16444 {
16445   // runtime values for custom element collect score
16446   int collect_score[NUM_CUSTOM_ELEMENTS];
16447
16448   // runtime values for group element choice position
16449   int choice_pos[NUM_GROUP_ELEMENTS];
16450
16451   // runtime values for belt position animations
16452   int belt_graphic[4][NUM_BELT_PARTS];
16453   int belt_anim_mode[4][NUM_BELT_PARTS];
16454 };
16455
16456 static struct EngineSnapshotInfo engine_snapshot_rnd;
16457 static char *snapshot_level_identifier = NULL;
16458 static int snapshot_level_nr = -1;
16459
16460 static void SaveEngineSnapshotValues_RND(void)
16461 {
16462   static int belt_base_active_element[4] =
16463   {
16464     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
16465     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
16466     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
16467     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
16468   };
16469   int i, j;
16470
16471   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16472   {
16473     int element = EL_CUSTOM_START + i;
16474
16475     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
16476   }
16477
16478   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16479   {
16480     int element = EL_GROUP_START + i;
16481
16482     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
16483   }
16484
16485   for (i = 0; i < 4; i++)
16486   {
16487     for (j = 0; j < NUM_BELT_PARTS; j++)
16488     {
16489       int element = belt_base_active_element[i] + j;
16490       int graphic = el2img(element);
16491       int anim_mode = graphic_info[graphic].anim_mode;
16492
16493       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
16494       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
16495     }
16496   }
16497 }
16498
16499 static void LoadEngineSnapshotValues_RND(void)
16500 {
16501   unsigned int num_random_calls = game.num_random_calls;
16502   int i, j;
16503
16504   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
16505   {
16506     int element = EL_CUSTOM_START + i;
16507
16508     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
16509   }
16510
16511   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
16512   {
16513     int element = EL_GROUP_START + i;
16514
16515     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
16516   }
16517
16518   for (i = 0; i < 4; i++)
16519   {
16520     for (j = 0; j < NUM_BELT_PARTS; j++)
16521     {
16522       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
16523       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
16524
16525       graphic_info[graphic].anim_mode = anim_mode;
16526     }
16527   }
16528
16529   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16530   {
16531     InitRND(tape.random_seed);
16532     for (i = 0; i < num_random_calls; i++)
16533       RND(1);
16534   }
16535
16536   if (game.num_random_calls != num_random_calls)
16537   {
16538     Error("number of random calls out of sync");
16539     Error("number of random calls should be %d", num_random_calls);
16540     Error("number of random calls is %d", game.num_random_calls);
16541
16542     Fail("this should not happen -- please debug");
16543   }
16544 }
16545
16546 void FreeEngineSnapshotSingle(void)
16547 {
16548   FreeSnapshotSingle();
16549
16550   setString(&snapshot_level_identifier, NULL);
16551   snapshot_level_nr = -1;
16552 }
16553
16554 void FreeEngineSnapshotList(void)
16555 {
16556   FreeSnapshotList();
16557 }
16558
16559 static ListNode *SaveEngineSnapshotBuffers(void)
16560 {
16561   ListNode *buffers = NULL;
16562
16563   // copy some special values to a structure better suited for the snapshot
16564
16565   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16566     SaveEngineSnapshotValues_RND();
16567   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16568     SaveEngineSnapshotValues_EM();
16569   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16570     SaveEngineSnapshotValues_SP(&buffers);
16571   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16572     SaveEngineSnapshotValues_MM();
16573
16574   // save values stored in special snapshot structure
16575
16576   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16577     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
16578   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16579     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
16580   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16581     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
16582   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16583     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
16584
16585   // save further RND engine values
16586
16587   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
16588   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
16589   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
16590
16591   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
16592   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
16593   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
16594   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
16595   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTimeFrames));
16596   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
16597
16598   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
16599   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
16600   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
16601
16602   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
16603
16604   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
16605   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
16606
16607   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
16608   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
16609   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
16610   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
16611   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
16612   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
16613   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
16614   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
16615   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
16616   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
16617   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
16618   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
16619   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
16620   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
16621   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
16622   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
16623   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
16624   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
16625
16626   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
16627   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
16628
16629   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
16630   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
16631   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
16632
16633   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
16634   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
16635
16636   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
16637   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
16638   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
16639   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
16640   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
16641   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
16642
16643   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
16644   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
16645
16646 #if 0
16647   ListNode *node = engine_snapshot_list_rnd;
16648   int num_bytes = 0;
16649
16650   while (node != NULL)
16651   {
16652     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
16653
16654     node = node->next;
16655   }
16656
16657   Debug("game:playing:SaveEngineSnapshotBuffers",
16658         "size of engine snapshot: %d bytes", num_bytes);
16659 #endif
16660
16661   return buffers;
16662 }
16663
16664 void SaveEngineSnapshotSingle(void)
16665 {
16666   ListNode *buffers = SaveEngineSnapshotBuffers();
16667
16668   // finally save all snapshot buffers to single snapshot
16669   SaveSnapshotSingle(buffers);
16670
16671   // save level identification information
16672   setString(&snapshot_level_identifier, leveldir_current->identifier);
16673   snapshot_level_nr = level_nr;
16674 }
16675
16676 boolean CheckSaveEngineSnapshotToList(void)
16677 {
16678   boolean save_snapshot =
16679     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
16680      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
16681       game.snapshot.changed_action) ||
16682      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16683       game.snapshot.collected_item));
16684
16685   game.snapshot.changed_action = FALSE;
16686   game.snapshot.collected_item = FALSE;
16687   game.snapshot.save_snapshot = save_snapshot;
16688
16689   return save_snapshot;
16690 }
16691
16692 void SaveEngineSnapshotToList(void)
16693 {
16694   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
16695       tape.quick_resume)
16696     return;
16697
16698   ListNode *buffers = SaveEngineSnapshotBuffers();
16699
16700   // finally save all snapshot buffers to snapshot list
16701   SaveSnapshotToList(buffers);
16702 }
16703
16704 void SaveEngineSnapshotToListInitial(void)
16705 {
16706   FreeEngineSnapshotList();
16707
16708   SaveEngineSnapshotToList();
16709 }
16710
16711 static void LoadEngineSnapshotValues(void)
16712 {
16713   // restore special values from snapshot structure
16714
16715   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
16716     LoadEngineSnapshotValues_RND();
16717   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
16718     LoadEngineSnapshotValues_EM();
16719   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
16720     LoadEngineSnapshotValues_SP();
16721   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
16722     LoadEngineSnapshotValues_MM();
16723 }
16724
16725 void LoadEngineSnapshotSingle(void)
16726 {
16727   LoadSnapshotSingle();
16728
16729   LoadEngineSnapshotValues();
16730 }
16731
16732 static void LoadEngineSnapshot_Undo(int steps)
16733 {
16734   LoadSnapshotFromList_Older(steps);
16735
16736   LoadEngineSnapshotValues();
16737 }
16738
16739 static void LoadEngineSnapshot_Redo(int steps)
16740 {
16741   LoadSnapshotFromList_Newer(steps);
16742
16743   LoadEngineSnapshotValues();
16744 }
16745
16746 boolean CheckEngineSnapshotSingle(void)
16747 {
16748   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16749           snapshot_level_nr == level_nr);
16750 }
16751
16752 boolean CheckEngineSnapshotList(void)
16753 {
16754   return CheckSnapshotList();
16755 }
16756
16757
16758 // ---------- new game button stuff -------------------------------------------
16759
16760 static struct
16761 {
16762   int graphic;
16763   struct XY *pos;
16764   int gadget_id;
16765   boolean *setup_value;
16766   boolean allowed_on_tape;
16767   boolean is_touch_button;
16768   char *infotext;
16769 } gamebutton_info[NUM_GAME_BUTTONS] =
16770 {
16771   {
16772     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16773     GAME_CTRL_ID_STOP,                          NULL,
16774     TRUE, FALSE,                                "stop game"
16775   },
16776   {
16777     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16778     GAME_CTRL_ID_PAUSE,                         NULL,
16779     TRUE, FALSE,                                "pause game"
16780   },
16781   {
16782     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16783     GAME_CTRL_ID_PLAY,                          NULL,
16784     TRUE, FALSE,                                "play game"
16785   },
16786   {
16787     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16788     GAME_CTRL_ID_UNDO,                          NULL,
16789     TRUE, FALSE,                                "undo step"
16790   },
16791   {
16792     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16793     GAME_CTRL_ID_REDO,                          NULL,
16794     TRUE, FALSE,                                "redo step"
16795   },
16796   {
16797     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16798     GAME_CTRL_ID_SAVE,                          NULL,
16799     TRUE, FALSE,                                "save game"
16800   },
16801   {
16802     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16803     GAME_CTRL_ID_PAUSE2,                        NULL,
16804     TRUE, FALSE,                                "pause game"
16805   },
16806   {
16807     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16808     GAME_CTRL_ID_LOAD,                          NULL,
16809     TRUE, FALSE,                                "load game"
16810   },
16811   {
16812     IMG_GFX_GAME_BUTTON_RESTART,                &game.button.restart,
16813     GAME_CTRL_ID_RESTART,                       NULL,
16814     TRUE, FALSE,                                "restart game"
16815   },
16816   {
16817     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16818     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16819     FALSE, FALSE,                               "stop game"
16820   },
16821   {
16822     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16823     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16824     FALSE, FALSE,                               "pause game"
16825   },
16826   {
16827     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16828     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16829     FALSE, FALSE,                               "play game"
16830   },
16831   {
16832     IMG_GFX_GAME_BUTTON_PANEL_RESTART,          &game.button.panel_restart,
16833     GAME_CTRL_ID_PANEL_RESTART,                 NULL,
16834     FALSE, FALSE,                               "restart game"
16835   },
16836   {
16837     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16838     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16839     FALSE, TRUE,                                "stop game"
16840   },
16841   {
16842     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16843     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16844     FALSE, TRUE,                                "pause game"
16845   },
16846   {
16847     IMG_GFX_GAME_BUTTON_TOUCH_RESTART,          &game.button.touch_restart,
16848     GAME_CTRL_ID_TOUCH_RESTART,                 NULL,
16849     FALSE, TRUE,                                "restart game"
16850   },
16851   {
16852     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16853     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16854     TRUE, FALSE,                                "background music on/off"
16855   },
16856   {
16857     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16858     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16859     TRUE, FALSE,                                "sound loops on/off"
16860   },
16861   {
16862     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16863     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16864     TRUE, FALSE,                                "normal sounds on/off"
16865   },
16866   {
16867     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16868     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16869     FALSE, FALSE,                               "background music on/off"
16870   },
16871   {
16872     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16873     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16874     FALSE, FALSE,                               "sound loops on/off"
16875   },
16876   {
16877     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16878     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16879     FALSE, FALSE,                               "normal sounds on/off"
16880   }
16881 };
16882
16883 void CreateGameButtons(void)
16884 {
16885   int i;
16886
16887   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16888   {
16889     int graphic = gamebutton_info[i].graphic;
16890     struct GraphicInfo *gfx = &graphic_info[graphic];
16891     struct XY *pos = gamebutton_info[i].pos;
16892     struct GadgetInfo *gi;
16893     int button_type;
16894     boolean checked;
16895     unsigned int event_mask;
16896     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16897     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16898     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16899     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16900     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16901     int gd_x   = gfx->src_x;
16902     int gd_y   = gfx->src_y;
16903     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16904     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16905     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16906     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16907     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16908     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16909     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16910     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16911     int id = i;
16912
16913     // do not use touch buttons if overlay touch buttons are disabled
16914     if (is_touch_button && !setup.touch.overlay_buttons)
16915       continue;
16916
16917     if (gfx->bitmap == NULL)
16918     {
16919       game_gadget[id] = NULL;
16920
16921       continue;
16922     }
16923
16924     if (id == GAME_CTRL_ID_STOP ||
16925         id == GAME_CTRL_ID_PANEL_STOP ||
16926         id == GAME_CTRL_ID_TOUCH_STOP ||
16927         id == GAME_CTRL_ID_PLAY ||
16928         id == GAME_CTRL_ID_PANEL_PLAY ||
16929         id == GAME_CTRL_ID_SAVE ||
16930         id == GAME_CTRL_ID_LOAD ||
16931         id == GAME_CTRL_ID_RESTART ||
16932         id == GAME_CTRL_ID_PANEL_RESTART ||
16933         id == GAME_CTRL_ID_TOUCH_RESTART)
16934     {
16935       button_type = GD_TYPE_NORMAL_BUTTON;
16936       checked = FALSE;
16937       event_mask = GD_EVENT_RELEASED;
16938     }
16939     else if (id == GAME_CTRL_ID_UNDO ||
16940              id == GAME_CTRL_ID_REDO)
16941     {
16942       button_type = GD_TYPE_NORMAL_BUTTON;
16943       checked = FALSE;
16944       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16945     }
16946     else
16947     {
16948       button_type = GD_TYPE_CHECK_BUTTON;
16949       checked = (gamebutton_info[i].setup_value != NULL ?
16950                  *gamebutton_info[i].setup_value : FALSE);
16951       event_mask = GD_EVENT_PRESSED;
16952     }
16953
16954     gi = CreateGadget(GDI_CUSTOM_ID, id,
16955                       GDI_IMAGE_ID, graphic,
16956                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16957                       GDI_X, base_x + x,
16958                       GDI_Y, base_y + y,
16959                       GDI_WIDTH, gfx->width,
16960                       GDI_HEIGHT, gfx->height,
16961                       GDI_TYPE, button_type,
16962                       GDI_STATE, GD_BUTTON_UNPRESSED,
16963                       GDI_CHECKED, checked,
16964                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16965                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16966                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16967                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16968                       GDI_DIRECT_DRAW, FALSE,
16969                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16970                       GDI_EVENT_MASK, event_mask,
16971                       GDI_CALLBACK_ACTION, HandleGameButtons,
16972                       GDI_END);
16973
16974     if (gi == NULL)
16975       Fail("cannot create gadget");
16976
16977     game_gadget[id] = gi;
16978   }
16979 }
16980
16981 void FreeGameButtons(void)
16982 {
16983   int i;
16984
16985   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16986     FreeGadget(game_gadget[i]);
16987 }
16988
16989 static void UnmapGameButtonsAtSamePosition(int id)
16990 {
16991   int i;
16992
16993   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16994     if (i != id &&
16995         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16996         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16997       UnmapGadget(game_gadget[i]);
16998 }
16999
17000 static void UnmapGameButtonsAtSamePosition_All(void)
17001 {
17002   if (setup.show_load_save_buttons)
17003   {
17004     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17005     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17006     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17007   }
17008   else if (setup.show_undo_redo_buttons)
17009   {
17010     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17011     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
17012     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17013   }
17014   else
17015   {
17016     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
17017     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
17018     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
17019
17020     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
17021     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
17022     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
17023   }
17024 }
17025
17026 void MapLoadSaveButtons(void)
17027 {
17028   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
17029   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
17030
17031   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
17032   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
17033 }
17034
17035 void MapUndoRedoButtons(void)
17036 {
17037   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
17038   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
17039
17040   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
17041   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
17042 }
17043
17044 void ModifyPauseButtons(void)
17045 {
17046   static int ids[] =
17047   {
17048     GAME_CTRL_ID_PAUSE,
17049     GAME_CTRL_ID_PAUSE2,
17050     GAME_CTRL_ID_PANEL_PAUSE,
17051     GAME_CTRL_ID_TOUCH_PAUSE,
17052     -1
17053   };
17054   int i;
17055
17056   // do not redraw pause button on closed door (may happen when restarting game)
17057   if (!(GetDoorState() & DOOR_OPEN_1))
17058     return;
17059
17060   for (i = 0; ids[i] > -1; i++)
17061     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
17062 }
17063
17064 static void MapGameButtonsExt(boolean on_tape)
17065 {
17066   int i;
17067
17068   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17069   {
17070     if ((i == GAME_CTRL_ID_UNDO ||
17071          i == GAME_CTRL_ID_REDO) &&
17072         game_status != GAME_MODE_PLAYING)
17073       continue;
17074
17075     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17076       MapGadget(game_gadget[i]);
17077   }
17078
17079   UnmapGameButtonsAtSamePosition_All();
17080
17081   RedrawGameButtons();
17082 }
17083
17084 static void UnmapGameButtonsExt(boolean on_tape)
17085 {
17086   int i;
17087
17088   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17089     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17090       UnmapGadget(game_gadget[i]);
17091 }
17092
17093 static void RedrawGameButtonsExt(boolean on_tape)
17094 {
17095   int i;
17096
17097   for (i = 0; i < NUM_GAME_BUTTONS; i++)
17098     if (!on_tape || gamebutton_info[i].allowed_on_tape)
17099       RedrawGadget(game_gadget[i]);
17100 }
17101
17102 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
17103 {
17104   if (gi == NULL)
17105     return;
17106
17107   gi->checked = state;
17108 }
17109
17110 static void RedrawSoundButtonGadget(int id)
17111 {
17112   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
17113              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
17114              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
17115              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
17116              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
17117              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
17118              id);
17119
17120   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
17121   RedrawGadget(game_gadget[id2]);
17122 }
17123
17124 void MapGameButtons(void)
17125 {
17126   MapGameButtonsExt(FALSE);
17127 }
17128
17129 void UnmapGameButtons(void)
17130 {
17131   UnmapGameButtonsExt(FALSE);
17132 }
17133
17134 void RedrawGameButtons(void)
17135 {
17136   RedrawGameButtonsExt(FALSE);
17137 }
17138
17139 void MapGameButtonsOnTape(void)
17140 {
17141   MapGameButtonsExt(TRUE);
17142 }
17143
17144 void UnmapGameButtonsOnTape(void)
17145 {
17146   UnmapGameButtonsExt(TRUE);
17147 }
17148
17149 void RedrawGameButtonsOnTape(void)
17150 {
17151   RedrawGameButtonsExt(TRUE);
17152 }
17153
17154 static void GameUndoRedoExt(void)
17155 {
17156   ClearPlayerAction();
17157
17158   tape.pausing = TRUE;
17159
17160   RedrawPlayfield();
17161   UpdateAndDisplayGameControlValues();
17162
17163   DrawCompleteVideoDisplay();
17164   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
17165   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
17166   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
17167
17168   ModifyPauseButtons();
17169
17170   BackToFront();
17171 }
17172
17173 static void GameUndo(int steps)
17174 {
17175   if (!CheckEngineSnapshotList())
17176     return;
17177
17178   int tape_property_bits = tape.property_bits;
17179
17180   LoadEngineSnapshot_Undo(steps);
17181
17182   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17183
17184   GameUndoRedoExt();
17185 }
17186
17187 static void GameRedo(int steps)
17188 {
17189   if (!CheckEngineSnapshotList())
17190     return;
17191
17192   int tape_property_bits = tape.property_bits;
17193
17194   LoadEngineSnapshot_Redo(steps);
17195
17196   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
17197
17198   GameUndoRedoExt();
17199 }
17200
17201 static void HandleGameButtonsExt(int id, int button)
17202 {
17203   static boolean game_undo_executed = FALSE;
17204   int steps = BUTTON_STEPSIZE(button);
17205   boolean handle_game_buttons =
17206     (game_status == GAME_MODE_PLAYING ||
17207      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
17208
17209   if (!handle_game_buttons)
17210     return;
17211
17212   switch (id)
17213   {
17214     case GAME_CTRL_ID_STOP:
17215     case GAME_CTRL_ID_PANEL_STOP:
17216     case GAME_CTRL_ID_TOUCH_STOP:
17217       TapeStopGame();
17218
17219       break;
17220
17221     case GAME_CTRL_ID_PAUSE:
17222     case GAME_CTRL_ID_PAUSE2:
17223     case GAME_CTRL_ID_PANEL_PAUSE:
17224     case GAME_CTRL_ID_TOUCH_PAUSE:
17225       if (network.enabled && game_status == GAME_MODE_PLAYING)
17226       {
17227         if (tape.pausing)
17228           SendToServer_ContinuePlaying();
17229         else
17230           SendToServer_PausePlaying();
17231       }
17232       else
17233         TapeTogglePause(TAPE_TOGGLE_MANUAL);
17234
17235       game_undo_executed = FALSE;
17236
17237       break;
17238
17239     case GAME_CTRL_ID_PLAY:
17240     case GAME_CTRL_ID_PANEL_PLAY:
17241       if (game_status == GAME_MODE_MAIN)
17242       {
17243         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
17244       }
17245       else if (tape.pausing)
17246       {
17247         if (network.enabled)
17248           SendToServer_ContinuePlaying();
17249         else
17250           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
17251       }
17252       break;
17253
17254     case GAME_CTRL_ID_UNDO:
17255       // Important: When using "save snapshot when collecting an item" mode,
17256       // load last (current) snapshot for first "undo" after pressing "pause"
17257       // (else the last-but-one snapshot would be loaded, because the snapshot
17258       // pointer already points to the last snapshot when pressing "pause",
17259       // which is fine for "every step/move" mode, but not for "every collect")
17260       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
17261           !game_undo_executed)
17262         steps--;
17263
17264       game_undo_executed = TRUE;
17265
17266       GameUndo(steps);
17267       break;
17268
17269     case GAME_CTRL_ID_REDO:
17270       GameRedo(steps);
17271       break;
17272
17273     case GAME_CTRL_ID_SAVE:
17274       TapeQuickSave();
17275       break;
17276
17277     case GAME_CTRL_ID_LOAD:
17278       TapeQuickLoad();
17279       break;
17280
17281     case GAME_CTRL_ID_RESTART:
17282     case GAME_CTRL_ID_PANEL_RESTART:
17283     case GAME_CTRL_ID_TOUCH_RESTART:
17284       TapeRestartGame();
17285
17286       break;
17287
17288     case SOUND_CTRL_ID_MUSIC:
17289     case SOUND_CTRL_ID_PANEL_MUSIC:
17290       if (setup.sound_music)
17291       { 
17292         setup.sound_music = FALSE;
17293
17294         FadeMusic();
17295       }
17296       else if (audio.music_available)
17297       { 
17298         setup.sound = setup.sound_music = TRUE;
17299
17300         SetAudioMode(setup.sound);
17301
17302         if (game_status == GAME_MODE_PLAYING)
17303           PlayLevelMusic();
17304       }
17305
17306       RedrawSoundButtonGadget(id);
17307
17308       break;
17309
17310     case SOUND_CTRL_ID_LOOPS:
17311     case SOUND_CTRL_ID_PANEL_LOOPS:
17312       if (setup.sound_loops)
17313         setup.sound_loops = FALSE;
17314       else if (audio.loops_available)
17315       {
17316         setup.sound = setup.sound_loops = TRUE;
17317
17318         SetAudioMode(setup.sound);
17319       }
17320
17321       RedrawSoundButtonGadget(id);
17322
17323       break;
17324
17325     case SOUND_CTRL_ID_SIMPLE:
17326     case SOUND_CTRL_ID_PANEL_SIMPLE:
17327       if (setup.sound_simple)
17328         setup.sound_simple = FALSE;
17329       else if (audio.sound_available)
17330       {
17331         setup.sound = setup.sound_simple = TRUE;
17332
17333         SetAudioMode(setup.sound);
17334       }
17335
17336       RedrawSoundButtonGadget(id);
17337
17338       break;
17339
17340     default:
17341       break;
17342   }
17343 }
17344
17345 static void HandleGameButtons(struct GadgetInfo *gi)
17346 {
17347   HandleGameButtonsExt(gi->custom_id, gi->event.button);
17348 }
17349
17350 void HandleSoundButtonKeys(Key key)
17351 {
17352   if (key == setup.shortcut.sound_simple)
17353     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
17354   else if (key == setup.shortcut.sound_loops)
17355     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
17356   else if (key == setup.shortcut.sound_music)
17357     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
17358 }