added new animation mode ".global_anim_sync" for game element graphics
[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_INVENTORY_COUNT              2
93 #define GAME_PANEL_INVENTORY_FIRST_1            3
94 #define GAME_PANEL_INVENTORY_FIRST_2            4
95 #define GAME_PANEL_INVENTORY_FIRST_3            5
96 #define GAME_PANEL_INVENTORY_FIRST_4            6
97 #define GAME_PANEL_INVENTORY_FIRST_5            7
98 #define GAME_PANEL_INVENTORY_FIRST_6            8
99 #define GAME_PANEL_INVENTORY_FIRST_7            9
100 #define GAME_PANEL_INVENTORY_FIRST_8            10
101 #define GAME_PANEL_INVENTORY_LAST_1             11
102 #define GAME_PANEL_INVENTORY_LAST_2             12
103 #define GAME_PANEL_INVENTORY_LAST_3             13
104 #define GAME_PANEL_INVENTORY_LAST_4             14
105 #define GAME_PANEL_INVENTORY_LAST_5             15
106 #define GAME_PANEL_INVENTORY_LAST_6             16
107 #define GAME_PANEL_INVENTORY_LAST_7             17
108 #define GAME_PANEL_INVENTORY_LAST_8             18
109 #define GAME_PANEL_KEY_1                        19
110 #define GAME_PANEL_KEY_2                        20
111 #define GAME_PANEL_KEY_3                        21
112 #define GAME_PANEL_KEY_4                        22
113 #define GAME_PANEL_KEY_5                        23
114 #define GAME_PANEL_KEY_6                        24
115 #define GAME_PANEL_KEY_7                        25
116 #define GAME_PANEL_KEY_8                        26
117 #define GAME_PANEL_KEY_WHITE                    27
118 #define GAME_PANEL_KEY_WHITE_COUNT              28
119 #define GAME_PANEL_SCORE                        29
120 #define GAME_PANEL_HIGHSCORE                    30
121 #define GAME_PANEL_TIME                         31
122 #define GAME_PANEL_TIME_HH                      32
123 #define GAME_PANEL_TIME_MM                      33
124 #define GAME_PANEL_TIME_SS                      34
125 #define GAME_PANEL_TIME_ANIM                    35
126 #define GAME_PANEL_HEALTH                       36
127 #define GAME_PANEL_HEALTH_ANIM                  37
128 #define GAME_PANEL_FRAME                        38
129 #define GAME_PANEL_SHIELD_NORMAL                39
130 #define GAME_PANEL_SHIELD_NORMAL_TIME           40
131 #define GAME_PANEL_SHIELD_DEADLY                41
132 #define GAME_PANEL_SHIELD_DEADLY_TIME           42
133 #define GAME_PANEL_EXIT                         43
134 #define GAME_PANEL_EMC_MAGIC_BALL               44
135 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        45
136 #define GAME_PANEL_LIGHT_SWITCH                 46
137 #define GAME_PANEL_LIGHT_SWITCH_TIME            47
138 #define GAME_PANEL_TIMEGATE_SWITCH              48
139 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         49
140 #define GAME_PANEL_SWITCHGATE_SWITCH            50
141 #define GAME_PANEL_EMC_LENSES                   51
142 #define GAME_PANEL_EMC_LENSES_TIME              52
143 #define GAME_PANEL_EMC_MAGNIFIER                53
144 #define GAME_PANEL_EMC_MAGNIFIER_TIME           54
145 #define GAME_PANEL_BALLOON_SWITCH               55
146 #define GAME_PANEL_DYNABOMB_NUMBER              56
147 #define GAME_PANEL_DYNABOMB_SIZE                57
148 #define GAME_PANEL_DYNABOMB_POWER               58
149 #define GAME_PANEL_PENGUINS                     59
150 #define GAME_PANEL_SOKOBAN_OBJECTS              60
151 #define GAME_PANEL_SOKOBAN_FIELDS               61
152 #define GAME_PANEL_ROBOT_WHEEL                  62
153 #define GAME_PANEL_CONVEYOR_BELT_1              63
154 #define GAME_PANEL_CONVEYOR_BELT_2              64
155 #define GAME_PANEL_CONVEYOR_BELT_3              65
156 #define GAME_PANEL_CONVEYOR_BELT_4              66
157 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       67
158 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       68
159 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       69
160 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       70
161 #define GAME_PANEL_MAGIC_WALL                   71
162 #define GAME_PANEL_MAGIC_WALL_TIME              72
163 #define GAME_PANEL_GRAVITY_STATE                73
164 #define GAME_PANEL_GRAPHIC_1                    74
165 #define GAME_PANEL_GRAPHIC_2                    75
166 #define GAME_PANEL_GRAPHIC_3                    76
167 #define GAME_PANEL_GRAPHIC_4                    77
168 #define GAME_PANEL_GRAPHIC_5                    78
169 #define GAME_PANEL_GRAPHIC_6                    79
170 #define GAME_PANEL_GRAPHIC_7                    80
171 #define GAME_PANEL_GRAPHIC_8                    81
172 #define GAME_PANEL_ELEMENT_1                    82
173 #define GAME_PANEL_ELEMENT_2                    83
174 #define GAME_PANEL_ELEMENT_3                    84
175 #define GAME_PANEL_ELEMENT_4                    85
176 #define GAME_PANEL_ELEMENT_5                    86
177 #define GAME_PANEL_ELEMENT_6                    87
178 #define GAME_PANEL_ELEMENT_7                    88
179 #define GAME_PANEL_ELEMENT_8                    89
180 #define GAME_PANEL_ELEMENT_COUNT_1              90
181 #define GAME_PANEL_ELEMENT_COUNT_2              91
182 #define GAME_PANEL_ELEMENT_COUNT_3              92
183 #define GAME_PANEL_ELEMENT_COUNT_4              93
184 #define GAME_PANEL_ELEMENT_COUNT_5              94
185 #define GAME_PANEL_ELEMENT_COUNT_6              95
186 #define GAME_PANEL_ELEMENT_COUNT_7              96
187 #define GAME_PANEL_ELEMENT_COUNT_8              97
188 #define GAME_PANEL_CE_SCORE_1                   98
189 #define GAME_PANEL_CE_SCORE_2                   99
190 #define GAME_PANEL_CE_SCORE_3                   100
191 #define GAME_PANEL_CE_SCORE_4                   101
192 #define GAME_PANEL_CE_SCORE_5                   102
193 #define GAME_PANEL_CE_SCORE_6                   103
194 #define GAME_PANEL_CE_SCORE_7                   104
195 #define GAME_PANEL_CE_SCORE_8                   105
196 #define GAME_PANEL_CE_SCORE_1_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_2_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_3_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_4_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_5_ELEMENT           110
201 #define GAME_PANEL_CE_SCORE_6_ELEMENT           111
202 #define GAME_PANEL_CE_SCORE_7_ELEMENT           112
203 #define GAME_PANEL_CE_SCORE_8_ELEMENT           113
204 #define GAME_PANEL_PLAYER_NAME                  114
205 #define GAME_PANEL_LEVEL_NAME                   115
206 #define GAME_PANEL_LEVEL_AUTHOR                 116
207
208 #define NUM_GAME_PANEL_CONTROLS                 117
209
210 struct GamePanelOrderInfo
211 {
212   int nr;
213   int sort_priority;
214 };
215
216 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
217
218 struct GamePanelControlInfo
219 {
220   int nr;
221
222   struct TextPosInfo *pos;
223   int type;
224
225   int graphic, graphic_active;
226
227   int value, last_value;
228   int frame, last_frame;
229   int gfx_frame;
230   int gfx_random;
231 };
232
233 static struct GamePanelControlInfo game_panel_controls[] =
234 {
235   {
236     GAME_PANEL_LEVEL_NUMBER,
237     &game.panel.level_number,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_GEMS,
242     &game.panel.gems,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_COUNT,
247     &game.panel.inventory_count,
248     TYPE_INTEGER,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_1,
252     &game.panel.inventory_first[0],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_2,
257     &game.panel.inventory_first[1],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_3,
262     &game.panel.inventory_first[2],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_4,
267     &game.panel.inventory_first[3],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_5,
272     &game.panel.inventory_first[4],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_6,
277     &game.panel.inventory_first[5],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_7,
282     &game.panel.inventory_first[6],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_FIRST_8,
287     &game.panel.inventory_first[7],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_1,
292     &game.panel.inventory_last[0],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_2,
297     &game.panel.inventory_last[1],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_3,
302     &game.panel.inventory_last[2],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_4,
307     &game.panel.inventory_last[3],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_5,
312     &game.panel.inventory_last[4],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_6,
317     &game.panel.inventory_last[5],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_7,
322     &game.panel.inventory_last[6],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_INVENTORY_LAST_8,
327     &game.panel.inventory_last[7],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_1,
332     &game.panel.key[0],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_2,
337     &game.panel.key[1],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_3,
342     &game.panel.key[2],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_4,
347     &game.panel.key[3],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_5,
352     &game.panel.key[4],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_6,
357     &game.panel.key[5],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_7,
362     &game.panel.key[6],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_8,
367     &game.panel.key[7],
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE,
372     &game.panel.key_white,
373     TYPE_ELEMENT,
374   },
375   {
376     GAME_PANEL_KEY_WHITE_COUNT,
377     &game.panel.key_white_count,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_SCORE,
382     &game.panel.score,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_HIGHSCORE,
387     &game.panel.highscore,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME,
392     &game.panel.time,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_HH,
397     &game.panel.time_hh,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_MM,
402     &game.panel.time_mm,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_TIME_SS,
407     &game.panel.time_ss,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_TIME_ANIM,
412     &game.panel.time_anim,
413     TYPE_GRAPHIC,
414
415     IMG_GFX_GAME_PANEL_TIME_ANIM,
416     IMG_GFX_GAME_PANEL_TIME_ANIM_ACTIVE
417   },
418   {
419     GAME_PANEL_HEALTH,
420     &game.panel.health,
421     TYPE_INTEGER,
422   },
423   {
424     GAME_PANEL_HEALTH_ANIM,
425     &game.panel.health_anim,
426     TYPE_GRAPHIC,
427
428     IMG_GFX_GAME_PANEL_HEALTH_ANIM,
429     IMG_GFX_GAME_PANEL_HEALTH_ANIM_ACTIVE
430   },
431   {
432     GAME_PANEL_FRAME,
433     &game.panel.frame,
434     TYPE_INTEGER,
435   },
436   {
437     GAME_PANEL_SHIELD_NORMAL,
438     &game.panel.shield_normal,
439     TYPE_ELEMENT,
440   },
441   {
442     GAME_PANEL_SHIELD_NORMAL_TIME,
443     &game.panel.shield_normal_time,
444     TYPE_INTEGER,
445   },
446   {
447     GAME_PANEL_SHIELD_DEADLY,
448     &game.panel.shield_deadly,
449     TYPE_ELEMENT,
450   },
451   {
452     GAME_PANEL_SHIELD_DEADLY_TIME,
453     &game.panel.shield_deadly_time,
454     TYPE_INTEGER,
455   },
456   {
457     GAME_PANEL_EXIT,
458     &game.panel.exit,
459     TYPE_ELEMENT,
460   },
461   {
462     GAME_PANEL_EMC_MAGIC_BALL,
463     &game.panel.emc_magic_ball,
464     TYPE_ELEMENT,
465   },
466   {
467     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
468     &game.panel.emc_magic_ball_switch,
469     TYPE_ELEMENT,
470   },
471   {
472     GAME_PANEL_LIGHT_SWITCH,
473     &game.panel.light_switch,
474     TYPE_ELEMENT,
475   },
476   {
477     GAME_PANEL_LIGHT_SWITCH_TIME,
478     &game.panel.light_switch_time,
479     TYPE_INTEGER,
480   },
481   {
482     GAME_PANEL_TIMEGATE_SWITCH,
483     &game.panel.timegate_switch,
484     TYPE_ELEMENT,
485   },
486   {
487     GAME_PANEL_TIMEGATE_SWITCH_TIME,
488     &game.panel.timegate_switch_time,
489     TYPE_INTEGER,
490   },
491   {
492     GAME_PANEL_SWITCHGATE_SWITCH,
493     &game.panel.switchgate_switch,
494     TYPE_ELEMENT,
495   },
496   {
497     GAME_PANEL_EMC_LENSES,
498     &game.panel.emc_lenses,
499     TYPE_ELEMENT,
500   },
501   {
502     GAME_PANEL_EMC_LENSES_TIME,
503     &game.panel.emc_lenses_time,
504     TYPE_INTEGER,
505   },
506   {
507     GAME_PANEL_EMC_MAGNIFIER,
508     &game.panel.emc_magnifier,
509     TYPE_ELEMENT,
510   },
511   {
512     GAME_PANEL_EMC_MAGNIFIER_TIME,
513     &game.panel.emc_magnifier_time,
514     TYPE_INTEGER,
515   },
516   {
517     GAME_PANEL_BALLOON_SWITCH,
518     &game.panel.balloon_switch,
519     TYPE_ELEMENT,
520   },
521   {
522     GAME_PANEL_DYNABOMB_NUMBER,
523     &game.panel.dynabomb_number,
524     TYPE_INTEGER,
525   },
526   {
527     GAME_PANEL_DYNABOMB_SIZE,
528     &game.panel.dynabomb_size,
529     TYPE_INTEGER,
530   },
531   {
532     GAME_PANEL_DYNABOMB_POWER,
533     &game.panel.dynabomb_power,
534     TYPE_ELEMENT,
535   },
536   {
537     GAME_PANEL_PENGUINS,
538     &game.panel.penguins,
539     TYPE_INTEGER,
540   },
541   {
542     GAME_PANEL_SOKOBAN_OBJECTS,
543     &game.panel.sokoban_objects,
544     TYPE_INTEGER,
545   },
546   {
547     GAME_PANEL_SOKOBAN_FIELDS,
548     &game.panel.sokoban_fields,
549     TYPE_INTEGER,
550   },
551   {
552     GAME_PANEL_ROBOT_WHEEL,
553     &game.panel.robot_wheel,
554     TYPE_ELEMENT,
555   },
556   {
557     GAME_PANEL_CONVEYOR_BELT_1,
558     &game.panel.conveyor_belt[0],
559     TYPE_ELEMENT,
560   },
561   {
562     GAME_PANEL_CONVEYOR_BELT_2,
563     &game.panel.conveyor_belt[1],
564     TYPE_ELEMENT,
565   },
566   {
567     GAME_PANEL_CONVEYOR_BELT_3,
568     &game.panel.conveyor_belt[2],
569     TYPE_ELEMENT,
570   },
571   {
572     GAME_PANEL_CONVEYOR_BELT_4,
573     &game.panel.conveyor_belt[3],
574     TYPE_ELEMENT,
575   },
576   {
577     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
578     &game.panel.conveyor_belt_switch[0],
579     TYPE_ELEMENT,
580   },
581   {
582     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
583     &game.panel.conveyor_belt_switch[1],
584     TYPE_ELEMENT,
585   },
586   {
587     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
588     &game.panel.conveyor_belt_switch[2],
589     TYPE_ELEMENT,
590   },
591   {
592     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
593     &game.panel.conveyor_belt_switch[3],
594     TYPE_ELEMENT,
595   },
596   {
597     GAME_PANEL_MAGIC_WALL,
598     &game.panel.magic_wall,
599     TYPE_ELEMENT,
600   },
601   {
602     GAME_PANEL_MAGIC_WALL_TIME,
603     &game.panel.magic_wall_time,
604     TYPE_INTEGER,
605   },
606   {
607     GAME_PANEL_GRAVITY_STATE,
608     &game.panel.gravity_state,
609     TYPE_STRING,
610   },
611   {
612     GAME_PANEL_GRAPHIC_1,
613     &game.panel.graphic[0],
614     TYPE_ELEMENT,
615   },
616   {
617     GAME_PANEL_GRAPHIC_2,
618     &game.panel.graphic[1],
619     TYPE_ELEMENT,
620   },
621   {
622     GAME_PANEL_GRAPHIC_3,
623     &game.panel.graphic[2],
624     TYPE_ELEMENT,
625   },
626   {
627     GAME_PANEL_GRAPHIC_4,
628     &game.panel.graphic[3],
629     TYPE_ELEMENT,
630   },
631   {
632     GAME_PANEL_GRAPHIC_5,
633     &game.panel.graphic[4],
634     TYPE_ELEMENT,
635   },
636   {
637     GAME_PANEL_GRAPHIC_6,
638     &game.panel.graphic[5],
639     TYPE_ELEMENT,
640   },
641   {
642     GAME_PANEL_GRAPHIC_7,
643     &game.panel.graphic[6],
644     TYPE_ELEMENT,
645   },
646   {
647     GAME_PANEL_GRAPHIC_8,
648     &game.panel.graphic[7],
649     TYPE_ELEMENT,
650   },
651   {
652     GAME_PANEL_ELEMENT_1,
653     &game.panel.element[0],
654     TYPE_ELEMENT,
655   },
656   {
657     GAME_PANEL_ELEMENT_2,
658     &game.panel.element[1],
659     TYPE_ELEMENT,
660   },
661   {
662     GAME_PANEL_ELEMENT_3,
663     &game.panel.element[2],
664     TYPE_ELEMENT,
665   },
666   {
667     GAME_PANEL_ELEMENT_4,
668     &game.panel.element[3],
669     TYPE_ELEMENT,
670   },
671   {
672     GAME_PANEL_ELEMENT_5,
673     &game.panel.element[4],
674     TYPE_ELEMENT,
675   },
676   {
677     GAME_PANEL_ELEMENT_6,
678     &game.panel.element[5],
679     TYPE_ELEMENT,
680   },
681   {
682     GAME_PANEL_ELEMENT_7,
683     &game.panel.element[6],
684     TYPE_ELEMENT,
685   },
686   {
687     GAME_PANEL_ELEMENT_8,
688     &game.panel.element[7],
689     TYPE_ELEMENT,
690   },
691   {
692     GAME_PANEL_ELEMENT_COUNT_1,
693     &game.panel.element_count[0],
694     TYPE_INTEGER,
695   },
696   {
697     GAME_PANEL_ELEMENT_COUNT_2,
698     &game.panel.element_count[1],
699     TYPE_INTEGER,
700   },
701   {
702     GAME_PANEL_ELEMENT_COUNT_3,
703     &game.panel.element_count[2],
704     TYPE_INTEGER,
705   },
706   {
707     GAME_PANEL_ELEMENT_COUNT_4,
708     &game.panel.element_count[3],
709     TYPE_INTEGER,
710   },
711   {
712     GAME_PANEL_ELEMENT_COUNT_5,
713     &game.panel.element_count[4],
714     TYPE_INTEGER,
715   },
716   {
717     GAME_PANEL_ELEMENT_COUNT_6,
718     &game.panel.element_count[5],
719     TYPE_INTEGER,
720   },
721   {
722     GAME_PANEL_ELEMENT_COUNT_7,
723     &game.panel.element_count[6],
724     TYPE_INTEGER,
725   },
726   {
727     GAME_PANEL_ELEMENT_COUNT_8,
728     &game.panel.element_count[7],
729     TYPE_INTEGER,
730   },
731   {
732     GAME_PANEL_CE_SCORE_1,
733     &game.panel.ce_score[0],
734     TYPE_INTEGER,
735   },
736   {
737     GAME_PANEL_CE_SCORE_2,
738     &game.panel.ce_score[1],
739     TYPE_INTEGER,
740   },
741   {
742     GAME_PANEL_CE_SCORE_3,
743     &game.panel.ce_score[2],
744     TYPE_INTEGER,
745   },
746   {
747     GAME_PANEL_CE_SCORE_4,
748     &game.panel.ce_score[3],
749     TYPE_INTEGER,
750   },
751   {
752     GAME_PANEL_CE_SCORE_5,
753     &game.panel.ce_score[4],
754     TYPE_INTEGER,
755   },
756   {
757     GAME_PANEL_CE_SCORE_6,
758     &game.panel.ce_score[5],
759     TYPE_INTEGER,
760   },
761   {
762     GAME_PANEL_CE_SCORE_7,
763     &game.panel.ce_score[6],
764     TYPE_INTEGER,
765   },
766   {
767     GAME_PANEL_CE_SCORE_8,
768     &game.panel.ce_score[7],
769     TYPE_INTEGER,
770   },
771   {
772     GAME_PANEL_CE_SCORE_1_ELEMENT,
773     &game.panel.ce_score_element[0],
774     TYPE_ELEMENT,
775   },
776   {
777     GAME_PANEL_CE_SCORE_2_ELEMENT,
778     &game.panel.ce_score_element[1],
779     TYPE_ELEMENT,
780   },
781   {
782     GAME_PANEL_CE_SCORE_3_ELEMENT,
783     &game.panel.ce_score_element[2],
784     TYPE_ELEMENT,
785   },
786   {
787     GAME_PANEL_CE_SCORE_4_ELEMENT,
788     &game.panel.ce_score_element[3],
789     TYPE_ELEMENT,
790   },
791   {
792     GAME_PANEL_CE_SCORE_5_ELEMENT,
793     &game.panel.ce_score_element[4],
794     TYPE_ELEMENT,
795   },
796   {
797     GAME_PANEL_CE_SCORE_6_ELEMENT,
798     &game.panel.ce_score_element[5],
799     TYPE_ELEMENT,
800   },
801   {
802     GAME_PANEL_CE_SCORE_7_ELEMENT,
803     &game.panel.ce_score_element[6],
804     TYPE_ELEMENT,
805   },
806   {
807     GAME_PANEL_CE_SCORE_8_ELEMENT,
808     &game.panel.ce_score_element[7],
809     TYPE_ELEMENT,
810   },
811   {
812     GAME_PANEL_PLAYER_NAME,
813     &game.panel.player_name,
814     TYPE_STRING,
815   },
816   {
817     GAME_PANEL_LEVEL_NAME,
818     &game.panel.level_name,
819     TYPE_STRING,
820   },
821   {
822     GAME_PANEL_LEVEL_AUTHOR,
823     &game.panel.level_author,
824     TYPE_STRING,
825   },
826
827   {
828     -1,
829     NULL,
830     -1,
831   }
832 };
833
834 // values for delayed check of falling and moving elements and for collision
835 #define CHECK_DELAY_MOVING      3
836 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
837 #define CHECK_DELAY_COLLISION   2
838 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
839
840 // values for initial player move delay (initial delay counter value)
841 #define INITIAL_MOVE_DELAY_OFF  -1
842 #define INITIAL_MOVE_DELAY_ON   0
843
844 // values for player movement speed (which is in fact a delay value)
845 #define MOVE_DELAY_MIN_SPEED    32
846 #define MOVE_DELAY_NORMAL_SPEED 8
847 #define MOVE_DELAY_HIGH_SPEED   4
848 #define MOVE_DELAY_MAX_SPEED    1
849
850 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
851 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
852
853 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
854 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
855
856 // values for scroll positions
857 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
858                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
859                                  (x) - MIDPOSX)
860 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
861                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
862                                  (y) - MIDPOSY)
863
864 // values for other actions
865 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
866 #define MOVE_STEPSIZE_MIN       (1)
867 #define MOVE_STEPSIZE_MAX       (TILEX)
868
869 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
870 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
871
872 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
873
874 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
875                                  RND(element_info[e].push_delay_random))
876 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
877                                  RND(element_info[e].drop_delay_random))
878 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
879                                  RND(element_info[e].move_delay_random))
880 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
881                                     (element_info[e].move_delay_random))
882 #define GET_NEW_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
883                                  RND(element_info[e].step_delay_random))
884 #define GET_MAX_STEP_DELAY(e)   (   (element_info[e].step_delay_fixed) + \
885                                     (element_info[e].step_delay_random))
886 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
887                                  RND(element_info[e].ce_value_random_initial))
888 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
889 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
890                                  RND((c)->delay_random * (c)->delay_frames))
891 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
892                                  RND((c)->delay_random))
893
894
895 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
896          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
897
898 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
899         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
900          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
901          (be) + (e) - EL_SELF)
902
903 #define GET_PLAYER_FROM_BITS(p)                                         \
904         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
905
906 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
907         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
908          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
909          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
910          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
911          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
912          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
913          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
914          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
915          (e))
916
917 #define CAN_GROW_INTO(e)                                                \
918         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
919
920 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
921                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
922                                         (condition)))
923
924 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
925                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
926                                         (CAN_MOVE_INTO_ACID(e) &&       \
927                                          Tile[x][y] == EL_ACID) ||      \
928                                         (condition)))
929
930 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
931                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
932                                         (CAN_MOVE_INTO_ACID(e) &&       \
933                                          Tile[x][y] == EL_ACID) ||      \
934                                         (condition)))
935
936 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
937                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
938                                         (condition) ||                  \
939                                         (CAN_MOVE_INTO_ACID(e) &&       \
940                                          Tile[x][y] == EL_ACID) ||      \
941                                         (DONT_COLLIDE_WITH(e) &&        \
942                                          IS_PLAYER(x, y) &&             \
943                                          !PLAYER_ENEMY_PROTECTED(x, y))))
944
945 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
946         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
947
948 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
949         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
950
951 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
952         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Tile[x][y] == EL_EMC_PLANT)
953
954 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
955         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Tile[x][y]) || \
956                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
957
958 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
959         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
960
961 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
962         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Tile[x][y] == EL_DIAMOND)
963
964 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
965         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Tile[x][y]))
966
967 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
968         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Tile[x][y]))
969
970 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
971         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Tile[x][y]))
972
973 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
974         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Tile[x][y] == EL_EXIT_OPEN || \
975                                                  Tile[x][y] == EL_EM_EXIT_OPEN || \
976                                                  Tile[x][y] == EL_STEEL_EXIT_OPEN || \
977                                                  Tile[x][y] == EL_EM_STEEL_EXIT_OPEN || \
978                                                  IS_FOOD_PENGUIN(Tile[x][y])))
979 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
980         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
981
982 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
983         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
984
985 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
986         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
987
988 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
989         (IN_LEV_FIELD(x, y) && (Tile[x][y] == EL_EMC_SPRING_BUMPER ||   \
990                                 Tile[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
991
992 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
993
994 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
995                 (!IS_PLAYER(x, y) &&                                    \
996                  IS_EQUAL_OR_IN_GROUP(Tile[x][y], MOVE_ENTER_EL(e)))
997
998 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
999         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
1000
1001 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
1002 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
1003
1004 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
1005 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
1006 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
1007 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
1008
1009 #define MM_HEALTH(x)            (MIN(MAX(0, MAX_HEALTH - (x)), MAX_HEALTH))
1010
1011 // game button identifiers
1012 #define GAME_CTRL_ID_STOP               0
1013 #define GAME_CTRL_ID_PAUSE              1
1014 #define GAME_CTRL_ID_PLAY               2
1015 #define GAME_CTRL_ID_UNDO               3
1016 #define GAME_CTRL_ID_REDO               4
1017 #define GAME_CTRL_ID_SAVE               5
1018 #define GAME_CTRL_ID_PAUSE2             6
1019 #define GAME_CTRL_ID_LOAD               7
1020 #define GAME_CTRL_ID_PANEL_STOP         8
1021 #define GAME_CTRL_ID_PANEL_PAUSE        9
1022 #define GAME_CTRL_ID_PANEL_PLAY         10
1023 #define GAME_CTRL_ID_TOUCH_STOP         11
1024 #define GAME_CTRL_ID_TOUCH_PAUSE        12
1025 #define SOUND_CTRL_ID_MUSIC             13
1026 #define SOUND_CTRL_ID_LOOPS             14
1027 #define SOUND_CTRL_ID_SIMPLE            15
1028 #define SOUND_CTRL_ID_PANEL_MUSIC       16
1029 #define SOUND_CTRL_ID_PANEL_LOOPS       17
1030 #define SOUND_CTRL_ID_PANEL_SIMPLE      18
1031
1032 #define NUM_GAME_BUTTONS                19
1033
1034
1035 // forward declaration for internal use
1036
1037 static void CreateField(int, int, int);
1038
1039 static void ResetGfxAnimation(int, int);
1040
1041 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1042 static void AdvanceFrameAndPlayerCounters(int);
1043
1044 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1045 static boolean MovePlayer(struct PlayerInfo *, int, int);
1046 static void ScrollPlayer(struct PlayerInfo *, int);
1047 static void ScrollScreen(struct PlayerInfo *, int);
1048
1049 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1050 static boolean DigFieldByCE(int, int, int);
1051 static boolean SnapField(struct PlayerInfo *, int, int);
1052 static boolean DropElement(struct PlayerInfo *);
1053
1054 static void InitBeltMovement(void);
1055 static void CloseAllOpenTimegates(void);
1056 static void CheckGravityMovement(struct PlayerInfo *);
1057 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1058 static void KillPlayerUnlessEnemyProtected(int, int);
1059 static void KillPlayerUnlessExplosionProtected(int, int);
1060
1061 static void CheckNextToConditions(int, int);
1062 static void TestIfPlayerNextToCustomElement(int, int);
1063 static void TestIfPlayerTouchesCustomElement(int, int);
1064 static void TestIfElementNextToCustomElement(int, int);
1065 static void TestIfElementTouchesCustomElement(int, int);
1066 static void TestIfElementHitsCustomElement(int, int, int);
1067
1068 static void HandleElementChange(int, int, int);
1069 static void ExecuteCustomElementAction(int, int, int, int);
1070 static boolean ChangeElement(int, int, int, int);
1071
1072 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1073 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1074         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1075 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1076         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1077 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1078         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1079 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1080         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1081 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1082         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1083
1084 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1085 #define CheckElementChange(x, y, e, te, ev)                             \
1086         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1087 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1088         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1089 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1090         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1091 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1092         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1093
1094 static void PlayLevelSound(int, int, int);
1095 static void PlayLevelSoundNearest(int, int, int);
1096 static void PlayLevelSoundAction(int, int, int);
1097 static void PlayLevelSoundElementAction(int, int, int, int);
1098 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1099 static void PlayLevelSoundActionIfLoop(int, int, int);
1100 static void StopLevelSoundActionIfLoop(int, int, int);
1101 static void PlayLevelMusic(void);
1102 static void FadeLevelSoundsAndMusic(void);
1103
1104 static void HandleGameButtons(struct GadgetInfo *);
1105
1106 int AmoebaNeighbourNr(int, int);
1107 void AmoebaToDiamond(int, int);
1108 void ContinueMoving(int, int);
1109 void Bang(int, int);
1110 void InitMovDir(int, int);
1111 void InitAmoebaNr(int, int);
1112 void NewHighScore(int, boolean);
1113
1114 void TestIfGoodThingHitsBadThing(int, int, int);
1115 void TestIfBadThingHitsGoodThing(int, int, int);
1116 void TestIfPlayerTouchesBadThing(int, int);
1117 void TestIfPlayerRunsIntoBadThing(int, int, int);
1118 void TestIfBadThingTouchesPlayer(int, int);
1119 void TestIfBadThingRunsIntoPlayer(int, int, int);
1120 void TestIfFriendTouchesBadThing(int, int);
1121 void TestIfBadThingTouchesFriend(int, int);
1122 void TestIfBadThingTouchesOtherBadThing(int, int);
1123 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1124
1125 void KillPlayer(struct PlayerInfo *);
1126 void BuryPlayer(struct PlayerInfo *);
1127 void RemovePlayer(struct PlayerInfo *);
1128 void ExitPlayer(struct PlayerInfo *);
1129
1130 static int getInvisibleActiveFromInvisibleElement(int);
1131 static int getInvisibleFromInvisibleActiveElement(int);
1132
1133 static void TestFieldAfterSnapping(int, int, int, int, int);
1134
1135 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1136
1137 // for detection of endless loops, caused by custom element programming
1138 // (using maximal playfield width x 10 is just a rough approximation)
1139 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1140
1141 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1142 {                                                                       \
1143   if (recursion_loop_detected)                                          \
1144     return (rc);                                                        \
1145                                                                         \
1146   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1147   {                                                                     \
1148     recursion_loop_detected = TRUE;                                     \
1149     recursion_loop_element = (e);                                       \
1150   }                                                                     \
1151                                                                         \
1152   recursion_loop_depth++;                                               \
1153 }
1154
1155 #define RECURSION_LOOP_DETECTION_END()                                  \
1156 {                                                                       \
1157   recursion_loop_depth--;                                               \
1158 }
1159
1160 static int recursion_loop_depth;
1161 static boolean recursion_loop_detected;
1162 static boolean recursion_loop_element;
1163
1164 static int map_player_action[MAX_PLAYERS];
1165
1166
1167 // ----------------------------------------------------------------------------
1168 // definition of elements that automatically change to other elements after
1169 // a specified time, eventually calling a function when changing
1170 // ----------------------------------------------------------------------------
1171
1172 // forward declaration for changer functions
1173 static void InitBuggyBase(int, int);
1174 static void WarnBuggyBase(int, int);
1175
1176 static void InitTrap(int, int);
1177 static void ActivateTrap(int, int);
1178 static void ChangeActiveTrap(int, int);
1179
1180 static void InitRobotWheel(int, int);
1181 static void RunRobotWheel(int, int);
1182 static void StopRobotWheel(int, int);
1183
1184 static void InitTimegateWheel(int, int);
1185 static void RunTimegateWheel(int, int);
1186
1187 static void InitMagicBallDelay(int, int);
1188 static void ActivateMagicBall(int, int);
1189
1190 struct ChangingElementInfo
1191 {
1192   int element;
1193   int target_element;
1194   int change_delay;
1195   void (*pre_change_function)(int x, int y);
1196   void (*change_function)(int x, int y);
1197   void (*post_change_function)(int x, int y);
1198 };
1199
1200 static struct ChangingElementInfo change_delay_list[] =
1201 {
1202   {
1203     EL_NUT_BREAKING,
1204     EL_EMERALD,
1205     6,
1206     NULL,
1207     NULL,
1208     NULL
1209   },
1210   {
1211     EL_PEARL_BREAKING,
1212     EL_EMPTY,
1213     8,
1214     NULL,
1215     NULL,
1216     NULL
1217   },
1218   {
1219     EL_EXIT_OPENING,
1220     EL_EXIT_OPEN,
1221     29,
1222     NULL,
1223     NULL,
1224     NULL
1225   },
1226   {
1227     EL_EXIT_CLOSING,
1228     EL_EXIT_CLOSED,
1229     29,
1230     NULL,
1231     NULL,
1232     NULL
1233   },
1234   {
1235     EL_STEEL_EXIT_OPENING,
1236     EL_STEEL_EXIT_OPEN,
1237     29,
1238     NULL,
1239     NULL,
1240     NULL
1241   },
1242   {
1243     EL_STEEL_EXIT_CLOSING,
1244     EL_STEEL_EXIT_CLOSED,
1245     29,
1246     NULL,
1247     NULL,
1248     NULL
1249   },
1250   {
1251     EL_EM_EXIT_OPENING,
1252     EL_EM_EXIT_OPEN,
1253     29,
1254     NULL,
1255     NULL,
1256     NULL
1257   },
1258   {
1259     EL_EM_EXIT_CLOSING,
1260     EL_EMPTY,
1261     29,
1262     NULL,
1263     NULL,
1264     NULL
1265   },
1266   {
1267     EL_EM_STEEL_EXIT_OPENING,
1268     EL_EM_STEEL_EXIT_OPEN,
1269     29,
1270     NULL,
1271     NULL,
1272     NULL
1273   },
1274   {
1275     EL_EM_STEEL_EXIT_CLOSING,
1276     EL_STEELWALL,
1277     29,
1278     NULL,
1279     NULL,
1280     NULL
1281   },
1282   {
1283     EL_SP_EXIT_OPENING,
1284     EL_SP_EXIT_OPEN,
1285     29,
1286     NULL,
1287     NULL,
1288     NULL
1289   },
1290   {
1291     EL_SP_EXIT_CLOSING,
1292     EL_SP_EXIT_CLOSED,
1293     29,
1294     NULL,
1295     NULL,
1296     NULL
1297   },
1298   {
1299     EL_SWITCHGATE_OPENING,
1300     EL_SWITCHGATE_OPEN,
1301     29,
1302     NULL,
1303     NULL,
1304     NULL
1305   },
1306   {
1307     EL_SWITCHGATE_CLOSING,
1308     EL_SWITCHGATE_CLOSED,
1309     29,
1310     NULL,
1311     NULL,
1312     NULL
1313   },
1314   {
1315     EL_TIMEGATE_OPENING,
1316     EL_TIMEGATE_OPEN,
1317     29,
1318     NULL,
1319     NULL,
1320     NULL
1321   },
1322   {
1323     EL_TIMEGATE_CLOSING,
1324     EL_TIMEGATE_CLOSED,
1325     29,
1326     NULL,
1327     NULL,
1328     NULL
1329   },
1330
1331   {
1332     EL_ACID_SPLASH_LEFT,
1333     EL_EMPTY,
1334     8,
1335     NULL,
1336     NULL,
1337     NULL
1338   },
1339   {
1340     EL_ACID_SPLASH_RIGHT,
1341     EL_EMPTY,
1342     8,
1343     NULL,
1344     NULL,
1345     NULL
1346   },
1347   {
1348     EL_SP_BUGGY_BASE,
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     0,
1351     InitBuggyBase,
1352     NULL,
1353     NULL
1354   },
1355   {
1356     EL_SP_BUGGY_BASE_ACTIVATING,
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     0,
1359     InitBuggyBase,
1360     NULL,
1361     NULL
1362   },
1363   {
1364     EL_SP_BUGGY_BASE_ACTIVE,
1365     EL_SP_BUGGY_BASE,
1366     0,
1367     InitBuggyBase,
1368     WarnBuggyBase,
1369     NULL
1370   },
1371   {
1372     EL_TRAP,
1373     EL_TRAP_ACTIVE,
1374     0,
1375     InitTrap,
1376     NULL,
1377     ActivateTrap
1378   },
1379   {
1380     EL_TRAP_ACTIVE,
1381     EL_TRAP,
1382     31,
1383     NULL,
1384     ChangeActiveTrap,
1385     NULL
1386   },
1387   {
1388     EL_ROBOT_WHEEL_ACTIVE,
1389     EL_ROBOT_WHEEL,
1390     0,
1391     InitRobotWheel,
1392     RunRobotWheel,
1393     StopRobotWheel
1394   },
1395   {
1396     EL_TIMEGATE_SWITCH_ACTIVE,
1397     EL_TIMEGATE_SWITCH,
1398     0,
1399     InitTimegateWheel,
1400     RunTimegateWheel,
1401     NULL
1402   },
1403   {
1404     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1405     EL_DC_TIMEGATE_SWITCH,
1406     0,
1407     InitTimegateWheel,
1408     RunTimegateWheel,
1409     NULL
1410   },
1411   {
1412     EL_EMC_MAGIC_BALL_ACTIVE,
1413     EL_EMC_MAGIC_BALL_ACTIVE,
1414     0,
1415     InitMagicBallDelay,
1416     NULL,
1417     ActivateMagicBall
1418   },
1419   {
1420     EL_EMC_SPRING_BUMPER_ACTIVE,
1421     EL_EMC_SPRING_BUMPER,
1422     8,
1423     NULL,
1424     NULL,
1425     NULL
1426   },
1427   {
1428     EL_DIAGONAL_SHRINKING,
1429     EL_UNDEFINED,
1430     0,
1431     NULL,
1432     NULL,
1433     NULL
1434   },
1435   {
1436     EL_DIAGONAL_GROWING,
1437     EL_UNDEFINED,
1438     0,
1439     NULL,
1440     NULL,
1441     NULL,
1442   },
1443
1444   {
1445     EL_UNDEFINED,
1446     EL_UNDEFINED,
1447     -1,
1448     NULL,
1449     NULL,
1450     NULL
1451   }
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int push_delay_fixed, push_delay_random;
1458 }
1459 push_delay_list[] =
1460 {
1461   { EL_SPRING,                  0, 0 },
1462   { EL_BALLOON,                 0, 0 },
1463
1464   { EL_SOKOBAN_OBJECT,          2, 0 },
1465   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1466   { EL_SATELLITE,               2, 0 },
1467   { EL_SP_DISK_YELLOW,          2, 0 },
1468
1469   { EL_UNDEFINED,               0, 0 },
1470 };
1471
1472 struct
1473 {
1474   int element;
1475   int move_stepsize;
1476 }
1477 move_stepsize_list[] =
1478 {
1479   { EL_AMOEBA_DROP,             2 },
1480   { EL_AMOEBA_DROPPING,         2 },
1481   { EL_QUICKSAND_FILLING,       1 },
1482   { EL_QUICKSAND_EMPTYING,      1 },
1483   { EL_QUICKSAND_FAST_FILLING,  2 },
1484   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1485   { EL_MAGIC_WALL_FILLING,      2 },
1486   { EL_MAGIC_WALL_EMPTYING,     2 },
1487   { EL_BD_MAGIC_WALL_FILLING,   2 },
1488   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1489   { EL_DC_MAGIC_WALL_FILLING,   2 },
1490   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1491
1492   { EL_UNDEFINED,               0 },
1493 };
1494
1495 struct
1496 {
1497   int element;
1498   int count;
1499 }
1500 collect_count_list[] =
1501 {
1502   { EL_EMERALD,                 1 },
1503   { EL_BD_DIAMOND,              1 },
1504   { EL_EMERALD_YELLOW,          1 },
1505   { EL_EMERALD_RED,             1 },
1506   { EL_EMERALD_PURPLE,          1 },
1507   { EL_DIAMOND,                 3 },
1508   { EL_SP_INFOTRON,             1 },
1509   { EL_PEARL,                   5 },
1510   { EL_CRYSTAL,                 8 },
1511
1512   { EL_UNDEFINED,               0 },
1513 };
1514
1515 struct
1516 {
1517   int element;
1518   int direction;
1519 }
1520 access_direction_list[] =
1521 {
1522   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1525   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1526   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1527   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1528   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1529   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1530   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1531   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1532   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1533
1534   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1535   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1536   { EL_SP_PORT_UP,                                                   MV_DOWN },
1537   { EL_SP_PORT_DOWN,                                         MV_UP           },
1538   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1539   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1540   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1542   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1543   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1544   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1545   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1546   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1547   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1548   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1549   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1550   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1551   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1552   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1553
1554   { EL_UNDEFINED,                       MV_NONE                              }
1555 };
1556
1557 static struct XY xy_topdown[] =
1558 {
1559   {  0, -1 },
1560   { -1,  0 },
1561   { +1,  0 },
1562   {  0, +1 }
1563 };
1564
1565 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1566
1567 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1568 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1569 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1570                                  IS_JUST_CHANGING(x, y))
1571
1572 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1573
1574 // static variables for playfield scan mode (scanning forward or backward)
1575 static int playfield_scan_start_x = 0;
1576 static int playfield_scan_start_y = 0;
1577 static int playfield_scan_delta_x = 1;
1578 static int playfield_scan_delta_y = 1;
1579
1580 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1581                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1582                                      (y) += playfield_scan_delta_y)     \
1583                                 for ((x) = playfield_scan_start_x;      \
1584                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1585                                      (x) += playfield_scan_delta_x)
1586
1587 #ifdef DEBUG
1588 void DEBUG_SetMaximumDynamite(void)
1589 {
1590   int i;
1591
1592   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1593     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1594       local_player->inventory_element[local_player->inventory_size++] =
1595         EL_DYNAMITE;
1596 }
1597 #endif
1598
1599 static void InitPlayfieldScanModeVars(void)
1600 {
1601   if (game.use_reverse_scan_direction)
1602   {
1603     playfield_scan_start_x = lev_fieldx - 1;
1604     playfield_scan_start_y = lev_fieldy - 1;
1605
1606     playfield_scan_delta_x = -1;
1607     playfield_scan_delta_y = -1;
1608   }
1609   else
1610   {
1611     playfield_scan_start_x = 0;
1612     playfield_scan_start_y = 0;
1613
1614     playfield_scan_delta_x = 1;
1615     playfield_scan_delta_y = 1;
1616   }
1617 }
1618
1619 static void InitPlayfieldScanMode(int mode)
1620 {
1621   game.use_reverse_scan_direction =
1622     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1623
1624   InitPlayfieldScanModeVars();
1625 }
1626
1627 static int get_move_delay_from_stepsize(int move_stepsize)
1628 {
1629   move_stepsize =
1630     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1631
1632   // make sure that stepsize value is always a power of 2
1633   move_stepsize = (1 << log_2(move_stepsize));
1634
1635   return TILEX / move_stepsize;
1636 }
1637
1638 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1639                                boolean init_game)
1640 {
1641   int player_nr = player->index_nr;
1642   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1643   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1644
1645   // do no immediately change move delay -- the player might just be moving
1646   player->move_delay_value_next = move_delay;
1647
1648   // information if player can move must be set separately
1649   player->cannot_move = cannot_move;
1650
1651   if (init_game)
1652   {
1653     player->move_delay       = game.initial_move_delay[player_nr];
1654     player->move_delay_value = game.initial_move_delay_value[player_nr];
1655
1656     player->move_delay_value_next = -1;
1657
1658     player->move_delay_reset_counter = 0;
1659   }
1660 }
1661
1662 void GetPlayerConfig(void)
1663 {
1664   GameFrameDelay = setup.game_frame_delay;
1665
1666   if (!audio.sound_available)
1667     setup.sound_simple = FALSE;
1668
1669   if (!audio.loops_available)
1670     setup.sound_loops = FALSE;
1671
1672   if (!audio.music_available)
1673     setup.sound_music = FALSE;
1674
1675   if (!video.fullscreen_available)
1676     setup.fullscreen = FALSE;
1677
1678   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1679
1680   SetAudioMode(setup.sound);
1681 }
1682
1683 int GetElementFromGroupElement(int element)
1684 {
1685   if (IS_GROUP_ELEMENT(element))
1686   {
1687     struct ElementGroupInfo *group = element_info[element].group;
1688     int last_anim_random_frame = gfx.anim_random_frame;
1689     int element_pos;
1690
1691     if (group->choice_mode == ANIM_RANDOM)
1692       gfx.anim_random_frame = RND(group->num_elements_resolved);
1693
1694     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1695                                     group->choice_mode, 0,
1696                                     group->choice_pos);
1697
1698     if (group->choice_mode == ANIM_RANDOM)
1699       gfx.anim_random_frame = last_anim_random_frame;
1700
1701     group->choice_pos++;
1702
1703     element = group->element_resolved[element_pos];
1704   }
1705
1706   return element;
1707 }
1708
1709 static void IncrementSokobanFieldsNeeded(void)
1710 {
1711   if (level.sb_fields_needed)
1712     game.sokoban_fields_still_needed++;
1713 }
1714
1715 static void IncrementSokobanObjectsNeeded(void)
1716 {
1717   if (level.sb_objects_needed)
1718     game.sokoban_objects_still_needed++;
1719 }
1720
1721 static void DecrementSokobanFieldsNeeded(void)
1722 {
1723   if (game.sokoban_fields_still_needed > 0)
1724     game.sokoban_fields_still_needed--;
1725 }
1726
1727 static void DecrementSokobanObjectsNeeded(void)
1728 {
1729   if (game.sokoban_objects_still_needed > 0)
1730     game.sokoban_objects_still_needed--;
1731 }
1732
1733 static void InitPlayerField(int x, int y, int element, boolean init_game)
1734 {
1735   if (element == EL_SP_MURPHY)
1736   {
1737     if (init_game)
1738     {
1739       if (stored_player[0].present)
1740       {
1741         Tile[x][y] = EL_SP_MURPHY_CLONE;
1742
1743         return;
1744       }
1745       else
1746       {
1747         stored_player[0].initial_element = element;
1748         stored_player[0].use_murphy = TRUE;
1749
1750         if (!level.use_artwork_element[0])
1751           stored_player[0].artwork_element = EL_SP_MURPHY;
1752       }
1753
1754       Tile[x][y] = EL_PLAYER_1;
1755     }
1756   }
1757
1758   if (init_game)
1759   {
1760     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1761     int jx = player->jx, jy = player->jy;
1762
1763     player->present = TRUE;
1764
1765     player->block_last_field = (element == EL_SP_MURPHY ?
1766                                 level.sp_block_last_field :
1767                                 level.block_last_field);
1768
1769     // ---------- initialize player's last field block delay ------------------
1770
1771     // always start with reliable default value (no adjustment needed)
1772     player->block_delay_adjustment = 0;
1773
1774     // special case 1: in Supaplex, Murphy blocks last field one more frame
1775     if (player->block_last_field && element == EL_SP_MURPHY)
1776       player->block_delay_adjustment = 1;
1777
1778     // special case 2: in game engines before 3.1.1, blocking was different
1779     if (game.use_block_last_field_bug)
1780       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1781
1782     if (!network.enabled || player->connected_network)
1783     {
1784       player->active = TRUE;
1785
1786       // remove potentially duplicate players
1787       if (IN_LEV_FIELD(jx, jy) && StorePlayer[jx][jy] == Tile[x][y])
1788         StorePlayer[jx][jy] = 0;
1789
1790       StorePlayer[x][y] = Tile[x][y];
1791
1792 #if DEBUG_INIT_PLAYER
1793       Debug("game:init:player", "- player element %d activated",
1794             player->element_nr);
1795       Debug("game:init:player", "  (local player is %d and currently %s)",
1796             local_player->element_nr,
1797             local_player->active ? "active" : "not active");
1798     }
1799 #endif
1800
1801     Tile[x][y] = EL_EMPTY;
1802
1803     player->jx = player->last_jx = x;
1804     player->jy = player->last_jy = y;
1805   }
1806
1807   // always check if player was just killed and should be reanimated
1808   {
1809     int player_nr = GET_PLAYER_NR(element);
1810     struct PlayerInfo *player = &stored_player[player_nr];
1811
1812     if (player->active && player->killed)
1813       player->reanimated = TRUE; // if player was just killed, reanimate him
1814   }
1815 }
1816
1817 static void InitField(int x, int y, boolean init_game)
1818 {
1819   int element = Tile[x][y];
1820
1821   switch (element)
1822   {
1823     case EL_SP_MURPHY:
1824     case EL_PLAYER_1:
1825     case EL_PLAYER_2:
1826     case EL_PLAYER_3:
1827     case EL_PLAYER_4:
1828       InitPlayerField(x, y, element, init_game);
1829       break;
1830
1831     case EL_SOKOBAN_FIELD_PLAYER:
1832       element = Tile[x][y] = EL_PLAYER_1;
1833       InitField(x, y, init_game);
1834
1835       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1836       InitField(x, y, init_game);
1837       break;
1838
1839     case EL_SOKOBAN_FIELD_EMPTY:
1840       IncrementSokobanFieldsNeeded();
1841       break;
1842
1843     case EL_SOKOBAN_OBJECT:
1844       IncrementSokobanObjectsNeeded();
1845       break;
1846
1847     case EL_STONEBLOCK:
1848       if (x < lev_fieldx - 1 && Tile[x + 1][y] == EL_ACID)
1849         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1850       else if (x > 0 && Tile[x - 1][y] == EL_ACID)
1851         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1852       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPLEFT)
1853         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1854       else if (y > 0 && Tile[x][y - 1] == EL_ACID)
1855         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1856       else if (y > 0 && Tile[x][y - 1] == EL_ACID_POOL_TOPRIGHT)
1857         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1858       break;
1859
1860     case EL_BUG:
1861     case EL_BUG_RIGHT:
1862     case EL_BUG_UP:
1863     case EL_BUG_LEFT:
1864     case EL_BUG_DOWN:
1865     case EL_SPACESHIP:
1866     case EL_SPACESHIP_RIGHT:
1867     case EL_SPACESHIP_UP:
1868     case EL_SPACESHIP_LEFT:
1869     case EL_SPACESHIP_DOWN:
1870     case EL_BD_BUTTERFLY:
1871     case EL_BD_BUTTERFLY_RIGHT:
1872     case EL_BD_BUTTERFLY_UP:
1873     case EL_BD_BUTTERFLY_LEFT:
1874     case EL_BD_BUTTERFLY_DOWN:
1875     case EL_BD_FIREFLY:
1876     case EL_BD_FIREFLY_RIGHT:
1877     case EL_BD_FIREFLY_UP:
1878     case EL_BD_FIREFLY_LEFT:
1879     case EL_BD_FIREFLY_DOWN:
1880     case EL_PACMAN_RIGHT:
1881     case EL_PACMAN_UP:
1882     case EL_PACMAN_LEFT:
1883     case EL_PACMAN_DOWN:
1884     case EL_YAMYAM:
1885     case EL_YAMYAM_LEFT:
1886     case EL_YAMYAM_RIGHT:
1887     case EL_YAMYAM_UP:
1888     case EL_YAMYAM_DOWN:
1889     case EL_DARK_YAMYAM:
1890     case EL_ROBOT:
1891     case EL_PACMAN:
1892     case EL_SP_SNIKSNAK:
1893     case EL_SP_ELECTRON:
1894     case EL_MOLE:
1895     case EL_MOLE_LEFT:
1896     case EL_MOLE_RIGHT:
1897     case EL_MOLE_UP:
1898     case EL_MOLE_DOWN:
1899     case EL_SPRING_LEFT:
1900     case EL_SPRING_RIGHT:
1901       InitMovDir(x, y);
1902       break;
1903
1904     case EL_AMOEBA_FULL:
1905     case EL_BD_AMOEBA:
1906       InitAmoebaNr(x, y);
1907       break;
1908
1909     case EL_AMOEBA_DROP:
1910       if (y == lev_fieldy - 1)
1911       {
1912         Tile[x][y] = EL_AMOEBA_GROWING;
1913         Store[x][y] = EL_AMOEBA_WET;
1914       }
1915       break;
1916
1917     case EL_DYNAMITE_ACTIVE:
1918     case EL_SP_DISK_RED_ACTIVE:
1919     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1920     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1921     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1922     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1923       MovDelay[x][y] = 96;
1924       break;
1925
1926     case EL_EM_DYNAMITE_ACTIVE:
1927       MovDelay[x][y] = 32;
1928       break;
1929
1930     case EL_LAMP:
1931       game.lights_still_needed++;
1932       break;
1933
1934     case EL_PENGUIN:
1935       game.friends_still_needed++;
1936       break;
1937
1938     case EL_PIG:
1939     case EL_DRAGON:
1940       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1941       break;
1942
1943     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1944     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1945     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1946     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1947     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1948     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1949     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1950     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1951     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1952     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1953     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1954     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1955       if (init_game)
1956       {
1957         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1958         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1959         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1960
1961         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1962         {
1963           game.belt_dir[belt_nr] = belt_dir;
1964           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1965         }
1966         else    // more than one switch -- set it like the first switch
1967         {
1968           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1969         }
1970       }
1971       break;
1972
1973     case EL_LIGHT_SWITCH_ACTIVE:
1974       if (init_game)
1975         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1976       break;
1977
1978     case EL_INVISIBLE_STEELWALL:
1979     case EL_INVISIBLE_WALL:
1980     case EL_INVISIBLE_SAND:
1981       if (game.light_time_left > 0 ||
1982           game.lenses_time_left > 0)
1983         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1984       break;
1985
1986     case EL_EMC_MAGIC_BALL:
1987       if (game.ball_active)
1988         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1989       break;
1990
1991     case EL_EMC_MAGIC_BALL_SWITCH:
1992       if (game.ball_active)
1993         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1994       break;
1995
1996     case EL_TRIGGER_PLAYER:
1997     case EL_TRIGGER_ELEMENT:
1998     case EL_TRIGGER_CE_VALUE:
1999     case EL_TRIGGER_CE_SCORE:
2000     case EL_SELF:
2001     case EL_ANY_ELEMENT:
2002     case EL_CURRENT_CE_VALUE:
2003     case EL_CURRENT_CE_SCORE:
2004     case EL_PREV_CE_1:
2005     case EL_PREV_CE_2:
2006     case EL_PREV_CE_3:
2007     case EL_PREV_CE_4:
2008     case EL_PREV_CE_5:
2009     case EL_PREV_CE_6:
2010     case EL_PREV_CE_7:
2011     case EL_PREV_CE_8:
2012     case EL_NEXT_CE_1:
2013     case EL_NEXT_CE_2:
2014     case EL_NEXT_CE_3:
2015     case EL_NEXT_CE_4:
2016     case EL_NEXT_CE_5:
2017     case EL_NEXT_CE_6:
2018     case EL_NEXT_CE_7:
2019     case EL_NEXT_CE_8:
2020       // reference elements should not be used on the playfield
2021       Tile[x][y] = EL_EMPTY;
2022       break;
2023
2024     default:
2025       if (IS_CUSTOM_ELEMENT(element))
2026       {
2027         if (CAN_MOVE(element))
2028           InitMovDir(x, y);
2029
2030         if (!element_info[element].use_last_ce_value || init_game)
2031           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2032       }
2033       else if (IS_GROUP_ELEMENT(element))
2034       {
2035         Tile[x][y] = GetElementFromGroupElement(element);
2036
2037         InitField(x, y, init_game);
2038       }
2039       else if (IS_EMPTY_ELEMENT(element))
2040       {
2041         GfxElementEmpty[x][y] = element;
2042         Tile[x][y] = EL_EMPTY;
2043
2044         if (element_info[element].use_gfx_element)
2045           game.use_masked_elements = TRUE;
2046       }
2047
2048       break;
2049   }
2050
2051   if (!init_game)
2052     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2053 }
2054
2055 static void InitField_WithBug1(int x, int y, boolean init_game)
2056 {
2057   InitField(x, y, init_game);
2058
2059   // not needed to call InitMovDir() -- already done by InitField()!
2060   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2061       CAN_MOVE(Tile[x][y]))
2062     InitMovDir(x, y);
2063 }
2064
2065 static void InitField_WithBug2(int x, int y, boolean init_game)
2066 {
2067   int old_element = Tile[x][y];
2068
2069   InitField(x, y, init_game);
2070
2071   // not needed to call InitMovDir() -- already done by InitField()!
2072   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2073       CAN_MOVE(old_element) &&
2074       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2075     InitMovDir(x, y);
2076
2077   /* this case is in fact a combination of not less than three bugs:
2078      first, it calls InitMovDir() for elements that can move, although this is
2079      already done by InitField(); then, it checks the element that was at this
2080      field _before_ the call to InitField() (which can change it); lastly, it
2081      was not called for "mole with direction" elements, which were treated as
2082      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2083   */
2084 }
2085
2086 static int get_key_element_from_nr(int key_nr)
2087 {
2088   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2089                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2090                           EL_EM_KEY_1 : EL_KEY_1);
2091
2092   return key_base_element + key_nr;
2093 }
2094
2095 static int get_next_dropped_element(struct PlayerInfo *player)
2096 {
2097   return (player->inventory_size > 0 ?
2098           player->inventory_element[player->inventory_size - 1] :
2099           player->inventory_infinite_element != EL_UNDEFINED ?
2100           player->inventory_infinite_element :
2101           player->dynabombs_left > 0 ?
2102           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2103           EL_UNDEFINED);
2104 }
2105
2106 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2107 {
2108   // pos >= 0: get element from bottom of the stack;
2109   // pos <  0: get element from top of the stack
2110
2111   if (pos < 0)
2112   {
2113     int min_inventory_size = -pos;
2114     int inventory_pos = player->inventory_size - min_inventory_size;
2115     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2116
2117     return (player->inventory_size >= min_inventory_size ?
2118             player->inventory_element[inventory_pos] :
2119             player->inventory_infinite_element != EL_UNDEFINED ?
2120             player->inventory_infinite_element :
2121             player->dynabombs_left >= min_dynabombs_left ?
2122             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2123             EL_UNDEFINED);
2124   }
2125   else
2126   {
2127     int min_dynabombs_left = pos + 1;
2128     int min_inventory_size = pos + 1 - player->dynabombs_left;
2129     int inventory_pos = pos - player->dynabombs_left;
2130
2131     return (player->inventory_infinite_element != EL_UNDEFINED ?
2132             player->inventory_infinite_element :
2133             player->dynabombs_left >= min_dynabombs_left ?
2134             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2135             player->inventory_size >= min_inventory_size ?
2136             player->inventory_element[inventory_pos] :
2137             EL_UNDEFINED);
2138   }
2139 }
2140
2141 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2142 {
2143   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2144   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2145   int compare_result;
2146
2147   if (gpo1->sort_priority != gpo2->sort_priority)
2148     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2149   else
2150     compare_result = gpo1->nr - gpo2->nr;
2151
2152   return compare_result;
2153 }
2154
2155 int getPlayerInventorySize(int player_nr)
2156 {
2157   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2158     return game_em.ply[player_nr]->dynamite;
2159   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2160     return game_sp.red_disk_count;
2161   else
2162     return stored_player[player_nr].inventory_size;
2163 }
2164
2165 static void InitGameControlValues(void)
2166 {
2167   int i;
2168
2169   for (i = 0; game_panel_controls[i].nr != -1; i++)
2170   {
2171     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2172     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2173     struct TextPosInfo *pos = gpc->pos;
2174     int nr = gpc->nr;
2175     int type = gpc->type;
2176
2177     if (nr != i)
2178     {
2179       Error("'game_panel_controls' structure corrupted at %d", i);
2180
2181       Fail("this should not happen -- please debug");
2182     }
2183
2184     // force update of game controls after initialization
2185     gpc->value = gpc->last_value = -1;
2186     gpc->frame = gpc->last_frame = -1;
2187     gpc->gfx_frame = -1;
2188
2189     // determine panel value width for later calculation of alignment
2190     if (type == TYPE_INTEGER || type == TYPE_STRING)
2191     {
2192       pos->width = pos->size * getFontWidth(pos->font);
2193       pos->height = getFontHeight(pos->font);
2194     }
2195     else if (type == TYPE_ELEMENT)
2196     {
2197       pos->width = pos->size;
2198       pos->height = pos->size;
2199     }
2200
2201     // fill structure for game panel draw order
2202     gpo->nr = gpc->nr;
2203     gpo->sort_priority = pos->sort_priority;
2204   }
2205
2206   // sort game panel controls according to sort_priority and control number
2207   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2208         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2209 }
2210
2211 static void UpdatePlayfieldElementCount(void)
2212 {
2213   boolean use_element_count = FALSE;
2214   int i, j, x, y;
2215
2216   // first check if it is needed at all to calculate playfield element count
2217   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2218     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2219       use_element_count = TRUE;
2220
2221   if (!use_element_count)
2222     return;
2223
2224   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2225     element_info[i].element_count = 0;
2226
2227   SCAN_PLAYFIELD(x, y)
2228   {
2229     element_info[Tile[x][y]].element_count++;
2230   }
2231
2232   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2233     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2234       if (IS_IN_GROUP(j, i))
2235         element_info[EL_GROUP_START + i].element_count +=
2236           element_info[j].element_count;
2237 }
2238
2239 static void UpdateGameControlValues(void)
2240 {
2241   int i, k;
2242   int time = (game.LevelSolved ?
2243               game.LevelSolved_CountingTime :
2244               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245               game_em.lev->time :
2246               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247               game_sp.time_played :
2248               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249               game_mm.energy_left :
2250               game.no_level_time_limit ? TimePlayed : TimeLeft);
2251   int score = (game.LevelSolved ?
2252                game.LevelSolved_CountingScore :
2253                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2254                game_em.lev->score :
2255                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2256                game_sp.score :
2257                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                game_mm.score :
2259                game.score);
2260   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2261               game_em.lev->gems_needed :
2262               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2263               game_sp.infotrons_still_needed :
2264               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2265               game_mm.kettles_still_needed :
2266               game.gems_still_needed);
2267   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2268                      game_em.lev->gems_needed > 0 :
2269                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2270                      game_sp.infotrons_still_needed > 0 :
2271                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2272                      game_mm.kettles_still_needed > 0 ||
2273                      game_mm.lights_still_needed > 0 :
2274                      game.gems_still_needed > 0 ||
2275                      game.sokoban_fields_still_needed > 0 ||
2276                      game.sokoban_objects_still_needed > 0 ||
2277                      game.lights_still_needed > 0);
2278   int health = (game.LevelSolved ?
2279                 game.LevelSolved_CountingHealth :
2280                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2281                 MM_HEALTH(game_mm.laser_overload_value) :
2282                 game.health);
2283   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2284
2285   UpdatePlayfieldElementCount();
2286
2287   // update game panel control values
2288
2289   // used instead of "level_nr" (for network games)
2290   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2291   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2292
2293   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2294   for (i = 0; i < MAX_NUM_KEYS; i++)
2295     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2296   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2297   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2298
2299   if (game.centered_player_nr == -1)
2300   {
2301     for (i = 0; i < MAX_PLAYERS; i++)
2302     {
2303       // only one player in Supaplex game engine
2304       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2305         break;
2306
2307       for (k = 0; k < MAX_NUM_KEYS; k++)
2308       {
2309         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2310         {
2311           if (game_em.ply[i]->keys & (1 << k))
2312             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2313               get_key_element_from_nr(k);
2314         }
2315         else if (stored_player[i].key[k])
2316           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2317             get_key_element_from_nr(k);
2318       }
2319
2320       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2321         getPlayerInventorySize(i);
2322
2323       if (stored_player[i].num_white_keys > 0)
2324         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2325           EL_DC_KEY_WHITE;
2326
2327       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2328         stored_player[i].num_white_keys;
2329     }
2330   }
2331   else
2332   {
2333     int player_nr = game.centered_player_nr;
2334
2335     for (k = 0; k < MAX_NUM_KEYS; k++)
2336     {
2337       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2338       {
2339         if (game_em.ply[player_nr]->keys & (1 << k))
2340           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2341             get_key_element_from_nr(k);
2342       }
2343       else if (stored_player[player_nr].key[k])
2344         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2345           get_key_element_from_nr(k);
2346     }
2347
2348     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2349       getPlayerInventorySize(player_nr);
2350
2351     if (stored_player[player_nr].num_white_keys > 0)
2352       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2353
2354     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2355       stored_player[player_nr].num_white_keys;
2356   }
2357
2358   // re-arrange keys on game panel, if needed or if defined by style settings
2359   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2360   {
2361     int nr = GAME_PANEL_KEY_1 + i;
2362     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2363     struct TextPosInfo *pos = gpc->pos;
2364
2365     // skip check if key is not in the player's inventory
2366     if (gpc->value == EL_EMPTY)
2367       continue;
2368
2369     // check if keys should be arranged on panel from left to right
2370     if (pos->style == STYLE_LEFTMOST_POSITION)
2371     {
2372       // check previous key positions (left from current key)
2373       for (k = 0; k < i; k++)
2374       {
2375         int nr_new = GAME_PANEL_KEY_1 + k;
2376
2377         if (game_panel_controls[nr_new].value == EL_EMPTY)
2378         {
2379           game_panel_controls[nr_new].value = gpc->value;
2380           gpc->value = EL_EMPTY;
2381
2382           break;
2383         }
2384       }
2385     }
2386
2387     // check if "undefined" keys can be placed at some other position
2388     if (pos->x == -1 && pos->y == -1)
2389     {
2390       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2391
2392       // 1st try: display key at the same position as normal or EM keys
2393       if (game_panel_controls[nr_new].value == EL_EMPTY)
2394       {
2395         game_panel_controls[nr_new].value = gpc->value;
2396       }
2397       else
2398       {
2399         // 2nd try: display key at the next free position in the key panel
2400         for (k = 0; k < STD_NUM_KEYS; k++)
2401         {
2402           nr_new = GAME_PANEL_KEY_1 + k;
2403
2404           if (game_panel_controls[nr_new].value == EL_EMPTY)
2405           {
2406             game_panel_controls[nr_new].value = gpc->value;
2407
2408             break;
2409           }
2410         }
2411       }
2412     }
2413   }
2414
2415   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2416   {
2417     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2418       get_inventory_element_from_pos(local_player, i);
2419     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2420       get_inventory_element_from_pos(local_player, -i - 1);
2421   }
2422
2423   game_panel_controls[GAME_PANEL_SCORE].value = score;
2424   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2425
2426   game_panel_controls[GAME_PANEL_TIME].value = time;
2427
2428   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2429   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2430   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2431
2432   if (level.time == 0)
2433     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2434   else
2435     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2436
2437   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2438   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2439
2440   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2441
2442   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2443     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2444      EL_EMPTY);
2445   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2446     local_player->shield_normal_time_left;
2447   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2448     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2449      EL_EMPTY);
2450   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2451     local_player->shield_deadly_time_left;
2452
2453   game_panel_controls[GAME_PANEL_EXIT].value =
2454     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2455
2456   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2457     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2458   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2459     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2460      EL_EMC_MAGIC_BALL_SWITCH);
2461
2462   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2463     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2464   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2465     game.light_time_left;
2466
2467   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2468     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2469   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2470     game.timegate_time_left;
2471
2472   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2473     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2474
2475   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2476     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2477   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2478     game.lenses_time_left;
2479
2480   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2481     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2482   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2483     game.magnify_time_left;
2484
2485   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2486     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2487      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2488      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2489      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2490      EL_BALLOON_SWITCH_NONE);
2491
2492   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2493     local_player->dynabomb_count;
2494   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2495     local_player->dynabomb_size;
2496   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2497     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2498
2499   game_panel_controls[GAME_PANEL_PENGUINS].value =
2500     game.friends_still_needed;
2501
2502   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2503     game.sokoban_objects_still_needed;
2504   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2505     game.sokoban_fields_still_needed;
2506
2507   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2508     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2509
2510   for (i = 0; i < NUM_BELTS; i++)
2511   {
2512     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2513       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2514        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2515     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2516       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2517   }
2518
2519   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2520     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2521   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2522     game.magic_wall_time_left;
2523
2524   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2525     local_player->gravity;
2526
2527   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2528     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2529
2530   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2531     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2532       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2533        game.panel.element[i].id : EL_UNDEFINED);
2534
2535   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2536     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2537       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2538        element_info[game.panel.element_count[i].id].element_count : 0);
2539
2540   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2541     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2542       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2543        element_info[game.panel.ce_score[i].id].collect_score : 0);
2544
2545   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2546     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2547       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2548        element_info[game.panel.ce_score_element[i].id].collect_score :
2549        EL_UNDEFINED);
2550
2551   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2552   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2553   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2554
2555   // update game panel control frames
2556
2557   for (i = 0; game_panel_controls[i].nr != -1; i++)
2558   {
2559     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2560
2561     if (gpc->type == TYPE_ELEMENT)
2562     {
2563       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2564       {
2565         int last_anim_random_frame = gfx.anim_random_frame;
2566         int element = gpc->value;
2567         int graphic = el2panelimg(element);
2568         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2569                                sync_random_frame :
2570                                graphic_info[graphic].anim_global_anim_sync ?
2571                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2572
2573         if (gpc->value != gpc->last_value)
2574         {
2575           gpc->gfx_frame = 0;
2576           gpc->gfx_random = init_gfx_random;
2577         }
2578         else
2579         {
2580           gpc->gfx_frame++;
2581
2582           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2583               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2584             gpc->gfx_random = init_gfx_random;
2585         }
2586
2587         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2588           gfx.anim_random_frame = gpc->gfx_random;
2589
2590         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2591           gpc->gfx_frame = element_info[element].collect_score;
2592
2593         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2594
2595         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2596           gfx.anim_random_frame = last_anim_random_frame;
2597       }
2598     }
2599     else if (gpc->type == TYPE_GRAPHIC)
2600     {
2601       if (gpc->graphic != IMG_UNDEFINED)
2602       {
2603         int last_anim_random_frame = gfx.anim_random_frame;
2604         int graphic = gpc->graphic;
2605         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2606                                sync_random_frame :
2607                                graphic_info[graphic].anim_global_anim_sync ?
2608                                getGlobalAnimSyncFrame() : INIT_GFX_RANDOM());
2609
2610         if (gpc->value != gpc->last_value)
2611         {
2612           gpc->gfx_frame = 0;
2613           gpc->gfx_random = init_gfx_random;
2614         }
2615         else
2616         {
2617           gpc->gfx_frame++;
2618
2619           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2620               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2621             gpc->gfx_random = init_gfx_random;
2622         }
2623
2624         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2625           gfx.anim_random_frame = gpc->gfx_random;
2626
2627         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2628
2629         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2630           gfx.anim_random_frame = last_anim_random_frame;
2631       }
2632     }
2633   }
2634 }
2635
2636 static void DisplayGameControlValues(void)
2637 {
2638   boolean redraw_panel = FALSE;
2639   int i;
2640
2641   for (i = 0; game_panel_controls[i].nr != -1; i++)
2642   {
2643     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2644
2645     if (PANEL_DEACTIVATED(gpc->pos))
2646       continue;
2647
2648     if (gpc->value == gpc->last_value &&
2649         gpc->frame == gpc->last_frame)
2650       continue;
2651
2652     redraw_panel = TRUE;
2653   }
2654
2655   if (!redraw_panel)
2656     return;
2657
2658   // copy default game door content to main double buffer
2659
2660   // !!! CHECK AGAIN !!!
2661   SetPanelBackground();
2662   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2663   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2664
2665   // redraw game control buttons
2666   RedrawGameButtons();
2667
2668   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2669
2670   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2671   {
2672     int nr = game_panel_order[i].nr;
2673     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2674     struct TextPosInfo *pos = gpc->pos;
2675     int type = gpc->type;
2676     int value = gpc->value;
2677     int frame = gpc->frame;
2678     int size = pos->size;
2679     int font = pos->font;
2680     boolean draw_masked = pos->draw_masked;
2681     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2682
2683     if (PANEL_DEACTIVATED(pos))
2684       continue;
2685
2686     if (pos->class == get_hash_from_key("extra_panel_items") &&
2687         !setup.prefer_extra_panel_items)
2688       continue;
2689
2690     gpc->last_value = value;
2691     gpc->last_frame = frame;
2692
2693     if (type == TYPE_INTEGER)
2694     {
2695       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2696           nr == GAME_PANEL_INVENTORY_COUNT ||
2697           nr == GAME_PANEL_SCORE ||
2698           nr == GAME_PANEL_HIGHSCORE ||
2699           nr == GAME_PANEL_TIME)
2700       {
2701         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2702
2703         if (use_dynamic_size)           // use dynamic number of digits
2704         {
2705           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2706                               nr == GAME_PANEL_INVENTORY_COUNT ||
2707                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2708           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2709                           nr == GAME_PANEL_INVENTORY_COUNT ||
2710                           nr == GAME_PANEL_TIME ? 1 : 2);
2711           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2712                        nr == GAME_PANEL_INVENTORY_COUNT ||
2713                        nr == GAME_PANEL_TIME ? 3 : 5);
2714           int size2 = size1 + size_add;
2715           int font1 = pos->font;
2716           int font2 = pos->font_alt;
2717
2718           size = (value < value_change ? size1 : size2);
2719           font = (value < value_change ? font1 : font2);
2720         }
2721       }
2722
2723       // correct text size if "digits" is zero or less
2724       if (size <= 0)
2725         size = strlen(int2str(value, size));
2726
2727       // dynamically correct text alignment
2728       pos->width = size * getFontWidth(font);
2729
2730       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2731                   int2str(value, size), font, mask_mode);
2732     }
2733     else if (type == TYPE_ELEMENT)
2734     {
2735       int element, graphic;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741
2742       if (value != EL_UNDEFINED && value != EL_EMPTY)
2743       {
2744         element = value;
2745         graphic = el2panelimg(value);
2746
2747 #if 0
2748         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2749               element, EL_NAME(element), size);
2750 #endif
2751
2752         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2753           size = TILESIZE;
2754
2755         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2756                               &src_x, &src_y);
2757
2758         width  = graphic_info[graphic].width  * size / TILESIZE;
2759         height = graphic_info[graphic].height * size / TILESIZE;
2760
2761         if (draw_masked)
2762           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2763                            dst_x, dst_y);
2764         else
2765           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2766                      dst_x, dst_y);
2767       }
2768     }
2769     else if (type == TYPE_GRAPHIC)
2770     {
2771       int graphic        = gpc->graphic;
2772       int graphic_active = gpc->graphic_active;
2773       Bitmap *src_bitmap;
2774       int src_x, src_y;
2775       int width, height;
2776       int dst_x = PANEL_XPOS(pos);
2777       int dst_y = PANEL_YPOS(pos);
2778       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2779                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2780
2781       if (graphic != IMG_UNDEFINED && !skip)
2782       {
2783         if (pos->style == STYLE_REVERSE)
2784           value = 100 - value;
2785
2786         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2787
2788         if (pos->direction & MV_HORIZONTAL)
2789         {
2790           width  = graphic_info[graphic_active].width * value / 100;
2791           height = graphic_info[graphic_active].height;
2792
2793           if (pos->direction == MV_LEFT)
2794           {
2795             src_x += graphic_info[graphic_active].width - width;
2796             dst_x += graphic_info[graphic_active].width - width;
2797           }
2798         }
2799         else
2800         {
2801           width  = graphic_info[graphic_active].width;
2802           height = graphic_info[graphic_active].height * value / 100;
2803
2804           if (pos->direction == MV_UP)
2805           {
2806             src_y += graphic_info[graphic_active].height - height;
2807             dst_y += graphic_info[graphic_active].height - height;
2808           }
2809         }
2810
2811         if (draw_masked)
2812           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2813                            dst_x, dst_y);
2814         else
2815           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2816                      dst_x, dst_y);
2817
2818         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2819
2820         if (pos->direction & MV_HORIZONTAL)
2821         {
2822           if (pos->direction == MV_RIGHT)
2823           {
2824             src_x += width;
2825             dst_x += width;
2826           }
2827           else
2828           {
2829             dst_x = PANEL_XPOS(pos);
2830           }
2831
2832           width = graphic_info[graphic].width - width;
2833         }
2834         else
2835         {
2836           if (pos->direction == MV_DOWN)
2837           {
2838             src_y += height;
2839             dst_y += height;
2840           }
2841           else
2842           {
2843             dst_y = PANEL_YPOS(pos);
2844           }
2845
2846           height = graphic_info[graphic].height - height;
2847         }
2848
2849         if (draw_masked)
2850           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2851                            dst_x, dst_y);
2852         else
2853           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2854                      dst_x, dst_y);
2855       }
2856     }
2857     else if (type == TYPE_STRING)
2858     {
2859       boolean active = (value != 0);
2860       char *state_normal = "off";
2861       char *state_active = "on";
2862       char *state = (active ? state_active : state_normal);
2863       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2864                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2865                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2866                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2867
2868       if (nr == GAME_PANEL_GRAVITY_STATE)
2869       {
2870         int font1 = pos->font;          // (used for normal state)
2871         int font2 = pos->font_alt;      // (used for active state)
2872
2873         font = (active ? font2 : font1);
2874       }
2875
2876       if (s != NULL)
2877       {
2878         char *s_cut;
2879
2880         if (size <= 0)
2881         {
2882           // don't truncate output if "chars" is zero or less
2883           size = strlen(s);
2884
2885           // dynamically correct text alignment
2886           pos->width = size * getFontWidth(font);
2887         }
2888
2889         s_cut = getStringCopyN(s, size);
2890
2891         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2892                     s_cut, font, mask_mode);
2893
2894         free(s_cut);
2895       }
2896     }
2897
2898     redraw_mask |= REDRAW_DOOR_1;
2899   }
2900
2901   SetGameStatus(GAME_MODE_PLAYING);
2902 }
2903
2904 void UpdateAndDisplayGameControlValues(void)
2905 {
2906   if (tape.deactivate_display)
2907     return;
2908
2909   UpdateGameControlValues();
2910   DisplayGameControlValues();
2911 }
2912
2913 void UpdateGameDoorValues(void)
2914 {
2915   UpdateGameControlValues();
2916 }
2917
2918 void DrawGameDoorValues(void)
2919 {
2920   DisplayGameControlValues();
2921 }
2922
2923
2924 // ============================================================================
2925 // InitGameEngine()
2926 // ----------------------------------------------------------------------------
2927 // initialize game engine due to level / tape version number
2928 // ============================================================================
2929
2930 static void InitGameEngine(void)
2931 {
2932   int i, j, k, l, x, y;
2933
2934   // set game engine from tape file when re-playing, else from level file
2935   game.engine_version = (tape.playing ? tape.engine_version :
2936                          level.game_version);
2937
2938   // set single or multi-player game mode (needed for re-playing tapes)
2939   game.team_mode = setup.team_mode;
2940
2941   if (tape.playing)
2942   {
2943     int num_players = 0;
2944
2945     for (i = 0; i < MAX_PLAYERS; i++)
2946       if (tape.player_participates[i])
2947         num_players++;
2948
2949     // multi-player tapes contain input data for more than one player
2950     game.team_mode = (num_players > 1);
2951   }
2952
2953 #if 0
2954   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2955         level.game_version);
2956   Debug("game:init:level", "          tape.file_version   == %06d",
2957         tape.file_version);
2958   Debug("game:init:level", "          tape.game_version   == %06d",
2959         tape.game_version);
2960   Debug("game:init:level", "          tape.engine_version == %06d",
2961         tape.engine_version);
2962   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2963         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2964 #endif
2965
2966   // --------------------------------------------------------------------------
2967   // set flags for bugs and changes according to active game engine version
2968   // --------------------------------------------------------------------------
2969
2970   /*
2971     Summary of bugfix:
2972     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2973
2974     Bug was introduced in version:
2975     2.0.1
2976
2977     Bug was fixed in version:
2978     4.2.0.0
2979
2980     Description:
2981     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2982     but the property "can fall" was missing, which caused some levels to be
2983     unsolvable. This was fixed in version 4.2.0.0.
2984
2985     Affected levels/tapes:
2986     An example for a tape that was fixed by this bugfix is tape 029 from the
2987     level set "rnd_sam_bateman".
2988     The wrong behaviour will still be used for all levels or tapes that were
2989     created/recorded with it. An example for this is tape 023 from the level
2990     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2991   */
2992
2993   boolean use_amoeba_dropping_cannot_fall_bug =
2994     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2995       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2996      (tape.playing &&
2997       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2998       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2999
3000   /*
3001     Summary of bugfix/change:
3002     Fixed move speed of elements entering or leaving magic wall.
3003
3004     Fixed/changed in version:
3005     2.0.1
3006
3007     Description:
3008     Before 2.0.1, move speed of elements entering or leaving magic wall was
3009     twice as fast as it is now.
3010     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3011
3012     Affected levels/tapes:
3013     The first condition is generally needed for all levels/tapes before version
3014     2.0.1, which might use the old behaviour before it was changed; known tapes
3015     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3018     above, but before it was known that this change would break tapes like the
3019     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3020     although the engine version while recording maybe was before 2.0.1. There
3021     are a lot of tapes that are affected by this exception, like tape 006 from
3022     the level set "rnd_conor_mancone".
3023   */
3024
3025   boolean use_old_move_stepsize_for_magic_wall =
3026     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3027      !(tape.playing &&
3028        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3029        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3030
3031   /*
3032     Summary of bugfix/change:
3033     Fixed handling for custom elements that change when pushed by the player.
3034
3035     Fixed/changed in version:
3036     3.1.0
3037
3038     Description:
3039     Before 3.1.0, custom elements that "change when pushing" changed directly
3040     after the player started pushing them (until then handled in "DigField()").
3041     Since 3.1.0, these custom elements are not changed until the "pushing"
3042     move of the element is finished (now handled in "ContinueMoving()").
3043
3044     Affected levels/tapes:
3045     The first condition is generally needed for all levels/tapes before version
3046     3.1.0, which might use the old behaviour before it was changed; known tapes
3047     that are affected are some tapes from the level set "Walpurgis Gardens" by
3048     Jamie Cullen.
3049     The second condition is an exception from the above case and is needed for
3050     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3051     above (including some development versions of 3.1.0), but before it was
3052     known that this change would break tapes like the above and was fixed in
3053     3.1.1, so that the changed behaviour was active although the engine version
3054     while recording maybe was before 3.1.0. There is at least one tape that is
3055     affected by this exception, which is the tape for the one-level set "Bug
3056     Machine" by Juergen Bonhagen.
3057   */
3058
3059   game.use_change_when_pushing_bug =
3060     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3061      !(tape.playing &&
3062        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3063        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3064
3065   /*
3066     Summary of bugfix/change:
3067     Fixed handling for blocking the field the player leaves when moving.
3068
3069     Fixed/changed in version:
3070     3.1.1
3071
3072     Description:
3073     Before 3.1.1, when "block last field when moving" was enabled, the field
3074     the player is leaving when moving was blocked for the time of the move,
3075     and was directly unblocked afterwards. This resulted in the last field
3076     being blocked for exactly one less than the number of frames of one player
3077     move. Additionally, even when blocking was disabled, the last field was
3078     blocked for exactly one frame.
3079     Since 3.1.1, due to changes in player movement handling, the last field
3080     is not blocked at all when blocking is disabled. When blocking is enabled,
3081     the last field is blocked for exactly the number of frames of one player
3082     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3083     last field is blocked for exactly one more than the number of frames of
3084     one player move.
3085
3086     Affected levels/tapes:
3087     (!!! yet to be determined -- probably many !!!)
3088   */
3089
3090   game.use_block_last_field_bug =
3091     (game.engine_version < VERSION_IDENT(3,1,1,0));
3092
3093   /* various special flags and settings for native Emerald Mine game engine */
3094
3095   game_em.use_single_button =
3096     (game.engine_version > VERSION_IDENT(4,0,0,2));
3097
3098   game_em.use_snap_key_bug =
3099     (game.engine_version < VERSION_IDENT(4,0,1,0));
3100
3101   game_em.use_random_bug =
3102     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3103
3104   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3105
3106   game_em.use_old_explosions            = use_old_em_engine;
3107   game_em.use_old_android               = use_old_em_engine;
3108   game_em.use_old_push_elements         = use_old_em_engine;
3109   game_em.use_old_push_into_acid        = use_old_em_engine;
3110
3111   game_em.use_wrap_around               = !use_old_em_engine;
3112
3113   // --------------------------------------------------------------------------
3114
3115   // set maximal allowed number of custom element changes per game frame
3116   game.max_num_changes_per_frame = 1;
3117
3118   // default scan direction: scan playfield from top/left to bottom/right
3119   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3120
3121   // dynamically adjust element properties according to game engine version
3122   InitElementPropertiesEngine(game.engine_version);
3123
3124   // ---------- initialize special element properties -------------------------
3125
3126   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3127   if (use_amoeba_dropping_cannot_fall_bug)
3128     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3129
3130   // ---------- initialize player's initial move delay ------------------------
3131
3132   // dynamically adjust player properties according to level information
3133   for (i = 0; i < MAX_PLAYERS; i++)
3134     game.initial_move_delay_value[i] =
3135       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3136
3137   // dynamically adjust player properties according to game engine version
3138   for (i = 0; i < MAX_PLAYERS; i++)
3139     game.initial_move_delay[i] =
3140       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3141        game.initial_move_delay_value[i] : 0);
3142
3143   // ---------- initialize player's initial push delay ------------------------
3144
3145   // dynamically adjust player properties according to game engine version
3146   game.initial_push_delay_value =
3147     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3148
3149   // ---------- initialize changing elements ----------------------------------
3150
3151   // initialize changing elements information
3152   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3153   {
3154     struct ElementInfo *ei = &element_info[i];
3155
3156     // this pointer might have been changed in the level editor
3157     ei->change = &ei->change_page[0];
3158
3159     if (!IS_CUSTOM_ELEMENT(i))
3160     {
3161       ei->change->target_element = EL_EMPTY_SPACE;
3162       ei->change->delay_fixed = 0;
3163       ei->change->delay_random = 0;
3164       ei->change->delay_frames = 1;
3165     }
3166
3167     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3168     {
3169       ei->has_change_event[j] = FALSE;
3170
3171       ei->event_page_nr[j] = 0;
3172       ei->event_page[j] = &ei->change_page[0];
3173     }
3174   }
3175
3176   // add changing elements from pre-defined list
3177   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3178   {
3179     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3180     struct ElementInfo *ei = &element_info[ch_delay->element];
3181
3182     ei->change->target_element       = ch_delay->target_element;
3183     ei->change->delay_fixed          = ch_delay->change_delay;
3184
3185     ei->change->pre_change_function  = ch_delay->pre_change_function;
3186     ei->change->change_function      = ch_delay->change_function;
3187     ei->change->post_change_function = ch_delay->post_change_function;
3188
3189     ei->change->can_change = TRUE;
3190     ei->change->can_change_or_has_action = TRUE;
3191
3192     ei->has_change_event[CE_DELAY] = TRUE;
3193
3194     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3195     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3196   }
3197
3198   // ---------- initialize internal run-time variables ------------------------
3199
3200   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3201   {
3202     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3203
3204     for (j = 0; j < ei->num_change_pages; j++)
3205     {
3206       ei->change_page[j].can_change_or_has_action =
3207         (ei->change_page[j].can_change |
3208          ei->change_page[j].has_action);
3209     }
3210   }
3211
3212   // add change events from custom element configuration
3213   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3214   {
3215     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3216
3217     for (j = 0; j < ei->num_change_pages; j++)
3218     {
3219       if (!ei->change_page[j].can_change_or_has_action)
3220         continue;
3221
3222       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3223       {
3224         // only add event page for the first page found with this event
3225         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3226         {
3227           ei->has_change_event[k] = TRUE;
3228
3229           ei->event_page_nr[k] = j;
3230           ei->event_page[k] = &ei->change_page[j];
3231         }
3232       }
3233     }
3234   }
3235
3236   // ---------- initialize reference elements in change conditions ------------
3237
3238   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3239   {
3240     int element = EL_CUSTOM_START + i;
3241     struct ElementInfo *ei = &element_info[element];
3242
3243     for (j = 0; j < ei->num_change_pages; j++)
3244     {
3245       int trigger_element = ei->change_page[j].initial_trigger_element;
3246
3247       if (trigger_element >= EL_PREV_CE_8 &&
3248           trigger_element <= EL_NEXT_CE_8)
3249         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3250
3251       ei->change_page[j].trigger_element = trigger_element;
3252     }
3253   }
3254
3255   // ---------- initialize run-time trigger player and element ----------------
3256
3257   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3258   {
3259     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3260
3261     for (j = 0; j < ei->num_change_pages; j++)
3262     {
3263       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3264       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3265       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3266       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3267       ei->change_page[j].actual_trigger_ce_value = 0;
3268       ei->change_page[j].actual_trigger_ce_score = 0;
3269     }
3270   }
3271
3272   // ---------- initialize trigger events -------------------------------------
3273
3274   // initialize trigger events information
3275   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3276     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3277       trigger_events[i][j] = FALSE;
3278
3279   // add trigger events from element change event properties
3280   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3281   {
3282     struct ElementInfo *ei = &element_info[i];
3283
3284     for (j = 0; j < ei->num_change_pages; j++)
3285     {
3286       if (!ei->change_page[j].can_change_or_has_action)
3287         continue;
3288
3289       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3290       {
3291         int trigger_element = ei->change_page[j].trigger_element;
3292
3293         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3294         {
3295           if (ei->change_page[j].has_event[k])
3296           {
3297             if (IS_GROUP_ELEMENT(trigger_element))
3298             {
3299               struct ElementGroupInfo *group =
3300                 element_info[trigger_element].group;
3301
3302               for (l = 0; l < group->num_elements_resolved; l++)
3303                 trigger_events[group->element_resolved[l]][k] = TRUE;
3304             }
3305             else if (trigger_element == EL_ANY_ELEMENT)
3306               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3307                 trigger_events[l][k] = TRUE;
3308             else
3309               trigger_events[trigger_element][k] = TRUE;
3310           }
3311         }
3312       }
3313     }
3314   }
3315
3316   // ---------- initialize push delay -----------------------------------------
3317
3318   // initialize push delay values to default
3319   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3320   {
3321     if (!IS_CUSTOM_ELEMENT(i))
3322     {
3323       // set default push delay values (corrected since version 3.0.7-1)
3324       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3325       {
3326         element_info[i].push_delay_fixed = 2;
3327         element_info[i].push_delay_random = 8;
3328       }
3329       else
3330       {
3331         element_info[i].push_delay_fixed = 8;
3332         element_info[i].push_delay_random = 8;
3333       }
3334     }
3335   }
3336
3337   // set push delay value for certain elements from pre-defined list
3338   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3339   {
3340     int e = push_delay_list[i].element;
3341
3342     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3343     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3344   }
3345
3346   // set push delay value for Supaplex elements for newer engine versions
3347   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3348   {
3349     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3350     {
3351       if (IS_SP_ELEMENT(i))
3352       {
3353         // set SP push delay to just enough to push under a falling zonk
3354         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3355
3356         element_info[i].push_delay_fixed  = delay;
3357         element_info[i].push_delay_random = 0;
3358       }
3359     }
3360   }
3361
3362   // ---------- initialize move stepsize --------------------------------------
3363
3364   // initialize move stepsize values to default
3365   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3366     if (!IS_CUSTOM_ELEMENT(i))
3367       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3368
3369   // set move stepsize value for certain elements from pre-defined list
3370   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3371   {
3372     int e = move_stepsize_list[i].element;
3373
3374     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3375
3376     // set move stepsize value for certain elements for older engine versions
3377     if (use_old_move_stepsize_for_magic_wall)
3378     {
3379       if (e == EL_MAGIC_WALL_FILLING ||
3380           e == EL_MAGIC_WALL_EMPTYING ||
3381           e == EL_BD_MAGIC_WALL_FILLING ||
3382           e == EL_BD_MAGIC_WALL_EMPTYING)
3383         element_info[e].move_stepsize *= 2;
3384     }
3385   }
3386
3387   // ---------- initialize collect score --------------------------------------
3388
3389   // initialize collect score values for custom elements from initial value
3390   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3391     if (IS_CUSTOM_ELEMENT(i))
3392       element_info[i].collect_score = element_info[i].collect_score_initial;
3393
3394   // ---------- initialize collect count --------------------------------------
3395
3396   // initialize collect count values for non-custom elements
3397   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3398     if (!IS_CUSTOM_ELEMENT(i))
3399       element_info[i].collect_count_initial = 0;
3400
3401   // add collect count values for all elements from pre-defined list
3402   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3403     element_info[collect_count_list[i].element].collect_count_initial =
3404       collect_count_list[i].count;
3405
3406   // ---------- initialize access direction -----------------------------------
3407
3408   // initialize access direction values to default (access from every side)
3409   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3410     if (!IS_CUSTOM_ELEMENT(i))
3411       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3412
3413   // set access direction value for certain elements from pre-defined list
3414   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3415     element_info[access_direction_list[i].element].access_direction =
3416       access_direction_list[i].direction;
3417
3418   // ---------- initialize explosion content ----------------------------------
3419   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3420   {
3421     if (IS_CUSTOM_ELEMENT(i))
3422       continue;
3423
3424     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3425     {
3426       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3427
3428       element_info[i].content.e[x][y] =
3429         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3430          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3431          i == EL_PLAYER_3 ? EL_EMERALD :
3432          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3433          i == EL_MOLE ? EL_EMERALD_RED :
3434          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3435          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3436          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3437          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3438          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3439          i == EL_WALL_EMERALD ? EL_EMERALD :
3440          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3441          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3442          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3443          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3444          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3445          i == EL_WALL_PEARL ? EL_PEARL :
3446          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3447          EL_EMPTY);
3448     }
3449   }
3450
3451   // ---------- initialize recursion detection --------------------------------
3452   recursion_loop_depth = 0;
3453   recursion_loop_detected = FALSE;
3454   recursion_loop_element = EL_UNDEFINED;
3455
3456   // ---------- initialize graphics engine ------------------------------------
3457   game.scroll_delay_value =
3458     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3459      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3460      !setup.forced_scroll_delay           ? 0 :
3461      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3462   game.scroll_delay_value =
3463     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3464
3465   // ---------- initialize game engine snapshots ------------------------------
3466   for (i = 0; i < MAX_PLAYERS; i++)
3467     game.snapshot.last_action[i] = 0;
3468   game.snapshot.changed_action = FALSE;
3469   game.snapshot.collected_item = FALSE;
3470   game.snapshot.mode =
3471     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3472      SNAPSHOT_MODE_EVERY_STEP :
3473      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3474      SNAPSHOT_MODE_EVERY_MOVE :
3475      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3476      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3477   game.snapshot.save_snapshot = FALSE;
3478
3479   // ---------- initialize level time for Supaplex engine ---------------------
3480   // Supaplex levels with time limit currently unsupported -- should be added
3481   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3482     level.time = 0;
3483
3484   // ---------- initialize flags for handling game actions --------------------
3485
3486   // set flags for game actions to default values
3487   game.use_key_actions = TRUE;
3488   game.use_mouse_actions = FALSE;
3489
3490   // when using Mirror Magic game engine, handle mouse events only
3491   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3492   {
3493     game.use_key_actions = FALSE;
3494     game.use_mouse_actions = TRUE;
3495   }
3496
3497   // check for custom elements with mouse click events
3498   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3499   {
3500     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3501     {
3502       int element = EL_CUSTOM_START + i;
3503
3504       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3505           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3506           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3507           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3508         game.use_mouse_actions = TRUE;
3509     }
3510   }
3511 }
3512
3513 static int get_num_special_action(int element, int action_first,
3514                                   int action_last)
3515 {
3516   int num_special_action = 0;
3517   int i, j;
3518
3519   for (i = action_first; i <= action_last; i++)
3520   {
3521     boolean found = FALSE;
3522
3523     for (j = 0; j < NUM_DIRECTIONS; j++)
3524       if (el_act_dir2img(element, i, j) !=
3525           el_act_dir2img(element, ACTION_DEFAULT, j))
3526         found = TRUE;
3527
3528     if (found)
3529       num_special_action++;
3530     else
3531       break;
3532   }
3533
3534   return num_special_action;
3535 }
3536
3537
3538 // ============================================================================
3539 // InitGame()
3540 // ----------------------------------------------------------------------------
3541 // initialize and start new game
3542 // ============================================================================
3543
3544 #if DEBUG_INIT_PLAYER
3545 static void DebugPrintPlayerStatus(char *message)
3546 {
3547   int i;
3548
3549   if (!options.debug)
3550     return;
3551
3552   Debug("game:init:player", "%s:", message);
3553
3554   for (i = 0; i < MAX_PLAYERS; i++)
3555   {
3556     struct PlayerInfo *player = &stored_player[i];
3557
3558     Debug("game:init:player",
3559           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3560           i + 1,
3561           player->present,
3562           player->connected,
3563           player->connected_locally,
3564           player->connected_network,
3565           player->active,
3566           (local_player == player ? " (local player)" : ""));
3567   }
3568 }
3569 #endif
3570
3571 void InitGame(void)
3572 {
3573   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3574   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3575   int fade_mask = REDRAW_FIELD;
3576
3577   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3578   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3579   int initial_move_dir = MV_DOWN;
3580   int i, j, x, y;
3581
3582   // required here to update video display before fading (FIX THIS)
3583   DrawMaskedBorder(REDRAW_DOOR_2);
3584
3585   if (!game.restart_level)
3586     CloseDoor(DOOR_CLOSE_1);
3587
3588   SetGameStatus(GAME_MODE_PLAYING);
3589
3590   if (level_editor_test_game)
3591     FadeSkipNextFadeOut();
3592   else
3593     FadeSetEnterScreen();
3594
3595   if (CheckFadeAll())
3596     fade_mask = REDRAW_ALL;
3597
3598   FadeLevelSoundsAndMusic();
3599
3600   ExpireSoundLoops(TRUE);
3601
3602   FadeOut(fade_mask);
3603
3604   if (level_editor_test_game)
3605     FadeSkipNextFadeIn();
3606
3607   // needed if different viewport properties defined for playing
3608   ChangeViewportPropertiesIfNeeded();
3609
3610   ClearField();
3611
3612   DrawCompleteVideoDisplay();
3613
3614   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3615
3616   InitGameEngine();
3617   InitGameControlValues();
3618
3619   if (tape.recording)
3620   {
3621     // initialize tape actions from game when recording tape
3622     tape.use_key_actions   = game.use_key_actions;
3623     tape.use_mouse_actions = game.use_mouse_actions;
3624
3625     // initialize visible playfield size when recording tape (for team mode)
3626     tape.scr_fieldx = SCR_FIELDX;
3627     tape.scr_fieldy = SCR_FIELDY;
3628   }
3629
3630   // don't play tapes over network
3631   network_playing = (network.enabled && !tape.playing);
3632
3633   for (i = 0; i < MAX_PLAYERS; i++)
3634   {
3635     struct PlayerInfo *player = &stored_player[i];
3636
3637     player->index_nr = i;
3638     player->index_bit = (1 << i);
3639     player->element_nr = EL_PLAYER_1 + i;
3640
3641     player->present = FALSE;
3642     player->active = FALSE;
3643     player->mapped = FALSE;
3644
3645     player->killed = FALSE;
3646     player->reanimated = FALSE;
3647     player->buried = FALSE;
3648
3649     player->action = 0;
3650     player->effective_action = 0;
3651     player->programmed_action = 0;
3652     player->snap_action = 0;
3653
3654     player->mouse_action.lx = 0;
3655     player->mouse_action.ly = 0;
3656     player->mouse_action.button = 0;
3657     player->mouse_action.button_hint = 0;
3658
3659     player->effective_mouse_action.lx = 0;
3660     player->effective_mouse_action.ly = 0;
3661     player->effective_mouse_action.button = 0;
3662     player->effective_mouse_action.button_hint = 0;
3663
3664     for (j = 0; j < MAX_NUM_KEYS; j++)
3665       player->key[j] = FALSE;
3666
3667     player->num_white_keys = 0;
3668
3669     player->dynabomb_count = 0;
3670     player->dynabomb_size = 1;
3671     player->dynabombs_left = 0;
3672     player->dynabomb_xl = FALSE;
3673
3674     player->MovDir = initial_move_dir;
3675     player->MovPos = 0;
3676     player->GfxPos = 0;
3677     player->GfxDir = initial_move_dir;
3678     player->GfxAction = ACTION_DEFAULT;
3679     player->Frame = 0;
3680     player->StepFrame = 0;
3681
3682     player->initial_element = player->element_nr;
3683     player->artwork_element =
3684       (level.use_artwork_element[i] ? level.artwork_element[i] :
3685        player->element_nr);
3686     player->use_murphy = FALSE;
3687
3688     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3689     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3690
3691     player->gravity = level.initial_player_gravity[i];
3692
3693     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3694
3695     player->actual_frame_counter.count = 0;
3696     player->actual_frame_counter.value = 1;
3697
3698     player->step_counter = 0;
3699
3700     player->last_move_dir = initial_move_dir;
3701
3702     player->is_active = FALSE;
3703
3704     player->is_waiting = FALSE;
3705     player->is_moving = FALSE;
3706     player->is_auto_moving = FALSE;
3707     player->is_digging = FALSE;
3708     player->is_snapping = FALSE;
3709     player->is_collecting = FALSE;
3710     player->is_pushing = FALSE;
3711     player->is_switching = FALSE;
3712     player->is_dropping = FALSE;
3713     player->is_dropping_pressed = FALSE;
3714
3715     player->is_bored = FALSE;
3716     player->is_sleeping = FALSE;
3717
3718     player->was_waiting = TRUE;
3719     player->was_moving = FALSE;
3720     player->was_snapping = FALSE;
3721     player->was_dropping = FALSE;
3722
3723     player->force_dropping = FALSE;
3724
3725     player->frame_counter_bored = -1;
3726     player->frame_counter_sleeping = -1;
3727
3728     player->anim_delay_counter = 0;
3729     player->post_delay_counter = 0;
3730
3731     player->dir_waiting = initial_move_dir;
3732     player->action_waiting = ACTION_DEFAULT;
3733     player->last_action_waiting = ACTION_DEFAULT;
3734     player->special_action_bored = ACTION_DEFAULT;
3735     player->special_action_sleeping = ACTION_DEFAULT;
3736
3737     player->switch_x = -1;
3738     player->switch_y = -1;
3739
3740     player->drop_x = -1;
3741     player->drop_y = -1;
3742
3743     player->show_envelope = 0;
3744
3745     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3746
3747     player->push_delay       = -1;      // initialized when pushing starts
3748     player->push_delay_value = game.initial_push_delay_value;
3749
3750     player->drop_delay = 0;
3751     player->drop_pressed_delay = 0;
3752
3753     player->last_jx = -1;
3754     player->last_jy = -1;
3755     player->jx = -1;
3756     player->jy = -1;
3757
3758     player->shield_normal_time_left = 0;
3759     player->shield_deadly_time_left = 0;
3760
3761     player->last_removed_element = EL_UNDEFINED;
3762
3763     player->inventory_infinite_element = EL_UNDEFINED;
3764     player->inventory_size = 0;
3765
3766     if (level.use_initial_inventory[i])
3767     {
3768       for (j = 0; j < level.initial_inventory_size[i]; j++)
3769       {
3770         int element = level.initial_inventory_content[i][j];
3771         int collect_count = element_info[element].collect_count_initial;
3772         int k;
3773
3774         if (!IS_CUSTOM_ELEMENT(element))
3775           collect_count = 1;
3776
3777         if (collect_count == 0)
3778           player->inventory_infinite_element = element;
3779         else
3780           for (k = 0; k < collect_count; k++)
3781             if (player->inventory_size < MAX_INVENTORY_SIZE)
3782               player->inventory_element[player->inventory_size++] = element;
3783       }
3784     }
3785
3786     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3787     SnapField(player, 0, 0);
3788
3789     map_player_action[i] = i;
3790   }
3791
3792   network_player_action_received = FALSE;
3793
3794   // initial null action
3795   if (network_playing)
3796     SendToServer_MovePlayer(MV_NONE);
3797
3798   FrameCounter = 0;
3799   TimeFrames = 0;
3800   TimePlayed = 0;
3801   TimeLeft = level.time;
3802   TapeTime = 0;
3803
3804   ScreenMovDir = MV_NONE;
3805   ScreenMovPos = 0;
3806   ScreenGfxPos = 0;
3807
3808   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3809
3810   game.robot_wheel_x = -1;
3811   game.robot_wheel_y = -1;
3812
3813   game.exit_x = -1;
3814   game.exit_y = -1;
3815
3816   game.all_players_gone = FALSE;
3817
3818   game.LevelSolved = FALSE;
3819   game.GameOver = FALSE;
3820
3821   game.GamePlayed = !tape.playing;
3822
3823   game.LevelSolved_GameWon = FALSE;
3824   game.LevelSolved_GameEnd = FALSE;
3825   game.LevelSolved_SaveTape = FALSE;
3826   game.LevelSolved_SaveScore = FALSE;
3827
3828   game.LevelSolved_CountingTime = 0;
3829   game.LevelSolved_CountingScore = 0;
3830   game.LevelSolved_CountingHealth = 0;
3831
3832   game.panel.active = TRUE;
3833
3834   game.no_level_time_limit = (level.time == 0);
3835   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3836
3837   game.yamyam_content_nr = 0;
3838   game.robot_wheel_active = FALSE;
3839   game.magic_wall_active = FALSE;
3840   game.magic_wall_time_left = 0;
3841   game.light_time_left = 0;
3842   game.timegate_time_left = 0;
3843   game.switchgate_pos = 0;
3844   game.wind_direction = level.wind_direction_initial;
3845
3846   game.time_final = 0;
3847   game.score_time_final = 0;
3848
3849   game.score = 0;
3850   game.score_final = 0;
3851
3852   game.health = MAX_HEALTH;
3853   game.health_final = MAX_HEALTH;
3854
3855   game.gems_still_needed = level.gems_needed;
3856   game.sokoban_fields_still_needed = 0;
3857   game.sokoban_objects_still_needed = 0;
3858   game.lights_still_needed = 0;
3859   game.players_still_needed = 0;
3860   game.friends_still_needed = 0;
3861
3862   game.lenses_time_left = 0;
3863   game.magnify_time_left = 0;
3864
3865   game.ball_active = level.ball_active_initial;
3866   game.ball_content_nr = 0;
3867
3868   game.explosions_delayed = TRUE;
3869
3870   game.envelope_active = FALSE;
3871
3872   // special case: set custom artwork setting to initial value
3873   game.use_masked_elements = game.use_masked_elements_initial;
3874
3875   for (i = 0; i < NUM_BELTS; i++)
3876   {
3877     game.belt_dir[i] = MV_NONE;
3878     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3879   }
3880
3881   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3882     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3883
3884 #if DEBUG_INIT_PLAYER
3885   DebugPrintPlayerStatus("Player status at level initialization");
3886 #endif
3887
3888   SCAN_PLAYFIELD(x, y)
3889   {
3890     Tile[x][y] = Last[x][y] = level.field[x][y];
3891     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3892     ChangeDelay[x][y] = 0;
3893     ChangePage[x][y] = -1;
3894     CustomValue[x][y] = 0;              // initialized in InitField()
3895     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3896     AmoebaNr[x][y] = 0;
3897     WasJustMoving[x][y] = 0;
3898     WasJustFalling[x][y] = 0;
3899     CheckCollision[x][y] = 0;
3900     CheckImpact[x][y] = 0;
3901     Stop[x][y] = FALSE;
3902     Pushed[x][y] = FALSE;
3903
3904     ChangeCount[x][y] = 0;
3905     ChangeEvent[x][y] = -1;
3906
3907     ExplodePhase[x][y] = 0;
3908     ExplodeDelay[x][y] = 0;
3909     ExplodeField[x][y] = EX_TYPE_NONE;
3910
3911     RunnerVisit[x][y] = 0;
3912     PlayerVisit[x][y] = 0;
3913
3914     GfxFrame[x][y] = 0;
3915     GfxRandom[x][y] = INIT_GFX_RANDOM();
3916     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3917     GfxElement[x][y] = EL_UNDEFINED;
3918     GfxElementEmpty[x][y] = EL_EMPTY;
3919     GfxAction[x][y] = ACTION_DEFAULT;
3920     GfxDir[x][y] = MV_NONE;
3921     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3922   }
3923
3924   SCAN_PLAYFIELD(x, y)
3925   {
3926     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3927       emulate_bd = FALSE;
3928     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3929       emulate_sp = FALSE;
3930
3931     InitField(x, y, TRUE);
3932
3933     ResetGfxAnimation(x, y);
3934   }
3935
3936   InitBeltMovement();
3937
3938   for (i = 0; i < MAX_PLAYERS; i++)
3939   {
3940     struct PlayerInfo *player = &stored_player[i];
3941
3942     // set number of special actions for bored and sleeping animation
3943     player->num_special_action_bored =
3944       get_num_special_action(player->artwork_element,
3945                              ACTION_BORING_1, ACTION_BORING_LAST);
3946     player->num_special_action_sleeping =
3947       get_num_special_action(player->artwork_element,
3948                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3949   }
3950
3951   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3952                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3953
3954   // initialize type of slippery elements
3955   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3956   {
3957     if (!IS_CUSTOM_ELEMENT(i))
3958     {
3959       // default: elements slip down either to the left or right randomly
3960       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3961
3962       // SP style elements prefer to slip down on the left side
3963       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965
3966       // BD style elements prefer to slip down on the left side
3967       if (game.emulation == EMU_BOULDERDASH)
3968         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3969     }
3970   }
3971
3972   // initialize explosion and ignition delay
3973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3974   {
3975     if (!IS_CUSTOM_ELEMENT(i))
3976     {
3977       int num_phase = 8;
3978       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3979                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3980                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3981       int last_phase = (num_phase + 1) * delay;
3982       int half_phase = (num_phase / 2) * delay;
3983
3984       element_info[i].explosion_delay = last_phase - 1;
3985       element_info[i].ignition_delay = half_phase;
3986
3987       if (i == EL_BLACK_ORB)
3988         element_info[i].ignition_delay = 1;
3989     }
3990   }
3991
3992   // correct non-moving belts to start moving left
3993   for (i = 0; i < NUM_BELTS; i++)
3994     if (game.belt_dir[i] == MV_NONE)
3995       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3996
3997 #if USE_NEW_PLAYER_ASSIGNMENTS
3998   // use preferred player also in local single-player mode
3999   if (!network.enabled && !game.team_mode)
4000   {
4001     int new_index_nr = setup.network_player_nr;
4002
4003     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4004     {
4005       for (i = 0; i < MAX_PLAYERS; i++)
4006         stored_player[i].connected_locally = FALSE;
4007
4008       stored_player[new_index_nr].connected_locally = TRUE;
4009     }
4010   }
4011
4012   for (i = 0; i < MAX_PLAYERS; i++)
4013   {
4014     stored_player[i].connected = FALSE;
4015
4016     // in network game mode, the local player might not be the first player
4017     if (stored_player[i].connected_locally)
4018       local_player = &stored_player[i];
4019   }
4020
4021   if (!network.enabled)
4022     local_player->connected = TRUE;
4023
4024   if (tape.playing)
4025   {
4026     for (i = 0; i < MAX_PLAYERS; i++)
4027       stored_player[i].connected = tape.player_participates[i];
4028   }
4029   else if (network.enabled)
4030   {
4031     // add team mode players connected over the network (needed for correct
4032     // assignment of player figures from level to locally playing players)
4033
4034     for (i = 0; i < MAX_PLAYERS; i++)
4035       if (stored_player[i].connected_network)
4036         stored_player[i].connected = TRUE;
4037   }
4038   else if (game.team_mode)
4039   {
4040     // try to guess locally connected team mode players (needed for correct
4041     // assignment of player figures from level to locally playing players)
4042
4043     for (i = 0; i < MAX_PLAYERS; i++)
4044       if (setup.input[i].use_joystick ||
4045           setup.input[i].key.left != KSYM_UNDEFINED)
4046         stored_player[i].connected = TRUE;
4047   }
4048
4049 #if DEBUG_INIT_PLAYER
4050   DebugPrintPlayerStatus("Player status after level initialization");
4051 #endif
4052
4053 #if DEBUG_INIT_PLAYER
4054   Debug("game:init:player", "Reassigning players ...");
4055 #endif
4056
4057   // check if any connected player was not found in playfield
4058   for (i = 0; i < MAX_PLAYERS; i++)
4059   {
4060     struct PlayerInfo *player = &stored_player[i];
4061
4062     if (player->connected && !player->present)
4063     {
4064       struct PlayerInfo *field_player = NULL;
4065
4066 #if DEBUG_INIT_PLAYER
4067       Debug("game:init:player",
4068             "- looking for field player for player %d ...", i + 1);
4069 #endif
4070
4071       // assign first free player found that is present in the playfield
4072
4073       // first try: look for unmapped playfield player that is not connected
4074       for (j = 0; j < MAX_PLAYERS; j++)
4075         if (field_player == NULL &&
4076             stored_player[j].present &&
4077             !stored_player[j].mapped &&
4078             !stored_player[j].connected)
4079           field_player = &stored_player[j];
4080
4081       // second try: look for *any* unmapped playfield player
4082       for (j = 0; j < MAX_PLAYERS; j++)
4083         if (field_player == NULL &&
4084             stored_player[j].present &&
4085             !stored_player[j].mapped)
4086           field_player = &stored_player[j];
4087
4088       if (field_player != NULL)
4089       {
4090         int jx = field_player->jx, jy = field_player->jy;
4091
4092 #if DEBUG_INIT_PLAYER
4093         Debug("game:init:player", "- found player %d",
4094               field_player->index_nr + 1);
4095 #endif
4096
4097         player->present = FALSE;
4098         player->active = FALSE;
4099
4100         field_player->present = TRUE;
4101         field_player->active = TRUE;
4102
4103         /*
4104         player->initial_element = field_player->initial_element;
4105         player->artwork_element = field_player->artwork_element;
4106
4107         player->block_last_field       = field_player->block_last_field;
4108         player->block_delay_adjustment = field_player->block_delay_adjustment;
4109         */
4110
4111         StorePlayer[jx][jy] = field_player->element_nr;
4112
4113         field_player->jx = field_player->last_jx = jx;
4114         field_player->jy = field_player->last_jy = jy;
4115
4116         if (local_player == player)
4117           local_player = field_player;
4118
4119         map_player_action[field_player->index_nr] = i;
4120
4121         field_player->mapped = TRUE;
4122
4123 #if DEBUG_INIT_PLAYER
4124         Debug("game:init:player", "- map_player_action[%d] == %d",
4125               field_player->index_nr + 1, i + 1);
4126 #endif
4127       }
4128     }
4129
4130     if (player->connected && player->present)
4131       player->mapped = TRUE;
4132   }
4133
4134 #if DEBUG_INIT_PLAYER
4135   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4136 #endif
4137
4138 #else
4139
4140   // check if any connected player was not found in playfield
4141   for (i = 0; i < MAX_PLAYERS; i++)
4142   {
4143     struct PlayerInfo *player = &stored_player[i];
4144
4145     if (player->connected && !player->present)
4146     {
4147       for (j = 0; j < MAX_PLAYERS; j++)
4148       {
4149         struct PlayerInfo *field_player = &stored_player[j];
4150         int jx = field_player->jx, jy = field_player->jy;
4151
4152         // assign first free player found that is present in the playfield
4153         if (field_player->present && !field_player->connected)
4154         {
4155           player->present = TRUE;
4156           player->active = TRUE;
4157
4158           field_player->present = FALSE;
4159           field_player->active = FALSE;
4160
4161           player->initial_element = field_player->initial_element;
4162           player->artwork_element = field_player->artwork_element;
4163
4164           player->block_last_field       = field_player->block_last_field;
4165           player->block_delay_adjustment = field_player->block_delay_adjustment;
4166
4167           StorePlayer[jx][jy] = player->element_nr;
4168
4169           player->jx = player->last_jx = jx;
4170           player->jy = player->last_jy = jy;
4171
4172           break;
4173         }
4174       }
4175     }
4176   }
4177 #endif
4178
4179 #if 0
4180   Debug("game:init:player", "local_player->present == %d",
4181         local_player->present);
4182 #endif
4183
4184   // set focus to local player for network games, else to all players
4185   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4186   game.centered_player_nr_next = game.centered_player_nr;
4187   game.set_centered_player = FALSE;
4188   game.set_centered_player_wrap = FALSE;
4189
4190   if (network_playing && tape.recording)
4191   {
4192     // store client dependent player focus when recording network games
4193     tape.centered_player_nr_next = game.centered_player_nr_next;
4194     tape.set_centered_player = TRUE;
4195   }
4196
4197   if (tape.playing)
4198   {
4199     // when playing a tape, eliminate all players who do not participate
4200
4201 #if USE_NEW_PLAYER_ASSIGNMENTS
4202
4203     if (!game.team_mode)
4204     {
4205       for (i = 0; i < MAX_PLAYERS; i++)
4206       {
4207         if (stored_player[i].active &&
4208             !tape.player_participates[map_player_action[i]])
4209         {
4210           struct PlayerInfo *player = &stored_player[i];
4211           int jx = player->jx, jy = player->jy;
4212
4213 #if DEBUG_INIT_PLAYER
4214           Debug("game:init:player", "Removing player %d at (%d, %d)",
4215                 i + 1, jx, jy);
4216 #endif
4217
4218           player->active = FALSE;
4219           StorePlayer[jx][jy] = 0;
4220           Tile[jx][jy] = EL_EMPTY;
4221         }
4222       }
4223     }
4224
4225 #else
4226
4227     for (i = 0; i < MAX_PLAYERS; i++)
4228     {
4229       if (stored_player[i].active &&
4230           !tape.player_participates[i])
4231       {
4232         struct PlayerInfo *player = &stored_player[i];
4233         int jx = player->jx, jy = player->jy;
4234
4235         player->active = FALSE;
4236         StorePlayer[jx][jy] = 0;
4237         Tile[jx][jy] = EL_EMPTY;
4238       }
4239     }
4240 #endif
4241   }
4242   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4243   {
4244     // when in single player mode, eliminate all but the local player
4245
4246     for (i = 0; i < MAX_PLAYERS; i++)
4247     {
4248       struct PlayerInfo *player = &stored_player[i];
4249
4250       if (player->active && player != local_player)
4251       {
4252         int jx = player->jx, jy = player->jy;
4253
4254         player->active = FALSE;
4255         player->present = FALSE;
4256
4257         StorePlayer[jx][jy] = 0;
4258         Tile[jx][jy] = EL_EMPTY;
4259       }
4260     }
4261   }
4262
4263   for (i = 0; i < MAX_PLAYERS; i++)
4264     if (stored_player[i].active)
4265       game.players_still_needed++;
4266
4267   if (level.solved_by_one_player)
4268     game.players_still_needed = 1;
4269
4270   // when recording the game, store which players take part in the game
4271   if (tape.recording)
4272   {
4273 #if USE_NEW_PLAYER_ASSIGNMENTS
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].connected)
4276         tape.player_participates[i] = TRUE;
4277 #else
4278     for (i = 0; i < MAX_PLAYERS; i++)
4279       if (stored_player[i].active)
4280         tape.player_participates[i] = TRUE;
4281 #endif
4282   }
4283
4284 #if DEBUG_INIT_PLAYER
4285   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4286 #endif
4287
4288   if (BorderElement == EL_EMPTY)
4289   {
4290     SBX_Left = 0;
4291     SBX_Right = lev_fieldx - SCR_FIELDX;
4292     SBY_Upper = 0;
4293     SBY_Lower = lev_fieldy - SCR_FIELDY;
4294   }
4295   else
4296   {
4297     SBX_Left = -1;
4298     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4299     SBY_Upper = -1;
4300     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4301   }
4302
4303   if (full_lev_fieldx <= SCR_FIELDX)
4304     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4305   if (full_lev_fieldy <= SCR_FIELDY)
4306     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4307
4308   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4309     SBX_Left--;
4310   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4311     SBY_Upper--;
4312
4313   // if local player not found, look for custom element that might create
4314   // the player (make some assumptions about the right custom element)
4315   if (!local_player->present)
4316   {
4317     int start_x = 0, start_y = 0;
4318     int found_rating = 0;
4319     int found_element = EL_UNDEFINED;
4320     int player_nr = local_player->index_nr;
4321
4322     SCAN_PLAYFIELD(x, y)
4323     {
4324       int element = Tile[x][y];
4325       int content;
4326       int xx, yy;
4327       boolean is_player;
4328
4329       if (level.use_start_element[player_nr] &&
4330           level.start_element[player_nr] == element &&
4331           found_rating < 4)
4332       {
4333         start_x = x;
4334         start_y = y;
4335
4336         found_rating = 4;
4337         found_element = element;
4338       }
4339
4340       if (!IS_CUSTOM_ELEMENT(element))
4341         continue;
4342
4343       if (CAN_CHANGE(element))
4344       {
4345         for (i = 0; i < element_info[element].num_change_pages; i++)
4346         {
4347           // check for player created from custom element as single target
4348           content = element_info[element].change_page[i].target_element;
4349           is_player = IS_PLAYER_ELEMENT(content);
4350
4351           if (is_player && (found_rating < 3 ||
4352                             (found_rating == 3 && element < found_element)))
4353           {
4354             start_x = x;
4355             start_y = y;
4356
4357             found_rating = 3;
4358             found_element = element;
4359           }
4360         }
4361       }
4362
4363       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4364       {
4365         // check for player created from custom element as explosion content
4366         content = element_info[element].content.e[xx][yy];
4367         is_player = IS_PLAYER_ELEMENT(content);
4368
4369         if (is_player && (found_rating < 2 ||
4370                           (found_rating == 2 && element < found_element)))
4371         {
4372           start_x = x + xx - 1;
4373           start_y = y + yy - 1;
4374
4375           found_rating = 2;
4376           found_element = element;
4377         }
4378
4379         if (!CAN_CHANGE(element))
4380           continue;
4381
4382         for (i = 0; i < element_info[element].num_change_pages; i++)
4383         {
4384           // check for player created from custom element as extended target
4385           content =
4386             element_info[element].change_page[i].target_content.e[xx][yy];
4387
4388           is_player = IS_PLAYER_ELEMENT(content);
4389
4390           if (is_player && (found_rating < 1 ||
4391                             (found_rating == 1 && element < found_element)))
4392           {
4393             start_x = x + xx - 1;
4394             start_y = y + yy - 1;
4395
4396             found_rating = 1;
4397             found_element = element;
4398           }
4399         }
4400       }
4401     }
4402
4403     scroll_x = SCROLL_POSITION_X(start_x);
4404     scroll_y = SCROLL_POSITION_Y(start_y);
4405   }
4406   else
4407   {
4408     scroll_x = SCROLL_POSITION_X(local_player->jx);
4409     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4410   }
4411
4412   // !!! FIX THIS (START) !!!
4413   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4414   {
4415     InitGameEngine_EM();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4418   {
4419     InitGameEngine_SP();
4420   }
4421   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4422   {
4423     InitGameEngine_MM();
4424   }
4425   else
4426   {
4427     DrawLevel(REDRAW_FIELD);
4428     DrawAllPlayers();
4429
4430     // after drawing the level, correct some elements
4431     if (game.timegate_time_left == 0)
4432       CloseAllOpenTimegates();
4433   }
4434
4435   // blit playfield from scroll buffer to normal back buffer for fading in
4436   BlitScreenToBitmap(backbuffer);
4437   // !!! FIX THIS (END) !!!
4438
4439   DrawMaskedBorder(fade_mask);
4440
4441   FadeIn(fade_mask);
4442
4443 #if 1
4444   // full screen redraw is required at this point in the following cases:
4445   // - special editor door undrawn when game was started from level editor
4446   // - drawing area (playfield) was changed and has to be removed completely
4447   redraw_mask = REDRAW_ALL;
4448   BackToFront();
4449 #endif
4450
4451   if (!game.restart_level)
4452   {
4453     // copy default game door content to main double buffer
4454
4455     // !!! CHECK AGAIN !!!
4456     SetPanelBackground();
4457     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4458     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4459   }
4460
4461   SetPanelBackground();
4462   SetDrawBackgroundMask(REDRAW_DOOR_1);
4463
4464   UpdateAndDisplayGameControlValues();
4465
4466   if (!game.restart_level)
4467   {
4468     UnmapGameButtons();
4469     UnmapTapeButtons();
4470
4471     FreeGameButtons();
4472     CreateGameButtons();
4473
4474     MapGameButtons();
4475     MapTapeButtons();
4476
4477     // copy actual game door content to door double buffer for OpenDoor()
4478     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4479
4480     OpenDoor(DOOR_OPEN_ALL);
4481
4482     KeyboardAutoRepeatOffUnlessAutoplay();
4483
4484 #if DEBUG_INIT_PLAYER
4485     DebugPrintPlayerStatus("Player status (final)");
4486 #endif
4487   }
4488
4489   UnmapAllGadgets();
4490
4491   MapGameButtons();
4492   MapTapeButtons();
4493
4494   if (!game.restart_level && !tape.playing)
4495   {
4496     LevelStats_incPlayed(level_nr);
4497
4498     SaveLevelSetup_SeriesInfo();
4499   }
4500
4501   game.restart_level = FALSE;
4502   game.restart_game_message = NULL;
4503
4504   game.request_active = FALSE;
4505   game.request_active_or_moving = FALSE;
4506
4507   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4508     InitGameActions_MM();
4509
4510   SaveEngineSnapshotToListInitial();
4511
4512   if (!game.restart_level)
4513   {
4514     PlaySound(SND_GAME_STARTING);
4515
4516     if (setup.sound_music)
4517       PlayLevelMusic();
4518   }
4519
4520   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4521 }
4522
4523 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4524                         int actual_player_x, int actual_player_y)
4525 {
4526   // this is used for non-R'n'D game engines to update certain engine values
4527
4528   // needed to determine if sounds are played within the visible screen area
4529   scroll_x = actual_scroll_x;
4530   scroll_y = actual_scroll_y;
4531
4532   // needed to get player position for "follow finger" playing input method
4533   local_player->jx = actual_player_x;
4534   local_player->jy = actual_player_y;
4535 }
4536
4537 void InitMovDir(int x, int y)
4538 {
4539   int i, element = Tile[x][y];
4540   static int xy[4][2] =
4541   {
4542     {  0, +1 },
4543     { +1,  0 },
4544     {  0, -1 },
4545     { -1,  0 }
4546   };
4547   static int direction[3][4] =
4548   {
4549     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4550     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4551     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4552   };
4553
4554   switch (element)
4555   {
4556     case EL_BUG_RIGHT:
4557     case EL_BUG_UP:
4558     case EL_BUG_LEFT:
4559     case EL_BUG_DOWN:
4560       Tile[x][y] = EL_BUG;
4561       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4562       break;
4563
4564     case EL_SPACESHIP_RIGHT:
4565     case EL_SPACESHIP_UP:
4566     case EL_SPACESHIP_LEFT:
4567     case EL_SPACESHIP_DOWN:
4568       Tile[x][y] = EL_SPACESHIP;
4569       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4570       break;
4571
4572     case EL_BD_BUTTERFLY_RIGHT:
4573     case EL_BD_BUTTERFLY_UP:
4574     case EL_BD_BUTTERFLY_LEFT:
4575     case EL_BD_BUTTERFLY_DOWN:
4576       Tile[x][y] = EL_BD_BUTTERFLY;
4577       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4578       break;
4579
4580     case EL_BD_FIREFLY_RIGHT:
4581     case EL_BD_FIREFLY_UP:
4582     case EL_BD_FIREFLY_LEFT:
4583     case EL_BD_FIREFLY_DOWN:
4584       Tile[x][y] = EL_BD_FIREFLY;
4585       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4586       break;
4587
4588     case EL_PACMAN_RIGHT:
4589     case EL_PACMAN_UP:
4590     case EL_PACMAN_LEFT:
4591     case EL_PACMAN_DOWN:
4592       Tile[x][y] = EL_PACMAN;
4593       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4594       break;
4595
4596     case EL_YAMYAM_LEFT:
4597     case EL_YAMYAM_RIGHT:
4598     case EL_YAMYAM_UP:
4599     case EL_YAMYAM_DOWN:
4600       Tile[x][y] = EL_YAMYAM;
4601       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4602       break;
4603
4604     case EL_SP_SNIKSNAK:
4605       MovDir[x][y] = MV_UP;
4606       break;
4607
4608     case EL_SP_ELECTRON:
4609       MovDir[x][y] = MV_LEFT;
4610       break;
4611
4612     case EL_MOLE_LEFT:
4613     case EL_MOLE_RIGHT:
4614     case EL_MOLE_UP:
4615     case EL_MOLE_DOWN:
4616       Tile[x][y] = EL_MOLE;
4617       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4618       break;
4619
4620     case EL_SPRING_LEFT:
4621     case EL_SPRING_RIGHT:
4622       Tile[x][y] = EL_SPRING;
4623       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4624       break;
4625
4626     default:
4627       if (IS_CUSTOM_ELEMENT(element))
4628       {
4629         struct ElementInfo *ei = &element_info[element];
4630         int move_direction_initial = ei->move_direction_initial;
4631         int move_pattern = ei->move_pattern;
4632
4633         if (move_direction_initial == MV_START_PREVIOUS)
4634         {
4635           if (MovDir[x][y] != MV_NONE)
4636             return;
4637
4638           move_direction_initial = MV_START_AUTOMATIC;
4639         }
4640
4641         if (move_direction_initial == MV_START_RANDOM)
4642           MovDir[x][y] = 1 << RND(4);
4643         else if (move_direction_initial & MV_ANY_DIRECTION)
4644           MovDir[x][y] = move_direction_initial;
4645         else if (move_pattern == MV_ALL_DIRECTIONS ||
4646                  move_pattern == MV_TURNING_LEFT ||
4647                  move_pattern == MV_TURNING_RIGHT ||
4648                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4649                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4650                  move_pattern == MV_TURNING_RANDOM)
4651           MovDir[x][y] = 1 << RND(4);
4652         else if (move_pattern == MV_HORIZONTAL)
4653           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4654         else if (move_pattern == MV_VERTICAL)
4655           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4656         else if (move_pattern & MV_ANY_DIRECTION)
4657           MovDir[x][y] = element_info[element].move_pattern;
4658         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4659                  move_pattern == MV_ALONG_RIGHT_SIDE)
4660         {
4661           // use random direction as default start direction
4662           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4663             MovDir[x][y] = 1 << RND(4);
4664
4665           for (i = 0; i < NUM_DIRECTIONS; i++)
4666           {
4667             int x1 = x + xy[i][0];
4668             int y1 = y + xy[i][1];
4669
4670             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4671             {
4672               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4673                 MovDir[x][y] = direction[0][i];
4674               else
4675                 MovDir[x][y] = direction[1][i];
4676
4677               break;
4678             }
4679           }
4680         }                
4681       }
4682       else
4683       {
4684         MovDir[x][y] = 1 << RND(4);
4685
4686         if (element != EL_BUG &&
4687             element != EL_SPACESHIP &&
4688             element != EL_BD_BUTTERFLY &&
4689             element != EL_BD_FIREFLY)
4690           break;
4691
4692         for (i = 0; i < NUM_DIRECTIONS; i++)
4693         {
4694           int x1 = x + xy[i][0];
4695           int y1 = y + xy[i][1];
4696
4697           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4698           {
4699             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4700             {
4701               MovDir[x][y] = direction[0][i];
4702               break;
4703             }
4704             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4705                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4706             {
4707               MovDir[x][y] = direction[1][i];
4708               break;
4709             }
4710           }
4711         }
4712       }
4713       break;
4714   }
4715
4716   GfxDir[x][y] = MovDir[x][y];
4717 }
4718
4719 void InitAmoebaNr(int x, int y)
4720 {
4721   int i;
4722   int group_nr = AmoebaNeighbourNr(x, y);
4723
4724   if (group_nr == 0)
4725   {
4726     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4727     {
4728       if (AmoebaCnt[i] == 0)
4729       {
4730         group_nr = i;
4731         break;
4732       }
4733     }
4734   }
4735
4736   AmoebaNr[x][y] = group_nr;
4737   AmoebaCnt[group_nr]++;
4738   AmoebaCnt2[group_nr]++;
4739 }
4740
4741 static void LevelSolved_SetFinalGameValues(void)
4742 {
4743   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4744   game.score_time_final = (level.use_step_counter ? TimePlayed :
4745                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4746
4747   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4748                       game_em.lev->score :
4749                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                       game_mm.score :
4751                       game.score);
4752
4753   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4754                        MM_HEALTH(game_mm.laser_overload_value) :
4755                        game.health);
4756
4757   game.LevelSolved_CountingTime = game.time_final;
4758   game.LevelSolved_CountingScore = game.score_final;
4759   game.LevelSolved_CountingHealth = game.health_final;
4760 }
4761
4762 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4763 {
4764   game.LevelSolved_CountingTime = time;
4765   game.LevelSolved_CountingScore = score;
4766   game.LevelSolved_CountingHealth = health;
4767
4768   game_panel_controls[GAME_PANEL_TIME].value = time;
4769   game_panel_controls[GAME_PANEL_SCORE].value = score;
4770   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4771
4772   DisplayGameControlValues();
4773 }
4774
4775 static void LevelSolved(void)
4776 {
4777   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4778       game.players_still_needed > 0)
4779     return;
4780
4781   game.LevelSolved = TRUE;
4782   game.GameOver = TRUE;
4783
4784   tape.solved = TRUE;
4785
4786   // needed here to display correct panel values while player walks into exit
4787   LevelSolved_SetFinalGameValues();
4788 }
4789
4790 void GameWon(void)
4791 {
4792   static int time_count_steps;
4793   static int time, time_final;
4794   static float score, score_final; // needed for time score < 10 for 10 seconds
4795   static int health, health_final;
4796   static int game_over_delay_1 = 0;
4797   static int game_over_delay_2 = 0;
4798   static int game_over_delay_3 = 0;
4799   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4800   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4801
4802   if (!game.LevelSolved_GameWon)
4803   {
4804     int i;
4805
4806     // do not start end game actions before the player stops moving (to exit)
4807     if (local_player->active && local_player->MovPos)
4808       return;
4809
4810     // calculate final game values after player finished walking into exit
4811     LevelSolved_SetFinalGameValues();
4812
4813     game.LevelSolved_GameWon = TRUE;
4814     game.LevelSolved_SaveTape = tape.recording;
4815     game.LevelSolved_SaveScore = !tape.playing;
4816
4817     if (!tape.playing)
4818     {
4819       LevelStats_incSolved(level_nr);
4820
4821       SaveLevelSetup_SeriesInfo();
4822     }
4823
4824     if (tape.auto_play)         // tape might already be stopped here
4825       tape.auto_play_level_solved = TRUE;
4826
4827     TapeStop();
4828
4829     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4830     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4831     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4832
4833     time = time_final = game.time_final;
4834     score = score_final = game.score_final;
4835     health = health_final = game.health_final;
4836
4837     // update game panel values before (delayed) counting of score (if any)
4838     LevelSolved_DisplayFinalGameValues(time, score, health);
4839
4840     // if level has time score defined, calculate new final game values
4841     if (time_score > 0)
4842     {
4843       int time_final_max = 999;
4844       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4845       int time_frames = 0;
4846       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4847       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4848
4849       if (TimeLeft > 0)
4850       {
4851         time_final = 0;
4852         time_frames = time_frames_left;
4853       }
4854       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4855       {
4856         time_final = time_final_max;
4857         time_frames = time_frames_final_max - time_frames_played;
4858       }
4859
4860       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4861
4862       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4863
4864       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4865       {
4866         health_final = 0;
4867         score_final += health * time_score;
4868       }
4869
4870       game.score_final = score_final;
4871       game.health_final = health_final;
4872     }
4873
4874     // if not counting score after game, immediately update game panel values
4875     if (level_editor_test_game || !setup.count_score_after_game)
4876     {
4877       time = time_final;
4878       score = score_final;
4879
4880       LevelSolved_DisplayFinalGameValues(time, score, health);
4881     }
4882
4883     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4884     {
4885       // check if last player has left the level
4886       if (game.exit_x >= 0 &&
4887           game.exit_y >= 0)
4888       {
4889         int x = game.exit_x;
4890         int y = game.exit_y;
4891         int element = Tile[x][y];
4892
4893         // close exit door after last player
4894         if ((game.all_players_gone &&
4895              (element == EL_EXIT_OPEN ||
4896               element == EL_SP_EXIT_OPEN ||
4897               element == EL_STEEL_EXIT_OPEN)) ||
4898             element == EL_EM_EXIT_OPEN ||
4899             element == EL_EM_STEEL_EXIT_OPEN)
4900         {
4901
4902           Tile[x][y] =
4903             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4904              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4905              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4906              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4907              EL_EM_STEEL_EXIT_CLOSING);
4908
4909           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4910         }
4911
4912         // player disappears
4913         DrawLevelField(x, y);
4914       }
4915
4916       for (i = 0; i < MAX_PLAYERS; i++)
4917       {
4918         struct PlayerInfo *player = &stored_player[i];
4919
4920         if (player->present)
4921         {
4922           RemovePlayer(player);
4923
4924           // player disappears
4925           DrawLevelField(player->jx, player->jy);
4926         }
4927       }
4928     }
4929
4930     PlaySound(SND_GAME_WINNING);
4931   }
4932
4933   if (setup.count_score_after_game)
4934   {
4935     if (time != time_final)
4936     {
4937       if (game_over_delay_1 > 0)
4938       {
4939         game_over_delay_1--;
4940
4941         return;
4942       }
4943
4944       int time_to_go = ABS(time_final - time);
4945       int time_count_dir = (time < time_final ? +1 : -1);
4946
4947       if (time_to_go < time_count_steps)
4948         time_count_steps = 1;
4949
4950       time  += time_count_steps * time_count_dir;
4951       score += time_count_steps * time_score;
4952
4953       // set final score to correct rounding differences after counting score
4954       if (time == time_final)
4955         score = score_final;
4956
4957       LevelSolved_DisplayFinalGameValues(time, score, health);
4958
4959       if (time == time_final)
4960         StopSound(SND_GAME_LEVELTIME_BONUS);
4961       else if (setup.sound_loops)
4962         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4963       else
4964         PlaySound(SND_GAME_LEVELTIME_BONUS);
4965
4966       return;
4967     }
4968
4969     if (health != health_final)
4970     {
4971       if (game_over_delay_2 > 0)
4972       {
4973         game_over_delay_2--;
4974
4975         return;
4976       }
4977
4978       int health_count_dir = (health < health_final ? +1 : -1);
4979
4980       health += health_count_dir;
4981       score  += time_score;
4982
4983       LevelSolved_DisplayFinalGameValues(time, score, health);
4984
4985       if (health == health_final)
4986         StopSound(SND_GAME_LEVELTIME_BONUS);
4987       else if (setup.sound_loops)
4988         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4989       else
4990         PlaySound(SND_GAME_LEVELTIME_BONUS);
4991
4992       return;
4993     }
4994   }
4995
4996   game.panel.active = FALSE;
4997
4998   if (game_over_delay_3 > 0)
4999   {
5000     game_over_delay_3--;
5001
5002     return;
5003   }
5004
5005   GameEnd();
5006 }
5007
5008 void GameEnd(void)
5009 {
5010   // used instead of "level_nr" (needed for network games)
5011   int last_level_nr = levelset.level_nr;
5012   boolean tape_saved = FALSE;
5013
5014   game.LevelSolved_GameEnd = TRUE;
5015
5016   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5017   {
5018     // make sure that request dialog to save tape does not open door again
5019     if (!global.use_envelope_request)
5020       CloseDoor(DOOR_CLOSE_1);
5021
5022     // ask to save tape
5023     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5024
5025     // set unique basename for score tape (also saved in high score table)
5026     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5027   }
5028
5029   // if no tape is to be saved, close both doors simultaneously
5030   CloseDoor(DOOR_CLOSE_ALL);
5031
5032   if (level_editor_test_game || score_info_tape_play)
5033   {
5034     SetGameStatus(GAME_MODE_MAIN);
5035
5036     DrawMainMenu();
5037
5038     return;
5039   }
5040
5041   if (!game.LevelSolved_SaveScore)
5042   {
5043     SetGameStatus(GAME_MODE_MAIN);
5044
5045     DrawMainMenu();
5046
5047     return;
5048   }
5049
5050   if (level_nr == leveldir_current->handicap_level)
5051   {
5052     leveldir_current->handicap_level++;
5053
5054     SaveLevelSetup_SeriesInfo();
5055   }
5056
5057   // save score and score tape before potentially erasing tape below
5058   NewHighScore(last_level_nr, tape_saved);
5059
5060   if (setup.increment_levels &&
5061       level_nr < leveldir_current->last_level &&
5062       !network_playing)
5063   {
5064     level_nr++;         // advance to next level
5065     TapeErase();        // start with empty tape
5066
5067     if (setup.auto_play_next_level)
5068     {
5069       scores.continue_playing = TRUE;
5070       scores.next_level_nr = level_nr;
5071
5072       LoadLevel(level_nr);
5073
5074       SaveLevelSetup_SeriesInfo();
5075     }
5076   }
5077
5078   if (scores.last_added >= 0 && setup.show_scores_after_game)
5079   {
5080     SetGameStatus(GAME_MODE_SCORES);
5081
5082     DrawHallOfFame(last_level_nr);
5083   }
5084   else if (scores.continue_playing)
5085   {
5086     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5087   }
5088   else
5089   {
5090     SetGameStatus(GAME_MODE_MAIN);
5091
5092     DrawMainMenu();
5093   }
5094 }
5095
5096 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5097                          boolean one_score_entry_per_name)
5098 {
5099   int i;
5100
5101   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5102     return -1;
5103
5104   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5105   {
5106     struct ScoreEntry *entry = &list->entry[i];
5107     boolean score_is_better = (new_entry->score >  entry->score);
5108     boolean score_is_equal  = (new_entry->score == entry->score);
5109     boolean time_is_better  = (new_entry->time  <  entry->time);
5110     boolean time_is_equal   = (new_entry->time  == entry->time);
5111     boolean better_by_score = (score_is_better ||
5112                                (score_is_equal && time_is_better));
5113     boolean better_by_time  = (time_is_better ||
5114                                (time_is_equal && score_is_better));
5115     boolean is_better = (level.rate_time_over_score ? better_by_time :
5116                          better_by_score);
5117     boolean entry_is_empty = (entry->score == 0 &&
5118                               entry->time == 0);
5119
5120     // prevent adding server score entries if also existing in local score file
5121     // (special case: historic score entries have an empty tape basename entry)
5122     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5123         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5124     {
5125       // add fields from server score entry not stored in local score entry
5126       // (currently, this means setting platform, version and country fields;
5127       // in rare cases, this may also correct an invalid score value, as
5128       // historic scores might have been truncated to 16-bit values locally)
5129       *entry = *new_entry;
5130
5131       return -1;
5132     }
5133
5134     if (is_better || entry_is_empty)
5135     {
5136       // player has made it to the hall of fame
5137
5138       if (i < MAX_SCORE_ENTRIES - 1)
5139       {
5140         int m = MAX_SCORE_ENTRIES - 1;
5141         int l;
5142
5143         if (one_score_entry_per_name)
5144         {
5145           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5146             if (strEqual(list->entry[l].name, new_entry->name))
5147               m = l;
5148
5149           if (m == i)   // player's new highscore overwrites his old one
5150             goto put_into_list;
5151         }
5152
5153         for (l = m; l > i; l--)
5154           list->entry[l] = list->entry[l - 1];
5155       }
5156
5157       put_into_list:
5158
5159       *entry = *new_entry;
5160
5161       return i;
5162     }
5163     else if (one_score_entry_per_name &&
5164              strEqual(entry->name, new_entry->name))
5165     {
5166       // player already in high score list with better score or time
5167
5168       return -1;
5169     }
5170   }
5171
5172   // special case: new score is beyond the last high score list position
5173   return MAX_SCORE_ENTRIES;
5174 }
5175
5176 void NewHighScore(int level_nr, boolean tape_saved)
5177 {
5178   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5179   boolean one_per_name = FALSE;
5180
5181   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5182   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5183
5184   new_entry.score = game.score_final;
5185   new_entry.time = game.score_time_final;
5186
5187   LoadScore(level_nr);
5188
5189   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5190
5191   if (scores.last_added >= MAX_SCORE_ENTRIES)
5192   {
5193     scores.last_added = MAX_SCORE_ENTRIES - 1;
5194     scores.force_last_added = TRUE;
5195
5196     scores.entry[scores.last_added] = new_entry;
5197
5198     // store last added local score entry (before merging server scores)
5199     scores.last_added_local = scores.last_added;
5200
5201     return;
5202   }
5203
5204   if (scores.last_added < 0)
5205     return;
5206
5207   SaveScore(level_nr);
5208
5209   // store last added local score entry (before merging server scores)
5210   scores.last_added_local = scores.last_added;
5211
5212   if (!game.LevelSolved_SaveTape)
5213     return;
5214
5215   SaveScoreTape(level_nr);
5216
5217   if (setup.ask_for_using_api_server)
5218   {
5219     setup.use_api_server =
5220       Request("Upload your score and tape to the high score server?", REQ_ASK);
5221
5222     if (!setup.use_api_server)
5223       Request("Not using high score server! Use setup menu to enable again!",
5224               REQ_CONFIRM);
5225
5226     runtime.use_api_server = setup.use_api_server;
5227
5228     // after asking for using API server once, do not ask again
5229     setup.ask_for_using_api_server = FALSE;
5230
5231     SaveSetup_ServerSetup();
5232   }
5233
5234   SaveServerScore(level_nr, tape_saved);
5235 }
5236
5237 void MergeServerScore(void)
5238 {
5239   struct ScoreEntry last_added_entry;
5240   boolean one_per_name = FALSE;
5241   int i;
5242
5243   if (scores.last_added >= 0)
5244     last_added_entry = scores.entry[scores.last_added];
5245
5246   for (i = 0; i < server_scores.num_entries; i++)
5247   {
5248     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5249
5250     if (pos >= 0 && pos <= scores.last_added)
5251       scores.last_added++;
5252   }
5253
5254   if (scores.last_added >= MAX_SCORE_ENTRIES)
5255   {
5256     scores.last_added = MAX_SCORE_ENTRIES - 1;
5257     scores.force_last_added = TRUE;
5258
5259     scores.entry[scores.last_added] = last_added_entry;
5260   }
5261 }
5262
5263 static int getElementMoveStepsizeExt(int x, int y, int direction)
5264 {
5265   int element = Tile[x][y];
5266   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5267   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5268   int horiz_move = (dx != 0);
5269   int sign = (horiz_move ? dx : dy);
5270   int step = sign * element_info[element].move_stepsize;
5271
5272   // special values for move stepsize for spring and things on conveyor belt
5273   if (horiz_move)
5274   {
5275     if (CAN_FALL(element) &&
5276         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5277       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5278     else if (element == EL_SPRING)
5279       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5280   }
5281
5282   return step;
5283 }
5284
5285 static int getElementMoveStepsize(int x, int y)
5286 {
5287   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5288 }
5289
5290 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5291 {
5292   if (player->GfxAction != action || player->GfxDir != dir)
5293   {
5294     player->GfxAction = action;
5295     player->GfxDir = dir;
5296     player->Frame = 0;
5297     player->StepFrame = 0;
5298   }
5299 }
5300
5301 static void ResetGfxFrame(int x, int y)
5302 {
5303   // profiling showed that "autotest" spends 10~20% of its time in this function
5304   if (DrawingDeactivatedField())
5305     return;
5306
5307   int element = Tile[x][y];
5308   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5309
5310   if (graphic_info[graphic].anim_global_sync)
5311     GfxFrame[x][y] = FrameCounter;
5312   else if (graphic_info[graphic].anim_global_anim_sync)
5313     GfxFrame[x][y] = getGlobalAnimSyncFrame();
5314   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5315     GfxFrame[x][y] = CustomValue[x][y];
5316   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5317     GfxFrame[x][y] = element_info[element].collect_score;
5318   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5319     GfxFrame[x][y] = ChangeDelay[x][y];
5320 }
5321
5322 static void ResetGfxAnimation(int x, int y)
5323 {
5324   GfxAction[x][y] = ACTION_DEFAULT;
5325   GfxDir[x][y] = MovDir[x][y];
5326   GfxFrame[x][y] = 0;
5327
5328   ResetGfxFrame(x, y);
5329 }
5330
5331 static void ResetRandomAnimationValue(int x, int y)
5332 {
5333   GfxRandom[x][y] = INIT_GFX_RANDOM();
5334 }
5335
5336 static void InitMovingField(int x, int y, int direction)
5337 {
5338   int element = Tile[x][y];
5339   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5340   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5341   int newx = x + dx;
5342   int newy = y + dy;
5343   boolean is_moving_before, is_moving_after;
5344
5345   // check if element was/is moving or being moved before/after mode change
5346   is_moving_before = (WasJustMoving[x][y] != 0);
5347   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5348
5349   // reset animation only for moving elements which change direction of moving
5350   // or which just started or stopped moving
5351   // (else CEs with property "can move" / "not moving" are reset each frame)
5352   if (is_moving_before != is_moving_after ||
5353       direction != MovDir[x][y])
5354     ResetGfxAnimation(x, y);
5355
5356   MovDir[x][y] = direction;
5357   GfxDir[x][y] = direction;
5358
5359   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5360                      direction == MV_DOWN && CAN_FALL(element) ?
5361                      ACTION_FALLING : ACTION_MOVING);
5362
5363   // this is needed for CEs with property "can move" / "not moving"
5364
5365   if (is_moving_after)
5366   {
5367     if (Tile[newx][newy] == EL_EMPTY)
5368       Tile[newx][newy] = EL_BLOCKED;
5369
5370     MovDir[newx][newy] = MovDir[x][y];
5371
5372     CustomValue[newx][newy] = CustomValue[x][y];
5373
5374     GfxFrame[newx][newy] = GfxFrame[x][y];
5375     GfxRandom[newx][newy] = GfxRandom[x][y];
5376     GfxAction[newx][newy] = GfxAction[x][y];
5377     GfxDir[newx][newy] = GfxDir[x][y];
5378   }
5379 }
5380
5381 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5382 {
5383   int direction = MovDir[x][y];
5384   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5385   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5386
5387   *goes_to_x = newx;
5388   *goes_to_y = newy;
5389 }
5390
5391 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5392 {
5393   int oldx = x, oldy = y;
5394   int direction = MovDir[x][y];
5395
5396   if (direction == MV_LEFT)
5397     oldx++;
5398   else if (direction == MV_RIGHT)
5399     oldx--;
5400   else if (direction == MV_UP)
5401     oldy++;
5402   else if (direction == MV_DOWN)
5403     oldy--;
5404
5405   *comes_from_x = oldx;
5406   *comes_from_y = oldy;
5407 }
5408
5409 static int MovingOrBlocked2Element(int x, int y)
5410 {
5411   int element = Tile[x][y];
5412
5413   if (element == EL_BLOCKED)
5414   {
5415     int oldx, oldy;
5416
5417     Blocked2Moving(x, y, &oldx, &oldy);
5418     return Tile[oldx][oldy];
5419   }
5420   else
5421     return element;
5422 }
5423
5424 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5425 {
5426   // like MovingOrBlocked2Element(), but if element is moving
5427   // and (x,y) is the field the moving element is just leaving,
5428   // return EL_BLOCKED instead of the element value
5429   int element = Tile[x][y];
5430
5431   if (IS_MOVING(x, y))
5432   {
5433     if (element == EL_BLOCKED)
5434     {
5435       int oldx, oldy;
5436
5437       Blocked2Moving(x, y, &oldx, &oldy);
5438       return Tile[oldx][oldy];
5439     }
5440     else
5441       return EL_BLOCKED;
5442   }
5443   else
5444     return element;
5445 }
5446
5447 static void RemoveField(int x, int y)
5448 {
5449   Tile[x][y] = EL_EMPTY;
5450
5451   MovPos[x][y] = 0;
5452   MovDir[x][y] = 0;
5453   MovDelay[x][y] = 0;
5454
5455   CustomValue[x][y] = 0;
5456
5457   AmoebaNr[x][y] = 0;
5458   ChangeDelay[x][y] = 0;
5459   ChangePage[x][y] = -1;
5460   Pushed[x][y] = FALSE;
5461
5462   GfxElement[x][y] = EL_UNDEFINED;
5463   GfxAction[x][y] = ACTION_DEFAULT;
5464   GfxDir[x][y] = MV_NONE;
5465 }
5466
5467 static void RemoveMovingField(int x, int y)
5468 {
5469   int oldx = x, oldy = y, newx = x, newy = y;
5470   int element = Tile[x][y];
5471   int next_element = EL_UNDEFINED;
5472
5473   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5474     return;
5475
5476   if (IS_MOVING(x, y))
5477   {
5478     Moving2Blocked(x, y, &newx, &newy);
5479
5480     if (Tile[newx][newy] != EL_BLOCKED)
5481     {
5482       // element is moving, but target field is not free (blocked), but
5483       // already occupied by something different (example: acid pool);
5484       // in this case, only remove the moving field, but not the target
5485
5486       RemoveField(oldx, oldy);
5487
5488       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5489
5490       TEST_DrawLevelField(oldx, oldy);
5491
5492       return;
5493     }
5494   }
5495   else if (element == EL_BLOCKED)
5496   {
5497     Blocked2Moving(x, y, &oldx, &oldy);
5498     if (!IS_MOVING(oldx, oldy))
5499       return;
5500   }
5501
5502   if (element == EL_BLOCKED &&
5503       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5504        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5505        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5506        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5507        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5508        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5509     next_element = get_next_element(Tile[oldx][oldy]);
5510
5511   RemoveField(oldx, oldy);
5512   RemoveField(newx, newy);
5513
5514   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5515
5516   if (next_element != EL_UNDEFINED)
5517     Tile[oldx][oldy] = next_element;
5518
5519   TEST_DrawLevelField(oldx, oldy);
5520   TEST_DrawLevelField(newx, newy);
5521 }
5522
5523 void DrawDynamite(int x, int y)
5524 {
5525   int sx = SCREENX(x), sy = SCREENY(y);
5526   int graphic = el2img(Tile[x][y]);
5527   int frame;
5528
5529   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5530     return;
5531
5532   if (IS_WALKABLE_INSIDE(Back[x][y]))
5533     return;
5534
5535   if (Back[x][y])
5536     DrawLevelElement(x, y, Back[x][y]);
5537   else if (Store[x][y])
5538     DrawLevelElement(x, y, Store[x][y]);
5539   else if (game.use_masked_elements)
5540     DrawLevelElement(x, y, EL_EMPTY);
5541
5542   frame = getGraphicAnimationFrameXY(graphic, x, y);
5543
5544   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5545     DrawGraphicThruMask(sx, sy, graphic, frame);
5546   else
5547     DrawGraphic(sx, sy, graphic, frame);
5548 }
5549
5550 static void CheckDynamite(int x, int y)
5551 {
5552   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5553   {
5554     MovDelay[x][y]--;
5555
5556     if (MovDelay[x][y] != 0)
5557     {
5558       DrawDynamite(x, y);
5559       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5560
5561       return;
5562     }
5563   }
5564
5565   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5566
5567   Bang(x, y);
5568 }
5569
5570 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5571 {
5572   boolean num_checked_players = 0;
5573   int i;
5574
5575   for (i = 0; i < MAX_PLAYERS; i++)
5576   {
5577     if (stored_player[i].active)
5578     {
5579       int sx = stored_player[i].jx;
5580       int sy = stored_player[i].jy;
5581
5582       if (num_checked_players == 0)
5583       {
5584         *sx1 = *sx2 = sx;
5585         *sy1 = *sy2 = sy;
5586       }
5587       else
5588       {
5589         *sx1 = MIN(*sx1, sx);
5590         *sy1 = MIN(*sy1, sy);
5591         *sx2 = MAX(*sx2, sx);
5592         *sy2 = MAX(*sy2, sy);
5593       }
5594
5595       num_checked_players++;
5596     }
5597   }
5598 }
5599
5600 static boolean checkIfAllPlayersFitToScreen_RND(void)
5601 {
5602   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5603
5604   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5605
5606   return (sx2 - sx1 < SCR_FIELDX &&
5607           sy2 - sy1 < SCR_FIELDY);
5608 }
5609
5610 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5611 {
5612   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5613
5614   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5615
5616   *sx = (sx1 + sx2) / 2;
5617   *sy = (sy1 + sy2) / 2;
5618 }
5619
5620 static void DrawRelocateScreen(int old_x, int old_y, int x, int y,
5621                                boolean center_screen, boolean quick_relocation)
5622 {
5623   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5624   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5625   boolean no_delay = (tape.warp_forward);
5626   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5627   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5628   int new_scroll_x, new_scroll_y;
5629
5630   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5631   {
5632     // case 1: quick relocation inside visible screen (without scrolling)
5633
5634     RedrawPlayfield();
5635
5636     return;
5637   }
5638
5639   if (!level.shifted_relocation || center_screen)
5640   {
5641     // relocation _with_ centering of screen
5642
5643     new_scroll_x = SCROLL_POSITION_X(x);
5644     new_scroll_y = SCROLL_POSITION_Y(y);
5645   }
5646   else
5647   {
5648     // relocation _without_ centering of screen
5649
5650     int center_scroll_x = SCROLL_POSITION_X(old_x);
5651     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5652     int offset_x = x + (scroll_x - center_scroll_x);
5653     int offset_y = y + (scroll_y - center_scroll_y);
5654
5655     // for new screen position, apply previous offset to center position
5656     new_scroll_x = SCROLL_POSITION_X(offset_x);
5657     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5658   }
5659
5660   if (quick_relocation)
5661   {
5662     // case 2: quick relocation (redraw without visible scrolling)
5663
5664     scroll_x = new_scroll_x;
5665     scroll_y = new_scroll_y;
5666
5667     RedrawPlayfield();
5668
5669     return;
5670   }
5671
5672   // case 3: visible relocation (with scrolling to new position)
5673
5674   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5675
5676   SetVideoFrameDelay(wait_delay_value);
5677
5678   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5679   {
5680     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5681     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5682
5683     if (dx == 0 && dy == 0)             // no scrolling needed at all
5684       break;
5685
5686     scroll_x -= dx;
5687     scroll_y -= dy;
5688
5689     // set values for horizontal/vertical screen scrolling (half tile size)
5690     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5691     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5692     int pos_x = dx * TILEX / 2;
5693     int pos_y = dy * TILEY / 2;
5694     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5695     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5696
5697     ScrollLevel(dx, dy);
5698     DrawAllPlayers();
5699
5700     // scroll in two steps of half tile size to make things smoother
5701     BlitScreenToBitmapExt_RND(window, fx, fy);
5702
5703     // scroll second step to align at full tile size
5704     BlitScreenToBitmap(window);
5705   }
5706
5707   DrawAllPlayers();
5708   BackToFront();
5709
5710   SetVideoFrameDelay(frame_delay_value_old);
5711 }
5712
5713 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5714 {
5715   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5716   int player_nr = GET_PLAYER_NR(el_player);
5717   struct PlayerInfo *player = &stored_player[player_nr];
5718   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5719   boolean no_delay = (tape.warp_forward);
5720   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5721   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5722   int old_jx = player->jx;
5723   int old_jy = player->jy;
5724   int old_element = Tile[old_jx][old_jy];
5725   int element = Tile[jx][jy];
5726   boolean player_relocated = (old_jx != jx || old_jy != jy);
5727
5728   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5729   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5730   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5731   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5732   int leave_side_horiz = move_dir_horiz;
5733   int leave_side_vert  = move_dir_vert;
5734   int enter_side = enter_side_horiz | enter_side_vert;
5735   int leave_side = leave_side_horiz | leave_side_vert;
5736
5737   if (player->buried)           // do not reanimate dead player
5738     return;
5739
5740   if (!player_relocated)        // no need to relocate the player
5741     return;
5742
5743   if (IS_PLAYER(jx, jy))        // player already placed at new position
5744   {
5745     RemoveField(jx, jy);        // temporarily remove newly placed player
5746     DrawLevelField(jx, jy);
5747   }
5748
5749   if (player->present)
5750   {
5751     while (player->MovPos)
5752     {
5753       ScrollPlayer(player, SCROLL_GO_ON);
5754       ScrollScreen(NULL, SCROLL_GO_ON);
5755
5756       AdvanceFrameAndPlayerCounters(player->index_nr);
5757
5758       DrawPlayer(player);
5759
5760       BackToFront_WithFrameDelay(wait_delay_value);
5761     }
5762
5763     DrawPlayer(player);         // needed here only to cleanup last field
5764     DrawLevelField(player->jx, player->jy);     // remove player graphic
5765
5766     player->is_moving = FALSE;
5767   }
5768
5769   if (IS_CUSTOM_ELEMENT(old_element))
5770     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5771                                CE_LEFT_BY_PLAYER,
5772                                player->index_bit, leave_side);
5773
5774   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5775                                       CE_PLAYER_LEAVES_X,
5776                                       player->index_bit, leave_side);
5777
5778   Tile[jx][jy] = el_player;
5779   InitPlayerField(jx, jy, el_player, TRUE);
5780
5781   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5782      possible that the relocation target field did not contain a player element,
5783      but a walkable element, to which the new player was relocated -- in this
5784      case, restore that (already initialized!) element on the player field */
5785   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5786   {
5787     Tile[jx][jy] = element;     // restore previously existing element
5788   }
5789
5790   // only visually relocate centered player
5791   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy,
5792                      FALSE, level.instant_relocation);
5793
5794   TestIfPlayerTouchesBadThing(jx, jy);
5795   TestIfPlayerTouchesCustomElement(jx, jy);
5796
5797   if (IS_CUSTOM_ELEMENT(element))
5798     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5799                                player->index_bit, enter_side);
5800
5801   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5802                                       player->index_bit, enter_side);
5803
5804   if (player->is_switching)
5805   {
5806     /* ensure that relocation while still switching an element does not cause
5807        a new element to be treated as also switched directly after relocation
5808        (this is important for teleporter switches that teleport the player to
5809        a place where another teleporter switch is in the same direction, which
5810        would then incorrectly be treated as immediately switched before the
5811        direction key that caused the switch was released) */
5812
5813     player->switch_x += jx - old_jx;
5814     player->switch_y += jy - old_jy;
5815   }
5816 }
5817
5818 static void Explode(int ex, int ey, int phase, int mode)
5819 {
5820   int x, y;
5821   int last_phase;
5822   int border_element;
5823
5824   // !!! eliminate this variable !!!
5825   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5826
5827   if (game.explosions_delayed)
5828   {
5829     ExplodeField[ex][ey] = mode;
5830     return;
5831   }
5832
5833   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5834   {
5835     int center_element = Tile[ex][ey];
5836     int artwork_element, explosion_element;     // set these values later
5837
5838     // remove things displayed in background while burning dynamite
5839     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5840       Back[ex][ey] = 0;
5841
5842     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5843     {
5844       // put moving element to center field (and let it explode there)
5845       center_element = MovingOrBlocked2Element(ex, ey);
5846       RemoveMovingField(ex, ey);
5847       Tile[ex][ey] = center_element;
5848     }
5849
5850     // now "center_element" is finally determined -- set related values now
5851     artwork_element = center_element;           // for custom player artwork
5852     explosion_element = center_element;         // for custom player artwork
5853
5854     if (IS_PLAYER(ex, ey))
5855     {
5856       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5857
5858       artwork_element = stored_player[player_nr].artwork_element;
5859
5860       if (level.use_explosion_element[player_nr])
5861       {
5862         explosion_element = level.explosion_element[player_nr];
5863         artwork_element = explosion_element;
5864       }
5865     }
5866
5867     if (mode == EX_TYPE_NORMAL ||
5868         mode == EX_TYPE_CENTER ||
5869         mode == EX_TYPE_CROSS)
5870       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5871
5872     last_phase = element_info[explosion_element].explosion_delay + 1;
5873
5874     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5875     {
5876       int xx = x - ex + 1;
5877       int yy = y - ey + 1;
5878       int element;
5879
5880       if (!IN_LEV_FIELD(x, y) ||
5881           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5882           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5883         continue;
5884
5885       element = Tile[x][y];
5886
5887       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5888       {
5889         element = MovingOrBlocked2Element(x, y);
5890
5891         if (!IS_EXPLOSION_PROOF(element))
5892           RemoveMovingField(x, y);
5893       }
5894
5895       // indestructible elements can only explode in center (but not flames)
5896       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5897                                            mode == EX_TYPE_BORDER)) ||
5898           element == EL_FLAMES)
5899         continue;
5900
5901       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5902          behaviour, for example when touching a yamyam that explodes to rocks
5903          with active deadly shield, a rock is created under the player !!! */
5904       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5905 #if 0
5906       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5907           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5908            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5909 #else
5910       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5911 #endif
5912       {
5913         if (IS_ACTIVE_BOMB(element))
5914         {
5915           // re-activate things under the bomb like gate or penguin
5916           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5917           Back[x][y] = 0;
5918         }
5919
5920         continue;
5921       }
5922
5923       // save walkable background elements while explosion on same tile
5924       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5925           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5926         Back[x][y] = element;
5927
5928       // ignite explodable elements reached by other explosion
5929       if (element == EL_EXPLOSION)
5930         element = Store2[x][y];
5931
5932       if (AmoebaNr[x][y] &&
5933           (element == EL_AMOEBA_FULL ||
5934            element == EL_BD_AMOEBA ||
5935            element == EL_AMOEBA_GROWING))
5936       {
5937         AmoebaCnt[AmoebaNr[x][y]]--;
5938         AmoebaCnt2[AmoebaNr[x][y]]--;
5939       }
5940
5941       RemoveField(x, y);
5942
5943       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5944       {
5945         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5946
5947         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5948
5949         if (PLAYERINFO(ex, ey)->use_murphy)
5950           Store[x][y] = EL_EMPTY;
5951       }
5952
5953       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5954       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5955       else if (IS_PLAYER_ELEMENT(center_element))
5956         Store[x][y] = EL_EMPTY;
5957       else if (center_element == EL_YAMYAM)
5958         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5959       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5960         Store[x][y] = element_info[center_element].content.e[xx][yy];
5961 #if 1
5962       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5963       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5964       // otherwise) -- FIX THIS !!!
5965       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5966         Store[x][y] = element_info[element].content.e[1][1];
5967 #else
5968       else if (!CAN_EXPLODE(element))
5969         Store[x][y] = element_info[element].content.e[1][1];
5970 #endif
5971       else
5972         Store[x][y] = EL_EMPTY;
5973
5974       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5975           center_element == EL_AMOEBA_TO_DIAMOND)
5976         Store2[x][y] = element;
5977
5978       Tile[x][y] = EL_EXPLOSION;
5979       GfxElement[x][y] = artwork_element;
5980
5981       ExplodePhase[x][y] = 1;
5982       ExplodeDelay[x][y] = last_phase;
5983
5984       Stop[x][y] = TRUE;
5985     }
5986
5987     if (center_element == EL_YAMYAM)
5988       game.yamyam_content_nr =
5989         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5990
5991     return;
5992   }
5993
5994   if (Stop[ex][ey])
5995     return;
5996
5997   x = ex;
5998   y = ey;
5999
6000   if (phase == 1)
6001     GfxFrame[x][y] = 0;         // restart explosion animation
6002
6003   last_phase = ExplodeDelay[x][y];
6004
6005   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6006
6007   // this can happen if the player leaves an explosion just in time
6008   if (GfxElement[x][y] == EL_UNDEFINED)
6009     GfxElement[x][y] = EL_EMPTY;
6010
6011   border_element = Store2[x][y];
6012   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6013     border_element = StorePlayer[x][y];
6014
6015   if (phase == element_info[border_element].ignition_delay ||
6016       phase == last_phase)
6017   {
6018     boolean border_explosion = FALSE;
6019
6020     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6021         !PLAYER_EXPLOSION_PROTECTED(x, y))
6022     {
6023       KillPlayerUnlessExplosionProtected(x, y);
6024       border_explosion = TRUE;
6025     }
6026     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6027     {
6028       Tile[x][y] = Store2[x][y];
6029       Store2[x][y] = 0;
6030       Bang(x, y);
6031       border_explosion = TRUE;
6032     }
6033     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6034     {
6035       AmoebaToDiamond(x, y);
6036       Store2[x][y] = 0;
6037       border_explosion = TRUE;
6038     }
6039
6040     // if an element just explodes due to another explosion (chain-reaction),
6041     // do not immediately end the new explosion when it was the last frame of
6042     // the explosion (as it would be done in the following "if"-statement!)
6043     if (border_explosion && phase == last_phase)
6044       return;
6045   }
6046
6047   // this can happen if the player was just killed by an explosion
6048   if (GfxElement[x][y] == EL_UNDEFINED)
6049     GfxElement[x][y] = EL_EMPTY;
6050
6051   if (phase == last_phase)
6052   {
6053     int element;
6054
6055     element = Tile[x][y] = Store[x][y];
6056     Store[x][y] = Store2[x][y] = 0;
6057     GfxElement[x][y] = EL_UNDEFINED;
6058
6059     // player can escape from explosions and might therefore be still alive
6060     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6061         element <= EL_PLAYER_IS_EXPLODING_4)
6062     {
6063       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6064       int explosion_element = EL_PLAYER_1 + player_nr;
6065       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6066       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6067
6068       if (level.use_explosion_element[player_nr])
6069         explosion_element = level.explosion_element[player_nr];
6070
6071       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6072                     element_info[explosion_element].content.e[xx][yy]);
6073     }
6074
6075     // restore probably existing indestructible background element
6076     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6077       element = Tile[x][y] = Back[x][y];
6078     Back[x][y] = 0;
6079
6080     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6081     GfxDir[x][y] = MV_NONE;
6082     ChangeDelay[x][y] = 0;
6083     ChangePage[x][y] = -1;
6084
6085     CustomValue[x][y] = 0;
6086
6087     InitField_WithBug2(x, y, FALSE);
6088
6089     TEST_DrawLevelField(x, y);
6090
6091     TestIfElementTouchesCustomElement(x, y);
6092
6093     if (GFX_CRUMBLED(element))
6094       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6095
6096     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6097       StorePlayer[x][y] = 0;
6098
6099     if (IS_PLAYER_ELEMENT(element))
6100       RelocatePlayer(x, y, element);
6101   }
6102   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6103   {
6104     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6105     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6106
6107     if (phase == delay)
6108       TEST_DrawLevelFieldCrumbled(x, y);
6109
6110     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6111     {
6112       DrawLevelElement(x, y, Back[x][y]);
6113       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6114     }
6115     else if (IS_WALKABLE_UNDER(Back[x][y]))
6116     {
6117       DrawLevelGraphic(x, y, graphic, frame);
6118       DrawLevelElementThruMask(x, y, Back[x][y]);
6119     }
6120     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6121       DrawLevelGraphic(x, y, graphic, frame);
6122   }
6123 }
6124
6125 static void DynaExplode(int ex, int ey)
6126 {
6127   int i, j;
6128   int dynabomb_element = Tile[ex][ey];
6129   int dynabomb_size = 1;
6130   boolean dynabomb_xl = FALSE;
6131   struct PlayerInfo *player;
6132   struct XY *xy = xy_topdown;
6133
6134   if (IS_ACTIVE_BOMB(dynabomb_element))
6135   {
6136     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6137     dynabomb_size = player->dynabomb_size;
6138     dynabomb_xl = player->dynabomb_xl;
6139     player->dynabombs_left++;
6140   }
6141
6142   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6143
6144   for (i = 0; i < NUM_DIRECTIONS; i++)
6145   {
6146     for (j = 1; j <= dynabomb_size; j++)
6147     {
6148       int x = ex + j * xy[i].x;
6149       int y = ey + j * xy[i].y;
6150       int element;
6151
6152       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6153         break;
6154
6155       element = Tile[x][y];
6156
6157       // do not restart explosions of fields with active bombs
6158       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6159         continue;
6160
6161       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6162
6163       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6164           !IS_DIGGABLE(element) && !dynabomb_xl)
6165         break;
6166     }
6167   }
6168 }
6169
6170 void Bang(int x, int y)
6171 {
6172   int element = MovingOrBlocked2Element(x, y);
6173   int explosion_type = EX_TYPE_NORMAL;
6174
6175   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6176   {
6177     struct PlayerInfo *player = PLAYERINFO(x, y);
6178
6179     element = Tile[x][y] = player->initial_element;
6180
6181     if (level.use_explosion_element[player->index_nr])
6182     {
6183       int explosion_element = level.explosion_element[player->index_nr];
6184
6185       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6186         explosion_type = EX_TYPE_CROSS;
6187       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6188         explosion_type = EX_TYPE_CENTER;
6189     }
6190   }
6191
6192   switch (element)
6193   {
6194     case EL_BUG:
6195     case EL_SPACESHIP:
6196     case EL_BD_BUTTERFLY:
6197     case EL_BD_FIREFLY:
6198     case EL_YAMYAM:
6199     case EL_DARK_YAMYAM:
6200     case EL_ROBOT:
6201     case EL_PACMAN:
6202     case EL_MOLE:
6203       RaiseScoreElement(element);
6204       break;
6205
6206     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6207     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6208     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6209     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6210     case EL_DYNABOMB_INCREASE_NUMBER:
6211     case EL_DYNABOMB_INCREASE_SIZE:
6212     case EL_DYNABOMB_INCREASE_POWER:
6213       explosion_type = EX_TYPE_DYNA;
6214       break;
6215
6216     case EL_DC_LANDMINE:
6217       explosion_type = EX_TYPE_CENTER;
6218       break;
6219
6220     case EL_PENGUIN:
6221     case EL_LAMP:
6222     case EL_LAMP_ACTIVE:
6223     case EL_AMOEBA_TO_DIAMOND:
6224       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6225         explosion_type = EX_TYPE_CENTER;
6226       break;
6227
6228     default:
6229       if (element_info[element].explosion_type == EXPLODES_CROSS)
6230         explosion_type = EX_TYPE_CROSS;
6231       else if (element_info[element].explosion_type == EXPLODES_1X1)
6232         explosion_type = EX_TYPE_CENTER;
6233       break;
6234   }
6235
6236   if (explosion_type == EX_TYPE_DYNA)
6237     DynaExplode(x, y);
6238   else
6239     Explode(x, y, EX_PHASE_START, explosion_type);
6240
6241   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6242 }
6243
6244 static void SplashAcid(int x, int y)
6245 {
6246   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6247       (!IN_LEV_FIELD(x - 1, y - 2) ||
6248        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6249     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6250
6251   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6252       (!IN_LEV_FIELD(x + 1, y - 2) ||
6253        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6254     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6255
6256   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6257 }
6258
6259 static void InitBeltMovement(void)
6260 {
6261   static int belt_base_element[4] =
6262   {
6263     EL_CONVEYOR_BELT_1_LEFT,
6264     EL_CONVEYOR_BELT_2_LEFT,
6265     EL_CONVEYOR_BELT_3_LEFT,
6266     EL_CONVEYOR_BELT_4_LEFT
6267   };
6268   static int belt_base_active_element[4] =
6269   {
6270     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6271     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6272     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6273     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6274   };
6275
6276   int x, y, i, j;
6277
6278   // set frame order for belt animation graphic according to belt direction
6279   for (i = 0; i < NUM_BELTS; i++)
6280   {
6281     int belt_nr = i;
6282
6283     for (j = 0; j < NUM_BELT_PARTS; j++)
6284     {
6285       int element = belt_base_active_element[belt_nr] + j;
6286       int graphic_1 = el2img(element);
6287       int graphic_2 = el2panelimg(element);
6288
6289       if (game.belt_dir[i] == MV_LEFT)
6290       {
6291         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6292         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6293       }
6294       else
6295       {
6296         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6297         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6298       }
6299     }
6300   }
6301
6302   SCAN_PLAYFIELD(x, y)
6303   {
6304     int element = Tile[x][y];
6305
6306     for (i = 0; i < NUM_BELTS; i++)
6307     {
6308       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6309       {
6310         int e_belt_nr = getBeltNrFromBeltElement(element);
6311         int belt_nr = i;
6312
6313         if (e_belt_nr == belt_nr)
6314         {
6315           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6316
6317           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6318         }
6319       }
6320     }
6321   }
6322 }
6323
6324 static void ToggleBeltSwitch(int x, int y)
6325 {
6326   static int belt_base_element[4] =
6327   {
6328     EL_CONVEYOR_BELT_1_LEFT,
6329     EL_CONVEYOR_BELT_2_LEFT,
6330     EL_CONVEYOR_BELT_3_LEFT,
6331     EL_CONVEYOR_BELT_4_LEFT
6332   };
6333   static int belt_base_active_element[4] =
6334   {
6335     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6336     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6337     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6338     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6339   };
6340   static int belt_base_switch_element[4] =
6341   {
6342     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6343     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6344     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6345     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6346   };
6347   static int belt_move_dir[4] =
6348   {
6349     MV_LEFT,
6350     MV_NONE,
6351     MV_RIGHT,
6352     MV_NONE,
6353   };
6354
6355   int element = Tile[x][y];
6356   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6357   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6358   int belt_dir = belt_move_dir[belt_dir_nr];
6359   int xx, yy, i;
6360
6361   if (!IS_BELT_SWITCH(element))
6362     return;
6363
6364   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6365   game.belt_dir[belt_nr] = belt_dir;
6366
6367   if (belt_dir_nr == 3)
6368     belt_dir_nr = 1;
6369
6370   // set frame order for belt animation graphic according to belt direction
6371   for (i = 0; i < NUM_BELT_PARTS; i++)
6372   {
6373     int element = belt_base_active_element[belt_nr] + i;
6374     int graphic_1 = el2img(element);
6375     int graphic_2 = el2panelimg(element);
6376
6377     if (belt_dir == MV_LEFT)
6378     {
6379       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6380       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6381     }
6382     else
6383     {
6384       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6385       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6386     }
6387   }
6388
6389   SCAN_PLAYFIELD(xx, yy)
6390   {
6391     int element = Tile[xx][yy];
6392
6393     if (IS_BELT_SWITCH(element))
6394     {
6395       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6396
6397       if (e_belt_nr == belt_nr)
6398       {
6399         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6400         TEST_DrawLevelField(xx, yy);
6401       }
6402     }
6403     else if (IS_BELT(element) && belt_dir != MV_NONE)
6404     {
6405       int e_belt_nr = getBeltNrFromBeltElement(element);
6406
6407       if (e_belt_nr == belt_nr)
6408       {
6409         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6410
6411         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6412         TEST_DrawLevelField(xx, yy);
6413       }
6414     }
6415     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6416     {
6417       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6418
6419       if (e_belt_nr == belt_nr)
6420       {
6421         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6422
6423         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6424         TEST_DrawLevelField(xx, yy);
6425       }
6426     }
6427   }
6428 }
6429
6430 static void ToggleSwitchgateSwitch(void)
6431 {
6432   int xx, yy;
6433
6434   game.switchgate_pos = !game.switchgate_pos;
6435
6436   SCAN_PLAYFIELD(xx, yy)
6437   {
6438     int element = Tile[xx][yy];
6439
6440     if (element == EL_SWITCHGATE_SWITCH_UP)
6441     {
6442       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6443       TEST_DrawLevelField(xx, yy);
6444     }
6445     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6446     {
6447       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6448       TEST_DrawLevelField(xx, yy);
6449     }
6450     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6451     {
6452       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6453       TEST_DrawLevelField(xx, yy);
6454     }
6455     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6456     {
6457       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6458       TEST_DrawLevelField(xx, yy);
6459     }
6460     else if (element == EL_SWITCHGATE_OPEN ||
6461              element == EL_SWITCHGATE_OPENING)
6462     {
6463       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6464
6465       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6466     }
6467     else if (element == EL_SWITCHGATE_CLOSED ||
6468              element == EL_SWITCHGATE_CLOSING)
6469     {
6470       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6471
6472       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6473     }
6474   }
6475 }
6476
6477 static int getInvisibleActiveFromInvisibleElement(int element)
6478 {
6479   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6480           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6481           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6482           element);
6483 }
6484
6485 static int getInvisibleFromInvisibleActiveElement(int element)
6486 {
6487   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6488           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6489           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6490           element);
6491 }
6492
6493 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6494 {
6495   int x, y;
6496
6497   SCAN_PLAYFIELD(x, y)
6498   {
6499     int element = Tile[x][y];
6500
6501     if (element == EL_LIGHT_SWITCH &&
6502         game.light_time_left > 0)
6503     {
6504       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6505       TEST_DrawLevelField(x, y);
6506     }
6507     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6508              game.light_time_left == 0)
6509     {
6510       Tile[x][y] = EL_LIGHT_SWITCH;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_EMC_DRIPPER &&
6514              game.light_time_left > 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6520              game.light_time_left == 0)
6521     {
6522       Tile[x][y] = EL_EMC_DRIPPER;
6523       TEST_DrawLevelField(x, y);
6524     }
6525     else if (element == EL_INVISIBLE_STEELWALL ||
6526              element == EL_INVISIBLE_WALL ||
6527              element == EL_INVISIBLE_SAND)
6528     {
6529       if (game.light_time_left > 0)
6530         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6531
6532       TEST_DrawLevelField(x, y);
6533
6534       // uncrumble neighbour fields, if needed
6535       if (element == EL_INVISIBLE_SAND)
6536         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6537     }
6538     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6539              element == EL_INVISIBLE_WALL_ACTIVE ||
6540              element == EL_INVISIBLE_SAND_ACTIVE)
6541     {
6542       if (game.light_time_left == 0)
6543         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6544
6545       TEST_DrawLevelField(x, y);
6546
6547       // re-crumble neighbour fields, if needed
6548       if (element == EL_INVISIBLE_SAND)
6549         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6550     }
6551   }
6552 }
6553
6554 static void RedrawAllInvisibleElementsForLenses(void)
6555 {
6556   int x, y;
6557
6558   SCAN_PLAYFIELD(x, y)
6559   {
6560     int element = Tile[x][y];
6561
6562     if (element == EL_EMC_DRIPPER &&
6563         game.lenses_time_left > 0)
6564     {
6565       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6569              game.lenses_time_left == 0)
6570     {
6571       Tile[x][y] = EL_EMC_DRIPPER;
6572       TEST_DrawLevelField(x, y);
6573     }
6574     else if (element == EL_INVISIBLE_STEELWALL ||
6575              element == EL_INVISIBLE_WALL ||
6576              element == EL_INVISIBLE_SAND)
6577     {
6578       if (game.lenses_time_left > 0)
6579         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6580
6581       TEST_DrawLevelField(x, y);
6582
6583       // uncrumble neighbour fields, if needed
6584       if (element == EL_INVISIBLE_SAND)
6585         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6586     }
6587     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6588              element == EL_INVISIBLE_WALL_ACTIVE ||
6589              element == EL_INVISIBLE_SAND_ACTIVE)
6590     {
6591       if (game.lenses_time_left == 0)
6592         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6593
6594       TEST_DrawLevelField(x, y);
6595
6596       // re-crumble neighbour fields, if needed
6597       if (element == EL_INVISIBLE_SAND)
6598         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6599     }
6600   }
6601 }
6602
6603 static void RedrawAllInvisibleElementsForMagnifier(void)
6604 {
6605   int x, y;
6606
6607   SCAN_PLAYFIELD(x, y)
6608   {
6609     int element = Tile[x][y];
6610
6611     if (element == EL_EMC_FAKE_GRASS &&
6612         game.magnify_time_left > 0)
6613     {
6614       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6615       TEST_DrawLevelField(x, y);
6616     }
6617     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6618              game.magnify_time_left == 0)
6619     {
6620       Tile[x][y] = EL_EMC_FAKE_GRASS;
6621       TEST_DrawLevelField(x, y);
6622     }
6623     else if (IS_GATE_GRAY(element) &&
6624              game.magnify_time_left > 0)
6625     {
6626       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6627                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6628                     IS_EM_GATE_GRAY(element) ?
6629                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6630                     IS_EMC_GATE_GRAY(element) ?
6631                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6632                     IS_DC_GATE_GRAY(element) ?
6633                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6634                     element);
6635       TEST_DrawLevelField(x, y);
6636     }
6637     else if (IS_GATE_GRAY_ACTIVE(element) &&
6638              game.magnify_time_left == 0)
6639     {
6640       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6641                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6642                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6643                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6644                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6645                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6646                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6647                     EL_DC_GATE_WHITE_GRAY :
6648                     element);
6649       TEST_DrawLevelField(x, y);
6650     }
6651   }
6652 }
6653
6654 static void ToggleLightSwitch(int x, int y)
6655 {
6656   int element = Tile[x][y];
6657
6658   game.light_time_left =
6659     (element == EL_LIGHT_SWITCH ?
6660      level.time_light * FRAMES_PER_SECOND : 0);
6661
6662   RedrawAllLightSwitchesAndInvisibleElements();
6663 }
6664
6665 static void ActivateTimegateSwitch(int x, int y)
6666 {
6667   int xx, yy;
6668
6669   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6670
6671   SCAN_PLAYFIELD(xx, yy)
6672   {
6673     int element = Tile[xx][yy];
6674
6675     if (element == EL_TIMEGATE_CLOSED ||
6676         element == EL_TIMEGATE_CLOSING)
6677     {
6678       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6679       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6680     }
6681
6682     /*
6683     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6684     {
6685       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6686       TEST_DrawLevelField(xx, yy);
6687     }
6688     */
6689
6690   }
6691
6692   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6693                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6694 }
6695
6696 static void Impact(int x, int y)
6697 {
6698   boolean last_line = (y == lev_fieldy - 1);
6699   boolean object_hit = FALSE;
6700   boolean impact = (last_line || object_hit);
6701   int element = Tile[x][y];
6702   int smashed = EL_STEELWALL;
6703
6704   if (!last_line)       // check if element below was hit
6705   {
6706     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6707       return;
6708
6709     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6710                                          MovDir[x][y + 1] != MV_DOWN ||
6711                                          MovPos[x][y + 1] <= TILEY / 2));
6712
6713     // do not smash moving elements that left the smashed field in time
6714     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6715         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6716       object_hit = FALSE;
6717
6718 #if USE_QUICKSAND_IMPACT_BUGFIX
6719     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6720     {
6721       RemoveMovingField(x, y + 1);
6722       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6723       Tile[x][y + 2] = EL_ROCK;
6724       TEST_DrawLevelField(x, y + 2);
6725
6726       object_hit = TRUE;
6727     }
6728
6729     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6730     {
6731       RemoveMovingField(x, y + 1);
6732       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6733       Tile[x][y + 2] = EL_ROCK;
6734       TEST_DrawLevelField(x, y + 2);
6735
6736       object_hit = TRUE;
6737     }
6738 #endif
6739
6740     if (object_hit)
6741       smashed = MovingOrBlocked2Element(x, y + 1);
6742
6743     impact = (last_line || object_hit);
6744   }
6745
6746   if (!last_line && smashed == EL_ACID) // element falls into acid
6747   {
6748     SplashAcid(x, y + 1);
6749     return;
6750   }
6751
6752   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6753   // only reset graphic animation if graphic really changes after impact
6754   if (impact &&
6755       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6756   {
6757     ResetGfxAnimation(x, y);
6758     TEST_DrawLevelField(x, y);
6759   }
6760
6761   if (impact && CAN_EXPLODE_IMPACT(element))
6762   {
6763     Bang(x, y);
6764     return;
6765   }
6766   else if (impact && element == EL_PEARL &&
6767            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6768   {
6769     ResetGfxAnimation(x, y);
6770
6771     Tile[x][y] = EL_PEARL_BREAKING;
6772     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6773     return;
6774   }
6775   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6776   {
6777     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6778
6779     return;
6780   }
6781
6782   if (impact && element == EL_AMOEBA_DROP)
6783   {
6784     if (object_hit && IS_PLAYER(x, y + 1))
6785       KillPlayerUnlessEnemyProtected(x, y + 1);
6786     else if (object_hit && smashed == EL_PENGUIN)
6787       Bang(x, y + 1);
6788     else
6789     {
6790       Tile[x][y] = EL_AMOEBA_GROWING;
6791       Store[x][y] = EL_AMOEBA_WET;
6792
6793       ResetRandomAnimationValue(x, y);
6794     }
6795     return;
6796   }
6797
6798   if (object_hit)               // check which object was hit
6799   {
6800     if ((CAN_PASS_MAGIC_WALL(element) && 
6801          (smashed == EL_MAGIC_WALL ||
6802           smashed == EL_BD_MAGIC_WALL)) ||
6803         (CAN_PASS_DC_MAGIC_WALL(element) &&
6804          smashed == EL_DC_MAGIC_WALL))
6805     {
6806       int xx, yy;
6807       int activated_magic_wall =
6808         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6809          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6810          EL_DC_MAGIC_WALL_ACTIVE);
6811
6812       // activate magic wall / mill
6813       SCAN_PLAYFIELD(xx, yy)
6814       {
6815         if (Tile[xx][yy] == smashed)
6816           Tile[xx][yy] = activated_magic_wall;
6817       }
6818
6819       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6820       game.magic_wall_active = TRUE;
6821
6822       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6823                             SND_MAGIC_WALL_ACTIVATING :
6824                             smashed == EL_BD_MAGIC_WALL ?
6825                             SND_BD_MAGIC_WALL_ACTIVATING :
6826                             SND_DC_MAGIC_WALL_ACTIVATING));
6827     }
6828
6829     if (IS_PLAYER(x, y + 1))
6830     {
6831       if (CAN_SMASH_PLAYER(element))
6832       {
6833         KillPlayerUnlessEnemyProtected(x, y + 1);
6834         return;
6835       }
6836     }
6837     else if (smashed == EL_PENGUIN)
6838     {
6839       if (CAN_SMASH_PLAYER(element))
6840       {
6841         Bang(x, y + 1);
6842         return;
6843       }
6844     }
6845     else if (element == EL_BD_DIAMOND)
6846     {
6847       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6848       {
6849         Bang(x, y + 1);
6850         return;
6851       }
6852     }
6853     else if (((element == EL_SP_INFOTRON ||
6854                element == EL_SP_ZONK) &&
6855               (smashed == EL_SP_SNIKSNAK ||
6856                smashed == EL_SP_ELECTRON ||
6857                smashed == EL_SP_DISK_ORANGE)) ||
6858              (element == EL_SP_INFOTRON &&
6859               smashed == EL_SP_DISK_YELLOW))
6860     {
6861       Bang(x, y + 1);
6862       return;
6863     }
6864     else if (CAN_SMASH_EVERYTHING(element))
6865     {
6866       if (IS_CLASSIC_ENEMY(smashed) ||
6867           CAN_EXPLODE_SMASHED(smashed))
6868       {
6869         Bang(x, y + 1);
6870         return;
6871       }
6872       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6873       {
6874         if (smashed == EL_LAMP ||
6875             smashed == EL_LAMP_ACTIVE)
6876         {
6877           Bang(x, y + 1);
6878           return;
6879         }
6880         else if (smashed == EL_NUT)
6881         {
6882           Tile[x][y + 1] = EL_NUT_BREAKING;
6883           PlayLevelSound(x, y, SND_NUT_BREAKING);
6884           RaiseScoreElement(EL_NUT);
6885           return;
6886         }
6887         else if (smashed == EL_PEARL)
6888         {
6889           ResetGfxAnimation(x, y);
6890
6891           Tile[x][y + 1] = EL_PEARL_BREAKING;
6892           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6893           return;
6894         }
6895         else if (smashed == EL_DIAMOND)
6896         {
6897           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6898           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6899           return;
6900         }
6901         else if (IS_BELT_SWITCH(smashed))
6902         {
6903           ToggleBeltSwitch(x, y + 1);
6904         }
6905         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6906                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6907                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6908                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6909         {
6910           ToggleSwitchgateSwitch();
6911         }
6912         else if (smashed == EL_LIGHT_SWITCH ||
6913                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6914         {
6915           ToggleLightSwitch(x, y + 1);
6916         }
6917         else
6918         {
6919           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6920
6921           CheckElementChangeBySide(x, y + 1, smashed, element,
6922                                    CE_SWITCHED, CH_SIDE_TOP);
6923           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6924                                             CH_SIDE_TOP);
6925         }
6926       }
6927       else
6928       {
6929         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6930       }
6931     }
6932   }
6933
6934   // play sound of magic wall / mill
6935   if (!last_line &&
6936       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6937        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6938        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6939   {
6940     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6941       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6942     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6943       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6944     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6945       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6946
6947     return;
6948   }
6949
6950   // play sound of object that hits the ground
6951   if (last_line || object_hit)
6952     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6953 }
6954
6955 static void TurnRoundExt(int x, int y)
6956 {
6957   static struct
6958   {
6959     int dx, dy;
6960   } move_xy[] =
6961   {
6962     {  0,  0 },
6963     { -1,  0 },
6964     { +1,  0 },
6965     {  0,  0 },
6966     {  0, -1 },
6967     {  0,  0 }, { 0, 0 }, { 0, 0 },
6968     {  0, +1 }
6969   };
6970   static struct
6971   {
6972     int left, right, back;
6973   } turn[] =
6974   {
6975     { 0,        0,              0        },
6976     { MV_DOWN,  MV_UP,          MV_RIGHT },
6977     { MV_UP,    MV_DOWN,        MV_LEFT  },
6978     { 0,        0,              0        },
6979     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6980     { 0,        0,              0        },
6981     { 0,        0,              0        },
6982     { 0,        0,              0        },
6983     { MV_RIGHT, MV_LEFT,        MV_UP    }
6984   };
6985
6986   int element = Tile[x][y];
6987   int move_pattern = element_info[element].move_pattern;
6988
6989   int old_move_dir = MovDir[x][y];
6990   int left_dir  = turn[old_move_dir].left;
6991   int right_dir = turn[old_move_dir].right;
6992   int back_dir  = turn[old_move_dir].back;
6993
6994   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6995   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6996   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6997   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6998
6999   int left_x  = x + left_dx,  left_y  = y + left_dy;
7000   int right_x = x + right_dx, right_y = y + right_dy;
7001   int move_x  = x + move_dx,  move_y  = y + move_dy;
7002
7003   int xx, yy;
7004
7005   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7006   {
7007     TestIfBadThingTouchesOtherBadThing(x, y);
7008
7009     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7010       MovDir[x][y] = right_dir;
7011     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7012       MovDir[x][y] = left_dir;
7013
7014     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7015       MovDelay[x][y] = 9;
7016     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7017       MovDelay[x][y] = 1;
7018   }
7019   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7020   {
7021     TestIfBadThingTouchesOtherBadThing(x, y);
7022
7023     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7024       MovDir[x][y] = left_dir;
7025     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7026       MovDir[x][y] = right_dir;
7027
7028     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7029       MovDelay[x][y] = 9;
7030     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7031       MovDelay[x][y] = 1;
7032   }
7033   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7034   {
7035     TestIfBadThingTouchesOtherBadThing(x, y);
7036
7037     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7038       MovDir[x][y] = left_dir;
7039     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7040       MovDir[x][y] = right_dir;
7041
7042     if (MovDir[x][y] != old_move_dir)
7043       MovDelay[x][y] = 9;
7044   }
7045   else if (element == EL_YAMYAM)
7046   {
7047     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7048     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7049
7050     if (can_turn_left && can_turn_right)
7051       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7052     else if (can_turn_left)
7053       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7054     else if (can_turn_right)
7055       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7056     else
7057       MovDir[x][y] = back_dir;
7058
7059     MovDelay[x][y] = 16 + 16 * RND(3);
7060   }
7061   else if (element == EL_DARK_YAMYAM)
7062   {
7063     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7064                                                          left_x, left_y);
7065     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7066                                                          right_x, right_y);
7067
7068     if (can_turn_left && can_turn_right)
7069       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7070     else if (can_turn_left)
7071       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7072     else if (can_turn_right)
7073       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7074     else
7075       MovDir[x][y] = back_dir;
7076
7077     MovDelay[x][y] = 16 + 16 * RND(3);
7078   }
7079   else if (element == EL_PACMAN)
7080   {
7081     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7082     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7083
7084     if (can_turn_left && can_turn_right)
7085       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7086     else if (can_turn_left)
7087       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7088     else if (can_turn_right)
7089       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7090     else
7091       MovDir[x][y] = back_dir;
7092
7093     MovDelay[x][y] = 6 + RND(40);
7094   }
7095   else if (element == EL_PIG)
7096   {
7097     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7098     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7099     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7100     boolean should_turn_left, should_turn_right, should_move_on;
7101     int rnd_value = 24;
7102     int rnd = RND(rnd_value);
7103
7104     should_turn_left = (can_turn_left &&
7105                         (!can_move_on ||
7106                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7107                                                    y + back_dy + left_dy)));
7108     should_turn_right = (can_turn_right &&
7109                          (!can_move_on ||
7110                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7111                                                     y + back_dy + right_dy)));
7112     should_move_on = (can_move_on &&
7113                       (!can_turn_left ||
7114                        !can_turn_right ||
7115                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7116                                                  y + move_dy + left_dy) ||
7117                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7118                                                  y + move_dy + right_dy)));
7119
7120     if (should_turn_left || should_turn_right || should_move_on)
7121     {
7122       if (should_turn_left && should_turn_right && should_move_on)
7123         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7124                         rnd < 2 * rnd_value / 3 ? right_dir :
7125                         old_move_dir);
7126       else if (should_turn_left && should_turn_right)
7127         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7128       else if (should_turn_left && should_move_on)
7129         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7130       else if (should_turn_right && should_move_on)
7131         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7132       else if (should_turn_left)
7133         MovDir[x][y] = left_dir;
7134       else if (should_turn_right)
7135         MovDir[x][y] = right_dir;
7136       else if (should_move_on)
7137         MovDir[x][y] = old_move_dir;
7138     }
7139     else if (can_move_on && rnd > rnd_value / 8)
7140       MovDir[x][y] = old_move_dir;
7141     else if (can_turn_left && can_turn_right)
7142       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7143     else if (can_turn_left && rnd > rnd_value / 8)
7144       MovDir[x][y] = left_dir;
7145     else if (can_turn_right && rnd > rnd_value/8)
7146       MovDir[x][y] = right_dir;
7147     else
7148       MovDir[x][y] = back_dir;
7149
7150     xx = x + move_xy[MovDir[x][y]].dx;
7151     yy = y + move_xy[MovDir[x][y]].dy;
7152
7153     if (!IN_LEV_FIELD(xx, yy) ||
7154         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7155       MovDir[x][y] = old_move_dir;
7156
7157     MovDelay[x][y] = 0;
7158   }
7159   else if (element == EL_DRAGON)
7160   {
7161     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7162     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7163     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7164     int rnd_value = 24;
7165     int rnd = RND(rnd_value);
7166
7167     if (can_move_on && rnd > rnd_value / 8)
7168       MovDir[x][y] = old_move_dir;
7169     else if (can_turn_left && can_turn_right)
7170       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7171     else if (can_turn_left && rnd > rnd_value / 8)
7172       MovDir[x][y] = left_dir;
7173     else if (can_turn_right && rnd > rnd_value / 8)
7174       MovDir[x][y] = right_dir;
7175     else
7176       MovDir[x][y] = back_dir;
7177
7178     xx = x + move_xy[MovDir[x][y]].dx;
7179     yy = y + move_xy[MovDir[x][y]].dy;
7180
7181     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7182       MovDir[x][y] = old_move_dir;
7183
7184     MovDelay[x][y] = 0;
7185   }
7186   else if (element == EL_MOLE)
7187   {
7188     boolean can_move_on =
7189       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7190                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7191                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7192     if (!can_move_on)
7193     {
7194       boolean can_turn_left =
7195         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7196                               IS_AMOEBOID(Tile[left_x][left_y])));
7197
7198       boolean can_turn_right =
7199         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7200                               IS_AMOEBOID(Tile[right_x][right_y])));
7201
7202       if (can_turn_left && can_turn_right)
7203         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7204       else if (can_turn_left)
7205         MovDir[x][y] = left_dir;
7206       else
7207         MovDir[x][y] = right_dir;
7208     }
7209
7210     if (MovDir[x][y] != old_move_dir)
7211       MovDelay[x][y] = 9;
7212   }
7213   else if (element == EL_BALLOON)
7214   {
7215     MovDir[x][y] = game.wind_direction;
7216     MovDelay[x][y] = 0;
7217   }
7218   else if (element == EL_SPRING)
7219   {
7220     if (MovDir[x][y] & MV_HORIZONTAL)
7221     {
7222       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7223           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7224       {
7225         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7226         ResetGfxAnimation(move_x, move_y);
7227         TEST_DrawLevelField(move_x, move_y);
7228
7229         MovDir[x][y] = back_dir;
7230       }
7231       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7232                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7233         MovDir[x][y] = MV_NONE;
7234     }
7235
7236     MovDelay[x][y] = 0;
7237   }
7238   else if (element == EL_ROBOT ||
7239            element == EL_SATELLITE ||
7240            element == EL_PENGUIN ||
7241            element == EL_EMC_ANDROID)
7242   {
7243     int attr_x = -1, attr_y = -1;
7244
7245     if (game.all_players_gone)
7246     {
7247       attr_x = game.exit_x;
7248       attr_y = game.exit_y;
7249     }
7250     else
7251     {
7252       int i;
7253
7254       for (i = 0; i < MAX_PLAYERS; i++)
7255       {
7256         struct PlayerInfo *player = &stored_player[i];
7257         int jx = player->jx, jy = player->jy;
7258
7259         if (!player->active)
7260           continue;
7261
7262         if (attr_x == -1 ||
7263             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7264         {
7265           attr_x = jx;
7266           attr_y = jy;
7267         }
7268       }
7269     }
7270
7271     if (element == EL_ROBOT &&
7272         game.robot_wheel_x >= 0 &&
7273         game.robot_wheel_y >= 0 &&
7274         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7275          game.engine_version < VERSION_IDENT(3,1,0,0)))
7276     {
7277       attr_x = game.robot_wheel_x;
7278       attr_y = game.robot_wheel_y;
7279     }
7280
7281     if (element == EL_PENGUIN)
7282     {
7283       int i;
7284       struct XY *xy = xy_topdown;
7285
7286       for (i = 0; i < NUM_DIRECTIONS; i++)
7287       {
7288         int ex = x + xy[i].x;
7289         int ey = y + xy[i].y;
7290
7291         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7292                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7293                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7294                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7295         {
7296           attr_x = ex;
7297           attr_y = ey;
7298           break;
7299         }
7300       }
7301     }
7302
7303     MovDir[x][y] = MV_NONE;
7304     if (attr_x < x)
7305       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7306     else if (attr_x > x)
7307       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7308     if (attr_y < y)
7309       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7310     else if (attr_y > y)
7311       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7312
7313     if (element == EL_ROBOT)
7314     {
7315       int newx, newy;
7316
7317       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7318         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7319       Moving2Blocked(x, y, &newx, &newy);
7320
7321       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7322         MovDelay[x][y] = 8 + 8 * !RND(3);
7323       else
7324         MovDelay[x][y] = 16;
7325     }
7326     else if (element == EL_PENGUIN)
7327     {
7328       int newx, newy;
7329
7330       MovDelay[x][y] = 1;
7331
7332       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7333       {
7334         boolean first_horiz = RND(2);
7335         int new_move_dir = MovDir[x][y];
7336
7337         MovDir[x][y] =
7338           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7339         Moving2Blocked(x, y, &newx, &newy);
7340
7341         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7342           return;
7343
7344         MovDir[x][y] =
7345           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7346         Moving2Blocked(x, y, &newx, &newy);
7347
7348         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7349           return;
7350
7351         MovDir[x][y] = old_move_dir;
7352         return;
7353       }
7354     }
7355     else if (element == EL_SATELLITE)
7356     {
7357       int newx, newy;
7358
7359       MovDelay[x][y] = 1;
7360
7361       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7362       {
7363         boolean first_horiz = RND(2);
7364         int new_move_dir = MovDir[x][y];
7365
7366         MovDir[x][y] =
7367           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7368         Moving2Blocked(x, y, &newx, &newy);
7369
7370         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7371           return;
7372
7373         MovDir[x][y] =
7374           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7375         Moving2Blocked(x, y, &newx, &newy);
7376
7377         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7378           return;
7379
7380         MovDir[x][y] = old_move_dir;
7381         return;
7382       }
7383     }
7384     else if (element == EL_EMC_ANDROID)
7385     {
7386       static int check_pos[16] =
7387       {
7388         -1,             //  0 => (invalid)
7389         7,              //  1 => MV_LEFT
7390         3,              //  2 => MV_RIGHT
7391         -1,             //  3 => (invalid)
7392         1,              //  4 =>            MV_UP
7393         0,              //  5 => MV_LEFT  | MV_UP
7394         2,              //  6 => MV_RIGHT | MV_UP
7395         -1,             //  7 => (invalid)
7396         5,              //  8 =>            MV_DOWN
7397         6,              //  9 => MV_LEFT  | MV_DOWN
7398         4,              // 10 => MV_RIGHT | MV_DOWN
7399         -1,             // 11 => (invalid)
7400         -1,             // 12 => (invalid)
7401         -1,             // 13 => (invalid)
7402         -1,             // 14 => (invalid)
7403         -1,             // 15 => (invalid)
7404       };
7405       static struct
7406       {
7407         int dx, dy;
7408         int dir;
7409       } check_xy[8] =
7410       {
7411         { -1, -1,       MV_LEFT  | MV_UP   },
7412         {  0, -1,                  MV_UP   },
7413         { +1, -1,       MV_RIGHT | MV_UP   },
7414         { +1,  0,       MV_RIGHT           },
7415         { +1, +1,       MV_RIGHT | MV_DOWN },
7416         {  0, +1,                  MV_DOWN },
7417         { -1, +1,       MV_LEFT  | MV_DOWN },
7418         { -1,  0,       MV_LEFT            },
7419       };
7420       int start_pos, check_order;
7421       boolean can_clone = FALSE;
7422       int i;
7423
7424       // check if there is any free field around current position
7425       for (i = 0; i < 8; i++)
7426       {
7427         int newx = x + check_xy[i].dx;
7428         int newy = y + check_xy[i].dy;
7429
7430         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7431         {
7432           can_clone = TRUE;
7433
7434           break;
7435         }
7436       }
7437
7438       if (can_clone)            // randomly find an element to clone
7439       {
7440         can_clone = FALSE;
7441
7442         start_pos = check_pos[RND(8)];
7443         check_order = (RND(2) ? -1 : +1);
7444
7445         for (i = 0; i < 8; i++)
7446         {
7447           int pos_raw = start_pos + i * check_order;
7448           int pos = (pos_raw + 8) % 8;
7449           int newx = x + check_xy[pos].dx;
7450           int newy = y + check_xy[pos].dy;
7451
7452           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7453           {
7454             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7455             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7456
7457             Store[x][y] = Tile[newx][newy];
7458
7459             can_clone = TRUE;
7460
7461             break;
7462           }
7463         }
7464       }
7465
7466       if (can_clone)            // randomly find a direction to move
7467       {
7468         can_clone = FALSE;
7469
7470         start_pos = check_pos[RND(8)];
7471         check_order = (RND(2) ? -1 : +1);
7472
7473         for (i = 0; i < 8; i++)
7474         {
7475           int pos_raw = start_pos + i * check_order;
7476           int pos = (pos_raw + 8) % 8;
7477           int newx = x + check_xy[pos].dx;
7478           int newy = y + check_xy[pos].dy;
7479           int new_move_dir = check_xy[pos].dir;
7480
7481           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7482           {
7483             MovDir[x][y] = new_move_dir;
7484             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7485
7486             can_clone = TRUE;
7487
7488             break;
7489           }
7490         }
7491       }
7492
7493       if (can_clone)            // cloning and moving successful
7494         return;
7495
7496       // cannot clone -- try to move towards player
7497
7498       start_pos = check_pos[MovDir[x][y] & 0x0f];
7499       check_order = (RND(2) ? -1 : +1);
7500
7501       for (i = 0; i < 3; i++)
7502       {
7503         // first check start_pos, then previous/next or (next/previous) pos
7504         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7505         int pos = (pos_raw + 8) % 8;
7506         int newx = x + check_xy[pos].dx;
7507         int newy = y + check_xy[pos].dy;
7508         int new_move_dir = check_xy[pos].dir;
7509
7510         if (IS_PLAYER(newx, newy))
7511           break;
7512
7513         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7514         {
7515           MovDir[x][y] = new_move_dir;
7516           MovDelay[x][y] = level.android_move_time * 8 + 1;
7517
7518           break;
7519         }
7520       }
7521     }
7522   }
7523   else if (move_pattern == MV_TURNING_LEFT ||
7524            move_pattern == MV_TURNING_RIGHT ||
7525            move_pattern == MV_TURNING_LEFT_RIGHT ||
7526            move_pattern == MV_TURNING_RIGHT_LEFT ||
7527            move_pattern == MV_TURNING_RANDOM ||
7528            move_pattern == MV_ALL_DIRECTIONS)
7529   {
7530     boolean can_turn_left =
7531       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7532     boolean can_turn_right =
7533       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7534
7535     if (element_info[element].move_stepsize == 0)       // "not moving"
7536       return;
7537
7538     if (move_pattern == MV_TURNING_LEFT)
7539       MovDir[x][y] = left_dir;
7540     else if (move_pattern == MV_TURNING_RIGHT)
7541       MovDir[x][y] = right_dir;
7542     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7543       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7544     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7545       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7546     else if (move_pattern == MV_TURNING_RANDOM)
7547       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7548                       can_turn_right && !can_turn_left ? right_dir :
7549                       RND(2) ? left_dir : right_dir);
7550     else if (can_turn_left && can_turn_right)
7551       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7552     else if (can_turn_left)
7553       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7554     else if (can_turn_right)
7555       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7556     else
7557       MovDir[x][y] = back_dir;
7558
7559     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7560   }
7561   else if (move_pattern == MV_HORIZONTAL ||
7562            move_pattern == MV_VERTICAL)
7563   {
7564     if (move_pattern & old_move_dir)
7565       MovDir[x][y] = back_dir;
7566     else if (move_pattern == MV_HORIZONTAL)
7567       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7568     else if (move_pattern == MV_VERTICAL)
7569       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7570
7571     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7572   }
7573   else if (move_pattern & MV_ANY_DIRECTION)
7574   {
7575     MovDir[x][y] = move_pattern;
7576     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7577   }
7578   else if (move_pattern & MV_WIND_DIRECTION)
7579   {
7580     MovDir[x][y] = game.wind_direction;
7581     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7582   }
7583   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7584   {
7585     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7586       MovDir[x][y] = left_dir;
7587     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7588       MovDir[x][y] = right_dir;
7589
7590     if (MovDir[x][y] != old_move_dir)
7591       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7592   }
7593   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7594   {
7595     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7596       MovDir[x][y] = right_dir;
7597     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7598       MovDir[x][y] = left_dir;
7599
7600     if (MovDir[x][y] != old_move_dir)
7601       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7602   }
7603   else if (move_pattern == MV_TOWARDS_PLAYER ||
7604            move_pattern == MV_AWAY_FROM_PLAYER)
7605   {
7606     int attr_x = -1, attr_y = -1;
7607     int newx, newy;
7608     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7609
7610     if (game.all_players_gone)
7611     {
7612       attr_x = game.exit_x;
7613       attr_y = game.exit_y;
7614     }
7615     else
7616     {
7617       int i;
7618
7619       for (i = 0; i < MAX_PLAYERS; i++)
7620       {
7621         struct PlayerInfo *player = &stored_player[i];
7622         int jx = player->jx, jy = player->jy;
7623
7624         if (!player->active)
7625           continue;
7626
7627         if (attr_x == -1 ||
7628             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7629         {
7630           attr_x = jx;
7631           attr_y = jy;
7632         }
7633       }
7634     }
7635
7636     MovDir[x][y] = MV_NONE;
7637     if (attr_x < x)
7638       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7639     else if (attr_x > x)
7640       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7641     if (attr_y < y)
7642       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7643     else if (attr_y > y)
7644       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7645
7646     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7647
7648     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7649     {
7650       boolean first_horiz = RND(2);
7651       int new_move_dir = MovDir[x][y];
7652
7653       if (element_info[element].move_stepsize == 0)     // "not moving"
7654       {
7655         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7656         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7657
7658         return;
7659       }
7660
7661       MovDir[x][y] =
7662         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7663       Moving2Blocked(x, y, &newx, &newy);
7664
7665       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7666         return;
7667
7668       MovDir[x][y] =
7669         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7670       Moving2Blocked(x, y, &newx, &newy);
7671
7672       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7673         return;
7674
7675       MovDir[x][y] = old_move_dir;
7676     }
7677   }
7678   else if (move_pattern == MV_WHEN_PUSHED ||
7679            move_pattern == MV_WHEN_DROPPED)
7680   {
7681     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7682       MovDir[x][y] = MV_NONE;
7683
7684     MovDelay[x][y] = 0;
7685   }
7686   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7687   {
7688     struct XY *test_xy = xy_topdown;
7689     static int test_dir[4] =
7690     {
7691       MV_UP,
7692       MV_LEFT,
7693       MV_RIGHT,
7694       MV_DOWN
7695     };
7696     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7697     int move_preference = -1000000;     // start with very low preference
7698     int new_move_dir = MV_NONE;
7699     int start_test = RND(4);
7700     int i;
7701
7702     for (i = 0; i < NUM_DIRECTIONS; i++)
7703     {
7704       int j = (start_test + i) % 4;
7705       int move_dir = test_dir[j];
7706       int move_dir_preference;
7707
7708       xx = x + test_xy[j].x;
7709       yy = y + test_xy[j].y;
7710
7711       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7712           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7713       {
7714         new_move_dir = move_dir;
7715
7716         break;
7717       }
7718
7719       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7720         continue;
7721
7722       move_dir_preference = -1 * RunnerVisit[xx][yy];
7723       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7724         move_dir_preference = PlayerVisit[xx][yy];
7725
7726       if (move_dir_preference > move_preference)
7727       {
7728         // prefer field that has not been visited for the longest time
7729         move_preference = move_dir_preference;
7730         new_move_dir = move_dir;
7731       }
7732       else if (move_dir_preference == move_preference &&
7733                move_dir == old_move_dir)
7734       {
7735         // prefer last direction when all directions are preferred equally
7736         move_preference = move_dir_preference;
7737         new_move_dir = move_dir;
7738       }
7739     }
7740
7741     MovDir[x][y] = new_move_dir;
7742     if (old_move_dir != new_move_dir)
7743       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7744   }
7745 }
7746
7747 static void TurnRound(int x, int y)
7748 {
7749   int direction = MovDir[x][y];
7750
7751   TurnRoundExt(x, y);
7752
7753   GfxDir[x][y] = MovDir[x][y];
7754
7755   if (direction != MovDir[x][y])
7756     GfxFrame[x][y] = 0;
7757
7758   if (MovDelay[x][y])
7759     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7760
7761   ResetGfxFrame(x, y);
7762 }
7763
7764 static boolean JustBeingPushed(int x, int y)
7765 {
7766   int i;
7767
7768   for (i = 0; i < MAX_PLAYERS; i++)
7769   {
7770     struct PlayerInfo *player = &stored_player[i];
7771
7772     if (player->active && player->is_pushing && player->MovPos)
7773     {
7774       int next_jx = player->jx + (player->jx - player->last_jx);
7775       int next_jy = player->jy + (player->jy - player->last_jy);
7776
7777       if (x == next_jx && y == next_jy)
7778         return TRUE;
7779     }
7780   }
7781
7782   return FALSE;
7783 }
7784
7785 static void StartMoving(int x, int y)
7786 {
7787   boolean started_moving = FALSE;       // some elements can fall _and_ move
7788   int element = Tile[x][y];
7789
7790   if (Stop[x][y])
7791     return;
7792
7793   if (MovDelay[x][y] == 0)
7794     GfxAction[x][y] = ACTION_DEFAULT;
7795
7796   if (CAN_FALL(element) && y < lev_fieldy - 1)
7797   {
7798     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7799         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7800       if (JustBeingPushed(x, y))
7801         return;
7802
7803     if (element == EL_QUICKSAND_FULL)
7804     {
7805       if (IS_FREE(x, y + 1))
7806       {
7807         InitMovingField(x, y, MV_DOWN);
7808         started_moving = TRUE;
7809
7810         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7811 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7812         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7813           Store[x][y] = EL_ROCK;
7814 #else
7815         Store[x][y] = EL_ROCK;
7816 #endif
7817
7818         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7819       }
7820       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7821       {
7822         if (!MovDelay[x][y])
7823         {
7824           MovDelay[x][y] = TILEY + 1;
7825
7826           ResetGfxAnimation(x, y);
7827           ResetGfxAnimation(x, y + 1);
7828         }
7829
7830         if (MovDelay[x][y])
7831         {
7832           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7833           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7834
7835           MovDelay[x][y]--;
7836           if (MovDelay[x][y])
7837             return;
7838         }
7839
7840         Tile[x][y] = EL_QUICKSAND_EMPTY;
7841         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7842         Store[x][y + 1] = Store[x][y];
7843         Store[x][y] = 0;
7844
7845         PlayLevelSoundAction(x, y, ACTION_FILLING);
7846       }
7847       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7848       {
7849         if (!MovDelay[x][y])
7850         {
7851           MovDelay[x][y] = TILEY + 1;
7852
7853           ResetGfxAnimation(x, y);
7854           ResetGfxAnimation(x, y + 1);
7855         }
7856
7857         if (MovDelay[x][y])
7858         {
7859           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7860           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7861
7862           MovDelay[x][y]--;
7863           if (MovDelay[x][y])
7864             return;
7865         }
7866
7867         Tile[x][y] = EL_QUICKSAND_EMPTY;
7868         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7869         Store[x][y + 1] = Store[x][y];
7870         Store[x][y] = 0;
7871
7872         PlayLevelSoundAction(x, y, ACTION_FILLING);
7873       }
7874     }
7875     else if (element == EL_QUICKSAND_FAST_FULL)
7876     {
7877       if (IS_FREE(x, y + 1))
7878       {
7879         InitMovingField(x, y, MV_DOWN);
7880         started_moving = TRUE;
7881
7882         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7883 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7884         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7885           Store[x][y] = EL_ROCK;
7886 #else
7887         Store[x][y] = EL_ROCK;
7888 #endif
7889
7890         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7891       }
7892       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7893       {
7894         if (!MovDelay[x][y])
7895         {
7896           MovDelay[x][y] = TILEY + 1;
7897
7898           ResetGfxAnimation(x, y);
7899           ResetGfxAnimation(x, y + 1);
7900         }
7901
7902         if (MovDelay[x][y])
7903         {
7904           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7905           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7906
7907           MovDelay[x][y]--;
7908           if (MovDelay[x][y])
7909             return;
7910         }
7911
7912         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7913         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7914         Store[x][y + 1] = Store[x][y];
7915         Store[x][y] = 0;
7916
7917         PlayLevelSoundAction(x, y, ACTION_FILLING);
7918       }
7919       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7920       {
7921         if (!MovDelay[x][y])
7922         {
7923           MovDelay[x][y] = TILEY + 1;
7924
7925           ResetGfxAnimation(x, y);
7926           ResetGfxAnimation(x, y + 1);
7927         }
7928
7929         if (MovDelay[x][y])
7930         {
7931           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7932           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7933
7934           MovDelay[x][y]--;
7935           if (MovDelay[x][y])
7936             return;
7937         }
7938
7939         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7940         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7941         Store[x][y + 1] = Store[x][y];
7942         Store[x][y] = 0;
7943
7944         PlayLevelSoundAction(x, y, ACTION_FILLING);
7945       }
7946     }
7947     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7948              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7949     {
7950       InitMovingField(x, y, MV_DOWN);
7951       started_moving = TRUE;
7952
7953       Tile[x][y] = EL_QUICKSAND_FILLING;
7954       Store[x][y] = element;
7955
7956       PlayLevelSoundAction(x, y, ACTION_FILLING);
7957     }
7958     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7959              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7960     {
7961       InitMovingField(x, y, MV_DOWN);
7962       started_moving = TRUE;
7963
7964       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7965       Store[x][y] = element;
7966
7967       PlayLevelSoundAction(x, y, ACTION_FILLING);
7968     }
7969     else if (element == EL_MAGIC_WALL_FULL)
7970     {
7971       if (IS_FREE(x, y + 1))
7972       {
7973         InitMovingField(x, y, MV_DOWN);
7974         started_moving = TRUE;
7975
7976         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7977         Store[x][y] = EL_CHANGED(Store[x][y]);
7978       }
7979       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7980       {
7981         if (!MovDelay[x][y])
7982           MovDelay[x][y] = TILEY / 4 + 1;
7983
7984         if (MovDelay[x][y])
7985         {
7986           MovDelay[x][y]--;
7987           if (MovDelay[x][y])
7988             return;
7989         }
7990
7991         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7992         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7993         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7994         Store[x][y] = 0;
7995       }
7996     }
7997     else if (element == EL_BD_MAGIC_WALL_FULL)
7998     {
7999       if (IS_FREE(x, y + 1))
8000       {
8001         InitMovingField(x, y, MV_DOWN);
8002         started_moving = TRUE;
8003
8004         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
8005         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8006       }
8007       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8008       {
8009         if (!MovDelay[x][y])
8010           MovDelay[x][y] = TILEY / 4 + 1;
8011
8012         if (MovDelay[x][y])
8013         {
8014           MovDelay[x][y]--;
8015           if (MovDelay[x][y])
8016             return;
8017         }
8018
8019         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8020         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8021         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8022         Store[x][y] = 0;
8023       }
8024     }
8025     else if (element == EL_DC_MAGIC_WALL_FULL)
8026     {
8027       if (IS_FREE(x, y + 1))
8028       {
8029         InitMovingField(x, y, MV_DOWN);
8030         started_moving = TRUE;
8031
8032         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8033         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8034       }
8035       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8036       {
8037         if (!MovDelay[x][y])
8038           MovDelay[x][y] = TILEY / 4 + 1;
8039
8040         if (MovDelay[x][y])
8041         {
8042           MovDelay[x][y]--;
8043           if (MovDelay[x][y])
8044             return;
8045         }
8046
8047         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8048         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8049         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8050         Store[x][y] = 0;
8051       }
8052     }
8053     else if ((CAN_PASS_MAGIC_WALL(element) &&
8054               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8055                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8056              (CAN_PASS_DC_MAGIC_WALL(element) &&
8057               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8058
8059     {
8060       InitMovingField(x, y, MV_DOWN);
8061       started_moving = TRUE;
8062
8063       Tile[x][y] =
8064         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8065          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8066          EL_DC_MAGIC_WALL_FILLING);
8067       Store[x][y] = element;
8068     }
8069     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8070     {
8071       SplashAcid(x, y + 1);
8072
8073       InitMovingField(x, y, MV_DOWN);
8074       started_moving = TRUE;
8075
8076       Store[x][y] = EL_ACID;
8077     }
8078     else if (
8079              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8080               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8081              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8082               CAN_FALL(element) && WasJustFalling[x][y] &&
8083               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8084
8085              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8086               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8087               (Tile[x][y + 1] == EL_BLOCKED)))
8088     {
8089       /* this is needed for a special case not covered by calling "Impact()"
8090          from "ContinueMoving()": if an element moves to a tile directly below
8091          another element which was just falling on that tile (which was empty
8092          in the previous frame), the falling element above would just stop
8093          instead of smashing the element below (in previous version, the above
8094          element was just checked for "moving" instead of "falling", resulting
8095          in incorrect smashes caused by horizontal movement of the above
8096          element; also, the case of the player being the element to smash was
8097          simply not covered here... :-/ ) */
8098
8099       CheckCollision[x][y] = 0;
8100       CheckImpact[x][y] = 0;
8101
8102       Impact(x, y);
8103     }
8104     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8105     {
8106       if (MovDir[x][y] == MV_NONE)
8107       {
8108         InitMovingField(x, y, MV_DOWN);
8109         started_moving = TRUE;
8110       }
8111     }
8112     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8113     {
8114       if (WasJustFalling[x][y]) // prevent animation from being restarted
8115         MovDir[x][y] = MV_DOWN;
8116
8117       InitMovingField(x, y, MV_DOWN);
8118       started_moving = TRUE;
8119     }
8120     else if (element == EL_AMOEBA_DROP)
8121     {
8122       Tile[x][y] = EL_AMOEBA_GROWING;
8123       Store[x][y] = EL_AMOEBA_WET;
8124     }
8125     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8126               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8127              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8128              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8129     {
8130       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8131                                 (IS_FREE(x - 1, y + 1) ||
8132                                  Tile[x - 1][y + 1] == EL_ACID));
8133       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8134                                 (IS_FREE(x + 1, y + 1) ||
8135                                  Tile[x + 1][y + 1] == EL_ACID));
8136       boolean can_fall_any  = (can_fall_left || can_fall_right);
8137       boolean can_fall_both = (can_fall_left && can_fall_right);
8138       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8139
8140       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8141       {
8142         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8143           can_fall_right = FALSE;
8144         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8145           can_fall_left = FALSE;
8146         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8147           can_fall_right = FALSE;
8148         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8149           can_fall_left = FALSE;
8150
8151         can_fall_any  = (can_fall_left || can_fall_right);
8152         can_fall_both = FALSE;
8153       }
8154
8155       if (can_fall_both)
8156       {
8157         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8158           can_fall_right = FALSE;       // slip down on left side
8159         else
8160           can_fall_left = !(can_fall_right = RND(2));
8161
8162         can_fall_both = FALSE;
8163       }
8164
8165       if (can_fall_any)
8166       {
8167         // if not determined otherwise, prefer left side for slipping down
8168         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8169         started_moving = TRUE;
8170       }
8171     }
8172     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8173     {
8174       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8175       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8176       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8177       int belt_dir = game.belt_dir[belt_nr];
8178
8179       if ((belt_dir == MV_LEFT  && left_is_free) ||
8180           (belt_dir == MV_RIGHT && right_is_free))
8181       {
8182         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8183
8184         InitMovingField(x, y, belt_dir);
8185         started_moving = TRUE;
8186
8187         Pushed[x][y] = TRUE;
8188         Pushed[nextx][y] = TRUE;
8189
8190         GfxAction[x][y] = ACTION_DEFAULT;
8191       }
8192       else
8193       {
8194         MovDir[x][y] = 0;       // if element was moving, stop it
8195       }
8196     }
8197   }
8198
8199   // not "else if" because of elements that can fall and move (EL_SPRING)
8200   if (CAN_MOVE(element) && !started_moving)
8201   {
8202     int move_pattern = element_info[element].move_pattern;
8203     int newx, newy;
8204
8205     Moving2Blocked(x, y, &newx, &newy);
8206
8207     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8208       return;
8209
8210     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8211         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8212     {
8213       WasJustMoving[x][y] = 0;
8214       CheckCollision[x][y] = 0;
8215
8216       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8217
8218       if (Tile[x][y] != element)        // element has changed
8219         return;
8220     }
8221
8222     if (!MovDelay[x][y])        // start new movement phase
8223     {
8224       // all objects that can change their move direction after each step
8225       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8226
8227       if (element != EL_YAMYAM &&
8228           element != EL_DARK_YAMYAM &&
8229           element != EL_PACMAN &&
8230           !(move_pattern & MV_ANY_DIRECTION) &&
8231           move_pattern != MV_TURNING_LEFT &&
8232           move_pattern != MV_TURNING_RIGHT &&
8233           move_pattern != MV_TURNING_LEFT_RIGHT &&
8234           move_pattern != MV_TURNING_RIGHT_LEFT &&
8235           move_pattern != MV_TURNING_RANDOM)
8236       {
8237         TurnRound(x, y);
8238
8239         if (MovDelay[x][y] && (element == EL_BUG ||
8240                                element == EL_SPACESHIP ||
8241                                element == EL_SP_SNIKSNAK ||
8242                                element == EL_SP_ELECTRON ||
8243                                element == EL_MOLE))
8244           TEST_DrawLevelField(x, y);
8245       }
8246     }
8247
8248     if (MovDelay[x][y])         // wait some time before next movement
8249     {
8250       MovDelay[x][y]--;
8251
8252       if (element == EL_ROBOT ||
8253           element == EL_YAMYAM ||
8254           element == EL_DARK_YAMYAM)
8255       {
8256         DrawLevelElementAnimationIfNeeded(x, y, element);
8257         PlayLevelSoundAction(x, y, ACTION_WAITING);
8258       }
8259       else if (element == EL_SP_ELECTRON)
8260         DrawLevelElementAnimationIfNeeded(x, y, element);
8261       else if (element == EL_DRAGON)
8262       {
8263         int i;
8264         int dir = MovDir[x][y];
8265         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8266         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8267         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8268                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8269                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8270                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8271         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8272
8273         GfxAction[x][y] = ACTION_ATTACKING;
8274
8275         if (IS_PLAYER(x, y))
8276           DrawPlayerField(x, y);
8277         else
8278           TEST_DrawLevelField(x, y);
8279
8280         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8281
8282         for (i = 1; i <= 3; i++)
8283         {
8284           int xx = x + i * dx;
8285           int yy = y + i * dy;
8286           int sx = SCREENX(xx);
8287           int sy = SCREENY(yy);
8288           int flame_graphic = graphic + (i - 1);
8289
8290           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8291             break;
8292
8293           if (MovDelay[x][y])
8294           {
8295             int flamed = MovingOrBlocked2Element(xx, yy);
8296
8297             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8298               Bang(xx, yy);
8299             else
8300               RemoveMovingField(xx, yy);
8301
8302             ChangeDelay[xx][yy] = 0;
8303
8304             Tile[xx][yy] = EL_FLAMES;
8305
8306             if (IN_SCR_FIELD(sx, sy))
8307             {
8308               TEST_DrawLevelFieldCrumbled(xx, yy);
8309               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8310             }
8311           }
8312           else
8313           {
8314             if (Tile[xx][yy] == EL_FLAMES)
8315               Tile[xx][yy] = EL_EMPTY;
8316             TEST_DrawLevelField(xx, yy);
8317           }
8318         }
8319       }
8320
8321       if (MovDelay[x][y])       // element still has to wait some time
8322       {
8323         PlayLevelSoundAction(x, y, ACTION_WAITING);
8324
8325         return;
8326       }
8327     }
8328
8329     // now make next step
8330
8331     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8332
8333     if (DONT_COLLIDE_WITH(element) &&
8334         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8335         !PLAYER_ENEMY_PROTECTED(newx, newy))
8336     {
8337       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8338
8339       return;
8340     }
8341
8342     else if (CAN_MOVE_INTO_ACID(element) &&
8343              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8344              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8345              (MovDir[x][y] == MV_DOWN ||
8346               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8347     {
8348       SplashAcid(newx, newy);
8349       Store[x][y] = EL_ACID;
8350     }
8351     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8352     {
8353       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8354           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8355           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8356           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8357       {
8358         RemoveField(x, y);
8359         TEST_DrawLevelField(x, y);
8360
8361         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8362         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8363           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8364
8365         game.friends_still_needed--;
8366         if (!game.friends_still_needed &&
8367             !game.GameOver &&
8368             game.all_players_gone)
8369           LevelSolved();
8370
8371         return;
8372       }
8373       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8374       {
8375         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8376           TEST_DrawLevelField(newx, newy);
8377         else
8378           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8379       }
8380       else if (!IS_FREE(newx, newy))
8381       {
8382         GfxAction[x][y] = ACTION_WAITING;
8383
8384         if (IS_PLAYER(x, y))
8385           DrawPlayerField(x, y);
8386         else
8387           TEST_DrawLevelField(x, y);
8388
8389         return;
8390       }
8391     }
8392     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8393     {
8394       if (IS_FOOD_PIG(Tile[newx][newy]))
8395       {
8396         if (IS_MOVING(newx, newy))
8397           RemoveMovingField(newx, newy);
8398         else
8399         {
8400           Tile[newx][newy] = EL_EMPTY;
8401           TEST_DrawLevelField(newx, newy);
8402         }
8403
8404         PlayLevelSound(x, y, SND_PIG_DIGGING);
8405       }
8406       else if (!IS_FREE(newx, newy))
8407       {
8408         if (IS_PLAYER(x, y))
8409           DrawPlayerField(x, y);
8410         else
8411           TEST_DrawLevelField(x, y);
8412
8413         return;
8414       }
8415     }
8416     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8417     {
8418       if (Store[x][y] != EL_EMPTY)
8419       {
8420         boolean can_clone = FALSE;
8421         int xx, yy;
8422
8423         // check if element to clone is still there
8424         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8425         {
8426           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8427           {
8428             can_clone = TRUE;
8429
8430             break;
8431           }
8432         }
8433
8434         // cannot clone or target field not free anymore -- do not clone
8435         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8436           Store[x][y] = EL_EMPTY;
8437       }
8438
8439       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8440       {
8441         if (IS_MV_DIAGONAL(MovDir[x][y]))
8442         {
8443           int diagonal_move_dir = MovDir[x][y];
8444           int stored = Store[x][y];
8445           int change_delay = 8;
8446           int graphic;
8447
8448           // android is moving diagonally
8449
8450           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8451
8452           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8453           GfxElement[x][y] = EL_EMC_ANDROID;
8454           GfxAction[x][y] = ACTION_SHRINKING;
8455           GfxDir[x][y] = diagonal_move_dir;
8456           ChangeDelay[x][y] = change_delay;
8457
8458           if (Store[x][y] == EL_EMPTY)
8459             Store[x][y] = GfxElementEmpty[x][y];
8460
8461           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8462                                    GfxDir[x][y]);
8463
8464           DrawLevelGraphicAnimation(x, y, graphic);
8465           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8466
8467           if (Tile[newx][newy] == EL_ACID)
8468           {
8469             SplashAcid(newx, newy);
8470
8471             return;
8472           }
8473
8474           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8475
8476           Store[newx][newy] = EL_EMC_ANDROID;
8477           GfxElement[newx][newy] = EL_EMC_ANDROID;
8478           GfxAction[newx][newy] = ACTION_GROWING;
8479           GfxDir[newx][newy] = diagonal_move_dir;
8480           ChangeDelay[newx][newy] = change_delay;
8481
8482           graphic = el_act_dir2img(GfxElement[newx][newy],
8483                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8484
8485           DrawLevelGraphicAnimation(newx, newy, graphic);
8486           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8487
8488           return;
8489         }
8490         else
8491         {
8492           Tile[newx][newy] = EL_EMPTY;
8493           TEST_DrawLevelField(newx, newy);
8494
8495           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8496         }
8497       }
8498       else if (!IS_FREE(newx, newy))
8499       {
8500         return;
8501       }
8502     }
8503     else if (IS_CUSTOM_ELEMENT(element) &&
8504              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8505     {
8506       if (!DigFieldByCE(newx, newy, element))
8507         return;
8508
8509       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8510       {
8511         RunnerVisit[x][y] = FrameCounter;
8512         PlayerVisit[x][y] /= 8;         // expire player visit path
8513       }
8514     }
8515     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8516     {
8517       if (!IS_FREE(newx, newy))
8518       {
8519         if (IS_PLAYER(x, y))
8520           DrawPlayerField(x, y);
8521         else
8522           TEST_DrawLevelField(x, y);
8523
8524         return;
8525       }
8526       else
8527       {
8528         boolean wanna_flame = !RND(10);
8529         int dx = newx - x, dy = newy - y;
8530         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8531         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8532         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8533                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8534         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8535                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8536
8537         if ((wanna_flame ||
8538              IS_CLASSIC_ENEMY(element1) ||
8539              IS_CLASSIC_ENEMY(element2)) &&
8540             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8541             element1 != EL_FLAMES && element2 != EL_FLAMES)
8542         {
8543           ResetGfxAnimation(x, y);
8544           GfxAction[x][y] = ACTION_ATTACKING;
8545
8546           if (IS_PLAYER(x, y))
8547             DrawPlayerField(x, y);
8548           else
8549             TEST_DrawLevelField(x, y);
8550
8551           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8552
8553           MovDelay[x][y] = 50;
8554
8555           Tile[newx][newy] = EL_FLAMES;
8556           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8557             Tile[newx1][newy1] = EL_FLAMES;
8558           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8559             Tile[newx2][newy2] = EL_FLAMES;
8560
8561           return;
8562         }
8563       }
8564     }
8565     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8566              Tile[newx][newy] == EL_DIAMOND)
8567     {
8568       if (IS_MOVING(newx, newy))
8569         RemoveMovingField(newx, newy);
8570       else
8571       {
8572         Tile[newx][newy] = EL_EMPTY;
8573         TEST_DrawLevelField(newx, newy);
8574       }
8575
8576       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8577     }
8578     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8579              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8580     {
8581       if (AmoebaNr[newx][newy])
8582       {
8583         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8584         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8585             Tile[newx][newy] == EL_BD_AMOEBA)
8586           AmoebaCnt[AmoebaNr[newx][newy]]--;
8587       }
8588
8589       if (IS_MOVING(newx, newy))
8590       {
8591         RemoveMovingField(newx, newy);
8592       }
8593       else
8594       {
8595         Tile[newx][newy] = EL_EMPTY;
8596         TEST_DrawLevelField(newx, newy);
8597       }
8598
8599       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8600     }
8601     else if ((element == EL_PACMAN || element == EL_MOLE)
8602              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8603     {
8604       if (AmoebaNr[newx][newy])
8605       {
8606         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8607         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8608             Tile[newx][newy] == EL_BD_AMOEBA)
8609           AmoebaCnt[AmoebaNr[newx][newy]]--;
8610       }
8611
8612       if (element == EL_MOLE)
8613       {
8614         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8615         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8616
8617         ResetGfxAnimation(x, y);
8618         GfxAction[x][y] = ACTION_DIGGING;
8619         TEST_DrawLevelField(x, y);
8620
8621         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8622
8623         return;                         // wait for shrinking amoeba
8624       }
8625       else      // element == EL_PACMAN
8626       {
8627         Tile[newx][newy] = EL_EMPTY;
8628         TEST_DrawLevelField(newx, newy);
8629         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8630       }
8631     }
8632     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8633              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8634               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8635     {
8636       // wait for shrinking amoeba to completely disappear
8637       return;
8638     }
8639     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8640     {
8641       // object was running against a wall
8642
8643       TurnRound(x, y);
8644
8645       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8646         DrawLevelElementAnimation(x, y, element);
8647
8648       if (DONT_TOUCH(element))
8649         TestIfBadThingTouchesPlayer(x, y);
8650
8651       return;
8652     }
8653
8654     InitMovingField(x, y, MovDir[x][y]);
8655
8656     PlayLevelSoundAction(x, y, ACTION_MOVING);
8657   }
8658
8659   if (MovDir[x][y])
8660     ContinueMoving(x, y);
8661 }
8662
8663 void ContinueMoving(int x, int y)
8664 {
8665   int element = Tile[x][y];
8666   struct ElementInfo *ei = &element_info[element];
8667   int direction = MovDir[x][y];
8668   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8669   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8670   int newx = x + dx, newy = y + dy;
8671   int stored = Store[x][y];
8672   int stored_new = Store[newx][newy];
8673   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8674   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8675   boolean last_line = (newy == lev_fieldy - 1);
8676   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8677
8678   if (pushed_by_player)         // special case: moving object pushed by player
8679   {
8680     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8681   }
8682   else if (use_step_delay)      // special case: moving object has step delay
8683   {
8684     if (!MovDelay[x][y])
8685       MovPos[x][y] += getElementMoveStepsize(x, y);
8686
8687     if (MovDelay[x][y])
8688       MovDelay[x][y]--;
8689     else
8690       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8691
8692     if (MovDelay[x][y])
8693     {
8694       TEST_DrawLevelField(x, y);
8695
8696       return;   // element is still waiting
8697     }
8698   }
8699   else                          // normal case: generically moving object
8700   {
8701     MovPos[x][y] += getElementMoveStepsize(x, y);
8702   }
8703
8704   if (ABS(MovPos[x][y]) < TILEX)
8705   {
8706     TEST_DrawLevelField(x, y);
8707
8708     return;     // element is still moving
8709   }
8710
8711   // element reached destination field
8712
8713   Tile[x][y] = EL_EMPTY;
8714   Tile[newx][newy] = element;
8715   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8716
8717   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8718   {
8719     element = Tile[newx][newy] = EL_ACID;
8720   }
8721   else if (element == EL_MOLE)
8722   {
8723     Tile[x][y] = EL_SAND;
8724
8725     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8726   }
8727   else if (element == EL_QUICKSAND_FILLING)
8728   {
8729     element = Tile[newx][newy] = get_next_element(element);
8730     Store[newx][newy] = Store[x][y];
8731   }
8732   else if (element == EL_QUICKSAND_EMPTYING)
8733   {
8734     Tile[x][y] = get_next_element(element);
8735     element = Tile[newx][newy] = Store[x][y];
8736   }
8737   else if (element == EL_QUICKSAND_FAST_FILLING)
8738   {
8739     element = Tile[newx][newy] = get_next_element(element);
8740     Store[newx][newy] = Store[x][y];
8741   }
8742   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8743   {
8744     Tile[x][y] = get_next_element(element);
8745     element = Tile[newx][newy] = Store[x][y];
8746   }
8747   else if (element == EL_MAGIC_WALL_FILLING)
8748   {
8749     element = Tile[newx][newy] = get_next_element(element);
8750     if (!game.magic_wall_active)
8751       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8752     Store[newx][newy] = Store[x][y];
8753   }
8754   else if (element == EL_MAGIC_WALL_EMPTYING)
8755   {
8756     Tile[x][y] = get_next_element(element);
8757     if (!game.magic_wall_active)
8758       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8759     element = Tile[newx][newy] = Store[x][y];
8760
8761     InitField(newx, newy, FALSE);
8762   }
8763   else if (element == EL_BD_MAGIC_WALL_FILLING)
8764   {
8765     element = Tile[newx][newy] = get_next_element(element);
8766     if (!game.magic_wall_active)
8767       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8768     Store[newx][newy] = Store[x][y];
8769   }
8770   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8771   {
8772     Tile[x][y] = get_next_element(element);
8773     if (!game.magic_wall_active)
8774       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8775     element = Tile[newx][newy] = Store[x][y];
8776
8777     InitField(newx, newy, FALSE);
8778   }
8779   else if (element == EL_DC_MAGIC_WALL_FILLING)
8780   {
8781     element = Tile[newx][newy] = get_next_element(element);
8782     if (!game.magic_wall_active)
8783       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8784     Store[newx][newy] = Store[x][y];
8785   }
8786   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8787   {
8788     Tile[x][y] = get_next_element(element);
8789     if (!game.magic_wall_active)
8790       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8791     element = Tile[newx][newy] = Store[x][y];
8792
8793     InitField(newx, newy, FALSE);
8794   }
8795   else if (element == EL_AMOEBA_DROPPING)
8796   {
8797     Tile[x][y] = get_next_element(element);
8798     element = Tile[newx][newy] = Store[x][y];
8799   }
8800   else if (element == EL_SOKOBAN_OBJECT)
8801   {
8802     if (Back[x][y])
8803       Tile[x][y] = Back[x][y];
8804
8805     if (Back[newx][newy])
8806       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8807
8808     Back[x][y] = Back[newx][newy] = 0;
8809   }
8810
8811   Store[x][y] = EL_EMPTY;
8812   MovPos[x][y] = 0;
8813   MovDir[x][y] = 0;
8814   MovDelay[x][y] = 0;
8815
8816   MovDelay[newx][newy] = 0;
8817
8818   if (CAN_CHANGE_OR_HAS_ACTION(element))
8819   {
8820     // copy element change control values to new field
8821     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8822     ChangePage[newx][newy]  = ChangePage[x][y];
8823     ChangeCount[newx][newy] = ChangeCount[x][y];
8824     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8825   }
8826
8827   CustomValue[newx][newy] = CustomValue[x][y];
8828
8829   ChangeDelay[x][y] = 0;
8830   ChangePage[x][y] = -1;
8831   ChangeCount[x][y] = 0;
8832   ChangeEvent[x][y] = -1;
8833
8834   CustomValue[x][y] = 0;
8835
8836   // copy animation control values to new field
8837   GfxFrame[newx][newy]  = GfxFrame[x][y];
8838   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8839   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8840   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8841
8842   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8843
8844   // some elements can leave other elements behind after moving
8845   if (ei->move_leave_element != EL_EMPTY &&
8846       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8847       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8848   {
8849     int move_leave_element = ei->move_leave_element;
8850
8851     // this makes it possible to leave the removed element again
8852     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8853       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8854
8855     Tile[x][y] = move_leave_element;
8856
8857     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8858       MovDir[x][y] = direction;
8859
8860     InitField(x, y, FALSE);
8861
8862     if (GFX_CRUMBLED(Tile[x][y]))
8863       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8864
8865     if (IS_PLAYER_ELEMENT(move_leave_element))
8866       RelocatePlayer(x, y, move_leave_element);
8867   }
8868
8869   // do this after checking for left-behind element
8870   ResetGfxAnimation(x, y);      // reset animation values for old field
8871
8872   if (!CAN_MOVE(element) ||
8873       (CAN_FALL(element) && direction == MV_DOWN &&
8874        (element == EL_SPRING ||
8875         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8876         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8877     GfxDir[x][y] = MovDir[newx][newy] = 0;
8878
8879   TEST_DrawLevelField(x, y);
8880   TEST_DrawLevelField(newx, newy);
8881
8882   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8883
8884   // prevent pushed element from moving on in pushed direction
8885   if (pushed_by_player && CAN_MOVE(element) &&
8886       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8887       !(element_info[element].move_pattern & direction))
8888     TurnRound(newx, newy);
8889
8890   // prevent elements on conveyor belt from moving on in last direction
8891   if (pushed_by_conveyor && CAN_FALL(element) &&
8892       direction & MV_HORIZONTAL)
8893     MovDir[newx][newy] = 0;
8894
8895   if (!pushed_by_player)
8896   {
8897     int nextx = newx + dx, nexty = newy + dy;
8898     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8899
8900     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8901
8902     if (CAN_FALL(element) && direction == MV_DOWN)
8903       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8904
8905     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8906       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8907
8908     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8909       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8910   }
8911
8912   if (DONT_TOUCH(element))      // object may be nasty to player or others
8913   {
8914     TestIfBadThingTouchesPlayer(newx, newy);
8915     TestIfBadThingTouchesFriend(newx, newy);
8916
8917     if (!IS_CUSTOM_ELEMENT(element))
8918       TestIfBadThingTouchesOtherBadThing(newx, newy);
8919   }
8920   else if (element == EL_PENGUIN)
8921     TestIfFriendTouchesBadThing(newx, newy);
8922
8923   if (DONT_GET_HIT_BY(element))
8924   {
8925     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8926   }
8927
8928   // give the player one last chance (one more frame) to move away
8929   if (CAN_FALL(element) && direction == MV_DOWN &&
8930       (last_line || (!IS_FREE(x, newy + 1) &&
8931                      (!IS_PLAYER(x, newy + 1) ||
8932                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8933     Impact(x, newy);
8934
8935   if (pushed_by_player && !game.use_change_when_pushing_bug)
8936   {
8937     int push_side = MV_DIR_OPPOSITE(direction);
8938     struct PlayerInfo *player = PLAYERINFO(x, y);
8939
8940     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8941                                player->index_bit, push_side);
8942     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8943                                         player->index_bit, push_side);
8944   }
8945
8946   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8947     MovDelay[newx][newy] = 1;
8948
8949   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8950
8951   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8952   TestIfElementHitsCustomElement(newx, newy, direction);
8953   TestIfPlayerTouchesCustomElement(newx, newy);
8954   TestIfElementTouchesCustomElement(newx, newy);
8955
8956   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8957       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8958     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8959                              MV_DIR_OPPOSITE(direction));
8960 }
8961
8962 int AmoebaNeighbourNr(int ax, int ay)
8963 {
8964   int i;
8965   int element = Tile[ax][ay];
8966   int group_nr = 0;
8967   struct XY *xy = xy_topdown;
8968
8969   for (i = 0; i < NUM_DIRECTIONS; i++)
8970   {
8971     int x = ax + xy[i].x;
8972     int y = ay + xy[i].y;
8973
8974     if (!IN_LEV_FIELD(x, y))
8975       continue;
8976
8977     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8978       group_nr = AmoebaNr[x][y];
8979   }
8980
8981   return group_nr;
8982 }
8983
8984 static void AmoebaMerge(int ax, int ay)
8985 {
8986   int i, x, y, xx, yy;
8987   int new_group_nr = AmoebaNr[ax][ay];
8988   struct XY *xy = xy_topdown;
8989
8990   if (new_group_nr == 0)
8991     return;
8992
8993   for (i = 0; i < NUM_DIRECTIONS; i++)
8994   {
8995     x = ax + xy[i].x;
8996     y = ay + xy[i].y;
8997
8998     if (!IN_LEV_FIELD(x, y))
8999       continue;
9000
9001     if ((Tile[x][y] == EL_AMOEBA_FULL ||
9002          Tile[x][y] == EL_BD_AMOEBA ||
9003          Tile[x][y] == EL_AMOEBA_DEAD) &&
9004         AmoebaNr[x][y] != new_group_nr)
9005     {
9006       int old_group_nr = AmoebaNr[x][y];
9007
9008       if (old_group_nr == 0)
9009         return;
9010
9011       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9012       AmoebaCnt[old_group_nr] = 0;
9013       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9014       AmoebaCnt2[old_group_nr] = 0;
9015
9016       SCAN_PLAYFIELD(xx, yy)
9017       {
9018         if (AmoebaNr[xx][yy] == old_group_nr)
9019           AmoebaNr[xx][yy] = new_group_nr;
9020       }
9021     }
9022   }
9023 }
9024
9025 void AmoebaToDiamond(int ax, int ay)
9026 {
9027   int i, x, y;
9028
9029   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9030   {
9031     int group_nr = AmoebaNr[ax][ay];
9032
9033 #ifdef DEBUG
9034     if (group_nr == 0)
9035     {
9036       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9037       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9038
9039       return;
9040     }
9041 #endif
9042
9043     SCAN_PLAYFIELD(x, y)
9044     {
9045       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9046       {
9047         AmoebaNr[x][y] = 0;
9048         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9049       }
9050     }
9051
9052     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9053                             SND_AMOEBA_TURNING_TO_GEM :
9054                             SND_AMOEBA_TURNING_TO_ROCK));
9055     Bang(ax, ay);
9056   }
9057   else
9058   {
9059     struct XY *xy = xy_topdown;
9060
9061     for (i = 0; i < NUM_DIRECTIONS; i++)
9062     {
9063       x = ax + xy[i].x;
9064       y = ay + xy[i].y;
9065
9066       if (!IN_LEV_FIELD(x, y))
9067         continue;
9068
9069       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9070       {
9071         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9072                               SND_AMOEBA_TURNING_TO_GEM :
9073                               SND_AMOEBA_TURNING_TO_ROCK));
9074         Bang(x, y);
9075       }
9076     }
9077   }
9078 }
9079
9080 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9081 {
9082   int x, y;
9083   int group_nr = AmoebaNr[ax][ay];
9084   boolean done = FALSE;
9085
9086 #ifdef DEBUG
9087   if (group_nr == 0)
9088   {
9089     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9090     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9091
9092     return;
9093   }
9094 #endif
9095
9096   SCAN_PLAYFIELD(x, y)
9097   {
9098     if (AmoebaNr[x][y] == group_nr &&
9099         (Tile[x][y] == EL_AMOEBA_DEAD ||
9100          Tile[x][y] == EL_BD_AMOEBA ||
9101          Tile[x][y] == EL_AMOEBA_GROWING))
9102     {
9103       AmoebaNr[x][y] = 0;
9104       Tile[x][y] = new_element;
9105       InitField(x, y, FALSE);
9106       TEST_DrawLevelField(x, y);
9107       done = TRUE;
9108     }
9109   }
9110
9111   if (done)
9112     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9113                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9114                             SND_BD_AMOEBA_TURNING_TO_GEM));
9115 }
9116
9117 static void AmoebaGrowing(int x, int y)
9118 {
9119   static DelayCounter sound_delay = { 0 };
9120
9121   if (!MovDelay[x][y])          // start new growing cycle
9122   {
9123     MovDelay[x][y] = 7;
9124
9125     if (DelayReached(&sound_delay))
9126     {
9127       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9128       sound_delay.value = 30;
9129     }
9130   }
9131
9132   if (MovDelay[x][y])           // wait some time before growing bigger
9133   {
9134     MovDelay[x][y]--;
9135     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9136     {
9137       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9138                                            6 - MovDelay[x][y]);
9139
9140       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9141     }
9142
9143     if (!MovDelay[x][y])
9144     {
9145       Tile[x][y] = Store[x][y];
9146       Store[x][y] = 0;
9147       TEST_DrawLevelField(x, y);
9148     }
9149   }
9150 }
9151
9152 static void AmoebaShrinking(int x, int y)
9153 {
9154   static DelayCounter sound_delay = { 0 };
9155
9156   if (!MovDelay[x][y])          // start new shrinking cycle
9157   {
9158     MovDelay[x][y] = 7;
9159
9160     if (DelayReached(&sound_delay))
9161       sound_delay.value = 30;
9162   }
9163
9164   if (MovDelay[x][y])           // wait some time before shrinking
9165   {
9166     MovDelay[x][y]--;
9167     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9168     {
9169       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9170                                            6 - MovDelay[x][y]);
9171
9172       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9173     }
9174
9175     if (!MovDelay[x][y])
9176     {
9177       Tile[x][y] = EL_EMPTY;
9178       TEST_DrawLevelField(x, y);
9179
9180       // don't let mole enter this field in this cycle;
9181       // (give priority to objects falling to this field from above)
9182       Stop[x][y] = TRUE;
9183     }
9184   }
9185 }
9186
9187 static void AmoebaReproduce(int ax, int ay)
9188 {
9189   int i;
9190   int element = Tile[ax][ay];
9191   int graphic = el2img(element);
9192   int newax = ax, neway = ay;
9193   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9194   struct XY *xy = xy_topdown;
9195
9196   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9197   {
9198     Tile[ax][ay] = EL_AMOEBA_DEAD;
9199     TEST_DrawLevelField(ax, ay);
9200     return;
9201   }
9202
9203   if (IS_ANIMATED(graphic))
9204     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9205
9206   if (!MovDelay[ax][ay])        // start making new amoeba field
9207     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9208
9209   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9210   {
9211     MovDelay[ax][ay]--;
9212     if (MovDelay[ax][ay])
9213       return;
9214   }
9215
9216   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9217   {
9218     int start = RND(4);
9219     int x = ax + xy[start].x;
9220     int y = ay + xy[start].y;
9221
9222     if (!IN_LEV_FIELD(x, y))
9223       return;
9224
9225     if (IS_FREE(x, y) ||
9226         CAN_GROW_INTO(Tile[x][y]) ||
9227         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9228         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9229     {
9230       newax = x;
9231       neway = y;
9232     }
9233
9234     if (newax == ax && neway == ay)
9235       return;
9236   }
9237   else                          // normal or "filled" (BD style) amoeba
9238   {
9239     int start = RND(4);
9240     boolean waiting_for_player = FALSE;
9241
9242     for (i = 0; i < NUM_DIRECTIONS; i++)
9243     {
9244       int j = (start + i) % 4;
9245       int x = ax + xy[j].x;
9246       int y = ay + xy[j].y;
9247
9248       if (!IN_LEV_FIELD(x, y))
9249         continue;
9250
9251       if (IS_FREE(x, y) ||
9252           CAN_GROW_INTO(Tile[x][y]) ||
9253           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9254           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9255       {
9256         newax = x;
9257         neway = y;
9258         break;
9259       }
9260       else if (IS_PLAYER(x, y))
9261         waiting_for_player = TRUE;
9262     }
9263
9264     if (newax == ax && neway == ay)             // amoeba cannot grow
9265     {
9266       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9267       {
9268         Tile[ax][ay] = EL_AMOEBA_DEAD;
9269         TEST_DrawLevelField(ax, ay);
9270         AmoebaCnt[AmoebaNr[ax][ay]]--;
9271
9272         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9273         {
9274           if (element == EL_AMOEBA_FULL)
9275             AmoebaToDiamond(ax, ay);
9276           else if (element == EL_BD_AMOEBA)
9277             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9278         }
9279       }
9280       return;
9281     }
9282     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9283     {
9284       // amoeba gets larger by growing in some direction
9285
9286       int new_group_nr = AmoebaNr[ax][ay];
9287
9288 #ifdef DEBUG
9289   if (new_group_nr == 0)
9290   {
9291     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9292           newax, neway);
9293     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9294
9295     return;
9296   }
9297 #endif
9298
9299       AmoebaNr[newax][neway] = new_group_nr;
9300       AmoebaCnt[new_group_nr]++;
9301       AmoebaCnt2[new_group_nr]++;
9302
9303       // if amoeba touches other amoeba(s) after growing, unify them
9304       AmoebaMerge(newax, neway);
9305
9306       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9307       {
9308         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9309         return;
9310       }
9311     }
9312   }
9313
9314   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9315       (neway == lev_fieldy - 1 && newax != ax))
9316   {
9317     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9318     Store[newax][neway] = element;
9319   }
9320   else if (neway == ay || element == EL_EMC_DRIPPER)
9321   {
9322     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9323
9324     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9325   }
9326   else
9327   {
9328     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9329     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9330     Store[ax][ay] = EL_AMOEBA_DROP;
9331     ContinueMoving(ax, ay);
9332     return;
9333   }
9334
9335   TEST_DrawLevelField(newax, neway);
9336 }
9337
9338 static void Life(int ax, int ay)
9339 {
9340   int x1, y1, x2, y2;
9341   int life_time = 40;
9342   int element = Tile[ax][ay];
9343   int graphic = el2img(element);
9344   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9345                          level.biomaze);
9346   boolean changed = FALSE;
9347
9348   if (IS_ANIMATED(graphic))
9349     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9350
9351   if (Stop[ax][ay])
9352     return;
9353
9354   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9355     MovDelay[ax][ay] = life_time;
9356
9357   if (MovDelay[ax][ay])         // wait some time before next cycle
9358   {
9359     MovDelay[ax][ay]--;
9360     if (MovDelay[ax][ay])
9361       return;
9362   }
9363
9364   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9365   {
9366     int xx = ax+x1, yy = ay+y1;
9367     int old_element = Tile[xx][yy];
9368     int num_neighbours = 0;
9369
9370     if (!IN_LEV_FIELD(xx, yy))
9371       continue;
9372
9373     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9374     {
9375       int x = xx+x2, y = yy+y2;
9376
9377       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9378         continue;
9379
9380       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9381       boolean is_neighbour = FALSE;
9382
9383       if (level.use_life_bugs)
9384         is_neighbour =
9385           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9386            (IS_FREE(x, y)                             &&  Stop[x][y]));
9387       else
9388         is_neighbour =
9389           (Last[x][y] == element || is_player_cell);
9390
9391       if (is_neighbour)
9392         num_neighbours++;
9393     }
9394
9395     boolean is_free = FALSE;
9396
9397     if (level.use_life_bugs)
9398       is_free = (IS_FREE(xx, yy));
9399     else
9400       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9401
9402     if (xx == ax && yy == ay)           // field in the middle
9403     {
9404       if (num_neighbours < life_parameter[0] ||
9405           num_neighbours > life_parameter[1])
9406       {
9407         Tile[xx][yy] = EL_EMPTY;
9408         if (Tile[xx][yy] != old_element)
9409           TEST_DrawLevelField(xx, yy);
9410         Stop[xx][yy] = TRUE;
9411         changed = TRUE;
9412       }
9413     }
9414     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9415     {                                   // free border field
9416       if (num_neighbours >= life_parameter[2] &&
9417           num_neighbours <= life_parameter[3])
9418       {
9419         Tile[xx][yy] = element;
9420         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9421         if (Tile[xx][yy] != old_element)
9422           TEST_DrawLevelField(xx, yy);
9423         Stop[xx][yy] = TRUE;
9424         changed = TRUE;
9425       }
9426     }
9427   }
9428
9429   if (changed)
9430     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9431                    SND_GAME_OF_LIFE_GROWING);
9432 }
9433
9434 static void InitRobotWheel(int x, int y)
9435 {
9436   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9437 }
9438
9439 static void RunRobotWheel(int x, int y)
9440 {
9441   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9442 }
9443
9444 static void StopRobotWheel(int x, int y)
9445 {
9446   if (game.robot_wheel_x == x &&
9447       game.robot_wheel_y == y)
9448   {
9449     game.robot_wheel_x = -1;
9450     game.robot_wheel_y = -1;
9451     game.robot_wheel_active = FALSE;
9452   }
9453 }
9454
9455 static void InitTimegateWheel(int x, int y)
9456 {
9457   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9458 }
9459
9460 static void RunTimegateWheel(int x, int y)
9461 {
9462   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9463 }
9464
9465 static void InitMagicBallDelay(int x, int y)
9466 {
9467   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9468 }
9469
9470 static void ActivateMagicBall(int bx, int by)
9471 {
9472   int x, y;
9473
9474   if (level.ball_random)
9475   {
9476     int pos_border = RND(8);    // select one of the eight border elements
9477     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9478     int xx = pos_content % 3;
9479     int yy = pos_content / 3;
9480
9481     x = bx - 1 + xx;
9482     y = by - 1 + yy;
9483
9484     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9485       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9486   }
9487   else
9488   {
9489     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9490     {
9491       int xx = x - bx + 1;
9492       int yy = y - by + 1;
9493
9494       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9495         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9496     }
9497   }
9498
9499   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9500 }
9501
9502 static void CheckExit(int x, int y)
9503 {
9504   if (game.gems_still_needed > 0 ||
9505       game.sokoban_fields_still_needed > 0 ||
9506       game.sokoban_objects_still_needed > 0 ||
9507       game.lights_still_needed > 0)
9508   {
9509     int element = Tile[x][y];
9510     int graphic = el2img(element);
9511
9512     if (IS_ANIMATED(graphic))
9513       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9514
9515     return;
9516   }
9517
9518   // do not re-open exit door closed after last player
9519   if (game.all_players_gone)
9520     return;
9521
9522   Tile[x][y] = EL_EXIT_OPENING;
9523
9524   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9525 }
9526
9527 static void CheckExitEM(int x, int y)
9528 {
9529   if (game.gems_still_needed > 0 ||
9530       game.sokoban_fields_still_needed > 0 ||
9531       game.sokoban_objects_still_needed > 0 ||
9532       game.lights_still_needed > 0)
9533   {
9534     int element = Tile[x][y];
9535     int graphic = el2img(element);
9536
9537     if (IS_ANIMATED(graphic))
9538       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9539
9540     return;
9541   }
9542
9543   // do not re-open exit door closed after last player
9544   if (game.all_players_gone)
9545     return;
9546
9547   Tile[x][y] = EL_EM_EXIT_OPENING;
9548
9549   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9550 }
9551
9552 static void CheckExitSteel(int x, int y)
9553 {
9554   if (game.gems_still_needed > 0 ||
9555       game.sokoban_fields_still_needed > 0 ||
9556       game.sokoban_objects_still_needed > 0 ||
9557       game.lights_still_needed > 0)
9558   {
9559     int element = Tile[x][y];
9560     int graphic = el2img(element);
9561
9562     if (IS_ANIMATED(graphic))
9563       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9564
9565     return;
9566   }
9567
9568   // do not re-open exit door closed after last player
9569   if (game.all_players_gone)
9570     return;
9571
9572   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9573
9574   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9575 }
9576
9577 static void CheckExitSteelEM(int x, int y)
9578 {
9579   if (game.gems_still_needed > 0 ||
9580       game.sokoban_fields_still_needed > 0 ||
9581       game.sokoban_objects_still_needed > 0 ||
9582       game.lights_still_needed > 0)
9583   {
9584     int element = Tile[x][y];
9585     int graphic = el2img(element);
9586
9587     if (IS_ANIMATED(graphic))
9588       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9589
9590     return;
9591   }
9592
9593   // do not re-open exit door closed after last player
9594   if (game.all_players_gone)
9595     return;
9596
9597   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9598
9599   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9600 }
9601
9602 static void CheckExitSP(int x, int y)
9603 {
9604   if (game.gems_still_needed > 0)
9605   {
9606     int element = Tile[x][y];
9607     int graphic = el2img(element);
9608
9609     if (IS_ANIMATED(graphic))
9610       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9611
9612     return;
9613   }
9614
9615   // do not re-open exit door closed after last player
9616   if (game.all_players_gone)
9617     return;
9618
9619   Tile[x][y] = EL_SP_EXIT_OPENING;
9620
9621   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9622 }
9623
9624 static void CloseAllOpenTimegates(void)
9625 {
9626   int x, y;
9627
9628   SCAN_PLAYFIELD(x, y)
9629   {
9630     int element = Tile[x][y];
9631
9632     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9633     {
9634       Tile[x][y] = EL_TIMEGATE_CLOSING;
9635
9636       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9637     }
9638   }
9639 }
9640
9641 static void DrawTwinkleOnField(int x, int y)
9642 {
9643   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9644     return;
9645
9646   if (Tile[x][y] == EL_BD_DIAMOND)
9647     return;
9648
9649   if (MovDelay[x][y] == 0)      // next animation frame
9650     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9651
9652   if (MovDelay[x][y] != 0)      // wait some time before next frame
9653   {
9654     MovDelay[x][y]--;
9655
9656     DrawLevelElementAnimation(x, y, Tile[x][y]);
9657
9658     if (MovDelay[x][y] != 0)
9659     {
9660       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9661                                            10 - MovDelay[x][y]);
9662
9663       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9664     }
9665   }
9666 }
9667
9668 static void WallGrowing(int x, int y)
9669 {
9670   int delay = 6;
9671
9672   if (!MovDelay[x][y])          // next animation frame
9673     MovDelay[x][y] = 3 * delay;
9674
9675   if (MovDelay[x][y])           // wait some time before next frame
9676   {
9677     MovDelay[x][y]--;
9678
9679     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9680     {
9681       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9682       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9683
9684       DrawLevelGraphic(x, y, graphic, frame);
9685     }
9686
9687     if (!MovDelay[x][y])
9688     {
9689       if (MovDir[x][y] == MV_LEFT)
9690       {
9691         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9692           TEST_DrawLevelField(x - 1, y);
9693       }
9694       else if (MovDir[x][y] == MV_RIGHT)
9695       {
9696         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9697           TEST_DrawLevelField(x + 1, y);
9698       }
9699       else if (MovDir[x][y] == MV_UP)
9700       {
9701         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9702           TEST_DrawLevelField(x, y - 1);
9703       }
9704       else
9705       {
9706         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9707           TEST_DrawLevelField(x, y + 1);
9708       }
9709
9710       Tile[x][y] = Store[x][y];
9711       Store[x][y] = 0;
9712       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9713       TEST_DrawLevelField(x, y);
9714     }
9715   }
9716 }
9717
9718 static void CheckWallGrowing(int ax, int ay)
9719 {
9720   int element = Tile[ax][ay];
9721   int graphic = el2img(element);
9722   boolean free_top    = FALSE;
9723   boolean free_bottom = FALSE;
9724   boolean free_left   = FALSE;
9725   boolean free_right  = FALSE;
9726   boolean stop_top    = FALSE;
9727   boolean stop_bottom = FALSE;
9728   boolean stop_left   = FALSE;
9729   boolean stop_right  = FALSE;
9730   boolean new_wall    = FALSE;
9731
9732   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9733                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9734                            element == EL_EXPANDABLE_STEELWALL_ANY);
9735
9736   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9737                              element == EL_EXPANDABLE_WALL_ANY ||
9738                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9739                              element == EL_EXPANDABLE_STEELWALL_ANY);
9740
9741   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9742                              element == EL_EXPANDABLE_WALL_ANY ||
9743                              element == EL_EXPANDABLE_WALL ||
9744                              element == EL_BD_EXPANDABLE_WALL ||
9745                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9746                              element == EL_EXPANDABLE_STEELWALL_ANY);
9747
9748   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9749                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9750
9751   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9752                              element == EL_EXPANDABLE_WALL ||
9753                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9754
9755   int wall_growing = (is_steelwall ?
9756                       EL_EXPANDABLE_STEELWALL_GROWING :
9757                       EL_EXPANDABLE_WALL_GROWING);
9758
9759   int gfx_wall_growing_up    = (is_steelwall ?
9760                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9761                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9762   int gfx_wall_growing_down  = (is_steelwall ?
9763                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9764                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9765   int gfx_wall_growing_left  = (is_steelwall ?
9766                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9767                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9768   int gfx_wall_growing_right = (is_steelwall ?
9769                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9770                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9771
9772   if (IS_ANIMATED(graphic))
9773     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9774
9775   if (!MovDelay[ax][ay])        // start building new wall
9776     MovDelay[ax][ay] = 6;
9777
9778   if (MovDelay[ax][ay])         // wait some time before building new wall
9779   {
9780     MovDelay[ax][ay]--;
9781     if (MovDelay[ax][ay])
9782       return;
9783   }
9784
9785   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9786     free_top = TRUE;
9787   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9788     free_bottom = TRUE;
9789   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9790     free_left = TRUE;
9791   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9792     free_right = TRUE;
9793
9794   if (grow_vertical)
9795   {
9796     if (free_top)
9797     {
9798       Tile[ax][ay - 1] = wall_growing;
9799       Store[ax][ay - 1] = element;
9800       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9801
9802       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9803         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9804
9805       new_wall = TRUE;
9806     }
9807
9808     if (free_bottom)
9809     {
9810       Tile[ax][ay + 1] = wall_growing;
9811       Store[ax][ay + 1] = element;
9812       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9813
9814       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9815         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9816
9817       new_wall = TRUE;
9818     }
9819   }
9820
9821   if (grow_horizontal)
9822   {
9823     if (free_left)
9824     {
9825       Tile[ax - 1][ay] = wall_growing;
9826       Store[ax - 1][ay] = element;
9827       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9828
9829       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9830         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9831
9832       new_wall = TRUE;
9833     }
9834
9835     if (free_right)
9836     {
9837       Tile[ax + 1][ay] = wall_growing;
9838       Store[ax + 1][ay] = element;
9839       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9840
9841       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9842         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9843
9844       new_wall = TRUE;
9845     }
9846   }
9847
9848   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9849     TEST_DrawLevelField(ax, ay);
9850
9851   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9852     stop_top = TRUE;
9853   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9854     stop_bottom = TRUE;
9855   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9856     stop_left = TRUE;
9857   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9858     stop_right = TRUE;
9859
9860   if (((stop_top && stop_bottom) || stop_horizontal) &&
9861       ((stop_left && stop_right) || stop_vertical))
9862     Tile[ax][ay] = EL_WALL;
9863
9864   if (new_wall)
9865     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9866 }
9867
9868 static void CheckForDragon(int x, int y)
9869 {
9870   int i, j;
9871   boolean dragon_found = FALSE;
9872   struct XY *xy = xy_topdown;
9873
9874   for (i = 0; i < NUM_DIRECTIONS; i++)
9875   {
9876     for (j = 0; j < 4; j++)
9877     {
9878       int xx = x + j * xy[i].x;
9879       int yy = y + j * xy[i].y;
9880
9881       if (IN_LEV_FIELD(xx, yy) &&
9882           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9883       {
9884         if (Tile[xx][yy] == EL_DRAGON)
9885           dragon_found = TRUE;
9886       }
9887       else
9888         break;
9889     }
9890   }
9891
9892   if (!dragon_found)
9893   {
9894     for (i = 0; i < NUM_DIRECTIONS; i++)
9895     {
9896       for (j = 0; j < 3; j++)
9897       {
9898         int xx = x + j * xy[i].x;
9899         int yy = y + j * xy[i].y;
9900
9901         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9902         {
9903           Tile[xx][yy] = EL_EMPTY;
9904           TEST_DrawLevelField(xx, yy);
9905         }
9906         else
9907           break;
9908       }
9909     }
9910   }
9911 }
9912
9913 static void InitBuggyBase(int x, int y)
9914 {
9915   int element = Tile[x][y];
9916   int activating_delay = FRAMES_PER_SECOND / 4;
9917
9918   ChangeDelay[x][y] =
9919     (element == EL_SP_BUGGY_BASE ?
9920      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9921      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9922      activating_delay :
9923      element == EL_SP_BUGGY_BASE_ACTIVE ?
9924      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9925 }
9926
9927 static void WarnBuggyBase(int x, int y)
9928 {
9929   int i;
9930   struct XY *xy = xy_topdown;
9931
9932   for (i = 0; i < NUM_DIRECTIONS; i++)
9933   {
9934     int xx = x + xy[i].x;
9935     int yy = y + xy[i].y;
9936
9937     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9938     {
9939       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9940
9941       break;
9942     }
9943   }
9944 }
9945
9946 static void InitTrap(int x, int y)
9947 {
9948   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9949 }
9950
9951 static void ActivateTrap(int x, int y)
9952 {
9953   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9954 }
9955
9956 static void ChangeActiveTrap(int x, int y)
9957 {
9958   int graphic = IMG_TRAP_ACTIVE;
9959
9960   // if new animation frame was drawn, correct crumbled sand border
9961   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9962     TEST_DrawLevelFieldCrumbled(x, y);
9963 }
9964
9965 static int getSpecialActionElement(int element, int number, int base_element)
9966 {
9967   return (element != EL_EMPTY ? element :
9968           number != -1 ? base_element + number - 1 :
9969           EL_EMPTY);
9970 }
9971
9972 static int getModifiedActionNumber(int value_old, int operator, int operand,
9973                                    int value_min, int value_max)
9974 {
9975   int value_new = (operator == CA_MODE_SET      ? operand :
9976                    operator == CA_MODE_ADD      ? value_old + operand :
9977                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9978                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9979                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9980                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9981                    value_old);
9982
9983   return (value_new < value_min ? value_min :
9984           value_new > value_max ? value_max :
9985           value_new);
9986 }
9987
9988 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9989 {
9990   struct ElementInfo *ei = &element_info[element];
9991   struct ElementChangeInfo *change = &ei->change_page[page];
9992   int target_element = change->target_element;
9993   int action_type = change->action_type;
9994   int action_mode = change->action_mode;
9995   int action_arg = change->action_arg;
9996   int action_element = change->action_element;
9997   int i;
9998
9999   if (!change->has_action)
10000     return;
10001
10002   // ---------- determine action paramater values -----------------------------
10003
10004   int level_time_value =
10005     (level.time > 0 ? TimeLeft :
10006      TimePlayed);
10007
10008   int action_arg_element_raw =
10009     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10010      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10011      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10012      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10013      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10014      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10015      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10016      EL_EMPTY);
10017   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10018
10019   int action_arg_direction =
10020     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10021      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10022      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10023      change->actual_trigger_side :
10024      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10025      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10026      MV_NONE);
10027
10028   int action_arg_number_min =
10029     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10030      CA_ARG_MIN);
10031
10032   int action_arg_number_max =
10033     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10034      action_type == CA_SET_LEVEL_GEMS ? 999 :
10035      action_type == CA_SET_LEVEL_TIME ? 9999 :
10036      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10037      action_type == CA_SET_CE_VALUE ? 9999 :
10038      action_type == CA_SET_CE_SCORE ? 9999 :
10039      CA_ARG_MAX);
10040
10041   int action_arg_number_reset =
10042     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10043      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10044      action_type == CA_SET_LEVEL_TIME ? level.time :
10045      action_type == CA_SET_LEVEL_SCORE ? 0 :
10046      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10047      action_type == CA_SET_CE_SCORE ? 0 :
10048      0);
10049
10050   int action_arg_number =
10051     (action_arg <= CA_ARG_MAX ? action_arg :
10052      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10053      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10054      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10055      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10056      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10057      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10058      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10059      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10060      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10061      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10062      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10063      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10064      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10065      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10066      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10067      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10068      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10069      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10070      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10071      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10072      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10073      -1);
10074
10075   int action_arg_number_old =
10076     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10077      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10078      action_type == CA_SET_LEVEL_SCORE ? game.score :
10079      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10080      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10081      0);
10082
10083   int action_arg_number_new =
10084     getModifiedActionNumber(action_arg_number_old,
10085                             action_mode, action_arg_number,
10086                             action_arg_number_min, action_arg_number_max);
10087
10088   int trigger_player_bits =
10089     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10090      change->actual_trigger_player_bits : change->trigger_player);
10091
10092   int action_arg_player_bits =
10093     (action_arg >= CA_ARG_PLAYER_1 &&
10094      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10095      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10096      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10097      PLAYER_BITS_ANY);
10098
10099   // ---------- execute action  -----------------------------------------------
10100
10101   switch (action_type)
10102   {
10103     case CA_NO_ACTION:
10104     {
10105       return;
10106     }
10107
10108     // ---------- level actions  ----------------------------------------------
10109
10110     case CA_RESTART_LEVEL:
10111     {
10112       game.restart_level = TRUE;
10113
10114       break;
10115     }
10116
10117     case CA_SHOW_ENVELOPE:
10118     {
10119       int element = getSpecialActionElement(action_arg_element,
10120                                             action_arg_number, EL_ENVELOPE_1);
10121
10122       if (IS_ENVELOPE(element))
10123         local_player->show_envelope = element;
10124
10125       break;
10126     }
10127
10128     case CA_SET_LEVEL_TIME:
10129     {
10130       if (level.time > 0)       // only modify limited time value
10131       {
10132         TimeLeft = action_arg_number_new;
10133
10134         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10135
10136         DisplayGameControlValues();
10137
10138         if (!TimeLeft && game.time_limit)
10139           for (i = 0; i < MAX_PLAYERS; i++)
10140             KillPlayer(&stored_player[i]);
10141       }
10142
10143       break;
10144     }
10145
10146     case CA_SET_LEVEL_SCORE:
10147     {
10148       game.score = action_arg_number_new;
10149
10150       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10151
10152       DisplayGameControlValues();
10153
10154       break;
10155     }
10156
10157     case CA_SET_LEVEL_GEMS:
10158     {
10159       game.gems_still_needed = action_arg_number_new;
10160
10161       game.snapshot.collected_item = TRUE;
10162
10163       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10164
10165       DisplayGameControlValues();
10166
10167       break;
10168     }
10169
10170     case CA_SET_LEVEL_WIND:
10171     {
10172       game.wind_direction = action_arg_direction;
10173
10174       break;
10175     }
10176
10177     case CA_SET_LEVEL_RANDOM_SEED:
10178     {
10179       // ensure that setting a new random seed while playing is predictable
10180       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10181
10182       break;
10183     }
10184
10185     // ---------- player actions  ---------------------------------------------
10186
10187     case CA_MOVE_PLAYER:
10188     case CA_MOVE_PLAYER_NEW:
10189     {
10190       // automatically move to the next field in specified direction
10191       for (i = 0; i < MAX_PLAYERS; i++)
10192         if (trigger_player_bits & (1 << i))
10193           if (action_type == CA_MOVE_PLAYER ||
10194               stored_player[i].MovPos == 0)
10195             stored_player[i].programmed_action = action_arg_direction;
10196
10197       break;
10198     }
10199
10200     case CA_EXIT_PLAYER:
10201     {
10202       for (i = 0; i < MAX_PLAYERS; i++)
10203         if (action_arg_player_bits & (1 << i))
10204           ExitPlayer(&stored_player[i]);
10205
10206       if (game.players_still_needed == 0)
10207         LevelSolved();
10208
10209       break;
10210     }
10211
10212     case CA_KILL_PLAYER:
10213     {
10214       for (i = 0; i < MAX_PLAYERS; i++)
10215         if (action_arg_player_bits & (1 << i))
10216           KillPlayer(&stored_player[i]);
10217
10218       break;
10219     }
10220
10221     case CA_SET_PLAYER_KEYS:
10222     {
10223       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10224       int element = getSpecialActionElement(action_arg_element,
10225                                             action_arg_number, EL_KEY_1);
10226
10227       if (IS_KEY(element))
10228       {
10229         for (i = 0; i < MAX_PLAYERS; i++)
10230         {
10231           if (trigger_player_bits & (1 << i))
10232           {
10233             stored_player[i].key[KEY_NR(element)] = key_state;
10234
10235             DrawGameDoorValues();
10236           }
10237         }
10238       }
10239
10240       break;
10241     }
10242
10243     case CA_SET_PLAYER_SPEED:
10244     {
10245       for (i = 0; i < MAX_PLAYERS; i++)
10246       {
10247         if (trigger_player_bits & (1 << i))
10248         {
10249           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10250
10251           if (action_arg == CA_ARG_SPEED_FASTER &&
10252               stored_player[i].cannot_move)
10253           {
10254             action_arg_number = STEPSIZE_VERY_SLOW;
10255           }
10256           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10257                    action_arg == CA_ARG_SPEED_FASTER)
10258           {
10259             action_arg_number = 2;
10260             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10261                            CA_MODE_MULTIPLY);
10262           }
10263           else if (action_arg == CA_ARG_NUMBER_RESET)
10264           {
10265             action_arg_number = level.initial_player_stepsize[i];
10266           }
10267
10268           move_stepsize =
10269             getModifiedActionNumber(move_stepsize,
10270                                     action_mode,
10271                                     action_arg_number,
10272                                     action_arg_number_min,
10273                                     action_arg_number_max);
10274
10275           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10276         }
10277       }
10278
10279       break;
10280     }
10281
10282     case CA_SET_PLAYER_SHIELD:
10283     {
10284       for (i = 0; i < MAX_PLAYERS; i++)
10285       {
10286         if (trigger_player_bits & (1 << i))
10287         {
10288           if (action_arg == CA_ARG_SHIELD_OFF)
10289           {
10290             stored_player[i].shield_normal_time_left = 0;
10291             stored_player[i].shield_deadly_time_left = 0;
10292           }
10293           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10294           {
10295             stored_player[i].shield_normal_time_left = 999999;
10296           }
10297           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10298           {
10299             stored_player[i].shield_normal_time_left = 999999;
10300             stored_player[i].shield_deadly_time_left = 999999;
10301           }
10302         }
10303       }
10304
10305       break;
10306     }
10307
10308     case CA_SET_PLAYER_GRAVITY:
10309     {
10310       for (i = 0; i < MAX_PLAYERS; i++)
10311       {
10312         if (trigger_player_bits & (1 << i))
10313         {
10314           stored_player[i].gravity =
10315             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10316              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10317              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10318              stored_player[i].gravity);
10319         }
10320       }
10321
10322       break;
10323     }
10324
10325     case CA_SET_PLAYER_ARTWORK:
10326     {
10327       for (i = 0; i < MAX_PLAYERS; i++)
10328       {
10329         if (trigger_player_bits & (1 << i))
10330         {
10331           int artwork_element = action_arg_element;
10332
10333           if (action_arg == CA_ARG_ELEMENT_RESET)
10334             artwork_element =
10335               (level.use_artwork_element[i] ? level.artwork_element[i] :
10336                stored_player[i].element_nr);
10337
10338           if (stored_player[i].artwork_element != artwork_element)
10339             stored_player[i].Frame = 0;
10340
10341           stored_player[i].artwork_element = artwork_element;
10342
10343           SetPlayerWaiting(&stored_player[i], FALSE);
10344
10345           // set number of special actions for bored and sleeping animation
10346           stored_player[i].num_special_action_bored =
10347             get_num_special_action(artwork_element,
10348                                    ACTION_BORING_1, ACTION_BORING_LAST);
10349           stored_player[i].num_special_action_sleeping =
10350             get_num_special_action(artwork_element,
10351                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10352         }
10353       }
10354
10355       break;
10356     }
10357
10358     case CA_SET_PLAYER_INVENTORY:
10359     {
10360       for (i = 0; i < MAX_PLAYERS; i++)
10361       {
10362         struct PlayerInfo *player = &stored_player[i];
10363         int j, k;
10364
10365         if (trigger_player_bits & (1 << i))
10366         {
10367           int inventory_element = action_arg_element;
10368
10369           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10370               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10371               action_arg == CA_ARG_ELEMENT_ACTION)
10372           {
10373             int element = inventory_element;
10374             int collect_count = element_info[element].collect_count_initial;
10375
10376             if (!IS_CUSTOM_ELEMENT(element))
10377               collect_count = 1;
10378
10379             if (collect_count == 0)
10380               player->inventory_infinite_element = element;
10381             else
10382               for (k = 0; k < collect_count; k++)
10383                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10384                   player->inventory_element[player->inventory_size++] =
10385                     element;
10386           }
10387           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10388                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10389                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10390           {
10391             if (player->inventory_infinite_element != EL_UNDEFINED &&
10392                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10393                                      action_arg_element_raw))
10394               player->inventory_infinite_element = EL_UNDEFINED;
10395
10396             for (k = 0, j = 0; j < player->inventory_size; j++)
10397             {
10398               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10399                                         action_arg_element_raw))
10400                 player->inventory_element[k++] = player->inventory_element[j];
10401             }
10402
10403             player->inventory_size = k;
10404           }
10405           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10406           {
10407             if (player->inventory_size > 0)
10408             {
10409               for (j = 0; j < player->inventory_size - 1; j++)
10410                 player->inventory_element[j] = player->inventory_element[j + 1];
10411
10412               player->inventory_size--;
10413             }
10414           }
10415           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10416           {
10417             if (player->inventory_size > 0)
10418               player->inventory_size--;
10419           }
10420           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10421           {
10422             player->inventory_infinite_element = EL_UNDEFINED;
10423             player->inventory_size = 0;
10424           }
10425           else if (action_arg == CA_ARG_INVENTORY_RESET)
10426           {
10427             player->inventory_infinite_element = EL_UNDEFINED;
10428             player->inventory_size = 0;
10429
10430             if (level.use_initial_inventory[i])
10431             {
10432               for (j = 0; j < level.initial_inventory_size[i]; j++)
10433               {
10434                 int element = level.initial_inventory_content[i][j];
10435                 int collect_count = element_info[element].collect_count_initial;
10436
10437                 if (!IS_CUSTOM_ELEMENT(element))
10438                   collect_count = 1;
10439
10440                 if (collect_count == 0)
10441                   player->inventory_infinite_element = element;
10442                 else
10443                   for (k = 0; k < collect_count; k++)
10444                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10445                       player->inventory_element[player->inventory_size++] =
10446                         element;
10447               }
10448             }
10449           }
10450         }
10451       }
10452
10453       break;
10454     }
10455
10456     // ---------- CE actions  -------------------------------------------------
10457
10458     case CA_SET_CE_VALUE:
10459     {
10460       int last_ce_value = CustomValue[x][y];
10461
10462       CustomValue[x][y] = action_arg_number_new;
10463
10464       if (CustomValue[x][y] != last_ce_value)
10465       {
10466         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10467         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10468
10469         if (CustomValue[x][y] == 0)
10470         {
10471           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10472           ChangeCount[x][y] = 0;        // allow at least one more change
10473
10474           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10475           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10476         }
10477       }
10478
10479       break;
10480     }
10481
10482     case CA_SET_CE_SCORE:
10483     {
10484       int last_ce_score = ei->collect_score;
10485
10486       ei->collect_score = action_arg_number_new;
10487
10488       if (ei->collect_score != last_ce_score)
10489       {
10490         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10491         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10492
10493         if (ei->collect_score == 0)
10494         {
10495           int xx, yy;
10496
10497           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10498           ChangeCount[x][y] = 0;        // allow at least one more change
10499
10500           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10501           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10502
10503           /*
10504             This is a very special case that seems to be a mixture between
10505             CheckElementChange() and CheckTriggeredElementChange(): while
10506             the first one only affects single elements that are triggered
10507             directly, the second one affects multiple elements in the playfield
10508             that are triggered indirectly by another element. This is a third
10509             case: Changing the CE score always affects multiple identical CEs,
10510             so every affected CE must be checked, not only the single CE for
10511             which the CE score was changed in the first place (as every instance
10512             of that CE shares the same CE score, and therefore also can change)!
10513           */
10514           SCAN_PLAYFIELD(xx, yy)
10515           {
10516             if (Tile[xx][yy] == element)
10517               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10518                                  CE_SCORE_GETS_ZERO);
10519           }
10520         }
10521       }
10522
10523       break;
10524     }
10525
10526     case CA_SET_CE_ARTWORK:
10527     {
10528       int artwork_element = action_arg_element;
10529       boolean reset_frame = FALSE;
10530       int xx, yy;
10531
10532       if (action_arg == CA_ARG_ELEMENT_RESET)
10533         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10534                            element);
10535
10536       if (ei->gfx_element != artwork_element)
10537         reset_frame = TRUE;
10538
10539       ei->gfx_element = artwork_element;
10540
10541       SCAN_PLAYFIELD(xx, yy)
10542       {
10543         if (Tile[xx][yy] == element)
10544         {
10545           if (reset_frame)
10546           {
10547             ResetGfxAnimation(xx, yy);
10548             ResetRandomAnimationValue(xx, yy);
10549           }
10550
10551           TEST_DrawLevelField(xx, yy);
10552         }
10553       }
10554
10555       break;
10556     }
10557
10558     // ---------- engine actions  ---------------------------------------------
10559
10560     case CA_SET_ENGINE_SCAN_MODE:
10561     {
10562       InitPlayfieldScanMode(action_arg);
10563
10564       break;
10565     }
10566
10567     default:
10568       break;
10569   }
10570 }
10571
10572 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10573 {
10574   int old_element = Tile[x][y];
10575   int new_element = GetElementFromGroupElement(element);
10576   int previous_move_direction = MovDir[x][y];
10577   int last_ce_value = CustomValue[x][y];
10578   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10579   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10580   boolean add_player_onto_element = (new_element_is_player &&
10581                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10582                                      IS_WALKABLE(old_element));
10583
10584   if (!add_player_onto_element)
10585   {
10586     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10587       RemoveMovingField(x, y);
10588     else
10589       RemoveField(x, y);
10590
10591     Tile[x][y] = new_element;
10592
10593     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10594       MovDir[x][y] = previous_move_direction;
10595
10596     if (element_info[new_element].use_last_ce_value)
10597       CustomValue[x][y] = last_ce_value;
10598
10599     InitField_WithBug1(x, y, FALSE);
10600
10601     new_element = Tile[x][y];   // element may have changed
10602
10603     ResetGfxAnimation(x, y);
10604     ResetRandomAnimationValue(x, y);
10605
10606     TEST_DrawLevelField(x, y);
10607
10608     if (GFX_CRUMBLED(new_element))
10609       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10610   }
10611
10612   // check if element under the player changes from accessible to unaccessible
10613   // (needed for special case of dropping element which then changes)
10614   // (must be checked after creating new element for walkable group elements)
10615   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10616       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10617   {
10618     Bang(x, y);
10619
10620     return;
10621   }
10622
10623   // "ChangeCount" not set yet to allow "entered by player" change one time
10624   if (new_element_is_player)
10625     RelocatePlayer(x, y, new_element);
10626
10627   if (is_change)
10628     ChangeCount[x][y]++;        // count number of changes in the same frame
10629
10630   TestIfBadThingTouchesPlayer(x, y);
10631   TestIfPlayerTouchesCustomElement(x, y);
10632   TestIfElementTouchesCustomElement(x, y);
10633 }
10634
10635 static void CreateField(int x, int y, int element)
10636 {
10637   CreateFieldExt(x, y, element, FALSE);
10638 }
10639
10640 static void CreateElementFromChange(int x, int y, int element)
10641 {
10642   element = GET_VALID_RUNTIME_ELEMENT(element);
10643
10644   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10645   {
10646     int old_element = Tile[x][y];
10647
10648     // prevent changed element from moving in same engine frame
10649     // unless both old and new element can either fall or move
10650     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10651         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10652       Stop[x][y] = TRUE;
10653   }
10654
10655   CreateFieldExt(x, y, element, TRUE);
10656 }
10657
10658 static boolean ChangeElement(int x, int y, int element, int page)
10659 {
10660   struct ElementInfo *ei = &element_info[element];
10661   struct ElementChangeInfo *change = &ei->change_page[page];
10662   int ce_value = CustomValue[x][y];
10663   int ce_score = ei->collect_score;
10664   int target_element;
10665   int old_element = Tile[x][y];
10666
10667   // always use default change event to prevent running into a loop
10668   if (ChangeEvent[x][y] == -1)
10669     ChangeEvent[x][y] = CE_DELAY;
10670
10671   if (ChangeEvent[x][y] == CE_DELAY)
10672   {
10673     // reset actual trigger element, trigger player and action element
10674     change->actual_trigger_element = EL_EMPTY;
10675     change->actual_trigger_player = EL_EMPTY;
10676     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10677     change->actual_trigger_side = CH_SIDE_NONE;
10678     change->actual_trigger_ce_value = 0;
10679     change->actual_trigger_ce_score = 0;
10680   }
10681
10682   // do not change elements more than a specified maximum number of changes
10683   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10684     return FALSE;
10685
10686   ChangeCount[x][y]++;          // count number of changes in the same frame
10687
10688   if (change->explode)
10689   {
10690     Bang(x, y);
10691
10692     return TRUE;
10693   }
10694
10695   if (change->use_target_content)
10696   {
10697     boolean complete_replace = TRUE;
10698     boolean can_replace[3][3];
10699     int xx, yy;
10700
10701     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10702     {
10703       boolean is_empty;
10704       boolean is_walkable;
10705       boolean is_diggable;
10706       boolean is_collectible;
10707       boolean is_removable;
10708       boolean is_destructible;
10709       int ex = x + xx - 1;
10710       int ey = y + yy - 1;
10711       int content_element = change->target_content.e[xx][yy];
10712       int e;
10713
10714       can_replace[xx][yy] = TRUE;
10715
10716       if (ex == x && ey == y)   // do not check changing element itself
10717         continue;
10718
10719       if (content_element == EL_EMPTY_SPACE)
10720       {
10721         can_replace[xx][yy] = FALSE;    // do not replace border with space
10722
10723         continue;
10724       }
10725
10726       if (!IN_LEV_FIELD(ex, ey))
10727       {
10728         can_replace[xx][yy] = FALSE;
10729         complete_replace = FALSE;
10730
10731         continue;
10732       }
10733
10734       e = Tile[ex][ey];
10735
10736       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10737         e = MovingOrBlocked2Element(ex, ey);
10738
10739       is_empty = (IS_FREE(ex, ey) ||
10740                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10741
10742       is_walkable     = (is_empty || IS_WALKABLE(e));
10743       is_diggable     = (is_empty || IS_DIGGABLE(e));
10744       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10745       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10746       is_removable    = (is_diggable || is_collectible);
10747
10748       can_replace[xx][yy] =
10749         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10750           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10751           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10752           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10753           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10754           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10755          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10756
10757       if (!can_replace[xx][yy])
10758         complete_replace = FALSE;
10759     }
10760
10761     if (!change->only_if_complete || complete_replace)
10762     {
10763       boolean something_has_changed = FALSE;
10764
10765       if (change->only_if_complete && change->use_random_replace &&
10766           RND(100) < change->random_percentage)
10767         return FALSE;
10768
10769       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10770       {
10771         int ex = x + xx - 1;
10772         int ey = y + yy - 1;
10773         int content_element;
10774
10775         if (can_replace[xx][yy] && (!change->use_random_replace ||
10776                                     RND(100) < change->random_percentage))
10777         {
10778           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10779             RemoveMovingField(ex, ey);
10780
10781           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10782
10783           content_element = change->target_content.e[xx][yy];
10784           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10785                                               ce_value, ce_score);
10786
10787           CreateElementFromChange(ex, ey, target_element);
10788
10789           something_has_changed = TRUE;
10790
10791           // for symmetry reasons, freeze newly created border elements
10792           if (ex != x || ey != y)
10793             Stop[ex][ey] = TRUE;        // no more moving in this frame
10794         }
10795       }
10796
10797       if (something_has_changed)
10798       {
10799         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10800         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10801       }
10802     }
10803   }
10804   else
10805   {
10806     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10807                                         ce_value, ce_score);
10808
10809     if (element == EL_DIAGONAL_GROWING ||
10810         element == EL_DIAGONAL_SHRINKING)
10811     {
10812       target_element = Store[x][y];
10813
10814       Store[x][y] = EL_EMPTY;
10815     }
10816
10817     // special case: element changes to player (and may be kept if walkable)
10818     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10819       CreateElementFromChange(x, y, EL_EMPTY);
10820
10821     CreateElementFromChange(x, y, target_element);
10822
10823     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10824     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10825   }
10826
10827   // this uses direct change before indirect change
10828   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10829
10830   return TRUE;
10831 }
10832
10833 static void HandleElementChange(int x, int y, int page)
10834 {
10835   int element = MovingOrBlocked2Element(x, y);
10836   struct ElementInfo *ei = &element_info[element];
10837   struct ElementChangeInfo *change = &ei->change_page[page];
10838   boolean handle_action_before_change = FALSE;
10839
10840 #ifdef DEBUG
10841   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10842       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10843   {
10844     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10845           x, y, element, element_info[element].token_name);
10846     Debug("game:playing:HandleElementChange", "This should never happen!");
10847   }
10848 #endif
10849
10850   // this can happen with classic bombs on walkable, changing elements
10851   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10852   {
10853     return;
10854   }
10855
10856   if (ChangeDelay[x][y] == 0)           // initialize element change
10857   {
10858     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10859
10860     if (change->can_change)
10861     {
10862       // !!! not clear why graphic animation should be reset at all here !!!
10863       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10864       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10865
10866       /*
10867         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10868
10869         When using an animation frame delay of 1 (this only happens with
10870         "sp_zonk.moving.left/right" in the classic graphics), the default
10871         (non-moving) animation shows wrong animation frames (while the
10872         moving animation, like "sp_zonk.moving.left/right", is correct,
10873         so this graphical bug never shows up with the classic graphics).
10874         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10875         be drawn instead of the correct frames 0,1,2,3. This is caused by
10876         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10877         an element change: First when the change delay ("ChangeDelay[][]")
10878         counter has reached zero after decrementing, then a second time in
10879         the next frame (after "GfxFrame[][]" was already incremented) when
10880         "ChangeDelay[][]" is reset to the initial delay value again.
10881
10882         This causes frame 0 to be drawn twice, while the last frame won't
10883         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10884
10885         As some animations may already be cleverly designed around this bug
10886         (at least the "Snake Bite" snake tail animation does this), it cannot
10887         simply be fixed here without breaking such existing animations.
10888         Unfortunately, it cannot easily be detected if a graphics set was
10889         designed "before" or "after" the bug was fixed. As a workaround,
10890         a new graphics set option "game.graphics_engine_version" was added
10891         to be able to specify the game's major release version for which the
10892         graphics set was designed, which can then be used to decide if the
10893         bugfix should be used (version 4 and above) or not (version 3 or
10894         below, or if no version was specified at all, as with old sets).
10895
10896         (The wrong/fixed animation frames can be tested with the test level set
10897         "test_gfxframe" and level "000", which contains a specially prepared
10898         custom element at level position (x/y) == (11/9) which uses the zonk
10899         animation mentioned above. Using "game.graphics_engine_version: 4"
10900         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10901         This can also be seen from the debug output for this test element.)
10902       */
10903
10904       // when a custom element is about to change (for example by change delay),
10905       // do not reset graphic animation when the custom element is moving
10906       if (game.graphics_engine_version < 4 &&
10907           !IS_MOVING(x, y))
10908       {
10909         ResetGfxAnimation(x, y);
10910         ResetRandomAnimationValue(x, y);
10911       }
10912
10913       if (change->pre_change_function)
10914         change->pre_change_function(x, y);
10915     }
10916   }
10917
10918   ChangeDelay[x][y]--;
10919
10920   if (ChangeDelay[x][y] != 0)           // continue element change
10921   {
10922     if (change->can_change)
10923     {
10924       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10925
10926       if (IS_ANIMATED(graphic))
10927         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10928
10929       if (change->change_function)
10930         change->change_function(x, y);
10931     }
10932   }
10933   else                                  // finish element change
10934   {
10935     if (ChangePage[x][y] != -1)         // remember page from delayed change
10936     {
10937       page = ChangePage[x][y];
10938       ChangePage[x][y] = -1;
10939
10940       change = &ei->change_page[page];
10941     }
10942
10943     if (IS_MOVING(x, y))                // never change a running system ;-)
10944     {
10945       ChangeDelay[x][y] = 1;            // try change after next move step
10946       ChangePage[x][y] = page;          // remember page to use for change
10947
10948       return;
10949     }
10950
10951     // special case: set new level random seed before changing element
10952     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10953       handle_action_before_change = TRUE;
10954
10955     if (change->has_action && handle_action_before_change)
10956       ExecuteCustomElementAction(x, y, element, page);
10957
10958     if (change->can_change)
10959     {
10960       if (ChangeElement(x, y, element, page))
10961       {
10962         if (change->post_change_function)
10963           change->post_change_function(x, y);
10964       }
10965     }
10966
10967     if (change->has_action && !handle_action_before_change)
10968       ExecuteCustomElementAction(x, y, element, page);
10969   }
10970 }
10971
10972 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10973                                               int trigger_element,
10974                                               int trigger_event,
10975                                               int trigger_player,
10976                                               int trigger_side,
10977                                               int trigger_page)
10978 {
10979   boolean change_done_any = FALSE;
10980   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10981   int i;
10982
10983   if (!(trigger_events[trigger_element][trigger_event]))
10984     return FALSE;
10985
10986   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10987
10988   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10989   {
10990     int element = EL_CUSTOM_START + i;
10991     boolean change_done = FALSE;
10992     int p;
10993
10994     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10995         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10996       continue;
10997
10998     for (p = 0; p < element_info[element].num_change_pages; p++)
10999     {
11000       struct ElementChangeInfo *change = &element_info[element].change_page[p];
11001
11002       if (change->can_change_or_has_action &&
11003           change->has_event[trigger_event] &&
11004           change->trigger_side & trigger_side &&
11005           change->trigger_player & trigger_player &&
11006           change->trigger_page & trigger_page_bits &&
11007           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11008       {
11009         change->actual_trigger_element = trigger_element;
11010         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11011         change->actual_trigger_player_bits = trigger_player;
11012         change->actual_trigger_side = trigger_side;
11013         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11014         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11015
11016         if ((change->can_change && !change_done) || change->has_action)
11017         {
11018           int x, y;
11019
11020           SCAN_PLAYFIELD(x, y)
11021           {
11022             if (Tile[x][y] == element)
11023             {
11024               if (change->can_change && !change_done)
11025               {
11026                 // if element already changed in this frame, not only prevent
11027                 // another element change (checked in ChangeElement()), but
11028                 // also prevent additional element actions for this element
11029
11030                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11031                     !level.use_action_after_change_bug)
11032                   continue;
11033
11034                 ChangeDelay[x][y] = 1;
11035                 ChangeEvent[x][y] = trigger_event;
11036
11037                 HandleElementChange(x, y, p);
11038               }
11039               else if (change->has_action)
11040               {
11041                 // if element already changed in this frame, not only prevent
11042                 // another element change (checked in ChangeElement()), but
11043                 // also prevent additional element actions for this element
11044
11045                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11046                     !level.use_action_after_change_bug)
11047                   continue;
11048
11049                 ExecuteCustomElementAction(x, y, element, p);
11050                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11051               }
11052             }
11053           }
11054
11055           if (change->can_change)
11056           {
11057             change_done = TRUE;
11058             change_done_any = TRUE;
11059           }
11060         }
11061       }
11062     }
11063   }
11064
11065   RECURSION_LOOP_DETECTION_END();
11066
11067   return change_done_any;
11068 }
11069
11070 static boolean CheckElementChangeExt(int x, int y,
11071                                      int element,
11072                                      int trigger_element,
11073                                      int trigger_event,
11074                                      int trigger_player,
11075                                      int trigger_side)
11076 {
11077   boolean change_done = FALSE;
11078   int p;
11079
11080   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11081       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11082     return FALSE;
11083
11084   if (Tile[x][y] == EL_BLOCKED)
11085   {
11086     Blocked2Moving(x, y, &x, &y);
11087     element = Tile[x][y];
11088   }
11089
11090   // check if element has already changed or is about to change after moving
11091   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11092        Tile[x][y] != element) ||
11093
11094       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11095        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11096         ChangePage[x][y] != -1)))
11097     return FALSE;
11098
11099   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11100
11101   for (p = 0; p < element_info[element].num_change_pages; p++)
11102   {
11103     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11104
11105     /* check trigger element for all events where the element that is checked
11106        for changing interacts with a directly adjacent element -- this is
11107        different to element changes that affect other elements to change on the
11108        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11109     boolean check_trigger_element =
11110       (trigger_event == CE_NEXT_TO_X ||
11111        trigger_event == CE_TOUCHING_X ||
11112        trigger_event == CE_HITTING_X ||
11113        trigger_event == CE_HIT_BY_X ||
11114        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11115
11116     if (change->can_change_or_has_action &&
11117         change->has_event[trigger_event] &&
11118         change->trigger_side & trigger_side &&
11119         change->trigger_player & trigger_player &&
11120         (!check_trigger_element ||
11121          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11122     {
11123       change->actual_trigger_element = trigger_element;
11124       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11125       change->actual_trigger_player_bits = trigger_player;
11126       change->actual_trigger_side = trigger_side;
11127       change->actual_trigger_ce_value = CustomValue[x][y];
11128       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11129
11130       // special case: trigger element not at (x,y) position for some events
11131       if (check_trigger_element)
11132       {
11133         static struct
11134         {
11135           int dx, dy;
11136         } move_xy[] =
11137           {
11138             {  0,  0 },
11139             { -1,  0 },
11140             { +1,  0 },
11141             {  0,  0 },
11142             {  0, -1 },
11143             {  0,  0 }, { 0, 0 }, { 0, 0 },
11144             {  0, +1 }
11145           };
11146
11147         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11148         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11149
11150         change->actual_trigger_ce_value = CustomValue[xx][yy];
11151         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11152       }
11153
11154       if (change->can_change && !change_done)
11155       {
11156         ChangeDelay[x][y] = 1;
11157         ChangeEvent[x][y] = trigger_event;
11158
11159         HandleElementChange(x, y, p);
11160
11161         change_done = TRUE;
11162       }
11163       else if (change->has_action)
11164       {
11165         ExecuteCustomElementAction(x, y, element, p);
11166         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11167       }
11168     }
11169   }
11170
11171   RECURSION_LOOP_DETECTION_END();
11172
11173   return change_done;
11174 }
11175
11176 static void PlayPlayerSound(struct PlayerInfo *player)
11177 {
11178   int jx = player->jx, jy = player->jy;
11179   int sound_element = player->artwork_element;
11180   int last_action = player->last_action_waiting;
11181   int action = player->action_waiting;
11182
11183   if (player->is_waiting)
11184   {
11185     if (action != last_action)
11186       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11187     else
11188       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11189   }
11190   else
11191   {
11192     if (action != last_action)
11193       StopSound(element_info[sound_element].sound[last_action]);
11194
11195     if (last_action == ACTION_SLEEPING)
11196       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11197   }
11198 }
11199
11200 static void PlayAllPlayersSound(void)
11201 {
11202   int i;
11203
11204   for (i = 0; i < MAX_PLAYERS; i++)
11205     if (stored_player[i].active)
11206       PlayPlayerSound(&stored_player[i]);
11207 }
11208
11209 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11210 {
11211   boolean last_waiting = player->is_waiting;
11212   int move_dir = player->MovDir;
11213
11214   player->dir_waiting = move_dir;
11215   player->last_action_waiting = player->action_waiting;
11216
11217   if (is_waiting)
11218   {
11219     if (!last_waiting)          // not waiting -> waiting
11220     {
11221       player->is_waiting = TRUE;
11222
11223       player->frame_counter_bored =
11224         FrameCounter +
11225         game.player_boring_delay_fixed +
11226         GetSimpleRandom(game.player_boring_delay_random);
11227       player->frame_counter_sleeping =
11228         FrameCounter +
11229         game.player_sleeping_delay_fixed +
11230         GetSimpleRandom(game.player_sleeping_delay_random);
11231
11232       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11233     }
11234
11235     if (game.player_sleeping_delay_fixed +
11236         game.player_sleeping_delay_random > 0 &&
11237         player->anim_delay_counter == 0 &&
11238         player->post_delay_counter == 0 &&
11239         FrameCounter >= player->frame_counter_sleeping)
11240       player->is_sleeping = TRUE;
11241     else if (game.player_boring_delay_fixed +
11242              game.player_boring_delay_random > 0 &&
11243              FrameCounter >= player->frame_counter_bored)
11244       player->is_bored = TRUE;
11245
11246     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11247                               player->is_bored ? ACTION_BORING :
11248                               ACTION_WAITING);
11249
11250     if (player->is_sleeping && player->use_murphy)
11251     {
11252       // special case for sleeping Murphy when leaning against non-free tile
11253
11254       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11255           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11256            !IS_MOVING(player->jx - 1, player->jy)))
11257         move_dir = MV_LEFT;
11258       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11259                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11260                 !IS_MOVING(player->jx + 1, player->jy)))
11261         move_dir = MV_RIGHT;
11262       else
11263         player->is_sleeping = FALSE;
11264
11265       player->dir_waiting = move_dir;
11266     }
11267
11268     if (player->is_sleeping)
11269     {
11270       if (player->num_special_action_sleeping > 0)
11271       {
11272         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11273         {
11274           int last_special_action = player->special_action_sleeping;
11275           int num_special_action = player->num_special_action_sleeping;
11276           int special_action =
11277             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11278              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11279              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11280              last_special_action + 1 : ACTION_SLEEPING);
11281           int special_graphic =
11282             el_act_dir2img(player->artwork_element, special_action, move_dir);
11283
11284           player->anim_delay_counter =
11285             graphic_info[special_graphic].anim_delay_fixed +
11286             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11287           player->post_delay_counter =
11288             graphic_info[special_graphic].post_delay_fixed +
11289             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11290
11291           player->special_action_sleeping = special_action;
11292         }
11293
11294         if (player->anim_delay_counter > 0)
11295         {
11296           player->action_waiting = player->special_action_sleeping;
11297           player->anim_delay_counter--;
11298         }
11299         else if (player->post_delay_counter > 0)
11300         {
11301           player->post_delay_counter--;
11302         }
11303       }
11304     }
11305     else if (player->is_bored)
11306     {
11307       if (player->num_special_action_bored > 0)
11308       {
11309         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11310         {
11311           int special_action =
11312             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11313           int special_graphic =
11314             el_act_dir2img(player->artwork_element, special_action, move_dir);
11315
11316           player->anim_delay_counter =
11317             graphic_info[special_graphic].anim_delay_fixed +
11318             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11319           player->post_delay_counter =
11320             graphic_info[special_graphic].post_delay_fixed +
11321             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11322
11323           player->special_action_bored = special_action;
11324         }
11325
11326         if (player->anim_delay_counter > 0)
11327         {
11328           player->action_waiting = player->special_action_bored;
11329           player->anim_delay_counter--;
11330         }
11331         else if (player->post_delay_counter > 0)
11332         {
11333           player->post_delay_counter--;
11334         }
11335       }
11336     }
11337   }
11338   else if (last_waiting)        // waiting -> not waiting
11339   {
11340     player->is_waiting = FALSE;
11341     player->is_bored = FALSE;
11342     player->is_sleeping = FALSE;
11343
11344     player->frame_counter_bored = -1;
11345     player->frame_counter_sleeping = -1;
11346
11347     player->anim_delay_counter = 0;
11348     player->post_delay_counter = 0;
11349
11350     player->dir_waiting = player->MovDir;
11351     player->action_waiting = ACTION_DEFAULT;
11352
11353     player->special_action_bored = ACTION_DEFAULT;
11354     player->special_action_sleeping = ACTION_DEFAULT;
11355   }
11356 }
11357
11358 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11359 {
11360   if ((!player->is_moving  && player->was_moving) ||
11361       (player->MovPos == 0 && player->was_moving) ||
11362       (player->is_snapping && !player->was_snapping) ||
11363       (player->is_dropping && !player->was_dropping))
11364   {
11365     if (!CheckSaveEngineSnapshotToList())
11366       return;
11367
11368     player->was_moving = FALSE;
11369     player->was_snapping = TRUE;
11370     player->was_dropping = TRUE;
11371   }
11372   else
11373   {
11374     if (player->is_moving)
11375       player->was_moving = TRUE;
11376
11377     if (!player->is_snapping)
11378       player->was_snapping = FALSE;
11379
11380     if (!player->is_dropping)
11381       player->was_dropping = FALSE;
11382   }
11383
11384   static struct MouseActionInfo mouse_action_last = { 0 };
11385   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11386   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11387
11388   if (new_released)
11389     CheckSaveEngineSnapshotToList();
11390
11391   mouse_action_last = mouse_action;
11392 }
11393
11394 static void CheckSingleStepMode(struct PlayerInfo *player)
11395 {
11396   if (tape.single_step && tape.recording && !tape.pausing)
11397   {
11398     // as it is called "single step mode", just return to pause mode when the
11399     // player stopped moving after one tile (or never starts moving at all)
11400     // (reverse logic needed here in case single step mode used in team mode)
11401     if (player->is_moving ||
11402         player->is_pushing ||
11403         player->is_dropping_pressed ||
11404         player->effective_mouse_action.button)
11405       game.enter_single_step_mode = FALSE;
11406   }
11407
11408   CheckSaveEngineSnapshot(player);
11409 }
11410
11411 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11412 {
11413   int left      = player_action & JOY_LEFT;
11414   int right     = player_action & JOY_RIGHT;
11415   int up        = player_action & JOY_UP;
11416   int down      = player_action & JOY_DOWN;
11417   int button1   = player_action & JOY_BUTTON_1;
11418   int button2   = player_action & JOY_BUTTON_2;
11419   int dx        = (left ? -1 : right ? 1 : 0);
11420   int dy        = (up   ? -1 : down  ? 1 : 0);
11421
11422   if (!player->active || tape.pausing)
11423     return 0;
11424
11425   if (player_action)
11426   {
11427     if (button1)
11428       SnapField(player, dx, dy);
11429     else
11430     {
11431       if (button2)
11432         DropElement(player);
11433
11434       MovePlayer(player, dx, dy);
11435     }
11436
11437     CheckSingleStepMode(player);
11438
11439     SetPlayerWaiting(player, FALSE);
11440
11441     return player_action;
11442   }
11443   else
11444   {
11445     // no actions for this player (no input at player's configured device)
11446
11447     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11448     SnapField(player, 0, 0);
11449     CheckGravityMovementWhenNotMoving(player);
11450
11451     if (player->MovPos == 0)
11452       SetPlayerWaiting(player, TRUE);
11453
11454     if (player->MovPos == 0)    // needed for tape.playing
11455       player->is_moving = FALSE;
11456
11457     player->is_dropping = FALSE;
11458     player->is_dropping_pressed = FALSE;
11459     player->drop_pressed_delay = 0;
11460
11461     CheckSingleStepMode(player);
11462
11463     return 0;
11464   }
11465 }
11466
11467 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11468                                          byte *tape_action)
11469 {
11470   if (!tape.use_mouse_actions)
11471     return;
11472
11473   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11474   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11475   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11476 }
11477
11478 static void SetTapeActionFromMouseAction(byte *tape_action,
11479                                          struct MouseActionInfo *mouse_action)
11480 {
11481   if (!tape.use_mouse_actions)
11482     return;
11483
11484   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11485   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11486   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11487 }
11488
11489 static void CheckLevelSolved(void)
11490 {
11491   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11492   {
11493     if (game_em.level_solved &&
11494         !game_em.game_over)                             // game won
11495     {
11496       LevelSolved();
11497
11498       game_em.game_over = TRUE;
11499
11500       game.all_players_gone = TRUE;
11501     }
11502
11503     if (game_em.game_over)                              // game lost
11504       game.all_players_gone = TRUE;
11505   }
11506   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11507   {
11508     if (game_sp.level_solved &&
11509         !game_sp.game_over)                             // game won
11510     {
11511       LevelSolved();
11512
11513       game_sp.game_over = TRUE;
11514
11515       game.all_players_gone = TRUE;
11516     }
11517
11518     if (game_sp.game_over)                              // game lost
11519       game.all_players_gone = TRUE;
11520   }
11521   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11522   {
11523     if (game_mm.level_solved &&
11524         !game_mm.game_over)                             // game won
11525     {
11526       LevelSolved();
11527
11528       game_mm.game_over = TRUE;
11529
11530       game.all_players_gone = TRUE;
11531     }
11532
11533     if (game_mm.game_over)                              // game lost
11534       game.all_players_gone = TRUE;
11535   }
11536 }
11537
11538 static void CheckLevelTime_StepCounter(void)
11539 {
11540   int i;
11541
11542   TimePlayed++;
11543
11544   if (TimeLeft > 0)
11545   {
11546     TimeLeft--;
11547
11548     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11549       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11550
11551     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11552
11553     DisplayGameControlValues();
11554
11555     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11556       for (i = 0; i < MAX_PLAYERS; i++)
11557         KillPlayer(&stored_player[i]);
11558   }
11559   else if (game.no_level_time_limit && !game.all_players_gone)
11560   {
11561     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11562
11563     DisplayGameControlValues();
11564   }
11565 }
11566
11567 static void CheckLevelTime(void)
11568 {
11569   int i;
11570
11571   if (TimeFrames >= FRAMES_PER_SECOND)
11572   {
11573     TimeFrames = 0;
11574     TapeTime++;
11575
11576     for (i = 0; i < MAX_PLAYERS; i++)
11577     {
11578       struct PlayerInfo *player = &stored_player[i];
11579
11580       if (SHIELD_ON(player))
11581       {
11582         player->shield_normal_time_left--;
11583
11584         if (player->shield_deadly_time_left > 0)
11585           player->shield_deadly_time_left--;
11586       }
11587     }
11588
11589     if (!game.LevelSolved && !level.use_step_counter)
11590     {
11591       TimePlayed++;
11592
11593       if (TimeLeft > 0)
11594       {
11595         TimeLeft--;
11596
11597         if (TimeLeft <= 10 && game.time_limit)
11598           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11599
11600         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11601            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11602
11603         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11604
11605         if (!TimeLeft && game.time_limit)
11606         {
11607           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11608             game_em.lev->killed_out_of_time = TRUE;
11609           else
11610             for (i = 0; i < MAX_PLAYERS; i++)
11611               KillPlayer(&stored_player[i]);
11612         }
11613       }
11614       else if (game.no_level_time_limit && !game.all_players_gone)
11615       {
11616         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11617       }
11618
11619       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11620     }
11621
11622     if (tape.recording || tape.playing)
11623       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11624   }
11625
11626   if (tape.recording || tape.playing)
11627     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11628
11629   UpdateAndDisplayGameControlValues();
11630 }
11631
11632 void AdvanceFrameAndPlayerCounters(int player_nr)
11633 {
11634   int i;
11635
11636   // advance frame counters (global frame counter and time frame counter)
11637   FrameCounter++;
11638   TimeFrames++;
11639
11640   // advance player counters (counters for move delay, move animation etc.)
11641   for (i = 0; i < MAX_PLAYERS; i++)
11642   {
11643     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11644     int move_delay_value = stored_player[i].move_delay_value;
11645     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11646
11647     if (!advance_player_counters)       // not all players may be affected
11648       continue;
11649
11650     if (move_frames == 0)       // less than one move per game frame
11651     {
11652       int stepsize = TILEX / move_delay_value;
11653       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11654       int count = (stored_player[i].is_moving ?
11655                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11656
11657       if (count % delay == 0)
11658         move_frames = 1;
11659     }
11660
11661     stored_player[i].Frame += move_frames;
11662
11663     if (stored_player[i].MovPos != 0)
11664       stored_player[i].StepFrame += move_frames;
11665
11666     if (stored_player[i].move_delay > 0)
11667       stored_player[i].move_delay--;
11668
11669     // due to bugs in previous versions, counter must count up, not down
11670     if (stored_player[i].push_delay != -1)
11671       stored_player[i].push_delay++;
11672
11673     if (stored_player[i].drop_delay > 0)
11674       stored_player[i].drop_delay--;
11675
11676     if (stored_player[i].is_dropping_pressed)
11677       stored_player[i].drop_pressed_delay++;
11678   }
11679 }
11680
11681 void StartGameActions(boolean init_network_game, boolean record_tape,
11682                       int random_seed)
11683 {
11684   unsigned int new_random_seed = InitRND(random_seed);
11685
11686   if (record_tape)
11687     TapeStartRecording(new_random_seed);
11688
11689   if (setup.auto_pause_on_start && !tape.pausing)
11690     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11691
11692   if (init_network_game)
11693   {
11694     SendToServer_LevelFile();
11695     SendToServer_StartPlaying();
11696
11697     return;
11698   }
11699
11700   InitGame();
11701 }
11702
11703 static void GameActionsExt(void)
11704 {
11705 #if 0
11706   static unsigned int game_frame_delay = 0;
11707 #endif
11708   unsigned int game_frame_delay_value;
11709   byte *recorded_player_action;
11710   byte summarized_player_action = 0;
11711   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11712   int i;
11713
11714   // detect endless loops, caused by custom element programming
11715   if (recursion_loop_detected && recursion_loop_depth == 0)
11716   {
11717     char *message = getStringCat3("Internal Error! Element ",
11718                                   EL_NAME(recursion_loop_element),
11719                                   " caused endless loop! Quit the game?");
11720
11721     Warn("element '%s' caused endless loop in game engine",
11722          EL_NAME(recursion_loop_element));
11723
11724     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11725
11726     recursion_loop_detected = FALSE;    // if game should be continued
11727
11728     free(message);
11729
11730     return;
11731   }
11732
11733   if (game.restart_level)
11734     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11735
11736   CheckLevelSolved();
11737
11738   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11739     GameWon();
11740
11741   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11742     TapeStop();
11743
11744   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11745     return;
11746
11747   game_frame_delay_value =
11748     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11749
11750   if (tape.playing && tape.warp_forward && !tape.pausing)
11751     game_frame_delay_value = 0;
11752
11753   SetVideoFrameDelay(game_frame_delay_value);
11754
11755   // (de)activate virtual buttons depending on current game status
11756   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11757   {
11758     if (game.all_players_gone)  // if no players there to be controlled anymore
11759       SetOverlayActive(FALSE);
11760     else if (!tape.playing)     // if game continues after tape stopped playing
11761       SetOverlayActive(TRUE);
11762   }
11763
11764 #if 0
11765 #if 0
11766   // ---------- main game synchronization point ----------
11767
11768   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11769
11770   Debug("game:playing:skip", "skip == %d", skip);
11771
11772 #else
11773   // ---------- main game synchronization point ----------
11774
11775   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11776 #endif
11777 #endif
11778
11779   if (network_playing && !network_player_action_received)
11780   {
11781     // try to get network player actions in time
11782
11783     // last chance to get network player actions without main loop delay
11784     HandleNetworking();
11785
11786     // game was quit by network peer
11787     if (game_status != GAME_MODE_PLAYING)
11788       return;
11789
11790     // check if network player actions still missing and game still running
11791     if (!network_player_action_received && !checkGameEnded())
11792       return;           // failed to get network player actions in time
11793
11794     // do not yet reset "network_player_action_received" (for tape.pausing)
11795   }
11796
11797   if (tape.pausing)
11798     return;
11799
11800   // at this point we know that we really continue executing the game
11801
11802   network_player_action_received = FALSE;
11803
11804   // when playing tape, read previously recorded player input from tape data
11805   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11806
11807   local_player->effective_mouse_action = local_player->mouse_action;
11808
11809   if (recorded_player_action != NULL)
11810     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11811                                  recorded_player_action);
11812
11813   // TapePlayAction() may return NULL when toggling to "pause before death"
11814   if (tape.pausing)
11815     return;
11816
11817   if (tape.set_centered_player)
11818   {
11819     game.centered_player_nr_next = tape.centered_player_nr_next;
11820     game.set_centered_player = TRUE;
11821   }
11822
11823   for (i = 0; i < MAX_PLAYERS; i++)
11824   {
11825     summarized_player_action |= stored_player[i].action;
11826
11827     if (!network_playing && (game.team_mode || tape.playing))
11828       stored_player[i].effective_action = stored_player[i].action;
11829   }
11830
11831   if (network_playing && !checkGameEnded())
11832     SendToServer_MovePlayer(summarized_player_action);
11833
11834   // summarize all actions at local players mapped input device position
11835   // (this allows using different input devices in single player mode)
11836   if (!network.enabled && !game.team_mode)
11837     stored_player[map_player_action[local_player->index_nr]].effective_action =
11838       summarized_player_action;
11839
11840   // summarize all actions at centered player in local team mode
11841   if (tape.recording &&
11842       setup.team_mode && !network.enabled &&
11843       setup.input_on_focus &&
11844       game.centered_player_nr != -1)
11845   {
11846     for (i = 0; i < MAX_PLAYERS; i++)
11847       stored_player[map_player_action[i]].effective_action =
11848         (i == game.centered_player_nr ? summarized_player_action : 0);
11849   }
11850
11851   if (recorded_player_action != NULL)
11852     for (i = 0; i < MAX_PLAYERS; i++)
11853       stored_player[i].effective_action = recorded_player_action[i];
11854
11855   for (i = 0; i < MAX_PLAYERS; i++)
11856   {
11857     tape_action[i] = stored_player[i].effective_action;
11858
11859     /* (this may happen in the RND game engine if a player was not present on
11860        the playfield on level start, but appeared later from a custom element */
11861     if (setup.team_mode &&
11862         tape.recording &&
11863         tape_action[i] &&
11864         !tape.player_participates[i])
11865       tape.player_participates[i] = TRUE;
11866   }
11867
11868   SetTapeActionFromMouseAction(tape_action,
11869                                &local_player->effective_mouse_action);
11870
11871   // only record actions from input devices, but not programmed actions
11872   if (tape.recording)
11873     TapeRecordAction(tape_action);
11874
11875   // remember if game was played (especially after tape stopped playing)
11876   if (!tape.playing && summarized_player_action)
11877     game.GamePlayed = TRUE;
11878
11879 #if USE_NEW_PLAYER_ASSIGNMENTS
11880   // !!! also map player actions in single player mode !!!
11881   // if (game.team_mode)
11882   if (1)
11883   {
11884     byte mapped_action[MAX_PLAYERS];
11885
11886 #if DEBUG_PLAYER_ACTIONS
11887     for (i = 0; i < MAX_PLAYERS; i++)
11888       DebugContinued("", "%d, ", stored_player[i].effective_action);
11889 #endif
11890
11891     for (i = 0; i < MAX_PLAYERS; i++)
11892       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11893
11894     for (i = 0; i < MAX_PLAYERS; i++)
11895       stored_player[i].effective_action = mapped_action[i];
11896
11897 #if DEBUG_PLAYER_ACTIONS
11898     DebugContinued("", "=> ");
11899     for (i = 0; i < MAX_PLAYERS; i++)
11900       DebugContinued("", "%d, ", stored_player[i].effective_action);
11901     DebugContinued("game:playing:player", "\n");
11902 #endif
11903   }
11904 #if DEBUG_PLAYER_ACTIONS
11905   else
11906   {
11907     for (i = 0; i < MAX_PLAYERS; i++)
11908       DebugContinued("", "%d, ", stored_player[i].effective_action);
11909     DebugContinued("game:playing:player", "\n");
11910   }
11911 #endif
11912 #endif
11913
11914   for (i = 0; i < MAX_PLAYERS; i++)
11915   {
11916     // allow engine snapshot in case of changed movement attempt
11917     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11918         (stored_player[i].effective_action & KEY_MOTION))
11919       game.snapshot.changed_action = TRUE;
11920
11921     // allow engine snapshot in case of snapping/dropping attempt
11922     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11923         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11924       game.snapshot.changed_action = TRUE;
11925
11926     game.snapshot.last_action[i] = stored_player[i].effective_action;
11927   }
11928
11929   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11930   {
11931     GameActions_EM_Main();
11932   }
11933   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11934   {
11935     GameActions_SP_Main();
11936   }
11937   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11938   {
11939     GameActions_MM_Main();
11940   }
11941   else
11942   {
11943     GameActions_RND_Main();
11944   }
11945
11946   BlitScreenToBitmap(backbuffer);
11947
11948   CheckLevelSolved();
11949   CheckLevelTime();
11950
11951   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11952
11953   if (global.show_frames_per_second)
11954   {
11955     static unsigned int fps_counter = 0;
11956     static int fps_frames = 0;
11957     unsigned int fps_delay_ms = Counter() - fps_counter;
11958
11959     fps_frames++;
11960
11961     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11962     {
11963       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11964
11965       fps_frames = 0;
11966       fps_counter = Counter();
11967
11968       // always draw FPS to screen after FPS value was updated
11969       redraw_mask |= REDRAW_FPS;
11970     }
11971
11972     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11973     if (GetDrawDeactivationMask() == REDRAW_NONE)
11974       redraw_mask |= REDRAW_FPS;
11975   }
11976 }
11977
11978 static void GameActions_CheckSaveEngineSnapshot(void)
11979 {
11980   if (!game.snapshot.save_snapshot)
11981     return;
11982
11983   // clear flag for saving snapshot _before_ saving snapshot
11984   game.snapshot.save_snapshot = FALSE;
11985
11986   SaveEngineSnapshotToList();
11987 }
11988
11989 void GameActions(void)
11990 {
11991   GameActionsExt();
11992
11993   GameActions_CheckSaveEngineSnapshot();
11994 }
11995
11996 void GameActions_EM_Main(void)
11997 {
11998   byte effective_action[MAX_PLAYERS];
11999   int i;
12000
12001   for (i = 0; i < MAX_PLAYERS; i++)
12002     effective_action[i] = stored_player[i].effective_action;
12003
12004   GameActions_EM(effective_action);
12005 }
12006
12007 void GameActions_SP_Main(void)
12008 {
12009   byte effective_action[MAX_PLAYERS];
12010   int i;
12011
12012   for (i = 0; i < MAX_PLAYERS; i++)
12013     effective_action[i] = stored_player[i].effective_action;
12014
12015   GameActions_SP(effective_action);
12016
12017   for (i = 0; i < MAX_PLAYERS; i++)
12018   {
12019     if (stored_player[i].force_dropping)
12020       stored_player[i].action |= KEY_BUTTON_DROP;
12021
12022     stored_player[i].force_dropping = FALSE;
12023   }
12024 }
12025
12026 void GameActions_MM_Main(void)
12027 {
12028   GameActions_MM(local_player->effective_mouse_action);
12029 }
12030
12031 void GameActions_RND_Main(void)
12032 {
12033   GameActions_RND();
12034 }
12035
12036 void GameActions_RND(void)
12037 {
12038   static struct MouseActionInfo mouse_action_last = { 0 };
12039   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12040   int magic_wall_x = 0, magic_wall_y = 0;
12041   int i, x, y, element, graphic, last_gfx_frame;
12042
12043   InitPlayfieldScanModeVars();
12044
12045   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12046   {
12047     SCAN_PLAYFIELD(x, y)
12048     {
12049       ChangeCount[x][y] = 0;
12050       ChangeEvent[x][y] = -1;
12051     }
12052   }
12053
12054   if (game.set_centered_player)
12055   {
12056     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12057
12058     // switching to "all players" only possible if all players fit to screen
12059     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12060     {
12061       game.centered_player_nr_next = game.centered_player_nr;
12062       game.set_centered_player = FALSE;
12063     }
12064
12065     // do not switch focus to non-existing (or non-active) player
12066     if (game.centered_player_nr_next >= 0 &&
12067         !stored_player[game.centered_player_nr_next].active)
12068     {
12069       game.centered_player_nr_next = game.centered_player_nr;
12070       game.set_centered_player = FALSE;
12071     }
12072   }
12073
12074   if (game.set_centered_player &&
12075       ScreenMovPos == 0)        // screen currently aligned at tile position
12076   {
12077     int sx, sy;
12078
12079     if (game.centered_player_nr_next == -1)
12080     {
12081       setScreenCenteredToAllPlayers(&sx, &sy);
12082     }
12083     else
12084     {
12085       sx = stored_player[game.centered_player_nr_next].jx;
12086       sy = stored_player[game.centered_player_nr_next].jy;
12087     }
12088
12089     game.centered_player_nr = game.centered_player_nr_next;
12090     game.set_centered_player = FALSE;
12091
12092     DrawRelocateScreen(0, 0, sx, sy, TRUE, setup.quick_switch);
12093     DrawGameDoorValues();
12094   }
12095
12096   // check single step mode (set flag and clear again if any player is active)
12097   game.enter_single_step_mode =
12098     (tape.single_step && tape.recording && !tape.pausing);
12099
12100   for (i = 0; i < MAX_PLAYERS; i++)
12101   {
12102     int actual_player_action = stored_player[i].effective_action;
12103
12104 #if 1
12105     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12106        - rnd_equinox_tetrachloride 048
12107        - rnd_equinox_tetrachloride_ii 096
12108        - rnd_emanuel_schmieg 002
12109        - doctor_sloan_ww 001, 020
12110     */
12111     if (stored_player[i].MovPos == 0)
12112       CheckGravityMovement(&stored_player[i]);
12113 #endif
12114
12115     // overwrite programmed action with tape action
12116     if (stored_player[i].programmed_action)
12117       actual_player_action = stored_player[i].programmed_action;
12118
12119     PlayerActions(&stored_player[i], actual_player_action);
12120
12121     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12122   }
12123
12124   // single step pause mode may already have been toggled by "ScrollPlayer()"
12125   if (game.enter_single_step_mode && !tape.pausing)
12126     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12127
12128   ScrollScreen(NULL, SCROLL_GO_ON);
12129
12130   /* for backwards compatibility, the following code emulates a fixed bug that
12131      occured when pushing elements (causing elements that just made their last
12132      pushing step to already (if possible) make their first falling step in the
12133      same game frame, which is bad); this code is also needed to use the famous
12134      "spring push bug" which is used in older levels and might be wanted to be
12135      used also in newer levels, but in this case the buggy pushing code is only
12136      affecting the "spring" element and no other elements */
12137
12138   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12139   {
12140     for (i = 0; i < MAX_PLAYERS; i++)
12141     {
12142       struct PlayerInfo *player = &stored_player[i];
12143       int x = player->jx;
12144       int y = player->jy;
12145
12146       if (player->active && player->is_pushing && player->is_moving &&
12147           IS_MOVING(x, y) &&
12148           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12149            Tile[x][y] == EL_SPRING))
12150       {
12151         ContinueMoving(x, y);
12152
12153         // continue moving after pushing (this is actually a bug)
12154         if (!IS_MOVING(x, y))
12155           Stop[x][y] = FALSE;
12156       }
12157     }
12158   }
12159
12160   SCAN_PLAYFIELD(x, y)
12161   {
12162     Last[x][y] = Tile[x][y];
12163
12164     ChangeCount[x][y] = 0;
12165     ChangeEvent[x][y] = -1;
12166
12167     // this must be handled before main playfield loop
12168     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12169     {
12170       MovDelay[x][y]--;
12171       if (MovDelay[x][y] <= 0)
12172         RemoveField(x, y);
12173     }
12174
12175     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12176     {
12177       MovDelay[x][y]--;
12178       if (MovDelay[x][y] <= 0)
12179       {
12180         int element = Store[x][y];
12181         int move_direction = MovDir[x][y];
12182         int player_index_bit = Store2[x][y];
12183
12184         Store[x][y] = 0;
12185         Store2[x][y] = 0;
12186
12187         RemoveField(x, y);
12188         TEST_DrawLevelField(x, y);
12189
12190         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12191
12192         if (IS_ENVELOPE(element))
12193           local_player->show_envelope = element;
12194       }
12195     }
12196
12197 #if DEBUG
12198     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12199     {
12200       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12201             x, y);
12202       Debug("game:playing:GameActions_RND", "This should never happen!");
12203
12204       ChangePage[x][y] = -1;
12205     }
12206 #endif
12207
12208     Stop[x][y] = FALSE;
12209     if (WasJustMoving[x][y] > 0)
12210       WasJustMoving[x][y]--;
12211     if (WasJustFalling[x][y] > 0)
12212       WasJustFalling[x][y]--;
12213     if (CheckCollision[x][y] > 0)
12214       CheckCollision[x][y]--;
12215     if (CheckImpact[x][y] > 0)
12216       CheckImpact[x][y]--;
12217
12218     GfxFrame[x][y]++;
12219
12220     /* reset finished pushing action (not done in ContinueMoving() to allow
12221        continuous pushing animation for elements with zero push delay) */
12222     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12223     {
12224       ResetGfxAnimation(x, y);
12225       TEST_DrawLevelField(x, y);
12226     }
12227
12228 #if DEBUG
12229     if (IS_BLOCKED(x, y))
12230     {
12231       int oldx, oldy;
12232
12233       Blocked2Moving(x, y, &oldx, &oldy);
12234       if (!IS_MOVING(oldx, oldy))
12235       {
12236         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12237         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12238         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12239         Debug("game:playing:GameActions_RND", "This should never happen!");
12240       }
12241     }
12242 #endif
12243   }
12244
12245   if (mouse_action.button)
12246   {
12247     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12248     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12249
12250     x = mouse_action.lx;
12251     y = mouse_action.ly;
12252     element = Tile[x][y];
12253
12254     if (new_button)
12255     {
12256       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12257       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12258                                          ch_button);
12259     }
12260
12261     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12262     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12263                                        ch_button);
12264
12265     if (level.use_step_counter)
12266     {
12267       boolean counted_click = FALSE;
12268
12269       // element clicked that can change when clicked/pressed
12270       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12271           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12272            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12273         counted_click = TRUE;
12274
12275       // element clicked that can trigger change when clicked/pressed
12276       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12277           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12278         counted_click = TRUE;
12279
12280       if (new_button && counted_click)
12281         CheckLevelTime_StepCounter();
12282     }
12283   }
12284
12285   SCAN_PLAYFIELD(x, y)
12286   {
12287     element = Tile[x][y];
12288     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12289     last_gfx_frame = GfxFrame[x][y];
12290
12291     if (element == EL_EMPTY)
12292       graphic = el2img(GfxElementEmpty[x][y]);
12293
12294     ResetGfxFrame(x, y);
12295
12296     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12297       DrawLevelGraphicAnimation(x, y, graphic);
12298
12299     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12300         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12301       ResetRandomAnimationValue(x, y);
12302
12303     SetRandomAnimationValue(x, y);
12304
12305     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12306
12307     if (IS_INACTIVE(element))
12308     {
12309       if (IS_ANIMATED(graphic))
12310         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12311
12312       continue;
12313     }
12314
12315     // this may take place after moving, so 'element' may have changed
12316     if (IS_CHANGING(x, y) &&
12317         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12318     {
12319       int page = element_info[element].event_page_nr[CE_DELAY];
12320
12321       HandleElementChange(x, y, page);
12322
12323       element = Tile[x][y];
12324       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12325     }
12326
12327     CheckNextToConditions(x, y);
12328
12329     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12330     {
12331       StartMoving(x, y);
12332
12333       element = Tile[x][y];
12334       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12335
12336       if (IS_ANIMATED(graphic) &&
12337           !IS_MOVING(x, y) &&
12338           !Stop[x][y])
12339         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12340
12341       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12342         TEST_DrawTwinkleOnField(x, y);
12343     }
12344     else if (element == EL_ACID)
12345     {
12346       if (!Stop[x][y])
12347         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12348     }
12349     else if ((element == EL_EXIT_OPEN ||
12350               element == EL_EM_EXIT_OPEN ||
12351               element == EL_SP_EXIT_OPEN ||
12352               element == EL_STEEL_EXIT_OPEN ||
12353               element == EL_EM_STEEL_EXIT_OPEN ||
12354               element == EL_SP_TERMINAL ||
12355               element == EL_SP_TERMINAL_ACTIVE ||
12356               element == EL_EXTRA_TIME ||
12357               element == EL_SHIELD_NORMAL ||
12358               element == EL_SHIELD_DEADLY) &&
12359              IS_ANIMATED(graphic))
12360       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12361     else if (IS_MOVING(x, y))
12362       ContinueMoving(x, y);
12363     else if (IS_ACTIVE_BOMB(element))
12364       CheckDynamite(x, y);
12365     else if (element == EL_AMOEBA_GROWING)
12366       AmoebaGrowing(x, y);
12367     else if (element == EL_AMOEBA_SHRINKING)
12368       AmoebaShrinking(x, y);
12369
12370 #if !USE_NEW_AMOEBA_CODE
12371     else if (IS_AMOEBALIVE(element))
12372       AmoebaReproduce(x, y);
12373 #endif
12374
12375     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12376       Life(x, y);
12377     else if (element == EL_EXIT_CLOSED)
12378       CheckExit(x, y);
12379     else if (element == EL_EM_EXIT_CLOSED)
12380       CheckExitEM(x, y);
12381     else if (element == EL_STEEL_EXIT_CLOSED)
12382       CheckExitSteel(x, y);
12383     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12384       CheckExitSteelEM(x, y);
12385     else if (element == EL_SP_EXIT_CLOSED)
12386       CheckExitSP(x, y);
12387     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12388              element == EL_EXPANDABLE_STEELWALL_GROWING)
12389       WallGrowing(x, y);
12390     else if (element == EL_EXPANDABLE_WALL ||
12391              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12392              element == EL_EXPANDABLE_WALL_VERTICAL ||
12393              element == EL_EXPANDABLE_WALL_ANY ||
12394              element == EL_BD_EXPANDABLE_WALL ||
12395              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12396              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12397              element == EL_EXPANDABLE_STEELWALL_ANY)
12398       CheckWallGrowing(x, y);
12399     else if (element == EL_FLAMES)
12400       CheckForDragon(x, y);
12401     else if (element == EL_EXPLOSION)
12402       ; // drawing of correct explosion animation is handled separately
12403     else if (element == EL_ELEMENT_SNAPPING ||
12404              element == EL_DIAGONAL_SHRINKING ||
12405              element == EL_DIAGONAL_GROWING)
12406     {
12407       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12408
12409       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12410     }
12411     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12412       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12413
12414     if (IS_BELT_ACTIVE(element))
12415       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12416
12417     if (game.magic_wall_active)
12418     {
12419       int jx = local_player->jx, jy = local_player->jy;
12420
12421       // play the element sound at the position nearest to the player
12422       if ((element == EL_MAGIC_WALL_FULL ||
12423            element == EL_MAGIC_WALL_ACTIVE ||
12424            element == EL_MAGIC_WALL_EMPTYING ||
12425            element == EL_BD_MAGIC_WALL_FULL ||
12426            element == EL_BD_MAGIC_WALL_ACTIVE ||
12427            element == EL_BD_MAGIC_WALL_EMPTYING ||
12428            element == EL_DC_MAGIC_WALL_FULL ||
12429            element == EL_DC_MAGIC_WALL_ACTIVE ||
12430            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12431           ABS(x - jx) + ABS(y - jy) <
12432           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12433       {
12434         magic_wall_x = x;
12435         magic_wall_y = y;
12436       }
12437     }
12438   }
12439
12440 #if USE_NEW_AMOEBA_CODE
12441   // new experimental amoeba growth stuff
12442   if (!(FrameCounter % 8))
12443   {
12444     static unsigned int random = 1684108901;
12445
12446     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12447     {
12448       x = RND(lev_fieldx);
12449       y = RND(lev_fieldy);
12450       element = Tile[x][y];
12451
12452       if (!IS_PLAYER(x,y) &&
12453           (element == EL_EMPTY ||
12454            CAN_GROW_INTO(element) ||
12455            element == EL_QUICKSAND_EMPTY ||
12456            element == EL_QUICKSAND_FAST_EMPTY ||
12457            element == EL_ACID_SPLASH_LEFT ||
12458            element == EL_ACID_SPLASH_RIGHT))
12459       {
12460         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12461             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12462             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12463             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12464           Tile[x][y] = EL_AMOEBA_DROP;
12465       }
12466
12467       random = random * 129 + 1;
12468     }
12469   }
12470 #endif
12471
12472   game.explosions_delayed = FALSE;
12473
12474   SCAN_PLAYFIELD(x, y)
12475   {
12476     element = Tile[x][y];
12477
12478     if (ExplodeField[x][y])
12479       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12480     else if (element == EL_EXPLOSION)
12481       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12482
12483     ExplodeField[x][y] = EX_TYPE_NONE;
12484   }
12485
12486   game.explosions_delayed = TRUE;
12487
12488   if (game.magic_wall_active)
12489   {
12490     if (!(game.magic_wall_time_left % 4))
12491     {
12492       int element = Tile[magic_wall_x][magic_wall_y];
12493
12494       if (element == EL_BD_MAGIC_WALL_FULL ||
12495           element == EL_BD_MAGIC_WALL_ACTIVE ||
12496           element == EL_BD_MAGIC_WALL_EMPTYING)
12497         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12498       else if (element == EL_DC_MAGIC_WALL_FULL ||
12499                element == EL_DC_MAGIC_WALL_ACTIVE ||
12500                element == EL_DC_MAGIC_WALL_EMPTYING)
12501         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12502       else
12503         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12504     }
12505
12506     if (game.magic_wall_time_left > 0)
12507     {
12508       game.magic_wall_time_left--;
12509
12510       if (!game.magic_wall_time_left)
12511       {
12512         SCAN_PLAYFIELD(x, y)
12513         {
12514           element = Tile[x][y];
12515
12516           if (element == EL_MAGIC_WALL_ACTIVE ||
12517               element == EL_MAGIC_WALL_FULL)
12518           {
12519             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12520             TEST_DrawLevelField(x, y);
12521           }
12522           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12523                    element == EL_BD_MAGIC_WALL_FULL)
12524           {
12525             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12526             TEST_DrawLevelField(x, y);
12527           }
12528           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12529                    element == EL_DC_MAGIC_WALL_FULL)
12530           {
12531             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12532             TEST_DrawLevelField(x, y);
12533           }
12534         }
12535
12536         game.magic_wall_active = FALSE;
12537       }
12538     }
12539   }
12540
12541   if (game.light_time_left > 0)
12542   {
12543     game.light_time_left--;
12544
12545     if (game.light_time_left == 0)
12546       RedrawAllLightSwitchesAndInvisibleElements();
12547   }
12548
12549   if (game.timegate_time_left > 0)
12550   {
12551     game.timegate_time_left--;
12552
12553     if (game.timegate_time_left == 0)
12554       CloseAllOpenTimegates();
12555   }
12556
12557   if (game.lenses_time_left > 0)
12558   {
12559     game.lenses_time_left--;
12560
12561     if (game.lenses_time_left == 0)
12562       RedrawAllInvisibleElementsForLenses();
12563   }
12564
12565   if (game.magnify_time_left > 0)
12566   {
12567     game.magnify_time_left--;
12568
12569     if (game.magnify_time_left == 0)
12570       RedrawAllInvisibleElementsForMagnifier();
12571   }
12572
12573   for (i = 0; i < MAX_PLAYERS; i++)
12574   {
12575     struct PlayerInfo *player = &stored_player[i];
12576
12577     if (SHIELD_ON(player))
12578     {
12579       if (player->shield_deadly_time_left)
12580         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12581       else if (player->shield_normal_time_left)
12582         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12583     }
12584   }
12585
12586 #if USE_DELAYED_GFX_REDRAW
12587   SCAN_PLAYFIELD(x, y)
12588   {
12589     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12590     {
12591       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12592          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12593
12594       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12595         DrawLevelField(x, y);
12596
12597       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12598         DrawLevelFieldCrumbled(x, y);
12599
12600       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12601         DrawLevelFieldCrumbledNeighbours(x, y);
12602
12603       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12604         DrawTwinkleOnField(x, y);
12605     }
12606
12607     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12608   }
12609 #endif
12610
12611   DrawAllPlayers();
12612   PlayAllPlayersSound();
12613
12614   for (i = 0; i < MAX_PLAYERS; i++)
12615   {
12616     struct PlayerInfo *player = &stored_player[i];
12617
12618     if (player->show_envelope != 0 && (!player->active ||
12619                                        player->MovPos == 0))
12620     {
12621       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12622
12623       player->show_envelope = 0;
12624     }
12625   }
12626
12627   // use random number generator in every frame to make it less predictable
12628   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12629     RND(1);
12630
12631   mouse_action_last = mouse_action;
12632 }
12633
12634 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12635 {
12636   int min_x = x, min_y = y, max_x = x, max_y = y;
12637   int scr_fieldx = getScreenFieldSizeX();
12638   int scr_fieldy = getScreenFieldSizeY();
12639   int i;
12640
12641   for (i = 0; i < MAX_PLAYERS; i++)
12642   {
12643     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12644
12645     if (!stored_player[i].active || &stored_player[i] == player)
12646       continue;
12647
12648     min_x = MIN(min_x, jx);
12649     min_y = MIN(min_y, jy);
12650     max_x = MAX(max_x, jx);
12651     max_y = MAX(max_y, jy);
12652   }
12653
12654   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12655 }
12656
12657 static boolean AllPlayersInVisibleScreen(void)
12658 {
12659   int i;
12660
12661   for (i = 0; i < MAX_PLAYERS; i++)
12662   {
12663     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12664
12665     if (!stored_player[i].active)
12666       continue;
12667
12668     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12669       return FALSE;
12670   }
12671
12672   return TRUE;
12673 }
12674
12675 void ScrollLevel(int dx, int dy)
12676 {
12677   int scroll_offset = 2 * TILEX_VAR;
12678   int x, y;
12679
12680   BlitBitmap(drawto_field, drawto_field,
12681              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12682              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12683              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12684              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12685              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12686              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12687
12688   if (dx != 0)
12689   {
12690     x = (dx == 1 ? BX1 : BX2);
12691     for (y = BY1; y <= BY2; y++)
12692       DrawScreenField(x, y);
12693   }
12694
12695   if (dy != 0)
12696   {
12697     y = (dy == 1 ? BY1 : BY2);
12698     for (x = BX1; x <= BX2; x++)
12699       DrawScreenField(x, y);
12700   }
12701
12702   redraw_mask |= REDRAW_FIELD;
12703 }
12704
12705 static boolean canFallDown(struct PlayerInfo *player)
12706 {
12707   int jx = player->jx, jy = player->jy;
12708
12709   return (IN_LEV_FIELD(jx, jy + 1) &&
12710           (IS_FREE(jx, jy + 1) ||
12711            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12712           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12713           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12714 }
12715
12716 static boolean canPassField(int x, int y, int move_dir)
12717 {
12718   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12719   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12720   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12721   int nextx = x + dx;
12722   int nexty = y + dy;
12723   int element = Tile[x][y];
12724
12725   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12726           !CAN_MOVE(element) &&
12727           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12728           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12729           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12730 }
12731
12732 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12733 {
12734   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12735   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12736   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12737   int newx = x + dx;
12738   int newy = y + dy;
12739
12740   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12741           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12742           (IS_DIGGABLE(Tile[newx][newy]) ||
12743            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12744            canPassField(newx, newy, move_dir)));
12745 }
12746
12747 static void CheckGravityMovement(struct PlayerInfo *player)
12748 {
12749   if (player->gravity && !player->programmed_action)
12750   {
12751     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12752     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12753     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12754     int jx = player->jx, jy = player->jy;
12755     boolean player_is_moving_to_valid_field =
12756       (!player_is_snapping &&
12757        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12758         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12759     boolean player_can_fall_down = canFallDown(player);
12760
12761     if (player_can_fall_down &&
12762         !player_is_moving_to_valid_field)
12763       player->programmed_action = MV_DOWN;
12764   }
12765 }
12766
12767 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12768 {
12769   return CheckGravityMovement(player);
12770
12771   if (player->gravity && !player->programmed_action)
12772   {
12773     int jx = player->jx, jy = player->jy;
12774     boolean field_under_player_is_free =
12775       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12776     boolean player_is_standing_on_valid_field =
12777       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12778        (IS_WALKABLE(Tile[jx][jy]) &&
12779         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12780
12781     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12782       player->programmed_action = MV_DOWN;
12783   }
12784 }
12785
12786 /*
12787   MovePlayerOneStep()
12788   -----------------------------------------------------------------------------
12789   dx, dy:               direction (non-diagonal) to try to move the player to
12790   real_dx, real_dy:     direction as read from input device (can be diagonal)
12791 */
12792
12793 boolean MovePlayerOneStep(struct PlayerInfo *player,
12794                           int dx, int dy, int real_dx, int real_dy)
12795 {
12796   int jx = player->jx, jy = player->jy;
12797   int new_jx = jx + dx, new_jy = jy + dy;
12798   int can_move;
12799   boolean player_can_move = !player->cannot_move;
12800
12801   if (!player->active || (!dx && !dy))
12802     return MP_NO_ACTION;
12803
12804   player->MovDir = (dx < 0 ? MV_LEFT :
12805                     dx > 0 ? MV_RIGHT :
12806                     dy < 0 ? MV_UP :
12807                     dy > 0 ? MV_DOWN :  MV_NONE);
12808
12809   if (!IN_LEV_FIELD(new_jx, new_jy))
12810     return MP_NO_ACTION;
12811
12812   if (!player_can_move)
12813   {
12814     if (player->MovPos == 0)
12815     {
12816       player->is_moving = FALSE;
12817       player->is_digging = FALSE;
12818       player->is_collecting = FALSE;
12819       player->is_snapping = FALSE;
12820       player->is_pushing = FALSE;
12821     }
12822   }
12823
12824   if (!network.enabled && game.centered_player_nr == -1 &&
12825       !AllPlayersInSight(player, new_jx, new_jy))
12826     return MP_NO_ACTION;
12827
12828   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12829   if (can_move != MP_MOVING)
12830     return can_move;
12831
12832   // check if DigField() has caused relocation of the player
12833   if (player->jx != jx || player->jy != jy)
12834     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12835
12836   StorePlayer[jx][jy] = 0;
12837   player->last_jx = jx;
12838   player->last_jy = jy;
12839   player->jx = new_jx;
12840   player->jy = new_jy;
12841   StorePlayer[new_jx][new_jy] = player->element_nr;
12842
12843   if (player->move_delay_value_next != -1)
12844   {
12845     player->move_delay_value = player->move_delay_value_next;
12846     player->move_delay_value_next = -1;
12847   }
12848
12849   player->MovPos =
12850     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12851
12852   player->step_counter++;
12853
12854   PlayerVisit[jx][jy] = FrameCounter;
12855
12856   player->is_moving = TRUE;
12857
12858 #if 1
12859   // should better be called in MovePlayer(), but this breaks some tapes
12860   ScrollPlayer(player, SCROLL_INIT);
12861 #endif
12862
12863   return MP_MOVING;
12864 }
12865
12866 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12867 {
12868   int jx = player->jx, jy = player->jy;
12869   int old_jx = jx, old_jy = jy;
12870   int moved = MP_NO_ACTION;
12871
12872   if (!player->active)
12873     return FALSE;
12874
12875   if (!dx && !dy)
12876   {
12877     if (player->MovPos == 0)
12878     {
12879       player->is_moving = FALSE;
12880       player->is_digging = FALSE;
12881       player->is_collecting = FALSE;
12882       player->is_snapping = FALSE;
12883       player->is_pushing = FALSE;
12884     }
12885
12886     return FALSE;
12887   }
12888
12889   if (player->move_delay > 0)
12890     return FALSE;
12891
12892   player->move_delay = -1;              // set to "uninitialized" value
12893
12894   // store if player is automatically moved to next field
12895   player->is_auto_moving = (player->programmed_action != MV_NONE);
12896
12897   // remove the last programmed player action
12898   player->programmed_action = 0;
12899
12900   if (player->MovPos)
12901   {
12902     // should only happen if pre-1.2 tape recordings are played
12903     // this is only for backward compatibility
12904
12905     int original_move_delay_value = player->move_delay_value;
12906
12907 #if DEBUG
12908     Debug("game:playing:MovePlayer",
12909           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12910           tape.counter);
12911 #endif
12912
12913     // scroll remaining steps with finest movement resolution
12914     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12915
12916     while (player->MovPos)
12917     {
12918       ScrollPlayer(player, SCROLL_GO_ON);
12919       ScrollScreen(NULL, SCROLL_GO_ON);
12920
12921       AdvanceFrameAndPlayerCounters(player->index_nr);
12922
12923       DrawAllPlayers();
12924       BackToFront_WithFrameDelay(0);
12925     }
12926
12927     player->move_delay_value = original_move_delay_value;
12928   }
12929
12930   player->is_active = FALSE;
12931
12932   if (player->last_move_dir & MV_HORIZONTAL)
12933   {
12934     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12935       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12936   }
12937   else
12938   {
12939     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12940       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12941   }
12942
12943   if (!moved && !player->is_active)
12944   {
12945     player->is_moving = FALSE;
12946     player->is_digging = FALSE;
12947     player->is_collecting = FALSE;
12948     player->is_snapping = FALSE;
12949     player->is_pushing = FALSE;
12950   }
12951
12952   jx = player->jx;
12953   jy = player->jy;
12954
12955   if (moved & MP_MOVING && !ScreenMovPos &&
12956       (player->index_nr == game.centered_player_nr ||
12957        game.centered_player_nr == -1))
12958   {
12959     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12960
12961     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12962     {
12963       // actual player has left the screen -- scroll in that direction
12964       if (jx != old_jx)         // player has moved horizontally
12965         scroll_x += (jx - old_jx);
12966       else                      // player has moved vertically
12967         scroll_y += (jy - old_jy);
12968     }
12969     else
12970     {
12971       int offset_raw = game.scroll_delay_value;
12972
12973       if (jx != old_jx)         // player has moved horizontally
12974       {
12975         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12976         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12977         int new_scroll_x = jx - MIDPOSX + offset_x;
12978
12979         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12980             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12981           scroll_x = new_scroll_x;
12982
12983         // don't scroll over playfield boundaries
12984         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12985
12986         // don't scroll more than one field at a time
12987         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12988
12989         // don't scroll against the player's moving direction
12990         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12991             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12992           scroll_x = old_scroll_x;
12993       }
12994       else                      // player has moved vertically
12995       {
12996         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12997         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12998         int new_scroll_y = jy - MIDPOSY + offset_y;
12999
13000         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
13001             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13002           scroll_y = new_scroll_y;
13003
13004         // don't scroll over playfield boundaries
13005         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13006
13007         // don't scroll more than one field at a time
13008         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13009
13010         // don't scroll against the player's moving direction
13011         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13012             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13013           scroll_y = old_scroll_y;
13014       }
13015     }
13016
13017     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13018     {
13019       if (!network.enabled && game.centered_player_nr == -1 &&
13020           !AllPlayersInVisibleScreen())
13021       {
13022         scroll_x = old_scroll_x;
13023         scroll_y = old_scroll_y;
13024       }
13025       else
13026       {
13027         ScrollScreen(player, SCROLL_INIT);
13028         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13029       }
13030     }
13031   }
13032
13033   player->StepFrame = 0;
13034
13035   if (moved & MP_MOVING)
13036   {
13037     if (old_jx != jx && old_jy == jy)
13038       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13039     else if (old_jx == jx && old_jy != jy)
13040       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13041
13042     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13043
13044     player->last_move_dir = player->MovDir;
13045     player->is_moving = TRUE;
13046     player->is_snapping = FALSE;
13047     player->is_switching = FALSE;
13048     player->is_dropping = FALSE;
13049     player->is_dropping_pressed = FALSE;
13050     player->drop_pressed_delay = 0;
13051
13052 #if 0
13053     // should better be called here than above, but this breaks some tapes
13054     ScrollPlayer(player, SCROLL_INIT);
13055 #endif
13056   }
13057   else
13058   {
13059     CheckGravityMovementWhenNotMoving(player);
13060
13061     player->is_moving = FALSE;
13062
13063     /* at this point, the player is allowed to move, but cannot move right now
13064        (e.g. because of something blocking the way) -- ensure that the player
13065        is also allowed to move in the next frame (in old versions before 3.1.1,
13066        the player was forced to wait again for eight frames before next try) */
13067
13068     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13069       player->move_delay = 0;   // allow direct movement in the next frame
13070   }
13071
13072   if (player->move_delay == -1)         // not yet initialized by DigField()
13073     player->move_delay = player->move_delay_value;
13074
13075   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13076   {
13077     TestIfPlayerTouchesBadThing(jx, jy);
13078     TestIfPlayerTouchesCustomElement(jx, jy);
13079   }
13080
13081   if (!player->active)
13082     RemovePlayer(player);
13083
13084   return moved;
13085 }
13086
13087 void ScrollPlayer(struct PlayerInfo *player, int mode)
13088 {
13089   int jx = player->jx, jy = player->jy;
13090   int last_jx = player->last_jx, last_jy = player->last_jy;
13091   int move_stepsize = TILEX / player->move_delay_value;
13092
13093   if (!player->active)
13094     return;
13095
13096   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13097     return;
13098
13099   if (mode == SCROLL_INIT)
13100   {
13101     player->actual_frame_counter.count = FrameCounter;
13102     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13103
13104     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13105         Tile[last_jx][last_jy] == EL_EMPTY)
13106     {
13107       int last_field_block_delay = 0;   // start with no blocking at all
13108       int block_delay_adjustment = player->block_delay_adjustment;
13109
13110       // if player blocks last field, add delay for exactly one move
13111       if (player->block_last_field)
13112       {
13113         last_field_block_delay += player->move_delay_value;
13114
13115         // when blocking enabled, prevent moving up despite gravity
13116         if (player->gravity && player->MovDir == MV_UP)
13117           block_delay_adjustment = -1;
13118       }
13119
13120       // add block delay adjustment (also possible when not blocking)
13121       last_field_block_delay += block_delay_adjustment;
13122
13123       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13124       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13125     }
13126
13127     if (player->MovPos != 0)    // player has not yet reached destination
13128       return;
13129   }
13130   else if (!FrameReached(&player->actual_frame_counter))
13131     return;
13132
13133   if (player->MovPos != 0)
13134   {
13135     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13136     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13137
13138     // before DrawPlayer() to draw correct player graphic for this case
13139     if (player->MovPos == 0)
13140       CheckGravityMovement(player);
13141   }
13142
13143   if (player->MovPos == 0)      // player reached destination field
13144   {
13145     if (player->move_delay_reset_counter > 0)
13146     {
13147       player->move_delay_reset_counter--;
13148
13149       if (player->move_delay_reset_counter == 0)
13150       {
13151         // continue with normal speed after quickly moving through gate
13152         HALVE_PLAYER_SPEED(player);
13153
13154         // be able to make the next move without delay
13155         player->move_delay = 0;
13156       }
13157     }
13158
13159     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13160         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13161         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13162         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13163         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13164         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13165         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13166         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13167     {
13168       ExitPlayer(player);
13169
13170       if (game.players_still_needed == 0 &&
13171           (game.friends_still_needed == 0 ||
13172            IS_SP_ELEMENT(Tile[jx][jy])))
13173         LevelSolved();
13174     }
13175
13176     player->last_jx = jx;
13177     player->last_jy = jy;
13178
13179     // this breaks one level: "machine", level 000
13180     {
13181       int move_direction = player->MovDir;
13182       int enter_side = MV_DIR_OPPOSITE(move_direction);
13183       int leave_side = move_direction;
13184       int old_jx = last_jx;
13185       int old_jy = last_jy;
13186       int old_element = Tile[old_jx][old_jy];
13187       int new_element = Tile[jx][jy];
13188
13189       if (IS_CUSTOM_ELEMENT(old_element))
13190         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13191                                    CE_LEFT_BY_PLAYER,
13192                                    player->index_bit, leave_side);
13193
13194       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13195                                           CE_PLAYER_LEAVES_X,
13196                                           player->index_bit, leave_side);
13197
13198       if (IS_CUSTOM_ELEMENT(new_element))
13199         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13200                                    player->index_bit, enter_side);
13201
13202       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13203                                           CE_PLAYER_ENTERS_X,
13204                                           player->index_bit, enter_side);
13205
13206       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13207                                         CE_MOVE_OF_X, move_direction);
13208     }
13209
13210     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13211     {
13212       TestIfPlayerTouchesBadThing(jx, jy);
13213       TestIfPlayerTouchesCustomElement(jx, jy);
13214
13215       /* needed because pushed element has not yet reached its destination,
13216          so it would trigger a change event at its previous field location */
13217       if (!player->is_pushing)
13218         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13219
13220       if (level.finish_dig_collect &&
13221           (player->is_digging || player->is_collecting))
13222       {
13223         int last_element = player->last_removed_element;
13224         int move_direction = player->MovDir;
13225         int enter_side = MV_DIR_OPPOSITE(move_direction);
13226         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13227                             CE_PLAYER_COLLECTS_X);
13228
13229         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13230                                             player->index_bit, enter_side);
13231
13232         player->last_removed_element = EL_UNDEFINED;
13233       }
13234
13235       if (!player->active)
13236         RemovePlayer(player);
13237     }
13238
13239     if (level.use_step_counter)
13240       CheckLevelTime_StepCounter();
13241
13242     if (tape.single_step && tape.recording && !tape.pausing &&
13243         !player->programmed_action)
13244       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13245
13246     if (!player->programmed_action)
13247       CheckSaveEngineSnapshot(player);
13248   }
13249 }
13250
13251 void ScrollScreen(struct PlayerInfo *player, int mode)
13252 {
13253   static DelayCounter screen_frame_counter = { 0 };
13254
13255   if (mode == SCROLL_INIT)
13256   {
13257     // set scrolling step size according to actual player's moving speed
13258     ScrollStepSize = TILEX / player->move_delay_value;
13259
13260     screen_frame_counter.count = FrameCounter;
13261     screen_frame_counter.value = 1;
13262
13263     ScreenMovDir = player->MovDir;
13264     ScreenMovPos = player->MovPos;
13265     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13266     return;
13267   }
13268   else if (!FrameReached(&screen_frame_counter))
13269     return;
13270
13271   if (ScreenMovPos)
13272   {
13273     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13274     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13275     redraw_mask |= REDRAW_FIELD;
13276   }
13277   else
13278     ScreenMovDir = MV_NONE;
13279 }
13280
13281 void CheckNextToConditions(int x, int y)
13282 {
13283   int element = Tile[x][y];
13284
13285   if (IS_PLAYER(x, y))
13286     TestIfPlayerNextToCustomElement(x, y);
13287
13288   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13289       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13290     TestIfElementNextToCustomElement(x, y);
13291 }
13292
13293 void TestIfPlayerNextToCustomElement(int x, int y)
13294 {
13295   struct XY *xy = xy_topdown;
13296   static int trigger_sides[4][2] =
13297   {
13298     // center side       border side
13299     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13300     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13301     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13302     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13303   };
13304   int i;
13305
13306   if (!IS_PLAYER(x, y))
13307     return;
13308
13309   struct PlayerInfo *player = PLAYERINFO(x, y);
13310
13311   if (player->is_moving)
13312     return;
13313
13314   for (i = 0; i < NUM_DIRECTIONS; i++)
13315   {
13316     int xx = x + xy[i].x;
13317     int yy = y + xy[i].y;
13318     int border_side = trigger_sides[i][1];
13319     int border_element;
13320
13321     if (!IN_LEV_FIELD(xx, yy))
13322       continue;
13323
13324     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13325       continue;         // center and border element not connected
13326
13327     border_element = Tile[xx][yy];
13328
13329     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13330                                player->index_bit, border_side);
13331     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13332                                         CE_PLAYER_NEXT_TO_X,
13333                                         player->index_bit, border_side);
13334
13335     /* use player element that is initially defined in the level playfield,
13336        not the player element that corresponds to the runtime player number
13337        (example: a level that contains EL_PLAYER_3 as the only player would
13338        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13339
13340     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13341                              CE_NEXT_TO_X, border_side);
13342   }
13343 }
13344
13345 void TestIfPlayerTouchesCustomElement(int x, int y)
13346 {
13347   struct XY *xy = xy_topdown;
13348   static int trigger_sides[4][2] =
13349   {
13350     // center side       border side
13351     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13352     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13353     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13354     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13355   };
13356   static int touch_dir[4] =
13357   {
13358     MV_LEFT | MV_RIGHT,
13359     MV_UP   | MV_DOWN,
13360     MV_UP   | MV_DOWN,
13361     MV_LEFT | MV_RIGHT
13362   };
13363   int center_element = Tile[x][y];      // should always be non-moving!
13364   int i;
13365
13366   for (i = 0; i < NUM_DIRECTIONS; i++)
13367   {
13368     int xx = x + xy[i].x;
13369     int yy = y + xy[i].y;
13370     int center_side = trigger_sides[i][0];
13371     int border_side = trigger_sides[i][1];
13372     int border_element;
13373
13374     if (!IN_LEV_FIELD(xx, yy))
13375       continue;
13376
13377     if (IS_PLAYER(x, y))                // player found at center element
13378     {
13379       struct PlayerInfo *player = PLAYERINFO(x, y);
13380
13381       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13382         border_element = Tile[xx][yy];          // may be moving!
13383       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13384         border_element = Tile[xx][yy];
13385       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13386         border_element = MovingOrBlocked2Element(xx, yy);
13387       else
13388         continue;               // center and border element do not touch
13389
13390       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13391                                  player->index_bit, border_side);
13392       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13393                                           CE_PLAYER_TOUCHES_X,
13394                                           player->index_bit, border_side);
13395
13396       {
13397         /* use player element that is initially defined in the level playfield,
13398            not the player element that corresponds to the runtime player number
13399            (example: a level that contains EL_PLAYER_3 as the only player would
13400            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13401         int player_element = PLAYERINFO(x, y)->initial_element;
13402
13403         CheckElementChangeBySide(xx, yy, border_element, player_element,
13404                                  CE_TOUCHING_X, border_side);
13405       }
13406     }
13407     else if (IS_PLAYER(xx, yy))         // player found at border element
13408     {
13409       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13410
13411       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13412       {
13413         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13414           continue;             // center and border element do not touch
13415       }
13416
13417       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13418                                  player->index_bit, center_side);
13419       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13420                                           CE_PLAYER_TOUCHES_X,
13421                                           player->index_bit, center_side);
13422
13423       {
13424         /* use player element that is initially defined in the level playfield,
13425            not the player element that corresponds to the runtime player number
13426            (example: a level that contains EL_PLAYER_3 as the only player would
13427            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13428         int player_element = PLAYERINFO(xx, yy)->initial_element;
13429
13430         CheckElementChangeBySide(x, y, center_element, player_element,
13431                                  CE_TOUCHING_X, center_side);
13432       }
13433
13434       break;
13435     }
13436   }
13437 }
13438
13439 void TestIfElementNextToCustomElement(int x, int y)
13440 {
13441   struct XY *xy = xy_topdown;
13442   static int trigger_sides[4][2] =
13443   {
13444     // center side      border side
13445     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13446     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13447     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13448     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13449   };
13450   int center_element = Tile[x][y];      // should always be non-moving!
13451   int i;
13452
13453   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13454     return;
13455
13456   for (i = 0; i < NUM_DIRECTIONS; i++)
13457   {
13458     int xx = x + xy[i].x;
13459     int yy = y + xy[i].y;
13460     int border_side = trigger_sides[i][1];
13461     int border_element;
13462
13463     if (!IN_LEV_FIELD(xx, yy))
13464       continue;
13465
13466     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13467       continue;                 // center and border element not connected
13468
13469     border_element = Tile[xx][yy];
13470
13471     // check for change of center element (but change it only once)
13472     if (CheckElementChangeBySide(x, y, center_element, border_element,
13473                                  CE_NEXT_TO_X, border_side))
13474       break;
13475   }
13476 }
13477
13478 void TestIfElementTouchesCustomElement(int x, int y)
13479 {
13480   struct XY *xy = xy_topdown;
13481   static int trigger_sides[4][2] =
13482   {
13483     // center side      border side
13484     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13485     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13486     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13487     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13488   };
13489   static int touch_dir[4] =
13490   {
13491     MV_LEFT | MV_RIGHT,
13492     MV_UP   | MV_DOWN,
13493     MV_UP   | MV_DOWN,
13494     MV_LEFT | MV_RIGHT
13495   };
13496   boolean change_center_element = FALSE;
13497   int center_element = Tile[x][y];      // should always be non-moving!
13498   int border_element_old[NUM_DIRECTIONS];
13499   int i;
13500
13501   for (i = 0; i < NUM_DIRECTIONS; i++)
13502   {
13503     int xx = x + xy[i].x;
13504     int yy = y + xy[i].y;
13505     int border_element;
13506
13507     border_element_old[i] = -1;
13508
13509     if (!IN_LEV_FIELD(xx, yy))
13510       continue;
13511
13512     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13513       border_element = Tile[xx][yy];    // may be moving!
13514     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13515       border_element = Tile[xx][yy];
13516     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13517       border_element = MovingOrBlocked2Element(xx, yy);
13518     else
13519       continue;                 // center and border element do not touch
13520
13521     border_element_old[i] = border_element;
13522   }
13523
13524   for (i = 0; i < NUM_DIRECTIONS; i++)
13525   {
13526     int xx = x + xy[i].x;
13527     int yy = y + xy[i].y;
13528     int center_side = trigger_sides[i][0];
13529     int border_element = border_element_old[i];
13530
13531     if (border_element == -1)
13532       continue;
13533
13534     // check for change of border element
13535     CheckElementChangeBySide(xx, yy, border_element, center_element,
13536                              CE_TOUCHING_X, center_side);
13537
13538     // (center element cannot be player, so we dont have to check this here)
13539   }
13540
13541   for (i = 0; i < NUM_DIRECTIONS; i++)
13542   {
13543     int xx = x + xy[i].x;
13544     int yy = y + xy[i].y;
13545     int border_side = trigger_sides[i][1];
13546     int border_element = border_element_old[i];
13547
13548     if (border_element == -1)
13549       continue;
13550
13551     // check for change of center element (but change it only once)
13552     if (!change_center_element)
13553       change_center_element =
13554         CheckElementChangeBySide(x, y, center_element, border_element,
13555                                  CE_TOUCHING_X, border_side);
13556
13557     if (IS_PLAYER(xx, yy))
13558     {
13559       /* use player element that is initially defined in the level playfield,
13560          not the player element that corresponds to the runtime player number
13561          (example: a level that contains EL_PLAYER_3 as the only player would
13562          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13563       int player_element = PLAYERINFO(xx, yy)->initial_element;
13564
13565       CheckElementChangeBySide(x, y, center_element, player_element,
13566                                CE_TOUCHING_X, border_side);
13567     }
13568   }
13569 }
13570
13571 void TestIfElementHitsCustomElement(int x, int y, int direction)
13572 {
13573   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13574   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13575   int hitx = x + dx, hity = y + dy;
13576   int hitting_element = Tile[x][y];
13577   int touched_element;
13578
13579   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13580     return;
13581
13582   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13583                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13584
13585   if (IN_LEV_FIELD(hitx, hity))
13586   {
13587     int opposite_direction = MV_DIR_OPPOSITE(direction);
13588     int hitting_side = direction;
13589     int touched_side = opposite_direction;
13590     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13591                           MovDir[hitx][hity] != direction ||
13592                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13593
13594     object_hit = TRUE;
13595
13596     if (object_hit)
13597     {
13598       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13599                                CE_HITTING_X, touched_side);
13600
13601       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13602                                CE_HIT_BY_X, hitting_side);
13603
13604       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13605                                CE_HIT_BY_SOMETHING, opposite_direction);
13606
13607       if (IS_PLAYER(hitx, hity))
13608       {
13609         /* use player element that is initially defined in the level playfield,
13610            not the player element that corresponds to the runtime player number
13611            (example: a level that contains EL_PLAYER_3 as the only player would
13612            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13613         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13614
13615         CheckElementChangeBySide(x, y, hitting_element, player_element,
13616                                  CE_HITTING_X, touched_side);
13617       }
13618     }
13619   }
13620
13621   // "hitting something" is also true when hitting the playfield border
13622   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13623                            CE_HITTING_SOMETHING, direction);
13624 }
13625
13626 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13627 {
13628   int i, kill_x = -1, kill_y = -1;
13629
13630   int bad_element = -1;
13631   struct XY *test_xy = xy_topdown;
13632   static int test_dir[4] =
13633   {
13634     MV_UP,
13635     MV_LEFT,
13636     MV_RIGHT,
13637     MV_DOWN
13638   };
13639
13640   for (i = 0; i < NUM_DIRECTIONS; i++)
13641   {
13642     int test_x, test_y, test_move_dir, test_element;
13643
13644     test_x = good_x + test_xy[i].x;
13645     test_y = good_y + test_xy[i].y;
13646
13647     if (!IN_LEV_FIELD(test_x, test_y))
13648       continue;
13649
13650     test_move_dir =
13651       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13652
13653     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13654
13655     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13656        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13657     */
13658     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13659         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13660     {
13661       kill_x = test_x;
13662       kill_y = test_y;
13663       bad_element = test_element;
13664
13665       break;
13666     }
13667   }
13668
13669   if (kill_x != -1 || kill_y != -1)
13670   {
13671     if (IS_PLAYER(good_x, good_y))
13672     {
13673       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13674
13675       if (player->shield_deadly_time_left > 0 &&
13676           !IS_INDESTRUCTIBLE(bad_element))
13677         Bang(kill_x, kill_y);
13678       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13679         KillPlayer(player);
13680     }
13681     else
13682       Bang(good_x, good_y);
13683   }
13684 }
13685
13686 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13687 {
13688   int i, kill_x = -1, kill_y = -1;
13689   int bad_element = Tile[bad_x][bad_y];
13690   struct XY *test_xy = xy_topdown;
13691   static int touch_dir[4] =
13692   {
13693     MV_LEFT | MV_RIGHT,
13694     MV_UP   | MV_DOWN,
13695     MV_UP   | MV_DOWN,
13696     MV_LEFT | MV_RIGHT
13697   };
13698   static int test_dir[4] =
13699   {
13700     MV_UP,
13701     MV_LEFT,
13702     MV_RIGHT,
13703     MV_DOWN
13704   };
13705
13706   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13707     return;
13708
13709   for (i = 0; i < NUM_DIRECTIONS; i++)
13710   {
13711     int test_x, test_y, test_move_dir, test_element;
13712
13713     test_x = bad_x + test_xy[i].x;
13714     test_y = bad_y + test_xy[i].y;
13715
13716     if (!IN_LEV_FIELD(test_x, test_y))
13717       continue;
13718
13719     test_move_dir =
13720       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13721
13722     test_element = Tile[test_x][test_y];
13723
13724     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13725        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13726     */
13727     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13728         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13729     {
13730       // good thing is player or penguin that does not move away
13731       if (IS_PLAYER(test_x, test_y))
13732       {
13733         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13734
13735         if (bad_element == EL_ROBOT && player->is_moving)
13736           continue;     // robot does not kill player if he is moving
13737
13738         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13739         {
13740           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13741             continue;           // center and border element do not touch
13742         }
13743
13744         kill_x = test_x;
13745         kill_y = test_y;
13746
13747         break;
13748       }
13749       else if (test_element == EL_PENGUIN)
13750       {
13751         kill_x = test_x;
13752         kill_y = test_y;
13753
13754         break;
13755       }
13756     }
13757   }
13758
13759   if (kill_x != -1 || kill_y != -1)
13760   {
13761     if (IS_PLAYER(kill_x, kill_y))
13762     {
13763       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13764
13765       if (player->shield_deadly_time_left > 0 &&
13766           !IS_INDESTRUCTIBLE(bad_element))
13767         Bang(bad_x, bad_y);
13768       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13769         KillPlayer(player);
13770     }
13771     else
13772       Bang(kill_x, kill_y);
13773   }
13774 }
13775
13776 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13777 {
13778   int bad_element = Tile[bad_x][bad_y];
13779   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13780   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13781   int test_x = bad_x + dx, test_y = bad_y + dy;
13782   int test_move_dir, test_element;
13783   int kill_x = -1, kill_y = -1;
13784
13785   if (!IN_LEV_FIELD(test_x, test_y))
13786     return;
13787
13788   test_move_dir =
13789     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13790
13791   test_element = Tile[test_x][test_y];
13792
13793   if (test_move_dir != bad_move_dir)
13794   {
13795     // good thing can be player or penguin that does not move away
13796     if (IS_PLAYER(test_x, test_y))
13797     {
13798       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13799
13800       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13801          player as being hit when he is moving towards the bad thing, because
13802          the "get hit by" condition would be lost after the player stops) */
13803       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13804         return;         // player moves away from bad thing
13805
13806       kill_x = test_x;
13807       kill_y = test_y;
13808     }
13809     else if (test_element == EL_PENGUIN)
13810     {
13811       kill_x = test_x;
13812       kill_y = test_y;
13813     }
13814   }
13815
13816   if (kill_x != -1 || kill_y != -1)
13817   {
13818     if (IS_PLAYER(kill_x, kill_y))
13819     {
13820       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13821
13822       if (player->shield_deadly_time_left > 0 &&
13823           !IS_INDESTRUCTIBLE(bad_element))
13824         Bang(bad_x, bad_y);
13825       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13826         KillPlayer(player);
13827     }
13828     else
13829       Bang(kill_x, kill_y);
13830   }
13831 }
13832
13833 void TestIfPlayerTouchesBadThing(int x, int y)
13834 {
13835   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13836 }
13837
13838 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13839 {
13840   TestIfGoodThingHitsBadThing(x, y, move_dir);
13841 }
13842
13843 void TestIfBadThingTouchesPlayer(int x, int y)
13844 {
13845   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13846 }
13847
13848 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13849 {
13850   TestIfBadThingHitsGoodThing(x, y, move_dir);
13851 }
13852
13853 void TestIfFriendTouchesBadThing(int x, int y)
13854 {
13855   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13856 }
13857
13858 void TestIfBadThingTouchesFriend(int x, int y)
13859 {
13860   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13861 }
13862
13863 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13864 {
13865   int i, kill_x = bad_x, kill_y = bad_y;
13866   struct XY *xy = xy_topdown;
13867
13868   for (i = 0; i < NUM_DIRECTIONS; i++)
13869   {
13870     int x, y, element;
13871
13872     x = bad_x + xy[i].x;
13873     y = bad_y + xy[i].y;
13874     if (!IN_LEV_FIELD(x, y))
13875       continue;
13876
13877     element = Tile[x][y];
13878     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13879         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13880     {
13881       kill_x = x;
13882       kill_y = y;
13883       break;
13884     }
13885   }
13886
13887   if (kill_x != bad_x || kill_y != bad_y)
13888     Bang(bad_x, bad_y);
13889 }
13890
13891 void KillPlayer(struct PlayerInfo *player)
13892 {
13893   int jx = player->jx, jy = player->jy;
13894
13895   if (!player->active)
13896     return;
13897
13898 #if 0
13899   Debug("game:playing:KillPlayer",
13900         "0: killed == %d, active == %d, reanimated == %d",
13901         player->killed, player->active, player->reanimated);
13902 #endif
13903
13904   /* the following code was introduced to prevent an infinite loop when calling
13905      -> Bang()
13906      -> CheckTriggeredElementChangeExt()
13907      -> ExecuteCustomElementAction()
13908      -> KillPlayer()
13909      -> (infinitely repeating the above sequence of function calls)
13910      which occurs when killing the player while having a CE with the setting
13911      "kill player X when explosion of <player X>"; the solution using a new
13912      field "player->killed" was chosen for backwards compatibility, although
13913      clever use of the fields "player->active" etc. would probably also work */
13914 #if 1
13915   if (player->killed)
13916     return;
13917 #endif
13918
13919   player->killed = TRUE;
13920
13921   // remove accessible field at the player's position
13922   Tile[jx][jy] = EL_EMPTY;
13923
13924   // deactivate shield (else Bang()/Explode() would not work right)
13925   player->shield_normal_time_left = 0;
13926   player->shield_deadly_time_left = 0;
13927
13928 #if 0
13929   Debug("game:playing:KillPlayer",
13930         "1: killed == %d, active == %d, reanimated == %d",
13931         player->killed, player->active, player->reanimated);
13932 #endif
13933
13934   Bang(jx, jy);
13935
13936 #if 0
13937   Debug("game:playing:KillPlayer",
13938         "2: killed == %d, active == %d, reanimated == %d",
13939         player->killed, player->active, player->reanimated);
13940 #endif
13941
13942   if (player->reanimated)       // killed player may have been reanimated
13943     player->killed = player->reanimated = FALSE;
13944   else
13945     BuryPlayer(player);
13946 }
13947
13948 static void KillPlayerUnlessEnemyProtected(int x, int y)
13949 {
13950   if (!PLAYER_ENEMY_PROTECTED(x, y))
13951     KillPlayer(PLAYERINFO(x, y));
13952 }
13953
13954 static void KillPlayerUnlessExplosionProtected(int x, int y)
13955 {
13956   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13957     KillPlayer(PLAYERINFO(x, y));
13958 }
13959
13960 void BuryPlayer(struct PlayerInfo *player)
13961 {
13962   int jx = player->jx, jy = player->jy;
13963
13964   if (!player->active)
13965     return;
13966
13967   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13968   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13969
13970   RemovePlayer(player);
13971
13972   player->buried = TRUE;
13973
13974   if (game.all_players_gone)
13975     game.GameOver = TRUE;
13976 }
13977
13978 void RemovePlayer(struct PlayerInfo *player)
13979 {
13980   int jx = player->jx, jy = player->jy;
13981   int i, found = FALSE;
13982
13983   player->present = FALSE;
13984   player->active = FALSE;
13985
13986   // required for some CE actions (even if the player is not active anymore)
13987   player->MovPos = 0;
13988
13989   if (!ExplodeField[jx][jy])
13990     StorePlayer[jx][jy] = 0;
13991
13992   if (player->is_moving)
13993     TEST_DrawLevelField(player->last_jx, player->last_jy);
13994
13995   for (i = 0; i < MAX_PLAYERS; i++)
13996     if (stored_player[i].active)
13997       found = TRUE;
13998
13999   if (!found)
14000   {
14001     game.all_players_gone = TRUE;
14002     game.GameOver = TRUE;
14003   }
14004
14005   game.exit_x = game.robot_wheel_x = jx;
14006   game.exit_y = game.robot_wheel_y = jy;
14007 }
14008
14009 void ExitPlayer(struct PlayerInfo *player)
14010 {
14011   DrawPlayer(player);   // needed here only to cleanup last field
14012   RemovePlayer(player);
14013
14014   if (game.players_still_needed > 0)
14015     game.players_still_needed--;
14016 }
14017
14018 static void SetFieldForSnapping(int x, int y, int element, int direction,
14019                                 int player_index_bit)
14020 {
14021   struct ElementInfo *ei = &element_info[element];
14022   int direction_bit = MV_DIR_TO_BIT(direction);
14023   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14024   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14025                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14026
14027   Tile[x][y] = EL_ELEMENT_SNAPPING;
14028   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14029   MovDir[x][y] = direction;
14030   Store[x][y] = element;
14031   Store2[x][y] = player_index_bit;
14032
14033   ResetGfxAnimation(x, y);
14034
14035   GfxElement[x][y] = element;
14036   GfxAction[x][y] = action;
14037   GfxDir[x][y] = direction;
14038   GfxFrame[x][y] = -1;
14039 }
14040
14041 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14042                                    int player_index_bit)
14043 {
14044   TestIfElementTouchesCustomElement(x, y);      // for empty space
14045
14046   if (level.finish_dig_collect)
14047   {
14048     int dig_side = MV_DIR_OPPOSITE(direction);
14049     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14050                         CE_PLAYER_COLLECTS_X);
14051
14052     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14053                                         player_index_bit, dig_side);
14054     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14055                                         player_index_bit, dig_side);
14056   }
14057 }
14058
14059 /*
14060   =============================================================================
14061   checkDiagonalPushing()
14062   -----------------------------------------------------------------------------
14063   check if diagonal input device direction results in pushing of object
14064   (by checking if the alternative direction is walkable, diggable, ...)
14065   =============================================================================
14066 */
14067
14068 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14069                                     int x, int y, int real_dx, int real_dy)
14070 {
14071   int jx, jy, dx, dy, xx, yy;
14072
14073   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14074     return TRUE;
14075
14076   // diagonal direction: check alternative direction
14077   jx = player->jx;
14078   jy = player->jy;
14079   dx = x - jx;
14080   dy = y - jy;
14081   xx = jx + (dx == 0 ? real_dx : 0);
14082   yy = jy + (dy == 0 ? real_dy : 0);
14083
14084   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14085 }
14086
14087 /*
14088   =============================================================================
14089   DigField()
14090   -----------------------------------------------------------------------------
14091   x, y:                 field next to player (non-diagonal) to try to dig to
14092   real_dx, real_dy:     direction as read from input device (can be diagonal)
14093   =============================================================================
14094 */
14095
14096 static int DigField(struct PlayerInfo *player,
14097                     int oldx, int oldy, int x, int y,
14098                     int real_dx, int real_dy, int mode)
14099 {
14100   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14101   boolean player_was_pushing = player->is_pushing;
14102   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14103   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14104   int jx = oldx, jy = oldy;
14105   int dx = x - jx, dy = y - jy;
14106   int nextx = x + dx, nexty = y + dy;
14107   int move_direction = (dx == -1 ? MV_LEFT  :
14108                         dx == +1 ? MV_RIGHT :
14109                         dy == -1 ? MV_UP    :
14110                         dy == +1 ? MV_DOWN  : MV_NONE);
14111   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14112   int dig_side = MV_DIR_OPPOSITE(move_direction);
14113   int old_element = Tile[jx][jy];
14114   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14115   int collect_count;
14116
14117   if (is_player)                // function can also be called by EL_PENGUIN
14118   {
14119     if (player->MovPos == 0)
14120     {
14121       player->is_digging = FALSE;
14122       player->is_collecting = FALSE;
14123     }
14124
14125     if (player->MovPos == 0)    // last pushing move finished
14126       player->is_pushing = FALSE;
14127
14128     if (mode == DF_NO_PUSH)     // player just stopped pushing
14129     {
14130       player->is_switching = FALSE;
14131       player->push_delay = -1;
14132
14133       return MP_NO_ACTION;
14134     }
14135   }
14136   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14137     old_element = Back[jx][jy];
14138
14139   // in case of element dropped at player position, check background
14140   else if (Back[jx][jy] != EL_EMPTY &&
14141            game.engine_version >= VERSION_IDENT(2,2,0,0))
14142     old_element = Back[jx][jy];
14143
14144   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14145     return MP_NO_ACTION;        // field has no opening in this direction
14146
14147   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14148     return MP_NO_ACTION;        // field has no opening in this direction
14149
14150   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14151   {
14152     SplashAcid(x, y);
14153
14154     Tile[jx][jy] = player->artwork_element;
14155     InitMovingField(jx, jy, MV_DOWN);
14156     Store[jx][jy] = EL_ACID;
14157     ContinueMoving(jx, jy);
14158     BuryPlayer(player);
14159
14160     return MP_DONT_RUN_INTO;
14161   }
14162
14163   if (player_can_move && DONT_RUN_INTO(element))
14164   {
14165     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14166
14167     return MP_DONT_RUN_INTO;
14168   }
14169
14170   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14171     return MP_NO_ACTION;
14172
14173   collect_count = element_info[element].collect_count_initial;
14174
14175   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14176     return MP_NO_ACTION;
14177
14178   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14179     player_can_move = player_can_move_or_snap;
14180
14181   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14182       game.engine_version >= VERSION_IDENT(2,2,0,0))
14183   {
14184     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14185                                player->index_bit, dig_side);
14186     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14187                                         player->index_bit, dig_side);
14188
14189     if (element == EL_DC_LANDMINE)
14190       Bang(x, y);
14191
14192     if (Tile[x][y] != element)          // field changed by snapping
14193       return MP_ACTION;
14194
14195     return MP_NO_ACTION;
14196   }
14197
14198   if (player->gravity && is_player && !player->is_auto_moving &&
14199       canFallDown(player) && move_direction != MV_DOWN &&
14200       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14201     return MP_NO_ACTION;        // player cannot walk here due to gravity
14202
14203   if (player_can_move &&
14204       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14205   {
14206     int sound_element = SND_ELEMENT(element);
14207     int sound_action = ACTION_WALKING;
14208
14209     if (IS_RND_GATE(element))
14210     {
14211       if (!player->key[RND_GATE_NR(element)])
14212         return MP_NO_ACTION;
14213     }
14214     else if (IS_RND_GATE_GRAY(element))
14215     {
14216       if (!player->key[RND_GATE_GRAY_NR(element)])
14217         return MP_NO_ACTION;
14218     }
14219     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14220     {
14221       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14222         return MP_NO_ACTION;
14223     }
14224     else if (element == EL_EXIT_OPEN ||
14225              element == EL_EM_EXIT_OPEN ||
14226              element == EL_EM_EXIT_OPENING ||
14227              element == EL_STEEL_EXIT_OPEN ||
14228              element == EL_EM_STEEL_EXIT_OPEN ||
14229              element == EL_EM_STEEL_EXIT_OPENING ||
14230              element == EL_SP_EXIT_OPEN ||
14231              element == EL_SP_EXIT_OPENING)
14232     {
14233       sound_action = ACTION_PASSING;    // player is passing exit
14234     }
14235     else if (element == EL_EMPTY)
14236     {
14237       sound_action = ACTION_MOVING;             // nothing to walk on
14238     }
14239
14240     // play sound from background or player, whatever is available
14241     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14242       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14243     else
14244       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14245   }
14246   else if (player_can_move &&
14247            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14248   {
14249     if (!ACCESS_FROM(element, opposite_direction))
14250       return MP_NO_ACTION;      // field not accessible from this direction
14251
14252     if (CAN_MOVE(element))      // only fixed elements can be passed!
14253       return MP_NO_ACTION;
14254
14255     if (IS_EM_GATE(element))
14256     {
14257       if (!player->key[EM_GATE_NR(element)])
14258         return MP_NO_ACTION;
14259     }
14260     else if (IS_EM_GATE_GRAY(element))
14261     {
14262       if (!player->key[EM_GATE_GRAY_NR(element)])
14263         return MP_NO_ACTION;
14264     }
14265     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14266     {
14267       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14268         return MP_NO_ACTION;
14269     }
14270     else if (IS_EMC_GATE(element))
14271     {
14272       if (!player->key[EMC_GATE_NR(element)])
14273         return MP_NO_ACTION;
14274     }
14275     else if (IS_EMC_GATE_GRAY(element))
14276     {
14277       if (!player->key[EMC_GATE_GRAY_NR(element)])
14278         return MP_NO_ACTION;
14279     }
14280     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14281     {
14282       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14283         return MP_NO_ACTION;
14284     }
14285     else if (element == EL_DC_GATE_WHITE ||
14286              element == EL_DC_GATE_WHITE_GRAY ||
14287              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14288     {
14289       if (player->num_white_keys == 0)
14290         return MP_NO_ACTION;
14291
14292       player->num_white_keys--;
14293     }
14294     else if (IS_SP_PORT(element))
14295     {
14296       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14297           element == EL_SP_GRAVITY_PORT_RIGHT ||
14298           element == EL_SP_GRAVITY_PORT_UP ||
14299           element == EL_SP_GRAVITY_PORT_DOWN)
14300         player->gravity = !player->gravity;
14301       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14302                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14303                element == EL_SP_GRAVITY_ON_PORT_UP ||
14304                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14305         player->gravity = TRUE;
14306       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14307                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14308                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14309                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14310         player->gravity = FALSE;
14311     }
14312
14313     // automatically move to the next field with double speed
14314     player->programmed_action = move_direction;
14315
14316     if (player->move_delay_reset_counter == 0)
14317     {
14318       player->move_delay_reset_counter = 2;     // two double speed steps
14319
14320       DOUBLE_PLAYER_SPEED(player);
14321     }
14322
14323     PlayLevelSoundAction(x, y, ACTION_PASSING);
14324   }
14325   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14326   {
14327     RemoveField(x, y);
14328
14329     if (mode != DF_SNAP)
14330     {
14331       GfxElement[x][y] = GFX_ELEMENT(element);
14332       player->is_digging = TRUE;
14333     }
14334
14335     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14336
14337     // use old behaviour for old levels (digging)
14338     if (!level.finish_dig_collect)
14339     {
14340       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14341                                           player->index_bit, dig_side);
14342
14343       // if digging triggered player relocation, finish digging tile
14344       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14345         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14346     }
14347
14348     if (mode == DF_SNAP)
14349     {
14350       if (level.block_snap_field)
14351         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14352       else
14353         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14354
14355       // use old behaviour for old levels (snapping)
14356       if (!level.finish_dig_collect)
14357         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14358                                             player->index_bit, dig_side);
14359     }
14360   }
14361   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14362   {
14363     RemoveField(x, y);
14364
14365     if (is_player && mode != DF_SNAP)
14366     {
14367       GfxElement[x][y] = element;
14368       player->is_collecting = TRUE;
14369     }
14370
14371     if (element == EL_SPEED_PILL)
14372     {
14373       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14374     }
14375     else if (element == EL_EXTRA_TIME && level.time > 0)
14376     {
14377       TimeLeft += level.extra_time;
14378
14379       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14380
14381       DisplayGameControlValues();
14382     }
14383     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14384     {
14385       int shield_time = (element == EL_SHIELD_DEADLY ?
14386                          level.shield_deadly_time :
14387                          level.shield_normal_time);
14388
14389       player->shield_normal_time_left += shield_time;
14390       if (element == EL_SHIELD_DEADLY)
14391         player->shield_deadly_time_left += shield_time;
14392     }
14393     else if (element == EL_DYNAMITE ||
14394              element == EL_EM_DYNAMITE ||
14395              element == EL_SP_DISK_RED)
14396     {
14397       if (player->inventory_size < MAX_INVENTORY_SIZE)
14398         player->inventory_element[player->inventory_size++] = element;
14399
14400       DrawGameDoorValues();
14401     }
14402     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14403     {
14404       player->dynabomb_count++;
14405       player->dynabombs_left++;
14406     }
14407     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14408     {
14409       player->dynabomb_size++;
14410     }
14411     else if (element == EL_DYNABOMB_INCREASE_POWER)
14412     {
14413       player->dynabomb_xl = TRUE;
14414     }
14415     else if (IS_KEY(element))
14416     {
14417       player->key[KEY_NR(element)] = TRUE;
14418
14419       DrawGameDoorValues();
14420     }
14421     else if (element == EL_DC_KEY_WHITE)
14422     {
14423       player->num_white_keys++;
14424
14425       // display white keys?
14426       // DrawGameDoorValues();
14427     }
14428     else if (IS_ENVELOPE(element))
14429     {
14430       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14431
14432       if (!wait_for_snapping)
14433         player->show_envelope = element;
14434     }
14435     else if (element == EL_EMC_LENSES)
14436     {
14437       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14438
14439       RedrawAllInvisibleElementsForLenses();
14440     }
14441     else if (element == EL_EMC_MAGNIFIER)
14442     {
14443       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14444
14445       RedrawAllInvisibleElementsForMagnifier();
14446     }
14447     else if (IS_DROPPABLE(element) ||
14448              IS_THROWABLE(element))     // can be collected and dropped
14449     {
14450       int i;
14451
14452       if (collect_count == 0)
14453         player->inventory_infinite_element = element;
14454       else
14455         for (i = 0; i < collect_count; i++)
14456           if (player->inventory_size < MAX_INVENTORY_SIZE)
14457             player->inventory_element[player->inventory_size++] = element;
14458
14459       DrawGameDoorValues();
14460     }
14461     else if (collect_count > 0)
14462     {
14463       game.gems_still_needed -= collect_count;
14464       if (game.gems_still_needed < 0)
14465         game.gems_still_needed = 0;
14466
14467       game.snapshot.collected_item = TRUE;
14468
14469       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14470
14471       DisplayGameControlValues();
14472     }
14473
14474     RaiseScoreElement(element);
14475     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14476
14477     // use old behaviour for old levels (collecting)
14478     if (!level.finish_dig_collect && is_player)
14479     {
14480       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14481                                           player->index_bit, dig_side);
14482
14483       // if collecting triggered player relocation, finish collecting tile
14484       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14485         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14486     }
14487
14488     if (mode == DF_SNAP)
14489     {
14490       if (level.block_snap_field)
14491         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14492       else
14493         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14494
14495       // use old behaviour for old levels (snapping)
14496       if (!level.finish_dig_collect)
14497         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14498                                             player->index_bit, dig_side);
14499     }
14500   }
14501   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14502   {
14503     if (mode == DF_SNAP && element != EL_BD_ROCK)
14504       return MP_NO_ACTION;
14505
14506     if (CAN_FALL(element) && dy)
14507       return MP_NO_ACTION;
14508
14509     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14510         !(element == EL_SPRING && level.use_spring_bug))
14511       return MP_NO_ACTION;
14512
14513     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14514         ((move_direction & MV_VERTICAL &&
14515           ((element_info[element].move_pattern & MV_LEFT &&
14516             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14517            (element_info[element].move_pattern & MV_RIGHT &&
14518             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14519          (move_direction & MV_HORIZONTAL &&
14520           ((element_info[element].move_pattern & MV_UP &&
14521             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14522            (element_info[element].move_pattern & MV_DOWN &&
14523             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14524       return MP_NO_ACTION;
14525
14526     // do not push elements already moving away faster than player
14527     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14528         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14529       return MP_NO_ACTION;
14530
14531     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14532     {
14533       if (player->push_delay_value == -1 || !player_was_pushing)
14534         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14535     }
14536     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14537     {
14538       if (player->push_delay_value == -1)
14539         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14540     }
14541     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14542     {
14543       if (!player->is_pushing)
14544         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14545     }
14546
14547     player->is_pushing = TRUE;
14548     player->is_active = TRUE;
14549
14550     if (!(IN_LEV_FIELD(nextx, nexty) &&
14551           (IS_FREE(nextx, nexty) ||
14552            (IS_SB_ELEMENT(element) &&
14553             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14554            (IS_CUSTOM_ELEMENT(element) &&
14555             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14556       return MP_NO_ACTION;
14557
14558     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14559       return MP_NO_ACTION;
14560
14561     if (player->push_delay == -1)       // new pushing; restart delay
14562       player->push_delay = 0;
14563
14564     if (player->push_delay < player->push_delay_value &&
14565         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14566         element != EL_SPRING && element != EL_BALLOON)
14567     {
14568       // make sure that there is no move delay before next try to push
14569       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14570         player->move_delay = 0;
14571
14572       return MP_NO_ACTION;
14573     }
14574
14575     if (IS_CUSTOM_ELEMENT(element) &&
14576         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14577     {
14578       if (!DigFieldByCE(nextx, nexty, element))
14579         return MP_NO_ACTION;
14580     }
14581
14582     if (IS_SB_ELEMENT(element))
14583     {
14584       boolean sokoban_task_solved = FALSE;
14585
14586       if (element == EL_SOKOBAN_FIELD_FULL)
14587       {
14588         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14589
14590         IncrementSokobanFieldsNeeded();
14591         IncrementSokobanObjectsNeeded();
14592       }
14593
14594       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14595       {
14596         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14597
14598         DecrementSokobanFieldsNeeded();
14599         DecrementSokobanObjectsNeeded();
14600
14601         // sokoban object was pushed from empty field to sokoban field
14602         if (Back[x][y] == EL_EMPTY)
14603           sokoban_task_solved = TRUE;
14604       }
14605
14606       Tile[x][y] = EL_SOKOBAN_OBJECT;
14607
14608       if (Back[x][y] == Back[nextx][nexty])
14609         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14610       else if (Back[x][y] != 0)
14611         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14612                                     ACTION_EMPTYING);
14613       else
14614         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14615                                     ACTION_FILLING);
14616
14617       if (sokoban_task_solved &&
14618           game.sokoban_fields_still_needed == 0 &&
14619           game.sokoban_objects_still_needed == 0 &&
14620           level.auto_exit_sokoban)
14621       {
14622         game.players_still_needed = 0;
14623
14624         LevelSolved();
14625
14626         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14627       }
14628     }
14629     else
14630       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14631
14632     InitMovingField(x, y, move_direction);
14633     GfxAction[x][y] = ACTION_PUSHING;
14634
14635     if (mode == DF_SNAP)
14636       ContinueMoving(x, y);
14637     else
14638       MovPos[x][y] = (dx != 0 ? dx : dy);
14639
14640     Pushed[x][y] = TRUE;
14641     Pushed[nextx][nexty] = TRUE;
14642
14643     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14644       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14645     else
14646       player->push_delay_value = -1;    // get new value later
14647
14648     // check for element change _after_ element has been pushed
14649     if (game.use_change_when_pushing_bug)
14650     {
14651       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14652                                  player->index_bit, dig_side);
14653       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14654                                           player->index_bit, dig_side);
14655     }
14656   }
14657   else if (IS_SWITCHABLE(element))
14658   {
14659     if (PLAYER_SWITCHING(player, x, y))
14660     {
14661       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14662                                           player->index_bit, dig_side);
14663
14664       return MP_ACTION;
14665     }
14666
14667     player->is_switching = TRUE;
14668     player->switch_x = x;
14669     player->switch_y = y;
14670
14671     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14672
14673     if (element == EL_ROBOT_WHEEL)
14674     {
14675       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14676
14677       game.robot_wheel_x = x;
14678       game.robot_wheel_y = y;
14679       game.robot_wheel_active = TRUE;
14680
14681       TEST_DrawLevelField(x, y);
14682     }
14683     else if (element == EL_SP_TERMINAL)
14684     {
14685       int xx, yy;
14686
14687       SCAN_PLAYFIELD(xx, yy)
14688       {
14689         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14690         {
14691           Bang(xx, yy);
14692         }
14693         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14694         {
14695           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14696
14697           ResetGfxAnimation(xx, yy);
14698           TEST_DrawLevelField(xx, yy);
14699         }
14700       }
14701     }
14702     else if (IS_BELT_SWITCH(element))
14703     {
14704       ToggleBeltSwitch(x, y);
14705     }
14706     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14707              element == EL_SWITCHGATE_SWITCH_DOWN ||
14708              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14709              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14710     {
14711       ToggleSwitchgateSwitch();
14712     }
14713     else if (element == EL_LIGHT_SWITCH ||
14714              element == EL_LIGHT_SWITCH_ACTIVE)
14715     {
14716       ToggleLightSwitch(x, y);
14717     }
14718     else if (element == EL_TIMEGATE_SWITCH ||
14719              element == EL_DC_TIMEGATE_SWITCH)
14720     {
14721       ActivateTimegateSwitch(x, y);
14722     }
14723     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14724              element == EL_BALLOON_SWITCH_RIGHT ||
14725              element == EL_BALLOON_SWITCH_UP    ||
14726              element == EL_BALLOON_SWITCH_DOWN  ||
14727              element == EL_BALLOON_SWITCH_NONE  ||
14728              element == EL_BALLOON_SWITCH_ANY)
14729     {
14730       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14731                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14732                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14733                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14734                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14735                              move_direction);
14736     }
14737     else if (element == EL_LAMP)
14738     {
14739       Tile[x][y] = EL_LAMP_ACTIVE;
14740       game.lights_still_needed--;
14741
14742       ResetGfxAnimation(x, y);
14743       TEST_DrawLevelField(x, y);
14744     }
14745     else if (element == EL_TIME_ORB_FULL)
14746     {
14747       Tile[x][y] = EL_TIME_ORB_EMPTY;
14748
14749       if (level.time > 0 || level.use_time_orb_bug)
14750       {
14751         TimeLeft += level.time_orb_time;
14752         game.no_level_time_limit = FALSE;
14753
14754         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14755
14756         DisplayGameControlValues();
14757       }
14758
14759       ResetGfxAnimation(x, y);
14760       TEST_DrawLevelField(x, y);
14761     }
14762     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14763              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14764     {
14765       int xx, yy;
14766
14767       game.ball_active = !game.ball_active;
14768
14769       SCAN_PLAYFIELD(xx, yy)
14770       {
14771         int e = Tile[xx][yy];
14772
14773         if (game.ball_active)
14774         {
14775           if (e == EL_EMC_MAGIC_BALL)
14776             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14777           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14778             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14779         }
14780         else
14781         {
14782           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14783             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14784           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14785             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14786         }
14787       }
14788     }
14789
14790     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14791                                         player->index_bit, dig_side);
14792
14793     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14794                                         player->index_bit, dig_side);
14795
14796     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14797                                         player->index_bit, dig_side);
14798
14799     return MP_ACTION;
14800   }
14801   else
14802   {
14803     if (!PLAYER_SWITCHING(player, x, y))
14804     {
14805       player->is_switching = TRUE;
14806       player->switch_x = x;
14807       player->switch_y = y;
14808
14809       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14810                                  player->index_bit, dig_side);
14811       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14812                                           player->index_bit, dig_side);
14813
14814       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14815                                  player->index_bit, dig_side);
14816       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14817                                           player->index_bit, dig_side);
14818     }
14819
14820     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14821                                player->index_bit, dig_side);
14822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14823                                         player->index_bit, dig_side);
14824
14825     return MP_NO_ACTION;
14826   }
14827
14828   player->push_delay = -1;
14829
14830   if (is_player)                // function can also be called by EL_PENGUIN
14831   {
14832     if (Tile[x][y] != element)          // really digged/collected something
14833     {
14834       player->is_collecting = !player->is_digging;
14835       player->is_active = TRUE;
14836
14837       player->last_removed_element = element;
14838     }
14839   }
14840
14841   return MP_MOVING;
14842 }
14843
14844 static boolean DigFieldByCE(int x, int y, int digging_element)
14845 {
14846   int element = Tile[x][y];
14847
14848   if (!IS_FREE(x, y))
14849   {
14850     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14851                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14852                   ACTION_BREAKING);
14853
14854     // no element can dig solid indestructible elements
14855     if (IS_INDESTRUCTIBLE(element) &&
14856         !IS_DIGGABLE(element) &&
14857         !IS_COLLECTIBLE(element))
14858       return FALSE;
14859
14860     if (AmoebaNr[x][y] &&
14861         (element == EL_AMOEBA_FULL ||
14862          element == EL_BD_AMOEBA ||
14863          element == EL_AMOEBA_GROWING))
14864     {
14865       AmoebaCnt[AmoebaNr[x][y]]--;
14866       AmoebaCnt2[AmoebaNr[x][y]]--;
14867     }
14868
14869     if (IS_MOVING(x, y))
14870       RemoveMovingField(x, y);
14871     else
14872     {
14873       RemoveField(x, y);
14874       TEST_DrawLevelField(x, y);
14875     }
14876
14877     // if digged element was about to explode, prevent the explosion
14878     ExplodeField[x][y] = EX_TYPE_NONE;
14879
14880     PlayLevelSoundAction(x, y, action);
14881   }
14882
14883   Store[x][y] = EL_EMPTY;
14884
14885   // this makes it possible to leave the removed element again
14886   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14887     Store[x][y] = element;
14888
14889   return TRUE;
14890 }
14891
14892 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14893 {
14894   int jx = player->jx, jy = player->jy;
14895   int x = jx + dx, y = jy + dy;
14896   int snap_direction = (dx == -1 ? MV_LEFT  :
14897                         dx == +1 ? MV_RIGHT :
14898                         dy == -1 ? MV_UP    :
14899                         dy == +1 ? MV_DOWN  : MV_NONE);
14900   boolean can_continue_snapping = (level.continuous_snapping &&
14901                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14902
14903   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14904     return FALSE;
14905
14906   if (!player->active || !IN_LEV_FIELD(x, y))
14907     return FALSE;
14908
14909   if (dx && dy)
14910     return FALSE;
14911
14912   if (!dx && !dy)
14913   {
14914     if (player->MovPos == 0)
14915       player->is_pushing = FALSE;
14916
14917     player->is_snapping = FALSE;
14918
14919     if (player->MovPos == 0)
14920     {
14921       player->is_moving = FALSE;
14922       player->is_digging = FALSE;
14923       player->is_collecting = FALSE;
14924     }
14925
14926     return FALSE;
14927   }
14928
14929   // prevent snapping with already pressed snap key when not allowed
14930   if (player->is_snapping && !can_continue_snapping)
14931     return FALSE;
14932
14933   player->MovDir = snap_direction;
14934
14935   if (player->MovPos == 0)
14936   {
14937     player->is_moving = FALSE;
14938     player->is_digging = FALSE;
14939     player->is_collecting = FALSE;
14940   }
14941
14942   player->is_dropping = FALSE;
14943   player->is_dropping_pressed = FALSE;
14944   player->drop_pressed_delay = 0;
14945
14946   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14947     return FALSE;
14948
14949   player->is_snapping = TRUE;
14950   player->is_active = TRUE;
14951
14952   if (player->MovPos == 0)
14953   {
14954     player->is_moving = FALSE;
14955     player->is_digging = FALSE;
14956     player->is_collecting = FALSE;
14957   }
14958
14959   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14960     TEST_DrawLevelField(player->last_jx, player->last_jy);
14961
14962   TEST_DrawLevelField(x, y);
14963
14964   return TRUE;
14965 }
14966
14967 static boolean DropElement(struct PlayerInfo *player)
14968 {
14969   int old_element, new_element;
14970   int dropx = player->jx, dropy = player->jy;
14971   int drop_direction = player->MovDir;
14972   int drop_side = drop_direction;
14973   int drop_element = get_next_dropped_element(player);
14974
14975   /* do not drop an element on top of another element; when holding drop key
14976      pressed without moving, dropped element must move away before the next
14977      element can be dropped (this is especially important if the next element
14978      is dynamite, which can be placed on background for historical reasons) */
14979   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14980     return MP_ACTION;
14981
14982   if (IS_THROWABLE(drop_element))
14983   {
14984     dropx += GET_DX_FROM_DIR(drop_direction);
14985     dropy += GET_DY_FROM_DIR(drop_direction);
14986
14987     if (!IN_LEV_FIELD(dropx, dropy))
14988       return FALSE;
14989   }
14990
14991   old_element = Tile[dropx][dropy];     // old element at dropping position
14992   new_element = drop_element;           // default: no change when dropping
14993
14994   // check if player is active, not moving and ready to drop
14995   if (!player->active || player->MovPos || player->drop_delay > 0)
14996     return FALSE;
14997
14998   // check if player has anything that can be dropped
14999   if (new_element == EL_UNDEFINED)
15000     return FALSE;
15001
15002   // only set if player has anything that can be dropped
15003   player->is_dropping_pressed = TRUE;
15004
15005   // check if drop key was pressed long enough for EM style dynamite
15006   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15007     return FALSE;
15008
15009   // check if anything can be dropped at the current position
15010   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15011     return FALSE;
15012
15013   // collected custom elements can only be dropped on empty fields
15014   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15015     return FALSE;
15016
15017   if (old_element != EL_EMPTY)
15018     Back[dropx][dropy] = old_element;   // store old element on this field
15019
15020   ResetGfxAnimation(dropx, dropy);
15021   ResetRandomAnimationValue(dropx, dropy);
15022
15023   if (player->inventory_size > 0 ||
15024       player->inventory_infinite_element != EL_UNDEFINED)
15025   {
15026     if (player->inventory_size > 0)
15027     {
15028       player->inventory_size--;
15029
15030       DrawGameDoorValues();
15031
15032       if (new_element == EL_DYNAMITE)
15033         new_element = EL_DYNAMITE_ACTIVE;
15034       else if (new_element == EL_EM_DYNAMITE)
15035         new_element = EL_EM_DYNAMITE_ACTIVE;
15036       else if (new_element == EL_SP_DISK_RED)
15037         new_element = EL_SP_DISK_RED_ACTIVE;
15038     }
15039
15040     Tile[dropx][dropy] = new_element;
15041
15042     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15043       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15044                           el2img(Tile[dropx][dropy]), 0);
15045
15046     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15047
15048     // needed if previous element just changed to "empty" in the last frame
15049     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15050
15051     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15052                                player->index_bit, drop_side);
15053     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15054                                         CE_PLAYER_DROPS_X,
15055                                         player->index_bit, drop_side);
15056
15057     TestIfElementTouchesCustomElement(dropx, dropy);
15058   }
15059   else          // player is dropping a dyna bomb
15060   {
15061     player->dynabombs_left--;
15062
15063     Tile[dropx][dropy] = new_element;
15064
15065     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15066       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15067                           el2img(Tile[dropx][dropy]), 0);
15068
15069     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15070   }
15071
15072   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15073     InitField_WithBug1(dropx, dropy, FALSE);
15074
15075   new_element = Tile[dropx][dropy];     // element might have changed
15076
15077   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15078       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15079   {
15080     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15081       MovDir[dropx][dropy] = drop_direction;
15082
15083     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15084
15085     // do not cause impact style collision by dropping elements that can fall
15086     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15087   }
15088
15089   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15090   player->is_dropping = TRUE;
15091
15092   player->drop_pressed_delay = 0;
15093   player->is_dropping_pressed = FALSE;
15094
15095   player->drop_x = dropx;
15096   player->drop_y = dropy;
15097
15098   return TRUE;
15099 }
15100
15101 // ----------------------------------------------------------------------------
15102 // game sound playing functions
15103 // ----------------------------------------------------------------------------
15104
15105 static int *loop_sound_frame = NULL;
15106 static int *loop_sound_volume = NULL;
15107
15108 void InitPlayLevelSound(void)
15109 {
15110   int num_sounds = getSoundListSize();
15111
15112   checked_free(loop_sound_frame);
15113   checked_free(loop_sound_volume);
15114
15115   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15116   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15117 }
15118
15119 static void PlayLevelSound(int x, int y, int nr)
15120 {
15121   int sx = SCREENX(x), sy = SCREENY(y);
15122   int volume, stereo_position;
15123   int max_distance = 8;
15124   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15125
15126   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15127       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15128     return;
15129
15130   if (!IN_LEV_FIELD(x, y) ||
15131       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15132       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15133     return;
15134
15135   volume = SOUND_MAX_VOLUME;
15136
15137   if (!IN_SCR_FIELD(sx, sy))
15138   {
15139     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15140     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15141
15142     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15143   }
15144
15145   stereo_position = (SOUND_MAX_LEFT +
15146                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15147                      (SCR_FIELDX + 2 * max_distance));
15148
15149   if (IS_LOOP_SOUND(nr))
15150   {
15151     /* This assures that quieter loop sounds do not overwrite louder ones,
15152        while restarting sound volume comparison with each new game frame. */
15153
15154     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15155       return;
15156
15157     loop_sound_volume[nr] = volume;
15158     loop_sound_frame[nr] = FrameCounter;
15159   }
15160
15161   PlaySoundExt(nr, volume, stereo_position, type);
15162 }
15163
15164 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15165 {
15166   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15167                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15168                  y < LEVELY(BY1) ? LEVELY(BY1) :
15169                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15170                  sound_action);
15171 }
15172
15173 static void PlayLevelSoundAction(int x, int y, int action)
15174 {
15175   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15176 }
15177
15178 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15179 {
15180   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15181
15182   if (sound_effect != SND_UNDEFINED)
15183     PlayLevelSound(x, y, sound_effect);
15184 }
15185
15186 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15187                                               int action)
15188 {
15189   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15190
15191   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15192     PlayLevelSound(x, y, sound_effect);
15193 }
15194
15195 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15196 {
15197   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15198
15199   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15200     PlayLevelSound(x, y, sound_effect);
15201 }
15202
15203 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15204 {
15205   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15206
15207   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15208     StopSound(sound_effect);
15209 }
15210
15211 static int getLevelMusicNr(void)
15212 {
15213   if (levelset.music[level_nr] != MUS_UNDEFINED)
15214     return levelset.music[level_nr];            // from config file
15215   else
15216     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15217 }
15218
15219 static void FadeLevelSounds(void)
15220 {
15221   FadeSounds();
15222 }
15223
15224 static void FadeLevelMusic(void)
15225 {
15226   int music_nr = getLevelMusicNr();
15227   char *curr_music = getCurrentlyPlayingMusicFilename();
15228   char *next_music = getMusicInfoEntryFilename(music_nr);
15229
15230   if (!strEqual(curr_music, next_music))
15231     FadeMusic();
15232 }
15233
15234 void FadeLevelSoundsAndMusic(void)
15235 {
15236   FadeLevelSounds();
15237   FadeLevelMusic();
15238 }
15239
15240 static void PlayLevelMusic(void)
15241 {
15242   int music_nr = getLevelMusicNr();
15243   char *curr_music = getCurrentlyPlayingMusicFilename();
15244   char *next_music = getMusicInfoEntryFilename(music_nr);
15245
15246   if (!strEqual(curr_music, next_music))
15247     PlayMusicLoop(music_nr);
15248 }
15249
15250 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15251 {
15252   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15253   int offset = 0;
15254   int x = xx - offset;
15255   int y = yy - offset;
15256
15257   switch (sample)
15258   {
15259     case SOUND_blank:
15260       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15261       break;
15262
15263     case SOUND_roll:
15264       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15265       break;
15266
15267     case SOUND_stone:
15268       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15269       break;
15270
15271     case SOUND_nut:
15272       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15273       break;
15274
15275     case SOUND_crack:
15276       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15277       break;
15278
15279     case SOUND_bug:
15280       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15281       break;
15282
15283     case SOUND_tank:
15284       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15285       break;
15286
15287     case SOUND_android_clone:
15288       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15289       break;
15290
15291     case SOUND_android_move:
15292       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15293       break;
15294
15295     case SOUND_spring:
15296       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15297       break;
15298
15299     case SOUND_slurp:
15300       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15301       break;
15302
15303     case SOUND_eater:
15304       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15305       break;
15306
15307     case SOUND_eater_eat:
15308       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15309       break;
15310
15311     case SOUND_alien:
15312       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15313       break;
15314
15315     case SOUND_collect:
15316       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15317       break;
15318
15319     case SOUND_diamond:
15320       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15321       break;
15322
15323     case SOUND_squash:
15324       // !!! CHECK THIS !!!
15325 #if 1
15326       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15327 #else
15328       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15329 #endif
15330       break;
15331
15332     case SOUND_wonderfall:
15333       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15334       break;
15335
15336     case SOUND_drip:
15337       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15338       break;
15339
15340     case SOUND_push:
15341       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15342       break;
15343
15344     case SOUND_dirt:
15345       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15346       break;
15347
15348     case SOUND_acid:
15349       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15350       break;
15351
15352     case SOUND_ball:
15353       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15354       break;
15355
15356     case SOUND_slide:
15357       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15358       break;
15359
15360     case SOUND_wonder:
15361       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15362       break;
15363
15364     case SOUND_door:
15365       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15366       break;
15367
15368     case SOUND_exit_open:
15369       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15370       break;
15371
15372     case SOUND_exit_leave:
15373       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15374       break;
15375
15376     case SOUND_dynamite:
15377       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15378       break;
15379
15380     case SOUND_tick:
15381       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15382       break;
15383
15384     case SOUND_press:
15385       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15386       break;
15387
15388     case SOUND_wheel:
15389       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15390       break;
15391
15392     case SOUND_boom:
15393       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15394       break;
15395
15396     case SOUND_die:
15397       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15398       break;
15399
15400     case SOUND_time:
15401       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15402       break;
15403
15404     default:
15405       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15406       break;
15407   }
15408 }
15409
15410 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15411 {
15412   int element = map_element_SP_to_RND(element_sp);
15413   int action = map_action_SP_to_RND(action_sp);
15414   int offset = (setup.sp_show_border_elements ? 0 : 1);
15415   int x = xx - offset;
15416   int y = yy - offset;
15417
15418   PlayLevelSoundElementAction(x, y, element, action);
15419 }
15420
15421 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15422 {
15423   int element = map_element_MM_to_RND(element_mm);
15424   int action = map_action_MM_to_RND(action_mm);
15425   int offset = 0;
15426   int x = xx - offset;
15427   int y = yy - offset;
15428
15429   if (!IS_MM_ELEMENT(element))
15430     element = EL_MM_DEFAULT;
15431
15432   PlayLevelSoundElementAction(x, y, element, action);
15433 }
15434
15435 void PlaySound_MM(int sound_mm)
15436 {
15437   int sound = map_sound_MM_to_RND(sound_mm);
15438
15439   if (sound == SND_UNDEFINED)
15440     return;
15441
15442   PlaySound(sound);
15443 }
15444
15445 void PlaySoundLoop_MM(int sound_mm)
15446 {
15447   int sound = map_sound_MM_to_RND(sound_mm);
15448
15449   if (sound == SND_UNDEFINED)
15450     return;
15451
15452   PlaySoundLoop(sound);
15453 }
15454
15455 void StopSound_MM(int sound_mm)
15456 {
15457   int sound = map_sound_MM_to_RND(sound_mm);
15458
15459   if (sound == SND_UNDEFINED)
15460     return;
15461
15462   StopSound(sound);
15463 }
15464
15465 void RaiseScore(int value)
15466 {
15467   game.score += value;
15468
15469   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15470
15471   DisplayGameControlValues();
15472 }
15473
15474 void RaiseScoreElement(int element)
15475 {
15476   switch (element)
15477   {
15478     case EL_EMERALD:
15479     case EL_BD_DIAMOND:
15480     case EL_EMERALD_YELLOW:
15481     case EL_EMERALD_RED:
15482     case EL_EMERALD_PURPLE:
15483     case EL_SP_INFOTRON:
15484       RaiseScore(level.score[SC_EMERALD]);
15485       break;
15486     case EL_DIAMOND:
15487       RaiseScore(level.score[SC_DIAMOND]);
15488       break;
15489     case EL_CRYSTAL:
15490       RaiseScore(level.score[SC_CRYSTAL]);
15491       break;
15492     case EL_PEARL:
15493       RaiseScore(level.score[SC_PEARL]);
15494       break;
15495     case EL_BUG:
15496     case EL_BD_BUTTERFLY:
15497     case EL_SP_ELECTRON:
15498       RaiseScore(level.score[SC_BUG]);
15499       break;
15500     case EL_SPACESHIP:
15501     case EL_BD_FIREFLY:
15502     case EL_SP_SNIKSNAK:
15503       RaiseScore(level.score[SC_SPACESHIP]);
15504       break;
15505     case EL_YAMYAM:
15506     case EL_DARK_YAMYAM:
15507       RaiseScore(level.score[SC_YAMYAM]);
15508       break;
15509     case EL_ROBOT:
15510       RaiseScore(level.score[SC_ROBOT]);
15511       break;
15512     case EL_PACMAN:
15513       RaiseScore(level.score[SC_PACMAN]);
15514       break;
15515     case EL_NUT:
15516       RaiseScore(level.score[SC_NUT]);
15517       break;
15518     case EL_DYNAMITE:
15519     case EL_EM_DYNAMITE:
15520     case EL_SP_DISK_RED:
15521     case EL_DYNABOMB_INCREASE_NUMBER:
15522     case EL_DYNABOMB_INCREASE_SIZE:
15523     case EL_DYNABOMB_INCREASE_POWER:
15524       RaiseScore(level.score[SC_DYNAMITE]);
15525       break;
15526     case EL_SHIELD_NORMAL:
15527     case EL_SHIELD_DEADLY:
15528       RaiseScore(level.score[SC_SHIELD]);
15529       break;
15530     case EL_EXTRA_TIME:
15531       RaiseScore(level.extra_time_score);
15532       break;
15533     case EL_KEY_1:
15534     case EL_KEY_2:
15535     case EL_KEY_3:
15536     case EL_KEY_4:
15537     case EL_EM_KEY_1:
15538     case EL_EM_KEY_2:
15539     case EL_EM_KEY_3:
15540     case EL_EM_KEY_4:
15541     case EL_EMC_KEY_5:
15542     case EL_EMC_KEY_6:
15543     case EL_EMC_KEY_7:
15544     case EL_EMC_KEY_8:
15545     case EL_DC_KEY_WHITE:
15546       RaiseScore(level.score[SC_KEY]);
15547       break;
15548     default:
15549       RaiseScore(element_info[element].collect_score);
15550       break;
15551   }
15552 }
15553
15554 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15555 {
15556   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15557   {
15558     if (!quick_quit)
15559     {
15560       // prevent short reactivation of overlay buttons while closing door
15561       SetOverlayActive(FALSE);
15562       UnmapGameButtons();
15563
15564       // door may still be open due to skipped or envelope style request
15565       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15566     }
15567
15568     if (network.enabled)
15569       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15570     else
15571     {
15572       if (quick_quit)
15573         FadeSkipNextFadeIn();
15574
15575       SetGameStatus(GAME_MODE_MAIN);
15576
15577       DrawMainMenu();
15578     }
15579   }
15580   else          // continue playing the game
15581   {
15582     if (tape.playing && tape.deactivate_display)
15583       TapeDeactivateDisplayOff(TRUE);
15584
15585     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15586
15587     if (tape.playing && tape.deactivate_display)
15588       TapeDeactivateDisplayOn();
15589   }
15590 }
15591
15592 void RequestQuitGame(boolean escape_key_pressed)
15593 {
15594   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15595   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15596                         level_editor_test_game);
15597   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15598                           quick_quit || score_info_tape_play);
15599
15600   RequestQuitGameExt(skip_request, quick_quit,
15601                      "Do you really want to quit the game?");
15602 }
15603
15604 void RequestRestartGame(char *message)
15605 {
15606   game.restart_game_message = NULL;
15607
15608   boolean has_started_game = hasStartedNetworkGame();
15609   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15610
15611   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15612   {
15613     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15614   }
15615   else
15616   {
15617     // needed in case of envelope request to close game panel
15618     CloseDoor(DOOR_CLOSE_1);
15619
15620     SetGameStatus(GAME_MODE_MAIN);
15621
15622     DrawMainMenu();
15623   }
15624 }
15625
15626 void CheckGameOver(void)
15627 {
15628   static boolean last_game_over = FALSE;
15629   static int game_over_delay = 0;
15630   int game_over_delay_value = 50;
15631   boolean game_over = checkGameFailed();
15632
15633   // do not handle game over if request dialog is already active
15634   if (game.request_active)
15635     return;
15636
15637   // do not ask to play again if game was never actually played
15638   if (!game.GamePlayed)
15639     return;
15640
15641   if (!game_over)
15642   {
15643     last_game_over = FALSE;
15644     game_over_delay = game_over_delay_value;
15645
15646     return;
15647   }
15648
15649   if (game_over_delay > 0)
15650   {
15651     game_over_delay--;
15652
15653     return;
15654   }
15655
15656   if (last_game_over != game_over)
15657     game.restart_game_message = (hasStartedNetworkGame() ?
15658                                  "Game over! Play it again?" :
15659                                  "Game over!");
15660
15661   last_game_over = game_over;
15662 }
15663
15664 boolean checkGameSolved(void)
15665 {
15666   // set for all game engines if level was solved
15667   return game.LevelSolved_GameEnd;
15668 }
15669
15670 boolean checkGameFailed(void)
15671 {
15672   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15673     return (game_em.game_over && !game_em.level_solved);
15674   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15675     return (game_sp.game_over && !game_sp.level_solved);
15676   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15677     return (game_mm.game_over && !game_mm.level_solved);
15678   else                          // GAME_ENGINE_TYPE_RND
15679     return (game.GameOver && !game.LevelSolved);
15680 }
15681
15682 boolean checkGameEnded(void)
15683 {
15684   return (checkGameSolved() || checkGameFailed());
15685 }
15686
15687
15688 // ----------------------------------------------------------------------------
15689 // random generator functions
15690 // ----------------------------------------------------------------------------
15691
15692 unsigned int InitEngineRandom_RND(int seed)
15693 {
15694   game.num_random_calls = 0;
15695
15696   return InitEngineRandom(seed);
15697 }
15698
15699 unsigned int RND(int max)
15700 {
15701   if (max > 0)
15702   {
15703     game.num_random_calls++;
15704
15705     return GetEngineRandom(max);
15706   }
15707
15708   return 0;
15709 }
15710
15711
15712 // ----------------------------------------------------------------------------
15713 // game engine snapshot handling functions
15714 // ----------------------------------------------------------------------------
15715
15716 struct EngineSnapshotInfo
15717 {
15718   // runtime values for custom element collect score
15719   int collect_score[NUM_CUSTOM_ELEMENTS];
15720
15721   // runtime values for group element choice position
15722   int choice_pos[NUM_GROUP_ELEMENTS];
15723
15724   // runtime values for belt position animations
15725   int belt_graphic[4][NUM_BELT_PARTS];
15726   int belt_anim_mode[4][NUM_BELT_PARTS];
15727 };
15728
15729 static struct EngineSnapshotInfo engine_snapshot_rnd;
15730 static char *snapshot_level_identifier = NULL;
15731 static int snapshot_level_nr = -1;
15732
15733 static void SaveEngineSnapshotValues_RND(void)
15734 {
15735   static int belt_base_active_element[4] =
15736   {
15737     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15738     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15739     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15740     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15741   };
15742   int i, j;
15743
15744   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15745   {
15746     int element = EL_CUSTOM_START + i;
15747
15748     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15749   }
15750
15751   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15752   {
15753     int element = EL_GROUP_START + i;
15754
15755     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15756   }
15757
15758   for (i = 0; i < 4; i++)
15759   {
15760     for (j = 0; j < NUM_BELT_PARTS; j++)
15761     {
15762       int element = belt_base_active_element[i] + j;
15763       int graphic = el2img(element);
15764       int anim_mode = graphic_info[graphic].anim_mode;
15765
15766       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15767       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15768     }
15769   }
15770 }
15771
15772 static void LoadEngineSnapshotValues_RND(void)
15773 {
15774   unsigned int num_random_calls = game.num_random_calls;
15775   int i, j;
15776
15777   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15778   {
15779     int element = EL_CUSTOM_START + i;
15780
15781     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15782   }
15783
15784   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15785   {
15786     int element = EL_GROUP_START + i;
15787
15788     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15789   }
15790
15791   for (i = 0; i < 4; i++)
15792   {
15793     for (j = 0; j < NUM_BELT_PARTS; j++)
15794     {
15795       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15796       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15797
15798       graphic_info[graphic].anim_mode = anim_mode;
15799     }
15800   }
15801
15802   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15803   {
15804     InitRND(tape.random_seed);
15805     for (i = 0; i < num_random_calls; i++)
15806       RND(1);
15807   }
15808
15809   if (game.num_random_calls != num_random_calls)
15810   {
15811     Error("number of random calls out of sync");
15812     Error("number of random calls should be %d", num_random_calls);
15813     Error("number of random calls is %d", game.num_random_calls);
15814
15815     Fail("this should not happen -- please debug");
15816   }
15817 }
15818
15819 void FreeEngineSnapshotSingle(void)
15820 {
15821   FreeSnapshotSingle();
15822
15823   setString(&snapshot_level_identifier, NULL);
15824   snapshot_level_nr = -1;
15825 }
15826
15827 void FreeEngineSnapshotList(void)
15828 {
15829   FreeSnapshotList();
15830 }
15831
15832 static ListNode *SaveEngineSnapshotBuffers(void)
15833 {
15834   ListNode *buffers = NULL;
15835
15836   // copy some special values to a structure better suited for the snapshot
15837
15838   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15839     SaveEngineSnapshotValues_RND();
15840   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15841     SaveEngineSnapshotValues_EM();
15842   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15843     SaveEngineSnapshotValues_SP(&buffers);
15844   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15845     SaveEngineSnapshotValues_MM();
15846
15847   // save values stored in special snapshot structure
15848
15849   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15850     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15851   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15852     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15853   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15854     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15855   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15856     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15857
15858   // save further RND engine values
15859
15860   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15861   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15862   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15863
15864   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15865   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15866   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15867   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15868   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15869
15870   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15871   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15872   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15873
15874   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15875
15876   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15877   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15878
15879   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15880   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15881   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15882   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15883   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15884   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15885   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15886   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15887   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15888   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15889   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15890   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15891   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15892   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15893   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15894   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15895   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15896   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15897
15898   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15899   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15900
15901   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15902   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15903   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15904
15905   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15906   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15907
15908   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15911   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15912   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15914
15915   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15916   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15917
15918 #if 0
15919   ListNode *node = engine_snapshot_list_rnd;
15920   int num_bytes = 0;
15921
15922   while (node != NULL)
15923   {
15924     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15925
15926     node = node->next;
15927   }
15928
15929   Debug("game:playing:SaveEngineSnapshotBuffers",
15930         "size of engine snapshot: %d bytes", num_bytes);
15931 #endif
15932
15933   return buffers;
15934 }
15935
15936 void SaveEngineSnapshotSingle(void)
15937 {
15938   ListNode *buffers = SaveEngineSnapshotBuffers();
15939
15940   // finally save all snapshot buffers to single snapshot
15941   SaveSnapshotSingle(buffers);
15942
15943   // save level identification information
15944   setString(&snapshot_level_identifier, leveldir_current->identifier);
15945   snapshot_level_nr = level_nr;
15946 }
15947
15948 boolean CheckSaveEngineSnapshotToList(void)
15949 {
15950   boolean save_snapshot =
15951     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15952      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15953       game.snapshot.changed_action) ||
15954      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15955       game.snapshot.collected_item));
15956
15957   game.snapshot.changed_action = FALSE;
15958   game.snapshot.collected_item = FALSE;
15959   game.snapshot.save_snapshot = save_snapshot;
15960
15961   return save_snapshot;
15962 }
15963
15964 void SaveEngineSnapshotToList(void)
15965 {
15966   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15967       tape.quick_resume)
15968     return;
15969
15970   ListNode *buffers = SaveEngineSnapshotBuffers();
15971
15972   // finally save all snapshot buffers to snapshot list
15973   SaveSnapshotToList(buffers);
15974 }
15975
15976 void SaveEngineSnapshotToListInitial(void)
15977 {
15978   FreeEngineSnapshotList();
15979
15980   SaveEngineSnapshotToList();
15981 }
15982
15983 static void LoadEngineSnapshotValues(void)
15984 {
15985   // restore special values from snapshot structure
15986
15987   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15988     LoadEngineSnapshotValues_RND();
15989   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15990     LoadEngineSnapshotValues_EM();
15991   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15992     LoadEngineSnapshotValues_SP();
15993   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15994     LoadEngineSnapshotValues_MM();
15995 }
15996
15997 void LoadEngineSnapshotSingle(void)
15998 {
15999   LoadSnapshotSingle();
16000
16001   LoadEngineSnapshotValues();
16002 }
16003
16004 static void LoadEngineSnapshot_Undo(int steps)
16005 {
16006   LoadSnapshotFromList_Older(steps);
16007
16008   LoadEngineSnapshotValues();
16009 }
16010
16011 static void LoadEngineSnapshot_Redo(int steps)
16012 {
16013   LoadSnapshotFromList_Newer(steps);
16014
16015   LoadEngineSnapshotValues();
16016 }
16017
16018 boolean CheckEngineSnapshotSingle(void)
16019 {
16020   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16021           snapshot_level_nr == level_nr);
16022 }
16023
16024 boolean CheckEngineSnapshotList(void)
16025 {
16026   return CheckSnapshotList();
16027 }
16028
16029
16030 // ---------- new game button stuff -------------------------------------------
16031
16032 static struct
16033 {
16034   int graphic;
16035   struct XY *pos;
16036   int gadget_id;
16037   boolean *setup_value;
16038   boolean allowed_on_tape;
16039   boolean is_touch_button;
16040   char *infotext;
16041 } gamebutton_info[NUM_GAME_BUTTONS] =
16042 {
16043   {
16044     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16045     GAME_CTRL_ID_STOP,                          NULL,
16046     TRUE, FALSE,                                "stop game"
16047   },
16048   {
16049     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16050     GAME_CTRL_ID_PAUSE,                         NULL,
16051     TRUE, FALSE,                                "pause game"
16052   },
16053   {
16054     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16055     GAME_CTRL_ID_PLAY,                          NULL,
16056     TRUE, FALSE,                                "play game"
16057   },
16058   {
16059     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16060     GAME_CTRL_ID_UNDO,                          NULL,
16061     TRUE, FALSE,                                "undo step"
16062   },
16063   {
16064     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16065     GAME_CTRL_ID_REDO,                          NULL,
16066     TRUE, FALSE,                                "redo step"
16067   },
16068   {
16069     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16070     GAME_CTRL_ID_SAVE,                          NULL,
16071     TRUE, FALSE,                                "save game"
16072   },
16073   {
16074     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16075     GAME_CTRL_ID_PAUSE2,                        NULL,
16076     TRUE, FALSE,                                "pause game"
16077   },
16078   {
16079     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16080     GAME_CTRL_ID_LOAD,                          NULL,
16081     TRUE, FALSE,                                "load game"
16082   },
16083   {
16084     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16085     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16086     FALSE, FALSE,                               "stop game"
16087   },
16088   {
16089     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16090     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16091     FALSE, FALSE,                               "pause game"
16092   },
16093   {
16094     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16095     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16096     FALSE, FALSE,                               "play game"
16097   },
16098   {
16099     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16100     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16101     FALSE, TRUE,                                "stop game"
16102   },
16103   {
16104     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16105     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16106     FALSE, TRUE,                                "pause game"
16107   },
16108   {
16109     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16110     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16111     TRUE, FALSE,                                "background music on/off"
16112   },
16113   {
16114     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16115     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16116     TRUE, FALSE,                                "sound loops on/off"
16117   },
16118   {
16119     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16120     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16121     TRUE, FALSE,                                "normal sounds on/off"
16122   },
16123   {
16124     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16125     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16126     FALSE, FALSE,                               "background music on/off"
16127   },
16128   {
16129     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16130     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16131     FALSE, FALSE,                               "sound loops on/off"
16132   },
16133   {
16134     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16135     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16136     FALSE, FALSE,                               "normal sounds on/off"
16137   }
16138 };
16139
16140 void CreateGameButtons(void)
16141 {
16142   int i;
16143
16144   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16145   {
16146     int graphic = gamebutton_info[i].graphic;
16147     struct GraphicInfo *gfx = &graphic_info[graphic];
16148     struct XY *pos = gamebutton_info[i].pos;
16149     struct GadgetInfo *gi;
16150     int button_type;
16151     boolean checked;
16152     unsigned int event_mask;
16153     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16154     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16155     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16156     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16157     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16158     int gd_x   = gfx->src_x;
16159     int gd_y   = gfx->src_y;
16160     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16161     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16162     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16163     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16164     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16165     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16166     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16167     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16168     int id = i;
16169
16170     // do not use touch buttons if overlay touch buttons are disabled
16171     if (is_touch_button && !setup.touch.overlay_buttons)
16172       continue;
16173
16174     if (gfx->bitmap == NULL)
16175     {
16176       game_gadget[id] = NULL;
16177
16178       continue;
16179     }
16180
16181     if (id == GAME_CTRL_ID_STOP ||
16182         id == GAME_CTRL_ID_PANEL_STOP ||
16183         id == GAME_CTRL_ID_TOUCH_STOP ||
16184         id == GAME_CTRL_ID_PLAY ||
16185         id == GAME_CTRL_ID_PANEL_PLAY ||
16186         id == GAME_CTRL_ID_SAVE ||
16187         id == GAME_CTRL_ID_LOAD)
16188     {
16189       button_type = GD_TYPE_NORMAL_BUTTON;
16190       checked = FALSE;
16191       event_mask = GD_EVENT_RELEASED;
16192     }
16193     else if (id == GAME_CTRL_ID_UNDO ||
16194              id == GAME_CTRL_ID_REDO)
16195     {
16196       button_type = GD_TYPE_NORMAL_BUTTON;
16197       checked = FALSE;
16198       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16199     }
16200     else
16201     {
16202       button_type = GD_TYPE_CHECK_BUTTON;
16203       checked = (gamebutton_info[i].setup_value != NULL ?
16204                  *gamebutton_info[i].setup_value : FALSE);
16205       event_mask = GD_EVENT_PRESSED;
16206     }
16207
16208     gi = CreateGadget(GDI_CUSTOM_ID, id,
16209                       GDI_IMAGE_ID, graphic,
16210                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16211                       GDI_X, base_x + x,
16212                       GDI_Y, base_y + y,
16213                       GDI_WIDTH, gfx->width,
16214                       GDI_HEIGHT, gfx->height,
16215                       GDI_TYPE, button_type,
16216                       GDI_STATE, GD_BUTTON_UNPRESSED,
16217                       GDI_CHECKED, checked,
16218                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16219                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16220                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16221                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16222                       GDI_DIRECT_DRAW, FALSE,
16223                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16224                       GDI_EVENT_MASK, event_mask,
16225                       GDI_CALLBACK_ACTION, HandleGameButtons,
16226                       GDI_END);
16227
16228     if (gi == NULL)
16229       Fail("cannot create gadget");
16230
16231     game_gadget[id] = gi;
16232   }
16233 }
16234
16235 void FreeGameButtons(void)
16236 {
16237   int i;
16238
16239   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16240     FreeGadget(game_gadget[i]);
16241 }
16242
16243 static void UnmapGameButtonsAtSamePosition(int id)
16244 {
16245   int i;
16246
16247   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16248     if (i != id &&
16249         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16250         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16251       UnmapGadget(game_gadget[i]);
16252 }
16253
16254 static void UnmapGameButtonsAtSamePosition_All(void)
16255 {
16256   if (setup.show_load_save_buttons)
16257   {
16258     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16259     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16260     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16261   }
16262   else if (setup.show_undo_redo_buttons)
16263   {
16264     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16265     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16266     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16267   }
16268   else
16269   {
16270     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16271     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16272     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16273
16274     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16275     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16276     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16277   }
16278 }
16279
16280 void MapLoadSaveButtons(void)
16281 {
16282   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16283   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16284
16285   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16286   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16287 }
16288
16289 void MapUndoRedoButtons(void)
16290 {
16291   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16292   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16293
16294   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16295   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16296 }
16297
16298 void ModifyPauseButtons(void)
16299 {
16300   static int ids[] =
16301   {
16302     GAME_CTRL_ID_PAUSE,
16303     GAME_CTRL_ID_PAUSE2,
16304     GAME_CTRL_ID_PANEL_PAUSE,
16305     GAME_CTRL_ID_TOUCH_PAUSE,
16306     -1
16307   };
16308   int i;
16309
16310   for (i = 0; ids[i] > -1; i++)
16311     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16312 }
16313
16314 static void MapGameButtonsExt(boolean on_tape)
16315 {
16316   int i;
16317
16318   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16319   {
16320     if ((i == GAME_CTRL_ID_UNDO ||
16321          i == GAME_CTRL_ID_REDO) &&
16322         game_status != GAME_MODE_PLAYING)
16323       continue;
16324
16325     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16326       MapGadget(game_gadget[i]);
16327   }
16328
16329   UnmapGameButtonsAtSamePosition_All();
16330
16331   RedrawGameButtons();
16332 }
16333
16334 static void UnmapGameButtonsExt(boolean on_tape)
16335 {
16336   int i;
16337
16338   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16339     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16340       UnmapGadget(game_gadget[i]);
16341 }
16342
16343 static void RedrawGameButtonsExt(boolean on_tape)
16344 {
16345   int i;
16346
16347   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16348     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16349       RedrawGadget(game_gadget[i]);
16350 }
16351
16352 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16353 {
16354   if (gi == NULL)
16355     return;
16356
16357   gi->checked = state;
16358 }
16359
16360 static void RedrawSoundButtonGadget(int id)
16361 {
16362   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16363              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16364              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16365              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16366              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16367              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16368              id);
16369
16370   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16371   RedrawGadget(game_gadget[id2]);
16372 }
16373
16374 void MapGameButtons(void)
16375 {
16376   MapGameButtonsExt(FALSE);
16377 }
16378
16379 void UnmapGameButtons(void)
16380 {
16381   UnmapGameButtonsExt(FALSE);
16382 }
16383
16384 void RedrawGameButtons(void)
16385 {
16386   RedrawGameButtonsExt(FALSE);
16387 }
16388
16389 void MapGameButtonsOnTape(void)
16390 {
16391   MapGameButtonsExt(TRUE);
16392 }
16393
16394 void UnmapGameButtonsOnTape(void)
16395 {
16396   UnmapGameButtonsExt(TRUE);
16397 }
16398
16399 void RedrawGameButtonsOnTape(void)
16400 {
16401   RedrawGameButtonsExt(TRUE);
16402 }
16403
16404 static void GameUndoRedoExt(void)
16405 {
16406   ClearPlayerAction();
16407
16408   tape.pausing = TRUE;
16409
16410   RedrawPlayfield();
16411   UpdateAndDisplayGameControlValues();
16412
16413   DrawCompleteVideoDisplay();
16414   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16415   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16416   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16417
16418   ModifyPauseButtons();
16419
16420   BackToFront();
16421 }
16422
16423 static void GameUndo(int steps)
16424 {
16425   if (!CheckEngineSnapshotList())
16426     return;
16427
16428   int tape_property_bits = tape.property_bits;
16429
16430   LoadEngineSnapshot_Undo(steps);
16431
16432   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16433
16434   GameUndoRedoExt();
16435 }
16436
16437 static void GameRedo(int steps)
16438 {
16439   if (!CheckEngineSnapshotList())
16440     return;
16441
16442   int tape_property_bits = tape.property_bits;
16443
16444   LoadEngineSnapshot_Redo(steps);
16445
16446   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16447
16448   GameUndoRedoExt();
16449 }
16450
16451 static void HandleGameButtonsExt(int id, int button)
16452 {
16453   static boolean game_undo_executed = FALSE;
16454   int steps = BUTTON_STEPSIZE(button);
16455   boolean handle_game_buttons =
16456     (game_status == GAME_MODE_PLAYING ||
16457      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16458
16459   if (!handle_game_buttons)
16460     return;
16461
16462   switch (id)
16463   {
16464     case GAME_CTRL_ID_STOP:
16465     case GAME_CTRL_ID_PANEL_STOP:
16466     case GAME_CTRL_ID_TOUCH_STOP:
16467       TapeStopGame();
16468
16469       break;
16470
16471     case GAME_CTRL_ID_PAUSE:
16472     case GAME_CTRL_ID_PAUSE2:
16473     case GAME_CTRL_ID_PANEL_PAUSE:
16474     case GAME_CTRL_ID_TOUCH_PAUSE:
16475       if (network.enabled && game_status == GAME_MODE_PLAYING)
16476       {
16477         if (tape.pausing)
16478           SendToServer_ContinuePlaying();
16479         else
16480           SendToServer_PausePlaying();
16481       }
16482       else
16483         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16484
16485       game_undo_executed = FALSE;
16486
16487       break;
16488
16489     case GAME_CTRL_ID_PLAY:
16490     case GAME_CTRL_ID_PANEL_PLAY:
16491       if (game_status == GAME_MODE_MAIN)
16492       {
16493         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16494       }
16495       else if (tape.pausing)
16496       {
16497         if (network.enabled)
16498           SendToServer_ContinuePlaying();
16499         else
16500           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16501       }
16502       break;
16503
16504     case GAME_CTRL_ID_UNDO:
16505       // Important: When using "save snapshot when collecting an item" mode,
16506       // load last (current) snapshot for first "undo" after pressing "pause"
16507       // (else the last-but-one snapshot would be loaded, because the snapshot
16508       // pointer already points to the last snapshot when pressing "pause",
16509       // which is fine for "every step/move" mode, but not for "every collect")
16510       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16511           !game_undo_executed)
16512         steps--;
16513
16514       game_undo_executed = TRUE;
16515
16516       GameUndo(steps);
16517       break;
16518
16519     case GAME_CTRL_ID_REDO:
16520       GameRedo(steps);
16521       break;
16522
16523     case GAME_CTRL_ID_SAVE:
16524       TapeQuickSave();
16525       break;
16526
16527     case GAME_CTRL_ID_LOAD:
16528       TapeQuickLoad();
16529       break;
16530
16531     case SOUND_CTRL_ID_MUSIC:
16532     case SOUND_CTRL_ID_PANEL_MUSIC:
16533       if (setup.sound_music)
16534       { 
16535         setup.sound_music = FALSE;
16536
16537         FadeMusic();
16538       }
16539       else if (audio.music_available)
16540       { 
16541         setup.sound = setup.sound_music = TRUE;
16542
16543         SetAudioMode(setup.sound);
16544
16545         if (game_status == GAME_MODE_PLAYING)
16546           PlayLevelMusic();
16547       }
16548
16549       RedrawSoundButtonGadget(id);
16550
16551       break;
16552
16553     case SOUND_CTRL_ID_LOOPS:
16554     case SOUND_CTRL_ID_PANEL_LOOPS:
16555       if (setup.sound_loops)
16556         setup.sound_loops = FALSE;
16557       else if (audio.loops_available)
16558       {
16559         setup.sound = setup.sound_loops = TRUE;
16560
16561         SetAudioMode(setup.sound);
16562       }
16563
16564       RedrawSoundButtonGadget(id);
16565
16566       break;
16567
16568     case SOUND_CTRL_ID_SIMPLE:
16569     case SOUND_CTRL_ID_PANEL_SIMPLE:
16570       if (setup.sound_simple)
16571         setup.sound_simple = FALSE;
16572       else if (audio.sound_available)
16573       {
16574         setup.sound = setup.sound_simple = TRUE;
16575
16576         SetAudioMode(setup.sound);
16577       }
16578
16579       RedrawSoundButtonGadget(id);
16580
16581       break;
16582
16583     default:
16584       break;
16585   }
16586 }
16587
16588 static void HandleGameButtons(struct GadgetInfo *gi)
16589 {
16590   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16591 }
16592
16593 void HandleSoundButtonKeys(Key key)
16594 {
16595   if (key == setup.shortcut.sound_simple)
16596     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16597   else if (key == setup.shortcut.sound_loops)
16598     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16599   else if (key == setup.shortcut.sound_music)
16600     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16601 }