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