fixed program usage text
[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 (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 : INIT_GFX_RANDOM());
2570
2571         if (gpc->value != gpc->last_value)
2572         {
2573           gpc->gfx_frame = 0;
2574           gpc->gfx_random = init_gfx_random;
2575         }
2576         else
2577         {
2578           gpc->gfx_frame++;
2579
2580           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2581               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2582             gpc->gfx_random = init_gfx_random;
2583         }
2584
2585         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2586           gfx.anim_random_frame = gpc->gfx_random;
2587
2588         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2589           gpc->gfx_frame = element_info[element].collect_score;
2590
2591         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2592
2593         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2594           gfx.anim_random_frame = last_anim_random_frame;
2595       }
2596     }
2597     else if (gpc->type == TYPE_GRAPHIC)
2598     {
2599       if (gpc->graphic != IMG_UNDEFINED)
2600       {
2601         int last_anim_random_frame = gfx.anim_random_frame;
2602         int graphic = gpc->graphic;
2603         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2604                                sync_random_frame : INIT_GFX_RANDOM());
2605
2606         if (gpc->value != gpc->last_value)
2607         {
2608           gpc->gfx_frame = 0;
2609           gpc->gfx_random = init_gfx_random;
2610         }
2611         else
2612         {
2613           gpc->gfx_frame++;
2614
2615           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2616               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2617             gpc->gfx_random = init_gfx_random;
2618         }
2619
2620         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2621           gfx.anim_random_frame = gpc->gfx_random;
2622
2623         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2624
2625         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2626           gfx.anim_random_frame = last_anim_random_frame;
2627       }
2628     }
2629   }
2630 }
2631
2632 static void DisplayGameControlValues(void)
2633 {
2634   boolean redraw_panel = FALSE;
2635   int i;
2636
2637   for (i = 0; game_panel_controls[i].nr != -1; i++)
2638   {
2639     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2640
2641     if (PANEL_DEACTIVATED(gpc->pos))
2642       continue;
2643
2644     if (gpc->value == gpc->last_value &&
2645         gpc->frame == gpc->last_frame)
2646       continue;
2647
2648     redraw_panel = TRUE;
2649   }
2650
2651   if (!redraw_panel)
2652     return;
2653
2654   // copy default game door content to main double buffer
2655
2656   // !!! CHECK AGAIN !!!
2657   SetPanelBackground();
2658   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2659   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2660
2661   // redraw game control buttons
2662   RedrawGameButtons();
2663
2664   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2665
2666   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2667   {
2668     int nr = game_panel_order[i].nr;
2669     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2670     struct TextPosInfo *pos = gpc->pos;
2671     int type = gpc->type;
2672     int value = gpc->value;
2673     int frame = gpc->frame;
2674     int size = pos->size;
2675     int font = pos->font;
2676     boolean draw_masked = pos->draw_masked;
2677     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2678
2679     if (PANEL_DEACTIVATED(pos))
2680       continue;
2681
2682     if (pos->class == get_hash_from_key("extra_panel_items") &&
2683         !setup.prefer_extra_panel_items)
2684       continue;
2685
2686     gpc->last_value = value;
2687     gpc->last_frame = frame;
2688
2689     if (type == TYPE_INTEGER)
2690     {
2691       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2692           nr == GAME_PANEL_INVENTORY_COUNT ||
2693           nr == GAME_PANEL_SCORE ||
2694           nr == GAME_PANEL_HIGHSCORE ||
2695           nr == GAME_PANEL_TIME)
2696       {
2697         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2698
2699         if (use_dynamic_size)           // use dynamic number of digits
2700         {
2701           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 :
2702                               nr == GAME_PANEL_INVENTORY_COUNT ||
2703                               nr == GAME_PANEL_TIME ? 1000 : 100000);
2704           int size_add = (nr == GAME_PANEL_LEVEL_NUMBER ||
2705                           nr == GAME_PANEL_INVENTORY_COUNT ||
2706                           nr == GAME_PANEL_TIME ? 1 : 2);
2707           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 :
2708                        nr == GAME_PANEL_INVENTORY_COUNT ||
2709                        nr == GAME_PANEL_TIME ? 3 : 5);
2710           int size2 = size1 + size_add;
2711           int font1 = pos->font;
2712           int font2 = pos->font_alt;
2713
2714           size = (value < value_change ? size1 : size2);
2715           font = (value < value_change ? font1 : font2);
2716         }
2717       }
2718
2719       // correct text size if "digits" is zero or less
2720       if (size <= 0)
2721         size = strlen(int2str(value, size));
2722
2723       // dynamically correct text alignment
2724       pos->width = size * getFontWidth(font);
2725
2726       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2727                   int2str(value, size), font, mask_mode);
2728     }
2729     else if (type == TYPE_ELEMENT)
2730     {
2731       int element, graphic;
2732       Bitmap *src_bitmap;
2733       int src_x, src_y;
2734       int width, height;
2735       int dst_x = PANEL_XPOS(pos);
2736       int dst_y = PANEL_YPOS(pos);
2737
2738       if (value != EL_UNDEFINED && value != EL_EMPTY)
2739       {
2740         element = value;
2741         graphic = el2panelimg(value);
2742
2743 #if 0
2744         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2745               element, EL_NAME(element), size);
2746 #endif
2747
2748         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2749           size = TILESIZE;
2750
2751         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2752                               &src_x, &src_y);
2753
2754         width  = graphic_info[graphic].width  * size / TILESIZE;
2755         height = graphic_info[graphic].height * size / TILESIZE;
2756
2757         if (draw_masked)
2758           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2759                            dst_x, dst_y);
2760         else
2761           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2762                      dst_x, dst_y);
2763       }
2764     }
2765     else if (type == TYPE_GRAPHIC)
2766     {
2767       int graphic        = gpc->graphic;
2768       int graphic_active = gpc->graphic_active;
2769       Bitmap *src_bitmap;
2770       int src_x, src_y;
2771       int width, height;
2772       int dst_x = PANEL_XPOS(pos);
2773       int dst_y = PANEL_YPOS(pos);
2774       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2775                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2776
2777       if (graphic != IMG_UNDEFINED && !skip)
2778       {
2779         if (pos->style == STYLE_REVERSE)
2780           value = 100 - value;
2781
2782         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2783
2784         if (pos->direction & MV_HORIZONTAL)
2785         {
2786           width  = graphic_info[graphic_active].width * value / 100;
2787           height = graphic_info[graphic_active].height;
2788
2789           if (pos->direction == MV_LEFT)
2790           {
2791             src_x += graphic_info[graphic_active].width - width;
2792             dst_x += graphic_info[graphic_active].width - width;
2793           }
2794         }
2795         else
2796         {
2797           width  = graphic_info[graphic_active].width;
2798           height = graphic_info[graphic_active].height * value / 100;
2799
2800           if (pos->direction == MV_UP)
2801           {
2802             src_y += graphic_info[graphic_active].height - height;
2803             dst_y += graphic_info[graphic_active].height - height;
2804           }
2805         }
2806
2807         if (draw_masked)
2808           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2809                            dst_x, dst_y);
2810         else
2811           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2812                      dst_x, dst_y);
2813
2814         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2815
2816         if (pos->direction & MV_HORIZONTAL)
2817         {
2818           if (pos->direction == MV_RIGHT)
2819           {
2820             src_x += width;
2821             dst_x += width;
2822           }
2823           else
2824           {
2825             dst_x = PANEL_XPOS(pos);
2826           }
2827
2828           width = graphic_info[graphic].width - width;
2829         }
2830         else
2831         {
2832           if (pos->direction == MV_DOWN)
2833           {
2834             src_y += height;
2835             dst_y += height;
2836           }
2837           else
2838           {
2839             dst_y = PANEL_YPOS(pos);
2840           }
2841
2842           height = graphic_info[graphic].height - height;
2843         }
2844
2845         if (draw_masked)
2846           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2847                            dst_x, dst_y);
2848         else
2849           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2850                      dst_x, dst_y);
2851       }
2852     }
2853     else if (type == TYPE_STRING)
2854     {
2855       boolean active = (value != 0);
2856       char *state_normal = "off";
2857       char *state_active = "on";
2858       char *state = (active ? state_active : state_normal);
2859       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2860                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2861                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2862                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2863
2864       if (nr == GAME_PANEL_GRAVITY_STATE)
2865       {
2866         int font1 = pos->font;          // (used for normal state)
2867         int font2 = pos->font_alt;      // (used for active state)
2868
2869         font = (active ? font2 : font1);
2870       }
2871
2872       if (s != NULL)
2873       {
2874         char *s_cut;
2875
2876         if (size <= 0)
2877         {
2878           // don't truncate output if "chars" is zero or less
2879           size = strlen(s);
2880
2881           // dynamically correct text alignment
2882           pos->width = size * getFontWidth(font);
2883         }
2884
2885         s_cut = getStringCopyN(s, size);
2886
2887         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2888                     s_cut, font, mask_mode);
2889
2890         free(s_cut);
2891       }
2892     }
2893
2894     redraw_mask |= REDRAW_DOOR_1;
2895   }
2896
2897   SetGameStatus(GAME_MODE_PLAYING);
2898 }
2899
2900 void UpdateAndDisplayGameControlValues(void)
2901 {
2902   if (tape.deactivate_display)
2903     return;
2904
2905   UpdateGameControlValues();
2906   DisplayGameControlValues();
2907 }
2908
2909 void UpdateGameDoorValues(void)
2910 {
2911   UpdateGameControlValues();
2912 }
2913
2914 void DrawGameDoorValues(void)
2915 {
2916   DisplayGameControlValues();
2917 }
2918
2919
2920 // ============================================================================
2921 // InitGameEngine()
2922 // ----------------------------------------------------------------------------
2923 // initialize game engine due to level / tape version number
2924 // ============================================================================
2925
2926 static void InitGameEngine(void)
2927 {
2928   int i, j, k, l, x, y;
2929
2930   // set game engine from tape file when re-playing, else from level file
2931   game.engine_version = (tape.playing ? tape.engine_version :
2932                          level.game_version);
2933
2934   // set single or multi-player game mode (needed for re-playing tapes)
2935   game.team_mode = setup.team_mode;
2936
2937   if (tape.playing)
2938   {
2939     int num_players = 0;
2940
2941     for (i = 0; i < MAX_PLAYERS; i++)
2942       if (tape.player_participates[i])
2943         num_players++;
2944
2945     // multi-player tapes contain input data for more than one player
2946     game.team_mode = (num_players > 1);
2947   }
2948
2949 #if 0
2950   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2951         level.game_version);
2952   Debug("game:init:level", "          tape.file_version   == %06d",
2953         tape.file_version);
2954   Debug("game:init:level", "          tape.game_version   == %06d",
2955         tape.game_version);
2956   Debug("game:init:level", "          tape.engine_version == %06d",
2957         tape.engine_version);
2958   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2959         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2960 #endif
2961
2962   // --------------------------------------------------------------------------
2963   // set flags for bugs and changes according to active game engine version
2964   // --------------------------------------------------------------------------
2965
2966   /*
2967     Summary of bugfix:
2968     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2969
2970     Bug was introduced in version:
2971     2.0.1
2972
2973     Bug was fixed in version:
2974     4.2.0.0
2975
2976     Description:
2977     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2978     but the property "can fall" was missing, which caused some levels to be
2979     unsolvable. This was fixed in version 4.2.0.0.
2980
2981     Affected levels/tapes:
2982     An example for a tape that was fixed by this bugfix is tape 029 from the
2983     level set "rnd_sam_bateman".
2984     The wrong behaviour will still be used for all levels or tapes that were
2985     created/recorded with it. An example for this is tape 023 from the level
2986     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2987   */
2988
2989   boolean use_amoeba_dropping_cannot_fall_bug =
2990     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2991       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2992      (tape.playing &&
2993       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2994       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2995
2996   /*
2997     Summary of bugfix/change:
2998     Fixed move speed of elements entering or leaving magic wall.
2999
3000     Fixed/changed in version:
3001     2.0.1
3002
3003     Description:
3004     Before 2.0.1, move speed of elements entering or leaving magic wall was
3005     twice as fast as it is now.
3006     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
3007
3008     Affected levels/tapes:
3009     The first condition is generally needed for all levels/tapes before version
3010     2.0.1, which might use the old behaviour before it was changed; known tapes
3011     that are affected: Tape 014 from the level set "rnd_conor_mancone".
3012     The second condition is an exception from the above case and is needed for
3013     the special case of tapes recorded with game (not engine!) version 2.0.1 or
3014     above, but before it was known that this change would break tapes like the
3015     above and was fixed in 4.2.0.0, so that the changed behaviour was active
3016     although the engine version while recording maybe was before 2.0.1. There
3017     are a lot of tapes that are affected by this exception, like tape 006 from
3018     the level set "rnd_conor_mancone".
3019   */
3020
3021   boolean use_old_move_stepsize_for_magic_wall =
3022     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
3023      !(tape.playing &&
3024        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
3025        tape.game_version <  VERSION_IDENT(4,2,0,0)));
3026
3027   /*
3028     Summary of bugfix/change:
3029     Fixed handling for custom elements that change when pushed by the player.
3030
3031     Fixed/changed in version:
3032     3.1.0
3033
3034     Description:
3035     Before 3.1.0, custom elements that "change when pushing" changed directly
3036     after the player started pushing them (until then handled in "DigField()").
3037     Since 3.1.0, these custom elements are not changed until the "pushing"
3038     move of the element is finished (now handled in "ContinueMoving()").
3039
3040     Affected levels/tapes:
3041     The first condition is generally needed for all levels/tapes before version
3042     3.1.0, which might use the old behaviour before it was changed; known tapes
3043     that are affected are some tapes from the level set "Walpurgis Gardens" by
3044     Jamie Cullen.
3045     The second condition is an exception from the above case and is needed for
3046     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3047     above (including some development versions of 3.1.0), but before it was
3048     known that this change would break tapes like the above and was fixed in
3049     3.1.1, so that the changed behaviour was active although the engine version
3050     while recording maybe was before 3.1.0. There is at least one tape that is
3051     affected by this exception, which is the tape for the one-level set "Bug
3052     Machine" by Juergen Bonhagen.
3053   */
3054
3055   game.use_change_when_pushing_bug =
3056     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3057      !(tape.playing &&
3058        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3059        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3060
3061   /*
3062     Summary of bugfix/change:
3063     Fixed handling for blocking the field the player leaves when moving.
3064
3065     Fixed/changed in version:
3066     3.1.1
3067
3068     Description:
3069     Before 3.1.1, when "block last field when moving" was enabled, the field
3070     the player is leaving when moving was blocked for the time of the move,
3071     and was directly unblocked afterwards. This resulted in the last field
3072     being blocked for exactly one less than the number of frames of one player
3073     move. Additionally, even when blocking was disabled, the last field was
3074     blocked for exactly one frame.
3075     Since 3.1.1, due to changes in player movement handling, the last field
3076     is not blocked at all when blocking is disabled. When blocking is enabled,
3077     the last field is blocked for exactly the number of frames of one player
3078     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3079     last field is blocked for exactly one more than the number of frames of
3080     one player move.
3081
3082     Affected levels/tapes:
3083     (!!! yet to be determined -- probably many !!!)
3084   */
3085
3086   game.use_block_last_field_bug =
3087     (game.engine_version < VERSION_IDENT(3,1,1,0));
3088
3089   /* various special flags and settings for native Emerald Mine game engine */
3090
3091   game_em.use_single_button =
3092     (game.engine_version > VERSION_IDENT(4,0,0,2));
3093
3094   game_em.use_snap_key_bug =
3095     (game.engine_version < VERSION_IDENT(4,0,1,0));
3096
3097   game_em.use_random_bug =
3098     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3099
3100   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3101
3102   game_em.use_old_explosions            = use_old_em_engine;
3103   game_em.use_old_android               = use_old_em_engine;
3104   game_em.use_old_push_elements         = use_old_em_engine;
3105   game_em.use_old_push_into_acid        = use_old_em_engine;
3106
3107   game_em.use_wrap_around               = !use_old_em_engine;
3108
3109   // --------------------------------------------------------------------------
3110
3111   // set maximal allowed number of custom element changes per game frame
3112   game.max_num_changes_per_frame = 1;
3113
3114   // default scan direction: scan playfield from top/left to bottom/right
3115   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3116
3117   // dynamically adjust element properties according to game engine version
3118   InitElementPropertiesEngine(game.engine_version);
3119
3120   // ---------- initialize special element properties -------------------------
3121
3122   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3123   if (use_amoeba_dropping_cannot_fall_bug)
3124     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3125
3126   // ---------- initialize player's initial move delay ------------------------
3127
3128   // dynamically adjust player properties according to level information
3129   for (i = 0; i < MAX_PLAYERS; i++)
3130     game.initial_move_delay_value[i] =
3131       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3132
3133   // dynamically adjust player properties according to game engine version
3134   for (i = 0; i < MAX_PLAYERS; i++)
3135     game.initial_move_delay[i] =
3136       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3137        game.initial_move_delay_value[i] : 0);
3138
3139   // ---------- initialize player's initial push delay ------------------------
3140
3141   // dynamically adjust player properties according to game engine version
3142   game.initial_push_delay_value =
3143     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3144
3145   // ---------- initialize changing elements ----------------------------------
3146
3147   // initialize changing elements information
3148   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3149   {
3150     struct ElementInfo *ei = &element_info[i];
3151
3152     // this pointer might have been changed in the level editor
3153     ei->change = &ei->change_page[0];
3154
3155     if (!IS_CUSTOM_ELEMENT(i))
3156     {
3157       ei->change->target_element = EL_EMPTY_SPACE;
3158       ei->change->delay_fixed = 0;
3159       ei->change->delay_random = 0;
3160       ei->change->delay_frames = 1;
3161     }
3162
3163     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3164     {
3165       ei->has_change_event[j] = FALSE;
3166
3167       ei->event_page_nr[j] = 0;
3168       ei->event_page[j] = &ei->change_page[0];
3169     }
3170   }
3171
3172   // add changing elements from pre-defined list
3173   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3174   {
3175     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3176     struct ElementInfo *ei = &element_info[ch_delay->element];
3177
3178     ei->change->target_element       = ch_delay->target_element;
3179     ei->change->delay_fixed          = ch_delay->change_delay;
3180
3181     ei->change->pre_change_function  = ch_delay->pre_change_function;
3182     ei->change->change_function      = ch_delay->change_function;
3183     ei->change->post_change_function = ch_delay->post_change_function;
3184
3185     ei->change->can_change = TRUE;
3186     ei->change->can_change_or_has_action = TRUE;
3187
3188     ei->has_change_event[CE_DELAY] = TRUE;
3189
3190     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3191     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3192   }
3193
3194   // ---------- initialize internal run-time variables ------------------------
3195
3196   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3197   {
3198     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3199
3200     for (j = 0; j < ei->num_change_pages; j++)
3201     {
3202       ei->change_page[j].can_change_or_has_action =
3203         (ei->change_page[j].can_change |
3204          ei->change_page[j].has_action);
3205     }
3206   }
3207
3208   // add change events from custom element configuration
3209   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3210   {
3211     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3212
3213     for (j = 0; j < ei->num_change_pages; j++)
3214     {
3215       if (!ei->change_page[j].can_change_or_has_action)
3216         continue;
3217
3218       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3219       {
3220         // only add event page for the first page found with this event
3221         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3222         {
3223           ei->has_change_event[k] = TRUE;
3224
3225           ei->event_page_nr[k] = j;
3226           ei->event_page[k] = &ei->change_page[j];
3227         }
3228       }
3229     }
3230   }
3231
3232   // ---------- initialize reference elements in change conditions ------------
3233
3234   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3235   {
3236     int element = EL_CUSTOM_START + i;
3237     struct ElementInfo *ei = &element_info[element];
3238
3239     for (j = 0; j < ei->num_change_pages; j++)
3240     {
3241       int trigger_element = ei->change_page[j].initial_trigger_element;
3242
3243       if (trigger_element >= EL_PREV_CE_8 &&
3244           trigger_element <= EL_NEXT_CE_8)
3245         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3246
3247       ei->change_page[j].trigger_element = trigger_element;
3248     }
3249   }
3250
3251   // ---------- initialize run-time trigger player and element ----------------
3252
3253   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3254   {
3255     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3256
3257     for (j = 0; j < ei->num_change_pages; j++)
3258     {
3259       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3260       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3261       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3262       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3263       ei->change_page[j].actual_trigger_ce_value = 0;
3264       ei->change_page[j].actual_trigger_ce_score = 0;
3265     }
3266   }
3267
3268   // ---------- initialize trigger events -------------------------------------
3269
3270   // initialize trigger events information
3271   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3272     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3273       trigger_events[i][j] = FALSE;
3274
3275   // add trigger events from element change event properties
3276   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3277   {
3278     struct ElementInfo *ei = &element_info[i];
3279
3280     for (j = 0; j < ei->num_change_pages; j++)
3281     {
3282       if (!ei->change_page[j].can_change_or_has_action)
3283         continue;
3284
3285       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3286       {
3287         int trigger_element = ei->change_page[j].trigger_element;
3288
3289         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3290         {
3291           if (ei->change_page[j].has_event[k])
3292           {
3293             if (IS_GROUP_ELEMENT(trigger_element))
3294             {
3295               struct ElementGroupInfo *group =
3296                 element_info[trigger_element].group;
3297
3298               for (l = 0; l < group->num_elements_resolved; l++)
3299                 trigger_events[group->element_resolved[l]][k] = TRUE;
3300             }
3301             else if (trigger_element == EL_ANY_ELEMENT)
3302               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3303                 trigger_events[l][k] = TRUE;
3304             else
3305               trigger_events[trigger_element][k] = TRUE;
3306           }
3307         }
3308       }
3309     }
3310   }
3311
3312   // ---------- initialize push delay -----------------------------------------
3313
3314   // initialize push delay values to default
3315   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3316   {
3317     if (!IS_CUSTOM_ELEMENT(i))
3318     {
3319       // set default push delay values (corrected since version 3.0.7-1)
3320       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3321       {
3322         element_info[i].push_delay_fixed = 2;
3323         element_info[i].push_delay_random = 8;
3324       }
3325       else
3326       {
3327         element_info[i].push_delay_fixed = 8;
3328         element_info[i].push_delay_random = 8;
3329       }
3330     }
3331   }
3332
3333   // set push delay value for certain elements from pre-defined list
3334   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3335   {
3336     int e = push_delay_list[i].element;
3337
3338     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3339     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3340   }
3341
3342   // set push delay value for Supaplex elements for newer engine versions
3343   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3344   {
3345     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3346     {
3347       if (IS_SP_ELEMENT(i))
3348       {
3349         // set SP push delay to just enough to push under a falling zonk
3350         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3351
3352         element_info[i].push_delay_fixed  = delay;
3353         element_info[i].push_delay_random = 0;
3354       }
3355     }
3356   }
3357
3358   // ---------- initialize move stepsize --------------------------------------
3359
3360   // initialize move stepsize values to default
3361   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3362     if (!IS_CUSTOM_ELEMENT(i))
3363       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3364
3365   // set move stepsize value for certain elements from pre-defined list
3366   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3367   {
3368     int e = move_stepsize_list[i].element;
3369
3370     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3371
3372     // set move stepsize value for certain elements for older engine versions
3373     if (use_old_move_stepsize_for_magic_wall)
3374     {
3375       if (e == EL_MAGIC_WALL_FILLING ||
3376           e == EL_MAGIC_WALL_EMPTYING ||
3377           e == EL_BD_MAGIC_WALL_FILLING ||
3378           e == EL_BD_MAGIC_WALL_EMPTYING)
3379         element_info[e].move_stepsize *= 2;
3380     }
3381   }
3382
3383   // ---------- initialize collect score --------------------------------------
3384
3385   // initialize collect score values for custom elements from initial value
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387     if (IS_CUSTOM_ELEMENT(i))
3388       element_info[i].collect_score = element_info[i].collect_score_initial;
3389
3390   // ---------- initialize collect count --------------------------------------
3391
3392   // initialize collect count values for non-custom elements
3393   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3394     if (!IS_CUSTOM_ELEMENT(i))
3395       element_info[i].collect_count_initial = 0;
3396
3397   // add collect count values for all elements from pre-defined list
3398   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3399     element_info[collect_count_list[i].element].collect_count_initial =
3400       collect_count_list[i].count;
3401
3402   // ---------- initialize access direction -----------------------------------
3403
3404   // initialize access direction values to default (access from every side)
3405   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3406     if (!IS_CUSTOM_ELEMENT(i))
3407       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3408
3409   // set access direction value for certain elements from pre-defined list
3410   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3411     element_info[access_direction_list[i].element].access_direction =
3412       access_direction_list[i].direction;
3413
3414   // ---------- initialize explosion content ----------------------------------
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416   {
3417     if (IS_CUSTOM_ELEMENT(i))
3418       continue;
3419
3420     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3421     {
3422       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3423
3424       element_info[i].content.e[x][y] =
3425         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3426          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3427          i == EL_PLAYER_3 ? EL_EMERALD :
3428          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3429          i == EL_MOLE ? EL_EMERALD_RED :
3430          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3431          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3432          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3433          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3434          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3435          i == EL_WALL_EMERALD ? EL_EMERALD :
3436          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3437          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3438          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3439          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3440          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3441          i == EL_WALL_PEARL ? EL_PEARL :
3442          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3443          EL_EMPTY);
3444     }
3445   }
3446
3447   // ---------- initialize recursion detection --------------------------------
3448   recursion_loop_depth = 0;
3449   recursion_loop_detected = FALSE;
3450   recursion_loop_element = EL_UNDEFINED;
3451
3452   // ---------- initialize graphics engine ------------------------------------
3453   game.scroll_delay_value =
3454     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3455      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3456      !setup.forced_scroll_delay           ? 0 :
3457      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3458   game.scroll_delay_value =
3459     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3460
3461   // ---------- initialize game engine snapshots ------------------------------
3462   for (i = 0; i < MAX_PLAYERS; i++)
3463     game.snapshot.last_action[i] = 0;
3464   game.snapshot.changed_action = FALSE;
3465   game.snapshot.collected_item = FALSE;
3466   game.snapshot.mode =
3467     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3468      SNAPSHOT_MODE_EVERY_STEP :
3469      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3470      SNAPSHOT_MODE_EVERY_MOVE :
3471      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3472      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3473   game.snapshot.save_snapshot = FALSE;
3474
3475   // ---------- initialize level time for Supaplex engine ---------------------
3476   // Supaplex levels with time limit currently unsupported -- should be added
3477   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3478     level.time = 0;
3479
3480   // ---------- initialize flags for handling game actions --------------------
3481
3482   // set flags for game actions to default values
3483   game.use_key_actions = TRUE;
3484   game.use_mouse_actions = FALSE;
3485
3486   // when using Mirror Magic game engine, handle mouse events only
3487   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3488   {
3489     game.use_key_actions = FALSE;
3490     game.use_mouse_actions = TRUE;
3491   }
3492
3493   // check for custom elements with mouse click events
3494   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3495   {
3496     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3497     {
3498       int element = EL_CUSTOM_START + i;
3499
3500       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3501           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3502           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3503           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3504         game.use_mouse_actions = TRUE;
3505     }
3506   }
3507 }
3508
3509 static int get_num_special_action(int element, int action_first,
3510                                   int action_last)
3511 {
3512   int num_special_action = 0;
3513   int i, j;
3514
3515   for (i = action_first; i <= action_last; i++)
3516   {
3517     boolean found = FALSE;
3518
3519     for (j = 0; j < NUM_DIRECTIONS; j++)
3520       if (el_act_dir2img(element, i, j) !=
3521           el_act_dir2img(element, ACTION_DEFAULT, j))
3522         found = TRUE;
3523
3524     if (found)
3525       num_special_action++;
3526     else
3527       break;
3528   }
3529
3530   return num_special_action;
3531 }
3532
3533
3534 // ============================================================================
3535 // InitGame()
3536 // ----------------------------------------------------------------------------
3537 // initialize and start new game
3538 // ============================================================================
3539
3540 #if DEBUG_INIT_PLAYER
3541 static void DebugPrintPlayerStatus(char *message)
3542 {
3543   int i;
3544
3545   if (!options.debug)
3546     return;
3547
3548   Debug("game:init:player", "%s:", message);
3549
3550   for (i = 0; i < MAX_PLAYERS; i++)
3551   {
3552     struct PlayerInfo *player = &stored_player[i];
3553
3554     Debug("game:init:player",
3555           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3556           i + 1,
3557           player->present,
3558           player->connected,
3559           player->connected_locally,
3560           player->connected_network,
3561           player->active,
3562           (local_player == player ? " (local player)" : ""));
3563   }
3564 }
3565 #endif
3566
3567 void InitGame(void)
3568 {
3569   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3570   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3571   int fade_mask = REDRAW_FIELD;
3572
3573   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3574   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3575   int initial_move_dir = MV_DOWN;
3576   int i, j, x, y;
3577
3578   // required here to update video display before fading (FIX THIS)
3579   DrawMaskedBorder(REDRAW_DOOR_2);
3580
3581   if (!game.restart_level)
3582     CloseDoor(DOOR_CLOSE_1);
3583
3584   SetGameStatus(GAME_MODE_PLAYING);
3585
3586   if (level_editor_test_game)
3587     FadeSkipNextFadeOut();
3588   else
3589     FadeSetEnterScreen();
3590
3591   if (CheckFadeAll())
3592     fade_mask = REDRAW_ALL;
3593
3594   FadeLevelSoundsAndMusic();
3595
3596   ExpireSoundLoops(TRUE);
3597
3598   FadeOut(fade_mask);
3599
3600   if (level_editor_test_game)
3601     FadeSkipNextFadeIn();
3602
3603   // needed if different viewport properties defined for playing
3604   ChangeViewportPropertiesIfNeeded();
3605
3606   ClearField();
3607
3608   DrawCompleteVideoDisplay();
3609
3610   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3611
3612   InitGameEngine();
3613   InitGameControlValues();
3614
3615   if (tape.recording)
3616   {
3617     // initialize tape actions from game when recording tape
3618     tape.use_key_actions   = game.use_key_actions;
3619     tape.use_mouse_actions = game.use_mouse_actions;
3620
3621     // initialize visible playfield size when recording tape (for team mode)
3622     tape.scr_fieldx = SCR_FIELDX;
3623     tape.scr_fieldy = SCR_FIELDY;
3624   }
3625
3626   // don't play tapes over network
3627   network_playing = (network.enabled && !tape.playing);
3628
3629   for (i = 0; i < MAX_PLAYERS; i++)
3630   {
3631     struct PlayerInfo *player = &stored_player[i];
3632
3633     player->index_nr = i;
3634     player->index_bit = (1 << i);
3635     player->element_nr = EL_PLAYER_1 + i;
3636
3637     player->present = FALSE;
3638     player->active = FALSE;
3639     player->mapped = FALSE;
3640
3641     player->killed = FALSE;
3642     player->reanimated = FALSE;
3643     player->buried = FALSE;
3644
3645     player->action = 0;
3646     player->effective_action = 0;
3647     player->programmed_action = 0;
3648     player->snap_action = 0;
3649
3650     player->mouse_action.lx = 0;
3651     player->mouse_action.ly = 0;
3652     player->mouse_action.button = 0;
3653     player->mouse_action.button_hint = 0;
3654
3655     player->effective_mouse_action.lx = 0;
3656     player->effective_mouse_action.ly = 0;
3657     player->effective_mouse_action.button = 0;
3658     player->effective_mouse_action.button_hint = 0;
3659
3660     for (j = 0; j < MAX_NUM_KEYS; j++)
3661       player->key[j] = FALSE;
3662
3663     player->num_white_keys = 0;
3664
3665     player->dynabomb_count = 0;
3666     player->dynabomb_size = 1;
3667     player->dynabombs_left = 0;
3668     player->dynabomb_xl = FALSE;
3669
3670     player->MovDir = initial_move_dir;
3671     player->MovPos = 0;
3672     player->GfxPos = 0;
3673     player->GfxDir = initial_move_dir;
3674     player->GfxAction = ACTION_DEFAULT;
3675     player->Frame = 0;
3676     player->StepFrame = 0;
3677
3678     player->initial_element = player->element_nr;
3679     player->artwork_element =
3680       (level.use_artwork_element[i] ? level.artwork_element[i] :
3681        player->element_nr);
3682     player->use_murphy = FALSE;
3683
3684     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3685     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3686
3687     player->gravity = level.initial_player_gravity[i];
3688
3689     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3690
3691     player->actual_frame_counter.count = 0;
3692     player->actual_frame_counter.value = 1;
3693
3694     player->step_counter = 0;
3695
3696     player->last_move_dir = initial_move_dir;
3697
3698     player->is_active = FALSE;
3699
3700     player->is_waiting = FALSE;
3701     player->is_moving = FALSE;
3702     player->is_auto_moving = FALSE;
3703     player->is_digging = FALSE;
3704     player->is_snapping = FALSE;
3705     player->is_collecting = FALSE;
3706     player->is_pushing = FALSE;
3707     player->is_switching = FALSE;
3708     player->is_dropping = FALSE;
3709     player->is_dropping_pressed = FALSE;
3710
3711     player->is_bored = FALSE;
3712     player->is_sleeping = FALSE;
3713
3714     player->was_waiting = TRUE;
3715     player->was_moving = FALSE;
3716     player->was_snapping = FALSE;
3717     player->was_dropping = FALSE;
3718
3719     player->force_dropping = FALSE;
3720
3721     player->frame_counter_bored = -1;
3722     player->frame_counter_sleeping = -1;
3723
3724     player->anim_delay_counter = 0;
3725     player->post_delay_counter = 0;
3726
3727     player->dir_waiting = initial_move_dir;
3728     player->action_waiting = ACTION_DEFAULT;
3729     player->last_action_waiting = ACTION_DEFAULT;
3730     player->special_action_bored = ACTION_DEFAULT;
3731     player->special_action_sleeping = ACTION_DEFAULT;
3732
3733     player->switch_x = -1;
3734     player->switch_y = -1;
3735
3736     player->drop_x = -1;
3737     player->drop_y = -1;
3738
3739     player->show_envelope = 0;
3740
3741     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3742
3743     player->push_delay       = -1;      // initialized when pushing starts
3744     player->push_delay_value = game.initial_push_delay_value;
3745
3746     player->drop_delay = 0;
3747     player->drop_pressed_delay = 0;
3748
3749     player->last_jx = -1;
3750     player->last_jy = -1;
3751     player->jx = -1;
3752     player->jy = -1;
3753
3754     player->shield_normal_time_left = 0;
3755     player->shield_deadly_time_left = 0;
3756
3757     player->last_removed_element = EL_UNDEFINED;
3758
3759     player->inventory_infinite_element = EL_UNDEFINED;
3760     player->inventory_size = 0;
3761
3762     if (level.use_initial_inventory[i])
3763     {
3764       for (j = 0; j < level.initial_inventory_size[i]; j++)
3765       {
3766         int element = level.initial_inventory_content[i][j];
3767         int collect_count = element_info[element].collect_count_initial;
3768         int k;
3769
3770         if (!IS_CUSTOM_ELEMENT(element))
3771           collect_count = 1;
3772
3773         if (collect_count == 0)
3774           player->inventory_infinite_element = element;
3775         else
3776           for (k = 0; k < collect_count; k++)
3777             if (player->inventory_size < MAX_INVENTORY_SIZE)
3778               player->inventory_element[player->inventory_size++] = element;
3779       }
3780     }
3781
3782     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3783     SnapField(player, 0, 0);
3784
3785     map_player_action[i] = i;
3786   }
3787
3788   network_player_action_received = FALSE;
3789
3790   // initial null action
3791   if (network_playing)
3792     SendToServer_MovePlayer(MV_NONE);
3793
3794   FrameCounter = 0;
3795   TimeFrames = 0;
3796   TimePlayed = 0;
3797   TimeLeft = level.time;
3798   TapeTime = 0;
3799
3800   ScreenMovDir = MV_NONE;
3801   ScreenMovPos = 0;
3802   ScreenGfxPos = 0;
3803
3804   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3805
3806   game.robot_wheel_x = -1;
3807   game.robot_wheel_y = -1;
3808
3809   game.exit_x = -1;
3810   game.exit_y = -1;
3811
3812   game.all_players_gone = FALSE;
3813
3814   game.LevelSolved = FALSE;
3815   game.GameOver = FALSE;
3816
3817   game.GamePlayed = !tape.playing;
3818
3819   game.LevelSolved_GameWon = FALSE;
3820   game.LevelSolved_GameEnd = FALSE;
3821   game.LevelSolved_SaveTape = FALSE;
3822   game.LevelSolved_SaveScore = FALSE;
3823
3824   game.LevelSolved_CountingTime = 0;
3825   game.LevelSolved_CountingScore = 0;
3826   game.LevelSolved_CountingHealth = 0;
3827
3828   game.panel.active = TRUE;
3829
3830   game.no_level_time_limit = (level.time == 0);
3831   game.time_limit = (leveldir_current->time_limit && setup.time_limit);
3832
3833   game.yamyam_content_nr = 0;
3834   game.robot_wheel_active = FALSE;
3835   game.magic_wall_active = FALSE;
3836   game.magic_wall_time_left = 0;
3837   game.light_time_left = 0;
3838   game.timegate_time_left = 0;
3839   game.switchgate_pos = 0;
3840   game.wind_direction = level.wind_direction_initial;
3841
3842   game.time_final = 0;
3843   game.score_time_final = 0;
3844
3845   game.score = 0;
3846   game.score_final = 0;
3847
3848   game.health = MAX_HEALTH;
3849   game.health_final = MAX_HEALTH;
3850
3851   game.gems_still_needed = level.gems_needed;
3852   game.sokoban_fields_still_needed = 0;
3853   game.sokoban_objects_still_needed = 0;
3854   game.lights_still_needed = 0;
3855   game.players_still_needed = 0;
3856   game.friends_still_needed = 0;
3857
3858   game.lenses_time_left = 0;
3859   game.magnify_time_left = 0;
3860
3861   game.ball_active = level.ball_active_initial;
3862   game.ball_content_nr = 0;
3863
3864   game.explosions_delayed = TRUE;
3865
3866   game.envelope_active = FALSE;
3867
3868   // special case: set custom artwork setting to initial value
3869   game.use_masked_elements = game.use_masked_elements_initial;
3870
3871   for (i = 0; i < NUM_BELTS; i++)
3872   {
3873     game.belt_dir[i] = MV_NONE;
3874     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3875   }
3876
3877   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3878     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3879
3880 #if DEBUG_INIT_PLAYER
3881   DebugPrintPlayerStatus("Player status at level initialization");
3882 #endif
3883
3884   SCAN_PLAYFIELD(x, y)
3885   {
3886     Tile[x][y] = Last[x][y] = level.field[x][y];
3887     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3888     ChangeDelay[x][y] = 0;
3889     ChangePage[x][y] = -1;
3890     CustomValue[x][y] = 0;              // initialized in InitField()
3891     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3892     AmoebaNr[x][y] = 0;
3893     WasJustMoving[x][y] = 0;
3894     WasJustFalling[x][y] = 0;
3895     CheckCollision[x][y] = 0;
3896     CheckImpact[x][y] = 0;
3897     Stop[x][y] = FALSE;
3898     Pushed[x][y] = FALSE;
3899
3900     ChangeCount[x][y] = 0;
3901     ChangeEvent[x][y] = -1;
3902
3903     ExplodePhase[x][y] = 0;
3904     ExplodeDelay[x][y] = 0;
3905     ExplodeField[x][y] = EX_TYPE_NONE;
3906
3907     RunnerVisit[x][y] = 0;
3908     PlayerVisit[x][y] = 0;
3909
3910     GfxFrame[x][y] = 0;
3911     GfxRandom[x][y] = INIT_GFX_RANDOM();
3912     GfxRandomStatic[x][y] = INIT_GFX_RANDOM();
3913     GfxElement[x][y] = EL_UNDEFINED;
3914     GfxElementEmpty[x][y] = EL_EMPTY;
3915     GfxAction[x][y] = ACTION_DEFAULT;
3916     GfxDir[x][y] = MV_NONE;
3917     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3918   }
3919
3920   SCAN_PLAYFIELD(x, y)
3921   {
3922     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3923       emulate_bd = FALSE;
3924     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3925       emulate_sp = FALSE;
3926
3927     InitField(x, y, TRUE);
3928
3929     ResetGfxAnimation(x, y);
3930   }
3931
3932   InitBeltMovement();
3933
3934   for (i = 0; i < MAX_PLAYERS; i++)
3935   {
3936     struct PlayerInfo *player = &stored_player[i];
3937
3938     // set number of special actions for bored and sleeping animation
3939     player->num_special_action_bored =
3940       get_num_special_action(player->artwork_element,
3941                              ACTION_BORING_1, ACTION_BORING_LAST);
3942     player->num_special_action_sleeping =
3943       get_num_special_action(player->artwork_element,
3944                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3945   }
3946
3947   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3948                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3949
3950   // initialize type of slippery elements
3951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3952   {
3953     if (!IS_CUSTOM_ELEMENT(i))
3954     {
3955       // default: elements slip down either to the left or right randomly
3956       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3957
3958       // SP style elements prefer to slip down on the left side
3959       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3960         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3961
3962       // BD style elements prefer to slip down on the left side
3963       if (game.emulation == EMU_BOULDERDASH)
3964         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3965     }
3966   }
3967
3968   // initialize explosion and ignition delay
3969   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3970   {
3971     if (!IS_CUSTOM_ELEMENT(i))
3972     {
3973       int num_phase = 8;
3974       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3975                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3976                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3977       int last_phase = (num_phase + 1) * delay;
3978       int half_phase = (num_phase / 2) * delay;
3979
3980       element_info[i].explosion_delay = last_phase - 1;
3981       element_info[i].ignition_delay = half_phase;
3982
3983       if (i == EL_BLACK_ORB)
3984         element_info[i].ignition_delay = 1;
3985     }
3986   }
3987
3988   // correct non-moving belts to start moving left
3989   for (i = 0; i < NUM_BELTS; i++)
3990     if (game.belt_dir[i] == MV_NONE)
3991       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3992
3993 #if USE_NEW_PLAYER_ASSIGNMENTS
3994   // use preferred player also in local single-player mode
3995   if (!network.enabled && !game.team_mode)
3996   {
3997     int new_index_nr = setup.network_player_nr;
3998
3999     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
4000     {
4001       for (i = 0; i < MAX_PLAYERS; i++)
4002         stored_player[i].connected_locally = FALSE;
4003
4004       stored_player[new_index_nr].connected_locally = TRUE;
4005     }
4006   }
4007
4008   for (i = 0; i < MAX_PLAYERS; i++)
4009   {
4010     stored_player[i].connected = FALSE;
4011
4012     // in network game mode, the local player might not be the first player
4013     if (stored_player[i].connected_locally)
4014       local_player = &stored_player[i];
4015   }
4016
4017   if (!network.enabled)
4018     local_player->connected = TRUE;
4019
4020   if (tape.playing)
4021   {
4022     for (i = 0; i < MAX_PLAYERS; i++)
4023       stored_player[i].connected = tape.player_participates[i];
4024   }
4025   else if (network.enabled)
4026   {
4027     // add team mode players connected over the network (needed for correct
4028     // assignment of player figures from level to locally playing players)
4029
4030     for (i = 0; i < MAX_PLAYERS; i++)
4031       if (stored_player[i].connected_network)
4032         stored_player[i].connected = TRUE;
4033   }
4034   else if (game.team_mode)
4035   {
4036     // try to guess locally connected team mode players (needed for correct
4037     // assignment of player figures from level to locally playing players)
4038
4039     for (i = 0; i < MAX_PLAYERS; i++)
4040       if (setup.input[i].use_joystick ||
4041           setup.input[i].key.left != KSYM_UNDEFINED)
4042         stored_player[i].connected = TRUE;
4043   }
4044
4045 #if DEBUG_INIT_PLAYER
4046   DebugPrintPlayerStatus("Player status after level initialization");
4047 #endif
4048
4049 #if DEBUG_INIT_PLAYER
4050   Debug("game:init:player", "Reassigning players ...");
4051 #endif
4052
4053   // check if any connected player was not found in playfield
4054   for (i = 0; i < MAX_PLAYERS; i++)
4055   {
4056     struct PlayerInfo *player = &stored_player[i];
4057
4058     if (player->connected && !player->present)
4059     {
4060       struct PlayerInfo *field_player = NULL;
4061
4062 #if DEBUG_INIT_PLAYER
4063       Debug("game:init:player",
4064             "- looking for field player for player %d ...", i + 1);
4065 #endif
4066
4067       // assign first free player found that is present in the playfield
4068
4069       // first try: look for unmapped playfield player that is not connected
4070       for (j = 0; j < MAX_PLAYERS; j++)
4071         if (field_player == NULL &&
4072             stored_player[j].present &&
4073             !stored_player[j].mapped &&
4074             !stored_player[j].connected)
4075           field_player = &stored_player[j];
4076
4077       // second try: look for *any* unmapped playfield player
4078       for (j = 0; j < MAX_PLAYERS; j++)
4079         if (field_player == NULL &&
4080             stored_player[j].present &&
4081             !stored_player[j].mapped)
4082           field_player = &stored_player[j];
4083
4084       if (field_player != NULL)
4085       {
4086         int jx = field_player->jx, jy = field_player->jy;
4087
4088 #if DEBUG_INIT_PLAYER
4089         Debug("game:init:player", "- found player %d",
4090               field_player->index_nr + 1);
4091 #endif
4092
4093         player->present = FALSE;
4094         player->active = FALSE;
4095
4096         field_player->present = TRUE;
4097         field_player->active = TRUE;
4098
4099         /*
4100         player->initial_element = field_player->initial_element;
4101         player->artwork_element = field_player->artwork_element;
4102
4103         player->block_last_field       = field_player->block_last_field;
4104         player->block_delay_adjustment = field_player->block_delay_adjustment;
4105         */
4106
4107         StorePlayer[jx][jy] = field_player->element_nr;
4108
4109         field_player->jx = field_player->last_jx = jx;
4110         field_player->jy = field_player->last_jy = jy;
4111
4112         if (local_player == player)
4113           local_player = field_player;
4114
4115         map_player_action[field_player->index_nr] = i;
4116
4117         field_player->mapped = TRUE;
4118
4119 #if DEBUG_INIT_PLAYER
4120         Debug("game:init:player", "- map_player_action[%d] == %d",
4121               field_player->index_nr + 1, i + 1);
4122 #endif
4123       }
4124     }
4125
4126     if (player->connected && player->present)
4127       player->mapped = TRUE;
4128   }
4129
4130 #if DEBUG_INIT_PLAYER
4131   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4132 #endif
4133
4134 #else
4135
4136   // check if any connected player was not found in playfield
4137   for (i = 0; i < MAX_PLAYERS; i++)
4138   {
4139     struct PlayerInfo *player = &stored_player[i];
4140
4141     if (player->connected && !player->present)
4142     {
4143       for (j = 0; j < MAX_PLAYERS; j++)
4144       {
4145         struct PlayerInfo *field_player = &stored_player[j];
4146         int jx = field_player->jx, jy = field_player->jy;
4147
4148         // assign first free player found that is present in the playfield
4149         if (field_player->present && !field_player->connected)
4150         {
4151           player->present = TRUE;
4152           player->active = TRUE;
4153
4154           field_player->present = FALSE;
4155           field_player->active = FALSE;
4156
4157           player->initial_element = field_player->initial_element;
4158           player->artwork_element = field_player->artwork_element;
4159
4160           player->block_last_field       = field_player->block_last_field;
4161           player->block_delay_adjustment = field_player->block_delay_adjustment;
4162
4163           StorePlayer[jx][jy] = player->element_nr;
4164
4165           player->jx = player->last_jx = jx;
4166           player->jy = player->last_jy = jy;
4167
4168           break;
4169         }
4170       }
4171     }
4172   }
4173 #endif
4174
4175 #if 0
4176   Debug("game:init:player", "local_player->present == %d",
4177         local_player->present);
4178 #endif
4179
4180   // set focus to local player for network games, else to all players
4181   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4182   game.centered_player_nr_next = game.centered_player_nr;
4183   game.set_centered_player = FALSE;
4184   game.set_centered_player_wrap = FALSE;
4185
4186   if (network_playing && tape.recording)
4187   {
4188     // store client dependent player focus when recording network games
4189     tape.centered_player_nr_next = game.centered_player_nr_next;
4190     tape.set_centered_player = TRUE;
4191   }
4192
4193   if (tape.playing)
4194   {
4195     // when playing a tape, eliminate all players who do not participate
4196
4197 #if USE_NEW_PLAYER_ASSIGNMENTS
4198
4199     if (!game.team_mode)
4200     {
4201       for (i = 0; i < MAX_PLAYERS; i++)
4202       {
4203         if (stored_player[i].active &&
4204             !tape.player_participates[map_player_action[i]])
4205         {
4206           struct PlayerInfo *player = &stored_player[i];
4207           int jx = player->jx, jy = player->jy;
4208
4209 #if DEBUG_INIT_PLAYER
4210           Debug("game:init:player", "Removing player %d at (%d, %d)",
4211                 i + 1, jx, jy);
4212 #endif
4213
4214           player->active = FALSE;
4215           StorePlayer[jx][jy] = 0;
4216           Tile[jx][jy] = EL_EMPTY;
4217         }
4218       }
4219     }
4220
4221 #else
4222
4223     for (i = 0; i < MAX_PLAYERS; i++)
4224     {
4225       if (stored_player[i].active &&
4226           !tape.player_participates[i])
4227       {
4228         struct PlayerInfo *player = &stored_player[i];
4229         int jx = player->jx, jy = player->jy;
4230
4231         player->active = FALSE;
4232         StorePlayer[jx][jy] = 0;
4233         Tile[jx][jy] = EL_EMPTY;
4234       }
4235     }
4236 #endif
4237   }
4238   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4239   {
4240     // when in single player mode, eliminate all but the local player
4241
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243     {
4244       struct PlayerInfo *player = &stored_player[i];
4245
4246       if (player->active && player != local_player)
4247       {
4248         int jx = player->jx, jy = player->jy;
4249
4250         player->active = FALSE;
4251         player->present = FALSE;
4252
4253         StorePlayer[jx][jy] = 0;
4254         Tile[jx][jy] = EL_EMPTY;
4255       }
4256     }
4257   }
4258
4259   for (i = 0; i < MAX_PLAYERS; i++)
4260     if (stored_player[i].active)
4261       game.players_still_needed++;
4262
4263   if (level.solved_by_one_player)
4264     game.players_still_needed = 1;
4265
4266   // when recording the game, store which players take part in the game
4267   if (tape.recording)
4268   {
4269 #if USE_NEW_PLAYER_ASSIGNMENTS
4270     for (i = 0; i < MAX_PLAYERS; i++)
4271       if (stored_player[i].connected)
4272         tape.player_participates[i] = TRUE;
4273 #else
4274     for (i = 0; i < MAX_PLAYERS; i++)
4275       if (stored_player[i].active)
4276         tape.player_participates[i] = TRUE;
4277 #endif
4278   }
4279
4280 #if DEBUG_INIT_PLAYER
4281   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4282 #endif
4283
4284   if (BorderElement == EL_EMPTY)
4285   {
4286     SBX_Left = 0;
4287     SBX_Right = lev_fieldx - SCR_FIELDX;
4288     SBY_Upper = 0;
4289     SBY_Lower = lev_fieldy - SCR_FIELDY;
4290   }
4291   else
4292   {
4293     SBX_Left = -1;
4294     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4295     SBY_Upper = -1;
4296     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4297   }
4298
4299   if (full_lev_fieldx <= SCR_FIELDX)
4300     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4301   if (full_lev_fieldy <= SCR_FIELDY)
4302     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4303
4304   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4305     SBX_Left--;
4306   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4307     SBY_Upper--;
4308
4309   // if local player not found, look for custom element that might create
4310   // the player (make some assumptions about the right custom element)
4311   if (!local_player->present)
4312   {
4313     int start_x = 0, start_y = 0;
4314     int found_rating = 0;
4315     int found_element = EL_UNDEFINED;
4316     int player_nr = local_player->index_nr;
4317
4318     SCAN_PLAYFIELD(x, y)
4319     {
4320       int element = Tile[x][y];
4321       int content;
4322       int xx, yy;
4323       boolean is_player;
4324
4325       if (level.use_start_element[player_nr] &&
4326           level.start_element[player_nr] == element &&
4327           found_rating < 4)
4328       {
4329         start_x = x;
4330         start_y = y;
4331
4332         found_rating = 4;
4333         found_element = element;
4334       }
4335
4336       if (!IS_CUSTOM_ELEMENT(element))
4337         continue;
4338
4339       if (CAN_CHANGE(element))
4340       {
4341         for (i = 0; i < element_info[element].num_change_pages; i++)
4342         {
4343           // check for player created from custom element as single target
4344           content = element_info[element].change_page[i].target_element;
4345           is_player = IS_PLAYER_ELEMENT(content);
4346
4347           if (is_player && (found_rating < 3 ||
4348                             (found_rating == 3 && element < found_element)))
4349           {
4350             start_x = x;
4351             start_y = y;
4352
4353             found_rating = 3;
4354             found_element = element;
4355           }
4356         }
4357       }
4358
4359       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4360       {
4361         // check for player created from custom element as explosion content
4362         content = element_info[element].content.e[xx][yy];
4363         is_player = IS_PLAYER_ELEMENT(content);
4364
4365         if (is_player && (found_rating < 2 ||
4366                           (found_rating == 2 && element < found_element)))
4367         {
4368           start_x = x + xx - 1;
4369           start_y = y + yy - 1;
4370
4371           found_rating = 2;
4372           found_element = element;
4373         }
4374
4375         if (!CAN_CHANGE(element))
4376           continue;
4377
4378         for (i = 0; i < element_info[element].num_change_pages; i++)
4379         {
4380           // check for player created from custom element as extended target
4381           content =
4382             element_info[element].change_page[i].target_content.e[xx][yy];
4383
4384           is_player = IS_PLAYER_ELEMENT(content);
4385
4386           if (is_player && (found_rating < 1 ||
4387                             (found_rating == 1 && element < found_element)))
4388           {
4389             start_x = x + xx - 1;
4390             start_y = y + yy - 1;
4391
4392             found_rating = 1;
4393             found_element = element;
4394           }
4395         }
4396       }
4397     }
4398
4399     scroll_x = SCROLL_POSITION_X(start_x);
4400     scroll_y = SCROLL_POSITION_Y(start_y);
4401   }
4402   else
4403   {
4404     scroll_x = SCROLL_POSITION_X(local_player->jx);
4405     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4406   }
4407
4408   // !!! FIX THIS (START) !!!
4409   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4410   {
4411     InitGameEngine_EM();
4412   }
4413   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4414   {
4415     InitGameEngine_SP();
4416   }
4417   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4418   {
4419     InitGameEngine_MM();
4420   }
4421   else
4422   {
4423     DrawLevel(REDRAW_FIELD);
4424     DrawAllPlayers();
4425
4426     // after drawing the level, correct some elements
4427     if (game.timegate_time_left == 0)
4428       CloseAllOpenTimegates();
4429   }
4430
4431   // blit playfield from scroll buffer to normal back buffer for fading in
4432   BlitScreenToBitmap(backbuffer);
4433   // !!! FIX THIS (END) !!!
4434
4435   DrawMaskedBorder(fade_mask);
4436
4437   FadeIn(fade_mask);
4438
4439 #if 1
4440   // full screen redraw is required at this point in the following cases:
4441   // - special editor door undrawn when game was started from level editor
4442   // - drawing area (playfield) was changed and has to be removed completely
4443   redraw_mask = REDRAW_ALL;
4444   BackToFront();
4445 #endif
4446
4447   if (!game.restart_level)
4448   {
4449     // copy default game door content to main double buffer
4450
4451     // !!! CHECK AGAIN !!!
4452     SetPanelBackground();
4453     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4454     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4455   }
4456
4457   SetPanelBackground();
4458   SetDrawBackgroundMask(REDRAW_DOOR_1);
4459
4460   UpdateAndDisplayGameControlValues();
4461
4462   if (!game.restart_level)
4463   {
4464     UnmapGameButtons();
4465     UnmapTapeButtons();
4466
4467     FreeGameButtons();
4468     CreateGameButtons();
4469
4470     MapGameButtons();
4471     MapTapeButtons();
4472
4473     // copy actual game door content to door double buffer for OpenDoor()
4474     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4475
4476     OpenDoor(DOOR_OPEN_ALL);
4477
4478     KeyboardAutoRepeatOffUnlessAutoplay();
4479
4480 #if DEBUG_INIT_PLAYER
4481     DebugPrintPlayerStatus("Player status (final)");
4482 #endif
4483   }
4484
4485   UnmapAllGadgets();
4486
4487   MapGameButtons();
4488   MapTapeButtons();
4489
4490   if (!game.restart_level && !tape.playing)
4491   {
4492     LevelStats_incPlayed(level_nr);
4493
4494     SaveLevelSetup_SeriesInfo();
4495   }
4496
4497   game.restart_level = FALSE;
4498   game.restart_game_message = NULL;
4499
4500   game.request_active = FALSE;
4501   game.request_active_or_moving = FALSE;
4502
4503   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4504     InitGameActions_MM();
4505
4506   SaveEngineSnapshotToListInitial();
4507
4508   if (!game.restart_level)
4509   {
4510     PlaySound(SND_GAME_STARTING);
4511
4512     if (setup.sound_music)
4513       PlayLevelMusic();
4514   }
4515
4516   SetPlayfieldMouseCursorEnabled(!game.use_mouse_actions);
4517 }
4518
4519 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4520                         int actual_player_x, int actual_player_y)
4521 {
4522   // this is used for non-R'n'D game engines to update certain engine values
4523
4524   // needed to determine if sounds are played within the visible screen area
4525   scroll_x = actual_scroll_x;
4526   scroll_y = actual_scroll_y;
4527
4528   // needed to get player position for "follow finger" playing input method
4529   local_player->jx = actual_player_x;
4530   local_player->jy = actual_player_y;
4531 }
4532
4533 void InitMovDir(int x, int y)
4534 {
4535   int i, element = Tile[x][y];
4536   static int xy[4][2] =
4537   {
4538     {  0, +1 },
4539     { +1,  0 },
4540     {  0, -1 },
4541     { -1,  0 }
4542   };
4543   static int direction[3][4] =
4544   {
4545     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4546     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4547     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4548   };
4549
4550   switch (element)
4551   {
4552     case EL_BUG_RIGHT:
4553     case EL_BUG_UP:
4554     case EL_BUG_LEFT:
4555     case EL_BUG_DOWN:
4556       Tile[x][y] = EL_BUG;
4557       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4558       break;
4559
4560     case EL_SPACESHIP_RIGHT:
4561     case EL_SPACESHIP_UP:
4562     case EL_SPACESHIP_LEFT:
4563     case EL_SPACESHIP_DOWN:
4564       Tile[x][y] = EL_SPACESHIP;
4565       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4566       break;
4567
4568     case EL_BD_BUTTERFLY_RIGHT:
4569     case EL_BD_BUTTERFLY_UP:
4570     case EL_BD_BUTTERFLY_LEFT:
4571     case EL_BD_BUTTERFLY_DOWN:
4572       Tile[x][y] = EL_BD_BUTTERFLY;
4573       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4574       break;
4575
4576     case EL_BD_FIREFLY_RIGHT:
4577     case EL_BD_FIREFLY_UP:
4578     case EL_BD_FIREFLY_LEFT:
4579     case EL_BD_FIREFLY_DOWN:
4580       Tile[x][y] = EL_BD_FIREFLY;
4581       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4582       break;
4583
4584     case EL_PACMAN_RIGHT:
4585     case EL_PACMAN_UP:
4586     case EL_PACMAN_LEFT:
4587     case EL_PACMAN_DOWN:
4588       Tile[x][y] = EL_PACMAN;
4589       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4590       break;
4591
4592     case EL_YAMYAM_LEFT:
4593     case EL_YAMYAM_RIGHT:
4594     case EL_YAMYAM_UP:
4595     case EL_YAMYAM_DOWN:
4596       Tile[x][y] = EL_YAMYAM;
4597       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4598       break;
4599
4600     case EL_SP_SNIKSNAK:
4601       MovDir[x][y] = MV_UP;
4602       break;
4603
4604     case EL_SP_ELECTRON:
4605       MovDir[x][y] = MV_LEFT;
4606       break;
4607
4608     case EL_MOLE_LEFT:
4609     case EL_MOLE_RIGHT:
4610     case EL_MOLE_UP:
4611     case EL_MOLE_DOWN:
4612       Tile[x][y] = EL_MOLE;
4613       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4614       break;
4615
4616     case EL_SPRING_LEFT:
4617     case EL_SPRING_RIGHT:
4618       Tile[x][y] = EL_SPRING;
4619       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4620       break;
4621
4622     default:
4623       if (IS_CUSTOM_ELEMENT(element))
4624       {
4625         struct ElementInfo *ei = &element_info[element];
4626         int move_direction_initial = ei->move_direction_initial;
4627         int move_pattern = ei->move_pattern;
4628
4629         if (move_direction_initial == MV_START_PREVIOUS)
4630         {
4631           if (MovDir[x][y] != MV_NONE)
4632             return;
4633
4634           move_direction_initial = MV_START_AUTOMATIC;
4635         }
4636
4637         if (move_direction_initial == MV_START_RANDOM)
4638           MovDir[x][y] = 1 << RND(4);
4639         else if (move_direction_initial & MV_ANY_DIRECTION)
4640           MovDir[x][y] = move_direction_initial;
4641         else if (move_pattern == MV_ALL_DIRECTIONS ||
4642                  move_pattern == MV_TURNING_LEFT ||
4643                  move_pattern == MV_TURNING_RIGHT ||
4644                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4645                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4646                  move_pattern == MV_TURNING_RANDOM)
4647           MovDir[x][y] = 1 << RND(4);
4648         else if (move_pattern == MV_HORIZONTAL)
4649           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4650         else if (move_pattern == MV_VERTICAL)
4651           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4652         else if (move_pattern & MV_ANY_DIRECTION)
4653           MovDir[x][y] = element_info[element].move_pattern;
4654         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4655                  move_pattern == MV_ALONG_RIGHT_SIDE)
4656         {
4657           // use random direction as default start direction
4658           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4659             MovDir[x][y] = 1 << RND(4);
4660
4661           for (i = 0; i < NUM_DIRECTIONS; i++)
4662           {
4663             int x1 = x + xy[i][0];
4664             int y1 = y + xy[i][1];
4665
4666             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4667             {
4668               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4669                 MovDir[x][y] = direction[0][i];
4670               else
4671                 MovDir[x][y] = direction[1][i];
4672
4673               break;
4674             }
4675           }
4676         }                
4677       }
4678       else
4679       {
4680         MovDir[x][y] = 1 << RND(4);
4681
4682         if (element != EL_BUG &&
4683             element != EL_SPACESHIP &&
4684             element != EL_BD_BUTTERFLY &&
4685             element != EL_BD_FIREFLY)
4686           break;
4687
4688         for (i = 0; i < NUM_DIRECTIONS; i++)
4689         {
4690           int x1 = x + xy[i][0];
4691           int y1 = y + xy[i][1];
4692
4693           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4694           {
4695             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4696             {
4697               MovDir[x][y] = direction[0][i];
4698               break;
4699             }
4700             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4701                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4702             {
4703               MovDir[x][y] = direction[1][i];
4704               break;
4705             }
4706           }
4707         }
4708       }
4709       break;
4710   }
4711
4712   GfxDir[x][y] = MovDir[x][y];
4713 }
4714
4715 void InitAmoebaNr(int x, int y)
4716 {
4717   int i;
4718   int group_nr = AmoebaNeighbourNr(x, y);
4719
4720   if (group_nr == 0)
4721   {
4722     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4723     {
4724       if (AmoebaCnt[i] == 0)
4725       {
4726         group_nr = i;
4727         break;
4728       }
4729     }
4730   }
4731
4732   AmoebaNr[x][y] = group_nr;
4733   AmoebaCnt[group_nr]++;
4734   AmoebaCnt2[group_nr]++;
4735 }
4736
4737 static void LevelSolved_SetFinalGameValues(void)
4738 {
4739   game.time_final = (game.no_level_time_limit ? TimePlayed : TimeLeft);
4740   game.score_time_final = (level.use_step_counter ? TimePlayed :
4741                            TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4742
4743   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4744                       game_em.lev->score :
4745                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4746                       game_mm.score :
4747                       game.score);
4748
4749   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4750                        MM_HEALTH(game_mm.laser_overload_value) :
4751                        game.health);
4752
4753   game.LevelSolved_CountingTime = game.time_final;
4754   game.LevelSolved_CountingScore = game.score_final;
4755   game.LevelSolved_CountingHealth = game.health_final;
4756 }
4757
4758 static void LevelSolved_DisplayFinalGameValues(int time, int score, int health)
4759 {
4760   game.LevelSolved_CountingTime = time;
4761   game.LevelSolved_CountingScore = score;
4762   game.LevelSolved_CountingHealth = health;
4763
4764   game_panel_controls[GAME_PANEL_TIME].value = time;
4765   game_panel_controls[GAME_PANEL_SCORE].value = score;
4766   game_panel_controls[GAME_PANEL_HEALTH].value = health;
4767
4768   DisplayGameControlValues();
4769 }
4770
4771 static void LevelSolved(void)
4772 {
4773   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4774       game.players_still_needed > 0)
4775     return;
4776
4777   game.LevelSolved = TRUE;
4778   game.GameOver = TRUE;
4779
4780   tape.solved = TRUE;
4781
4782   // needed here to display correct panel values while player walks into exit
4783   LevelSolved_SetFinalGameValues();
4784 }
4785
4786 void GameWon(void)
4787 {
4788   static int time_count_steps;
4789   static int time, time_final;
4790   static float score, score_final; // needed for time score < 10 for 10 seconds
4791   static int health, health_final;
4792   static int game_over_delay_1 = 0;
4793   static int game_over_delay_2 = 0;
4794   static int game_over_delay_3 = 0;
4795   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4796   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4797
4798   if (!game.LevelSolved_GameWon)
4799   {
4800     int i;
4801
4802     // do not start end game actions before the player stops moving (to exit)
4803     if (local_player->active && local_player->MovPos)
4804       return;
4805
4806     // calculate final game values after player finished walking into exit
4807     LevelSolved_SetFinalGameValues();
4808
4809     game.LevelSolved_GameWon = TRUE;
4810     game.LevelSolved_SaveTape = tape.recording;
4811     game.LevelSolved_SaveScore = !tape.playing;
4812
4813     if (!tape.playing)
4814     {
4815       LevelStats_incSolved(level_nr);
4816
4817       SaveLevelSetup_SeriesInfo();
4818     }
4819
4820     if (tape.auto_play)         // tape might already be stopped here
4821       tape.auto_play_level_solved = TRUE;
4822
4823     TapeStop();
4824
4825     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4826     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4827     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4828
4829     time = time_final = game.time_final;
4830     score = score_final = game.score_final;
4831     health = health_final = game.health_final;
4832
4833     // update game panel values before (delayed) counting of score (if any)
4834     LevelSolved_DisplayFinalGameValues(time, score, health);
4835
4836     // if level has time score defined, calculate new final game values
4837     if (time_score > 0)
4838     {
4839       int time_final_max = 999;
4840       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4841       int time_frames = 0;
4842       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4843       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4844
4845       if (TimeLeft > 0)
4846       {
4847         time_final = 0;
4848         time_frames = time_frames_left;
4849       }
4850       else if (game.no_level_time_limit && TimePlayed < time_final_max)
4851       {
4852         time_final = time_final_max;
4853         time_frames = time_frames_final_max - time_frames_played;
4854       }
4855
4856       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4857
4858       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4859
4860       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4861       {
4862         health_final = 0;
4863         score_final += health * time_score;
4864       }
4865
4866       game.score_final = score_final;
4867       game.health_final = health_final;
4868     }
4869
4870     // if not counting score after game, immediately update game panel values
4871     if (level_editor_test_game || !setup.count_score_after_game)
4872     {
4873       time = time_final;
4874       score = score_final;
4875
4876       LevelSolved_DisplayFinalGameValues(time, score, health);
4877     }
4878
4879     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4880     {
4881       // check if last player has left the level
4882       if (game.exit_x >= 0 &&
4883           game.exit_y >= 0)
4884       {
4885         int x = game.exit_x;
4886         int y = game.exit_y;
4887         int element = Tile[x][y];
4888
4889         // close exit door after last player
4890         if ((game.all_players_gone &&
4891              (element == EL_EXIT_OPEN ||
4892               element == EL_SP_EXIT_OPEN ||
4893               element == EL_STEEL_EXIT_OPEN)) ||
4894             element == EL_EM_EXIT_OPEN ||
4895             element == EL_EM_STEEL_EXIT_OPEN)
4896         {
4897
4898           Tile[x][y] =
4899             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4900              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4901              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4902              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4903              EL_EM_STEEL_EXIT_CLOSING);
4904
4905           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4906         }
4907
4908         // player disappears
4909         DrawLevelField(x, y);
4910       }
4911
4912       for (i = 0; i < MAX_PLAYERS; i++)
4913       {
4914         struct PlayerInfo *player = &stored_player[i];
4915
4916         if (player->present)
4917         {
4918           RemovePlayer(player);
4919
4920           // player disappears
4921           DrawLevelField(player->jx, player->jy);
4922         }
4923       }
4924     }
4925
4926     PlaySound(SND_GAME_WINNING);
4927   }
4928
4929   if (setup.count_score_after_game)
4930   {
4931     if (time != time_final)
4932     {
4933       if (game_over_delay_1 > 0)
4934       {
4935         game_over_delay_1--;
4936
4937         return;
4938       }
4939
4940       int time_to_go = ABS(time_final - time);
4941       int time_count_dir = (time < time_final ? +1 : -1);
4942
4943       if (time_to_go < time_count_steps)
4944         time_count_steps = 1;
4945
4946       time  += time_count_steps * time_count_dir;
4947       score += time_count_steps * time_score;
4948
4949       // set final score to correct rounding differences after counting score
4950       if (time == time_final)
4951         score = score_final;
4952
4953       LevelSolved_DisplayFinalGameValues(time, score, health);
4954
4955       if (time == time_final)
4956         StopSound(SND_GAME_LEVELTIME_BONUS);
4957       else if (setup.sound_loops)
4958         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4959       else
4960         PlaySound(SND_GAME_LEVELTIME_BONUS);
4961
4962       return;
4963     }
4964
4965     if (health != health_final)
4966     {
4967       if (game_over_delay_2 > 0)
4968       {
4969         game_over_delay_2--;
4970
4971         return;
4972       }
4973
4974       int health_count_dir = (health < health_final ? +1 : -1);
4975
4976       health += health_count_dir;
4977       score  += time_score;
4978
4979       LevelSolved_DisplayFinalGameValues(time, score, health);
4980
4981       if (health == health_final)
4982         StopSound(SND_GAME_LEVELTIME_BONUS);
4983       else if (setup.sound_loops)
4984         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4985       else
4986         PlaySound(SND_GAME_LEVELTIME_BONUS);
4987
4988       return;
4989     }
4990   }
4991
4992   game.panel.active = FALSE;
4993
4994   if (game_over_delay_3 > 0)
4995   {
4996     game_over_delay_3--;
4997
4998     return;
4999   }
5000
5001   GameEnd();
5002 }
5003
5004 void GameEnd(void)
5005 {
5006   // used instead of "level_nr" (needed for network games)
5007   int last_level_nr = levelset.level_nr;
5008   boolean tape_saved = FALSE;
5009
5010   game.LevelSolved_GameEnd = TRUE;
5011
5012   if (game.LevelSolved_SaveTape && !score_info_tape_play)
5013   {
5014     // make sure that request dialog to save tape does not open door again
5015     if (!global.use_envelope_request)
5016       CloseDoor(DOOR_CLOSE_1);
5017
5018     // ask to save tape
5019     tape_saved = SaveTapeChecked_LevelSolved(tape.level_nr);
5020
5021     // set unique basename for score tape (also saved in high score table)
5022     strcpy(tape.score_tape_basename, getScoreTapeBasename(setup.player_name));
5023   }
5024
5025   // if no tape is to be saved, close both doors simultaneously
5026   CloseDoor(DOOR_CLOSE_ALL);
5027
5028   if (level_editor_test_game || score_info_tape_play)
5029   {
5030     SetGameStatus(GAME_MODE_MAIN);
5031
5032     DrawMainMenu();
5033
5034     return;
5035   }
5036
5037   if (!game.LevelSolved_SaveScore)
5038   {
5039     SetGameStatus(GAME_MODE_MAIN);
5040
5041     DrawMainMenu();
5042
5043     return;
5044   }
5045
5046   if (level_nr == leveldir_current->handicap_level)
5047   {
5048     leveldir_current->handicap_level++;
5049
5050     SaveLevelSetup_SeriesInfo();
5051   }
5052
5053   // save score and score tape before potentially erasing tape below
5054   NewHighScore(last_level_nr, tape_saved);
5055
5056   if (setup.increment_levels &&
5057       level_nr < leveldir_current->last_level &&
5058       !network_playing)
5059   {
5060     level_nr++;         // advance to next level
5061     TapeErase();        // start with empty tape
5062
5063     if (setup.auto_play_next_level)
5064     {
5065       scores.continue_playing = TRUE;
5066       scores.next_level_nr = level_nr;
5067
5068       LoadLevel(level_nr);
5069
5070       SaveLevelSetup_SeriesInfo();
5071     }
5072   }
5073
5074   if (scores.last_added >= 0 && setup.show_scores_after_game)
5075   {
5076     SetGameStatus(GAME_MODE_SCORES);
5077
5078     DrawHallOfFame(last_level_nr);
5079   }
5080   else if (scores.continue_playing)
5081   {
5082     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5083   }
5084   else
5085   {
5086     SetGameStatus(GAME_MODE_MAIN);
5087
5088     DrawMainMenu();
5089   }
5090 }
5091
5092 static int addScoreEntry(struct ScoreInfo *list, struct ScoreEntry *new_entry,
5093                          boolean one_score_entry_per_name)
5094 {
5095   int i;
5096
5097   if (strEqual(new_entry->name, EMPTY_PLAYER_NAME))
5098     return -1;
5099
5100   for (i = 0; i < MAX_SCORE_ENTRIES; i++)
5101   {
5102     struct ScoreEntry *entry = &list->entry[i];
5103     boolean score_is_better = (new_entry->score >  entry->score);
5104     boolean score_is_equal  = (new_entry->score == entry->score);
5105     boolean time_is_better  = (new_entry->time  <  entry->time);
5106     boolean time_is_equal   = (new_entry->time  == entry->time);
5107     boolean better_by_score = (score_is_better ||
5108                                (score_is_equal && time_is_better));
5109     boolean better_by_time  = (time_is_better ||
5110                                (time_is_equal && score_is_better));
5111     boolean is_better = (level.rate_time_over_score ? better_by_time :
5112                          better_by_score);
5113     boolean entry_is_empty = (entry->score == 0 &&
5114                               entry->time == 0);
5115
5116     // prevent adding server score entries if also existing in local score file
5117     // (special case: historic score entries have an empty tape basename entry)
5118     if (strEqual(new_entry->tape_basename, entry->tape_basename) &&
5119         !strEqual(new_entry->tape_basename, UNDEFINED_FILENAME))
5120     {
5121       // add fields from server score entry not stored in local score entry
5122       // (currently, this means setting platform, version and country fields;
5123       // in rare cases, this may also correct an invalid score value, as
5124       // historic scores might have been truncated to 16-bit values locally)
5125       *entry = *new_entry;
5126
5127       return -1;
5128     }
5129
5130     if (is_better || entry_is_empty)
5131     {
5132       // player has made it to the hall of fame
5133
5134       if (i < MAX_SCORE_ENTRIES - 1)
5135       {
5136         int m = MAX_SCORE_ENTRIES - 1;
5137         int l;
5138
5139         if (one_score_entry_per_name)
5140         {
5141           for (l = i; l < MAX_SCORE_ENTRIES; l++)
5142             if (strEqual(list->entry[l].name, new_entry->name))
5143               m = l;
5144
5145           if (m == i)   // player's new highscore overwrites his old one
5146             goto put_into_list;
5147         }
5148
5149         for (l = m; l > i; l--)
5150           list->entry[l] = list->entry[l - 1];
5151       }
5152
5153       put_into_list:
5154
5155       *entry = *new_entry;
5156
5157       return i;
5158     }
5159     else if (one_score_entry_per_name &&
5160              strEqual(entry->name, new_entry->name))
5161     {
5162       // player already in high score list with better score or time
5163
5164       return -1;
5165     }
5166   }
5167
5168   // special case: new score is beyond the last high score list position
5169   return MAX_SCORE_ENTRIES;
5170 }
5171
5172 void NewHighScore(int level_nr, boolean tape_saved)
5173 {
5174   struct ScoreEntry new_entry = {{ 0 }}; // (prevent warning from GCC bug 53119)
5175   boolean one_per_name = FALSE;
5176
5177   strncpy(new_entry.tape_basename, tape.score_tape_basename, MAX_FILENAME_LEN);
5178   strncpy(new_entry.name, setup.player_name, MAX_PLAYER_NAME_LEN);
5179
5180   new_entry.score = game.score_final;
5181   new_entry.time = game.score_time_final;
5182
5183   LoadScore(level_nr);
5184
5185   scores.last_added = addScoreEntry(&scores, &new_entry, one_per_name);
5186
5187   if (scores.last_added >= MAX_SCORE_ENTRIES)
5188   {
5189     scores.last_added = MAX_SCORE_ENTRIES - 1;
5190     scores.force_last_added = TRUE;
5191
5192     scores.entry[scores.last_added] = new_entry;
5193
5194     // store last added local score entry (before merging server scores)
5195     scores.last_added_local = scores.last_added;
5196
5197     return;
5198   }
5199
5200   if (scores.last_added < 0)
5201     return;
5202
5203   SaveScore(level_nr);
5204
5205   // store last added local score entry (before merging server scores)
5206   scores.last_added_local = scores.last_added;
5207
5208   if (!game.LevelSolved_SaveTape)
5209     return;
5210
5211   SaveScoreTape(level_nr);
5212
5213   if (setup.ask_for_using_api_server)
5214   {
5215     setup.use_api_server =
5216       Request("Upload your score and tape to the high score server?", REQ_ASK);
5217
5218     if (!setup.use_api_server)
5219       Request("Not using high score server! Use setup menu to enable again!",
5220               REQ_CONFIRM);
5221
5222     runtime.use_api_server = setup.use_api_server;
5223
5224     // after asking for using API server once, do not ask again
5225     setup.ask_for_using_api_server = FALSE;
5226
5227     SaveSetup_ServerSetup();
5228   }
5229
5230   SaveServerScore(level_nr, tape_saved);
5231 }
5232
5233 void MergeServerScore(void)
5234 {
5235   struct ScoreEntry last_added_entry;
5236   boolean one_per_name = FALSE;
5237   int i;
5238
5239   if (scores.last_added >= 0)
5240     last_added_entry = scores.entry[scores.last_added];
5241
5242   for (i = 0; i < server_scores.num_entries; i++)
5243   {
5244     int pos = addScoreEntry(&scores, &server_scores.entry[i], one_per_name);
5245
5246     if (pos >= 0 && pos <= scores.last_added)
5247       scores.last_added++;
5248   }
5249
5250   if (scores.last_added >= MAX_SCORE_ENTRIES)
5251   {
5252     scores.last_added = MAX_SCORE_ENTRIES - 1;
5253     scores.force_last_added = TRUE;
5254
5255     scores.entry[scores.last_added] = last_added_entry;
5256   }
5257 }
5258
5259 static int getElementMoveStepsizeExt(int x, int y, int direction)
5260 {
5261   int element = Tile[x][y];
5262   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5263   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5264   int horiz_move = (dx != 0);
5265   int sign = (horiz_move ? dx : dy);
5266   int step = sign * element_info[element].move_stepsize;
5267
5268   // special values for move stepsize for spring and things on conveyor belt
5269   if (horiz_move)
5270   {
5271     if (CAN_FALL(element) &&
5272         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5273       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5274     else if (element == EL_SPRING)
5275       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5276   }
5277
5278   return step;
5279 }
5280
5281 static int getElementMoveStepsize(int x, int y)
5282 {
5283   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5284 }
5285
5286 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5287 {
5288   if (player->GfxAction != action || player->GfxDir != dir)
5289   {
5290     player->GfxAction = action;
5291     player->GfxDir = dir;
5292     player->Frame = 0;
5293     player->StepFrame = 0;
5294   }
5295 }
5296
5297 static void ResetGfxFrame(int x, int y)
5298 {
5299   // profiling showed that "autotest" spends 10~20% of its time in this function
5300   if (DrawingDeactivatedField())
5301     return;
5302
5303   int element = Tile[x][y];
5304   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5305
5306   if (graphic_info[graphic].anim_global_sync)
5307     GfxFrame[x][y] = FrameCounter;
5308   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5309     GfxFrame[x][y] = CustomValue[x][y];
5310   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5311     GfxFrame[x][y] = element_info[element].collect_score;
5312   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5313     GfxFrame[x][y] = ChangeDelay[x][y];
5314 }
5315
5316 static void ResetGfxAnimation(int x, int y)
5317 {
5318   GfxAction[x][y] = ACTION_DEFAULT;
5319   GfxDir[x][y] = MovDir[x][y];
5320   GfxFrame[x][y] = 0;
5321
5322   ResetGfxFrame(x, y);
5323 }
5324
5325 static void ResetRandomAnimationValue(int x, int y)
5326 {
5327   GfxRandom[x][y] = INIT_GFX_RANDOM();
5328 }
5329
5330 static void InitMovingField(int x, int y, int direction)
5331 {
5332   int element = Tile[x][y];
5333   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5334   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5335   int newx = x + dx;
5336   int newy = y + dy;
5337   boolean is_moving_before, is_moving_after;
5338
5339   // check if element was/is moving or being moved before/after mode change
5340   is_moving_before = (WasJustMoving[x][y] != 0);
5341   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5342
5343   // reset animation only for moving elements which change direction of moving
5344   // or which just started or stopped moving
5345   // (else CEs with property "can move" / "not moving" are reset each frame)
5346   if (is_moving_before != is_moving_after ||
5347       direction != MovDir[x][y])
5348     ResetGfxAnimation(x, y);
5349
5350   MovDir[x][y] = direction;
5351   GfxDir[x][y] = direction;
5352
5353   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5354                      direction == MV_DOWN && CAN_FALL(element) ?
5355                      ACTION_FALLING : ACTION_MOVING);
5356
5357   // this is needed for CEs with property "can move" / "not moving"
5358
5359   if (is_moving_after)
5360   {
5361     if (Tile[newx][newy] == EL_EMPTY)
5362       Tile[newx][newy] = EL_BLOCKED;
5363
5364     MovDir[newx][newy] = MovDir[x][y];
5365
5366     CustomValue[newx][newy] = CustomValue[x][y];
5367
5368     GfxFrame[newx][newy] = GfxFrame[x][y];
5369     GfxRandom[newx][newy] = GfxRandom[x][y];
5370     GfxAction[newx][newy] = GfxAction[x][y];
5371     GfxDir[newx][newy] = GfxDir[x][y];
5372   }
5373 }
5374
5375 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5376 {
5377   int direction = MovDir[x][y];
5378   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5379   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5380
5381   *goes_to_x = newx;
5382   *goes_to_y = newy;
5383 }
5384
5385 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5386 {
5387   int oldx = x, oldy = y;
5388   int direction = MovDir[x][y];
5389
5390   if (direction == MV_LEFT)
5391     oldx++;
5392   else if (direction == MV_RIGHT)
5393     oldx--;
5394   else if (direction == MV_UP)
5395     oldy++;
5396   else if (direction == MV_DOWN)
5397     oldy--;
5398
5399   *comes_from_x = oldx;
5400   *comes_from_y = oldy;
5401 }
5402
5403 static int MovingOrBlocked2Element(int x, int y)
5404 {
5405   int element = Tile[x][y];
5406
5407   if (element == EL_BLOCKED)
5408   {
5409     int oldx, oldy;
5410
5411     Blocked2Moving(x, y, &oldx, &oldy);
5412     return Tile[oldx][oldy];
5413   }
5414   else
5415     return element;
5416 }
5417
5418 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5419 {
5420   // like MovingOrBlocked2Element(), but if element is moving
5421   // and (x,y) is the field the moving element is just leaving,
5422   // return EL_BLOCKED instead of the element value
5423   int element = Tile[x][y];
5424
5425   if (IS_MOVING(x, y))
5426   {
5427     if (element == EL_BLOCKED)
5428     {
5429       int oldx, oldy;
5430
5431       Blocked2Moving(x, y, &oldx, &oldy);
5432       return Tile[oldx][oldy];
5433     }
5434     else
5435       return EL_BLOCKED;
5436   }
5437   else
5438     return element;
5439 }
5440
5441 static void RemoveField(int x, int y)
5442 {
5443   Tile[x][y] = EL_EMPTY;
5444
5445   MovPos[x][y] = 0;
5446   MovDir[x][y] = 0;
5447   MovDelay[x][y] = 0;
5448
5449   CustomValue[x][y] = 0;
5450
5451   AmoebaNr[x][y] = 0;
5452   ChangeDelay[x][y] = 0;
5453   ChangePage[x][y] = -1;
5454   Pushed[x][y] = FALSE;
5455
5456   GfxElement[x][y] = EL_UNDEFINED;
5457   GfxAction[x][y] = ACTION_DEFAULT;
5458   GfxDir[x][y] = MV_NONE;
5459 }
5460
5461 static void RemoveMovingField(int x, int y)
5462 {
5463   int oldx = x, oldy = y, newx = x, newy = y;
5464   int element = Tile[x][y];
5465   int next_element = EL_UNDEFINED;
5466
5467   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5468     return;
5469
5470   if (IS_MOVING(x, y))
5471   {
5472     Moving2Blocked(x, y, &newx, &newy);
5473
5474     if (Tile[newx][newy] != EL_BLOCKED)
5475     {
5476       // element is moving, but target field is not free (blocked), but
5477       // already occupied by something different (example: acid pool);
5478       // in this case, only remove the moving field, but not the target
5479
5480       RemoveField(oldx, oldy);
5481
5482       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5483
5484       TEST_DrawLevelField(oldx, oldy);
5485
5486       return;
5487     }
5488   }
5489   else if (element == EL_BLOCKED)
5490   {
5491     Blocked2Moving(x, y, &oldx, &oldy);
5492     if (!IS_MOVING(oldx, oldy))
5493       return;
5494   }
5495
5496   if (element == EL_BLOCKED &&
5497       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5498        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5499        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5500        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5501        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5502        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5503     next_element = get_next_element(Tile[oldx][oldy]);
5504
5505   RemoveField(oldx, oldy);
5506   RemoveField(newx, newy);
5507
5508   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5509
5510   if (next_element != EL_UNDEFINED)
5511     Tile[oldx][oldy] = next_element;
5512
5513   TEST_DrawLevelField(oldx, oldy);
5514   TEST_DrawLevelField(newx, newy);
5515 }
5516
5517 void DrawDynamite(int x, int y)
5518 {
5519   int sx = SCREENX(x), sy = SCREENY(y);
5520   int graphic = el2img(Tile[x][y]);
5521   int frame;
5522
5523   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5524     return;
5525
5526   if (IS_WALKABLE_INSIDE(Back[x][y]))
5527     return;
5528
5529   if (Back[x][y])
5530     DrawLevelElement(x, y, Back[x][y]);
5531   else if (Store[x][y])
5532     DrawLevelElement(x, y, Store[x][y]);
5533   else if (game.use_masked_elements)
5534     DrawLevelElement(x, y, EL_EMPTY);
5535
5536   frame = getGraphicAnimationFrameXY(graphic, x, y);
5537
5538   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5539     DrawGraphicThruMask(sx, sy, graphic, frame);
5540   else
5541     DrawGraphic(sx, sy, graphic, frame);
5542 }
5543
5544 static void CheckDynamite(int x, int y)
5545 {
5546   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5547   {
5548     MovDelay[x][y]--;
5549
5550     if (MovDelay[x][y] != 0)
5551     {
5552       DrawDynamite(x, y);
5553       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5554
5555       return;
5556     }
5557   }
5558
5559   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5560
5561   Bang(x, y);
5562 }
5563
5564 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5565 {
5566   boolean num_checked_players = 0;
5567   int i;
5568
5569   for (i = 0; i < MAX_PLAYERS; i++)
5570   {
5571     if (stored_player[i].active)
5572     {
5573       int sx = stored_player[i].jx;
5574       int sy = stored_player[i].jy;
5575
5576       if (num_checked_players == 0)
5577       {
5578         *sx1 = *sx2 = sx;
5579         *sy1 = *sy2 = sy;
5580       }
5581       else
5582       {
5583         *sx1 = MIN(*sx1, sx);
5584         *sy1 = MIN(*sy1, sy);
5585         *sx2 = MAX(*sx2, sx);
5586         *sy2 = MAX(*sy2, sy);
5587       }
5588
5589       num_checked_players++;
5590     }
5591   }
5592 }
5593
5594 static boolean checkIfAllPlayersFitToScreen_RND(void)
5595 {
5596   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5597
5598   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5599
5600   return (sx2 - sx1 < SCR_FIELDX &&
5601           sy2 - sy1 < SCR_FIELDY);
5602 }
5603
5604 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5605 {
5606   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5607
5608   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5609
5610   *sx = (sx1 + sx2) / 2;
5611   *sy = (sy1 + sy2) / 2;
5612 }
5613
5614 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5615                                boolean center_screen, boolean quick_relocation)
5616 {
5617   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5618   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5619   boolean no_delay = (tape.warp_forward);
5620   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5621   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5622   int new_scroll_x, new_scroll_y;
5623
5624   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5625   {
5626     // case 1: quick relocation inside visible screen (without scrolling)
5627
5628     RedrawPlayfield();
5629
5630     return;
5631   }
5632
5633   if (!level.shifted_relocation || center_screen)
5634   {
5635     // relocation _with_ centering of screen
5636
5637     new_scroll_x = SCROLL_POSITION_X(x);
5638     new_scroll_y = SCROLL_POSITION_Y(y);
5639   }
5640   else
5641   {
5642     // relocation _without_ centering of screen
5643
5644     int center_scroll_x = SCROLL_POSITION_X(old_x);
5645     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5646     int offset_x = x + (scroll_x - center_scroll_x);
5647     int offset_y = y + (scroll_y - center_scroll_y);
5648
5649     // for new screen position, apply previous offset to center position
5650     new_scroll_x = SCROLL_POSITION_X(offset_x);
5651     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5652   }
5653
5654   if (quick_relocation)
5655   {
5656     // case 2: quick relocation (redraw without visible scrolling)
5657
5658     scroll_x = new_scroll_x;
5659     scroll_y = new_scroll_y;
5660
5661     RedrawPlayfield();
5662
5663     return;
5664   }
5665
5666   // case 3: visible relocation (with scrolling to new position)
5667
5668   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5669
5670   SetVideoFrameDelay(wait_delay_value);
5671
5672   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5673   {
5674     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5675     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5676
5677     if (dx == 0 && dy == 0)             // no scrolling needed at all
5678       break;
5679
5680     scroll_x -= dx;
5681     scroll_y -= dy;
5682
5683     // set values for horizontal/vertical screen scrolling (half tile size)
5684     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5685     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5686     int pos_x = dx * TILEX / 2;
5687     int pos_y = dy * TILEY / 2;
5688     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5689     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5690
5691     ScrollLevel(dx, dy);
5692     DrawAllPlayers();
5693
5694     // scroll in two steps of half tile size to make things smoother
5695     BlitScreenToBitmapExt_RND(window, fx, fy);
5696
5697     // scroll second step to align at full tile size
5698     BlitScreenToBitmap(window);
5699   }
5700
5701   DrawAllPlayers();
5702   BackToFront();
5703
5704   SetVideoFrameDelay(frame_delay_value_old);
5705 }
5706
5707 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5708 {
5709   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5710   int player_nr = GET_PLAYER_NR(el_player);
5711   struct PlayerInfo *player = &stored_player[player_nr];
5712   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5713   boolean no_delay = (tape.warp_forward);
5714   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5715   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5716   int old_jx = player->jx;
5717   int old_jy = player->jy;
5718   int old_element = Tile[old_jx][old_jy];
5719   int element = Tile[jx][jy];
5720   boolean player_relocated = (old_jx != jx || old_jy != jy);
5721
5722   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5723   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5724   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5725   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5726   int leave_side_horiz = move_dir_horiz;
5727   int leave_side_vert  = move_dir_vert;
5728   int enter_side = enter_side_horiz | enter_side_vert;
5729   int leave_side = leave_side_horiz | leave_side_vert;
5730
5731   if (player->buried)           // do not reanimate dead player
5732     return;
5733
5734   if (!player_relocated)        // no need to relocate the player
5735     return;
5736
5737   if (IS_PLAYER(jx, jy))        // player already placed at new position
5738   {
5739     RemoveField(jx, jy);        // temporarily remove newly placed player
5740     DrawLevelField(jx, jy);
5741   }
5742
5743   if (player->present)
5744   {
5745     while (player->MovPos)
5746     {
5747       ScrollPlayer(player, SCROLL_GO_ON);
5748       ScrollScreen(NULL, SCROLL_GO_ON);
5749
5750       AdvanceFrameAndPlayerCounters(player->index_nr);
5751
5752       DrawPlayer(player);
5753
5754       BackToFront_WithFrameDelay(wait_delay_value);
5755     }
5756
5757     DrawPlayer(player);         // needed here only to cleanup last field
5758     DrawLevelField(player->jx, player->jy);     // remove player graphic
5759
5760     player->is_moving = FALSE;
5761   }
5762
5763   if (IS_CUSTOM_ELEMENT(old_element))
5764     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5765                                CE_LEFT_BY_PLAYER,
5766                                player->index_bit, leave_side);
5767
5768   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5769                                       CE_PLAYER_LEAVES_X,
5770                                       player->index_bit, leave_side);
5771
5772   Tile[jx][jy] = el_player;
5773   InitPlayerField(jx, jy, el_player, TRUE);
5774
5775   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5776      possible that the relocation target field did not contain a player element,
5777      but a walkable element, to which the new player was relocated -- in this
5778      case, restore that (already initialized!) element on the player field */
5779   if (!IS_PLAYER_ELEMENT(element))      // player may be set on walkable element
5780   {
5781     Tile[jx][jy] = element;     // restore previously existing element
5782   }
5783
5784   // only visually relocate centered player
5785   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5786                      FALSE, level.instant_relocation);
5787
5788   TestIfPlayerTouchesBadThing(jx, jy);
5789   TestIfPlayerTouchesCustomElement(jx, jy);
5790
5791   if (IS_CUSTOM_ELEMENT(element))
5792     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5793                                player->index_bit, enter_side);
5794
5795   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5796                                       player->index_bit, enter_side);
5797
5798   if (player->is_switching)
5799   {
5800     /* ensure that relocation while still switching an element does not cause
5801        a new element to be treated as also switched directly after relocation
5802        (this is important for teleporter switches that teleport the player to
5803        a place where another teleporter switch is in the same direction, which
5804        would then incorrectly be treated as immediately switched before the
5805        direction key that caused the switch was released) */
5806
5807     player->switch_x += jx - old_jx;
5808     player->switch_y += jy - old_jy;
5809   }
5810 }
5811
5812 static void Explode(int ex, int ey, int phase, int mode)
5813 {
5814   int x, y;
5815   int last_phase;
5816   int border_element;
5817
5818   // !!! eliminate this variable !!!
5819   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5820
5821   if (game.explosions_delayed)
5822   {
5823     ExplodeField[ex][ey] = mode;
5824     return;
5825   }
5826
5827   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5828   {
5829     int center_element = Tile[ex][ey];
5830     int artwork_element, explosion_element;     // set these values later
5831
5832     // remove things displayed in background while burning dynamite
5833     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5834       Back[ex][ey] = 0;
5835
5836     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5837     {
5838       // put moving element to center field (and let it explode there)
5839       center_element = MovingOrBlocked2Element(ex, ey);
5840       RemoveMovingField(ex, ey);
5841       Tile[ex][ey] = center_element;
5842     }
5843
5844     // now "center_element" is finally determined -- set related values now
5845     artwork_element = center_element;           // for custom player artwork
5846     explosion_element = center_element;         // for custom player artwork
5847
5848     if (IS_PLAYER(ex, ey))
5849     {
5850       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5851
5852       artwork_element = stored_player[player_nr].artwork_element;
5853
5854       if (level.use_explosion_element[player_nr])
5855       {
5856         explosion_element = level.explosion_element[player_nr];
5857         artwork_element = explosion_element;
5858       }
5859     }
5860
5861     if (mode == EX_TYPE_NORMAL ||
5862         mode == EX_TYPE_CENTER ||
5863         mode == EX_TYPE_CROSS)
5864       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5865
5866     last_phase = element_info[explosion_element].explosion_delay + 1;
5867
5868     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5869     {
5870       int xx = x - ex + 1;
5871       int yy = y - ey + 1;
5872       int element;
5873
5874       if (!IN_LEV_FIELD(x, y) ||
5875           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5876           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5877         continue;
5878
5879       element = Tile[x][y];
5880
5881       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5882       {
5883         element = MovingOrBlocked2Element(x, y);
5884
5885         if (!IS_EXPLOSION_PROOF(element))
5886           RemoveMovingField(x, y);
5887       }
5888
5889       // indestructible elements can only explode in center (but not flames)
5890       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5891                                            mode == EX_TYPE_BORDER)) ||
5892           element == EL_FLAMES)
5893         continue;
5894
5895       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5896          behaviour, for example when touching a yamyam that explodes to rocks
5897          with active deadly shield, a rock is created under the player !!! */
5898       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5899 #if 0
5900       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5901           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5902            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5903 #else
5904       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5905 #endif
5906       {
5907         if (IS_ACTIVE_BOMB(element))
5908         {
5909           // re-activate things under the bomb like gate or penguin
5910           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5911           Back[x][y] = 0;
5912         }
5913
5914         continue;
5915       }
5916
5917       // save walkable background elements while explosion on same tile
5918       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5919           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5920         Back[x][y] = element;
5921
5922       // ignite explodable elements reached by other explosion
5923       if (element == EL_EXPLOSION)
5924         element = Store2[x][y];
5925
5926       if (AmoebaNr[x][y] &&
5927           (element == EL_AMOEBA_FULL ||
5928            element == EL_BD_AMOEBA ||
5929            element == EL_AMOEBA_GROWING))
5930       {
5931         AmoebaCnt[AmoebaNr[x][y]]--;
5932         AmoebaCnt2[AmoebaNr[x][y]]--;
5933       }
5934
5935       RemoveField(x, y);
5936
5937       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5938       {
5939         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5940
5941         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5942
5943         if (PLAYERINFO(ex, ey)->use_murphy)
5944           Store[x][y] = EL_EMPTY;
5945       }
5946
5947       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5948       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5949       else if (IS_PLAYER_ELEMENT(center_element))
5950         Store[x][y] = EL_EMPTY;
5951       else if (center_element == EL_YAMYAM)
5952         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5953       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5954         Store[x][y] = element_info[center_element].content.e[xx][yy];
5955 #if 1
5956       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5957       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5958       // otherwise) -- FIX THIS !!!
5959       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5960         Store[x][y] = element_info[element].content.e[1][1];
5961 #else
5962       else if (!CAN_EXPLODE(element))
5963         Store[x][y] = element_info[element].content.e[1][1];
5964 #endif
5965       else
5966         Store[x][y] = EL_EMPTY;
5967
5968       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5969           center_element == EL_AMOEBA_TO_DIAMOND)
5970         Store2[x][y] = element;
5971
5972       Tile[x][y] = EL_EXPLOSION;
5973       GfxElement[x][y] = artwork_element;
5974
5975       ExplodePhase[x][y] = 1;
5976       ExplodeDelay[x][y] = last_phase;
5977
5978       Stop[x][y] = TRUE;
5979     }
5980
5981     if (center_element == EL_YAMYAM)
5982       game.yamyam_content_nr =
5983         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5984
5985     return;
5986   }
5987
5988   if (Stop[ex][ey])
5989     return;
5990
5991   x = ex;
5992   y = ey;
5993
5994   if (phase == 1)
5995     GfxFrame[x][y] = 0;         // restart explosion animation
5996
5997   last_phase = ExplodeDelay[x][y];
5998
5999   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
6000
6001   // this can happen if the player leaves an explosion just in time
6002   if (GfxElement[x][y] == EL_UNDEFINED)
6003     GfxElement[x][y] = EL_EMPTY;
6004
6005   border_element = Store2[x][y];
6006   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6007     border_element = StorePlayer[x][y];
6008
6009   if (phase == element_info[border_element].ignition_delay ||
6010       phase == last_phase)
6011   {
6012     boolean border_explosion = FALSE;
6013
6014     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
6015         !PLAYER_EXPLOSION_PROTECTED(x, y))
6016     {
6017       KillPlayerUnlessExplosionProtected(x, y);
6018       border_explosion = TRUE;
6019     }
6020     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
6021     {
6022       Tile[x][y] = Store2[x][y];
6023       Store2[x][y] = 0;
6024       Bang(x, y);
6025       border_explosion = TRUE;
6026     }
6027     else if (border_element == EL_AMOEBA_TO_DIAMOND)
6028     {
6029       AmoebaToDiamond(x, y);
6030       Store2[x][y] = 0;
6031       border_explosion = TRUE;
6032     }
6033
6034     // if an element just explodes due to another explosion (chain-reaction),
6035     // do not immediately end the new explosion when it was the last frame of
6036     // the explosion (as it would be done in the following "if"-statement!)
6037     if (border_explosion && phase == last_phase)
6038       return;
6039   }
6040
6041   // this can happen if the player was just killed by an explosion
6042   if (GfxElement[x][y] == EL_UNDEFINED)
6043     GfxElement[x][y] = EL_EMPTY;
6044
6045   if (phase == last_phase)
6046   {
6047     int element;
6048
6049     element = Tile[x][y] = Store[x][y];
6050     Store[x][y] = Store2[x][y] = 0;
6051     GfxElement[x][y] = EL_UNDEFINED;
6052
6053     // player can escape from explosions and might therefore be still alive
6054     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
6055         element <= EL_PLAYER_IS_EXPLODING_4)
6056     {
6057       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
6058       int explosion_element = EL_PLAYER_1 + player_nr;
6059       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
6060       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
6061
6062       if (level.use_explosion_element[player_nr])
6063         explosion_element = level.explosion_element[player_nr];
6064
6065       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
6066                     element_info[explosion_element].content.e[xx][yy]);
6067     }
6068
6069     // restore probably existing indestructible background element
6070     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
6071       element = Tile[x][y] = Back[x][y];
6072     Back[x][y] = 0;
6073
6074     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
6075     GfxDir[x][y] = MV_NONE;
6076     ChangeDelay[x][y] = 0;
6077     ChangePage[x][y] = -1;
6078
6079     CustomValue[x][y] = 0;
6080
6081     InitField_WithBug2(x, y, FALSE);
6082
6083     TEST_DrawLevelField(x, y);
6084
6085     TestIfElementTouchesCustomElement(x, y);
6086
6087     if (GFX_CRUMBLED(element))
6088       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6089
6090     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
6091       StorePlayer[x][y] = 0;
6092
6093     if (IS_PLAYER_ELEMENT(element))
6094       RelocatePlayer(x, y, element);
6095   }
6096   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
6097   {
6098     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
6099     int frame = getGraphicAnimationFrameXY(graphic, x, y);
6100
6101     if (phase == delay)
6102       TEST_DrawLevelFieldCrumbled(x, y);
6103
6104     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
6105     {
6106       DrawLevelElement(x, y, Back[x][y]);
6107       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
6108     }
6109     else if (IS_WALKABLE_UNDER(Back[x][y]))
6110     {
6111       DrawLevelGraphic(x, y, graphic, frame);
6112       DrawLevelElementThruMask(x, y, Back[x][y]);
6113     }
6114     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
6115       DrawLevelGraphic(x, y, graphic, frame);
6116   }
6117 }
6118
6119 static void DynaExplode(int ex, int ey)
6120 {
6121   int i, j;
6122   int dynabomb_element = Tile[ex][ey];
6123   int dynabomb_size = 1;
6124   boolean dynabomb_xl = FALSE;
6125   struct PlayerInfo *player;
6126   struct XY *xy = xy_topdown;
6127
6128   if (IS_ACTIVE_BOMB(dynabomb_element))
6129   {
6130     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
6131     dynabomb_size = player->dynabomb_size;
6132     dynabomb_xl = player->dynabomb_xl;
6133     player->dynabombs_left++;
6134   }
6135
6136   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
6137
6138   for (i = 0; i < NUM_DIRECTIONS; i++)
6139   {
6140     for (j = 1; j <= dynabomb_size; j++)
6141     {
6142       int x = ex + j * xy[i].x;
6143       int y = ey + j * xy[i].y;
6144       int element;
6145
6146       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6147         break;
6148
6149       element = Tile[x][y];
6150
6151       // do not restart explosions of fields with active bombs
6152       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6153         continue;
6154
6155       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6156
6157       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6158           !IS_DIGGABLE(element) && !dynabomb_xl)
6159         break;
6160     }
6161   }
6162 }
6163
6164 void Bang(int x, int y)
6165 {
6166   int element = MovingOrBlocked2Element(x, y);
6167   int explosion_type = EX_TYPE_NORMAL;
6168
6169   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6170   {
6171     struct PlayerInfo *player = PLAYERINFO(x, y);
6172
6173     element = Tile[x][y] = player->initial_element;
6174
6175     if (level.use_explosion_element[player->index_nr])
6176     {
6177       int explosion_element = level.explosion_element[player->index_nr];
6178
6179       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6180         explosion_type = EX_TYPE_CROSS;
6181       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6182         explosion_type = EX_TYPE_CENTER;
6183     }
6184   }
6185
6186   switch (element)
6187   {
6188     case EL_BUG:
6189     case EL_SPACESHIP:
6190     case EL_BD_BUTTERFLY:
6191     case EL_BD_FIREFLY:
6192     case EL_YAMYAM:
6193     case EL_DARK_YAMYAM:
6194     case EL_ROBOT:
6195     case EL_PACMAN:
6196     case EL_MOLE:
6197       RaiseScoreElement(element);
6198       break;
6199
6200     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6201     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6202     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6203     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6204     case EL_DYNABOMB_INCREASE_NUMBER:
6205     case EL_DYNABOMB_INCREASE_SIZE:
6206     case EL_DYNABOMB_INCREASE_POWER:
6207       explosion_type = EX_TYPE_DYNA;
6208       break;
6209
6210     case EL_DC_LANDMINE:
6211       explosion_type = EX_TYPE_CENTER;
6212       break;
6213
6214     case EL_PENGUIN:
6215     case EL_LAMP:
6216     case EL_LAMP_ACTIVE:
6217     case EL_AMOEBA_TO_DIAMOND:
6218       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6219         explosion_type = EX_TYPE_CENTER;
6220       break;
6221
6222     default:
6223       if (element_info[element].explosion_type == EXPLODES_CROSS)
6224         explosion_type = EX_TYPE_CROSS;
6225       else if (element_info[element].explosion_type == EXPLODES_1X1)
6226         explosion_type = EX_TYPE_CENTER;
6227       break;
6228   }
6229
6230   if (explosion_type == EX_TYPE_DYNA)
6231     DynaExplode(x, y);
6232   else
6233     Explode(x, y, EX_PHASE_START, explosion_type);
6234
6235   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6236 }
6237
6238 static void SplashAcid(int x, int y)
6239 {
6240   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6241       (!IN_LEV_FIELD(x - 1, y - 2) ||
6242        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6243     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6244
6245   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6246       (!IN_LEV_FIELD(x + 1, y - 2) ||
6247        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6248     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6249
6250   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6251 }
6252
6253 static void InitBeltMovement(void)
6254 {
6255   static int belt_base_element[4] =
6256   {
6257     EL_CONVEYOR_BELT_1_LEFT,
6258     EL_CONVEYOR_BELT_2_LEFT,
6259     EL_CONVEYOR_BELT_3_LEFT,
6260     EL_CONVEYOR_BELT_4_LEFT
6261   };
6262   static int belt_base_active_element[4] =
6263   {
6264     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6265     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6266     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6267     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6268   };
6269
6270   int x, y, i, j;
6271
6272   // set frame order for belt animation graphic according to belt direction
6273   for (i = 0; i < NUM_BELTS; i++)
6274   {
6275     int belt_nr = i;
6276
6277     for (j = 0; j < NUM_BELT_PARTS; j++)
6278     {
6279       int element = belt_base_active_element[belt_nr] + j;
6280       int graphic_1 = el2img(element);
6281       int graphic_2 = el2panelimg(element);
6282
6283       if (game.belt_dir[i] == MV_LEFT)
6284       {
6285         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6286         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6287       }
6288       else
6289       {
6290         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6291         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6292       }
6293     }
6294   }
6295
6296   SCAN_PLAYFIELD(x, y)
6297   {
6298     int element = Tile[x][y];
6299
6300     for (i = 0; i < NUM_BELTS; i++)
6301     {
6302       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6303       {
6304         int e_belt_nr = getBeltNrFromBeltElement(element);
6305         int belt_nr = i;
6306
6307         if (e_belt_nr == belt_nr)
6308         {
6309           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6310
6311           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6312         }
6313       }
6314     }
6315   }
6316 }
6317
6318 static void ToggleBeltSwitch(int x, int y)
6319 {
6320   static int belt_base_element[4] =
6321   {
6322     EL_CONVEYOR_BELT_1_LEFT,
6323     EL_CONVEYOR_BELT_2_LEFT,
6324     EL_CONVEYOR_BELT_3_LEFT,
6325     EL_CONVEYOR_BELT_4_LEFT
6326   };
6327   static int belt_base_active_element[4] =
6328   {
6329     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6330     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6331     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6332     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6333   };
6334   static int belt_base_switch_element[4] =
6335   {
6336     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6337     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6338     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6339     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6340   };
6341   static int belt_move_dir[4] =
6342   {
6343     MV_LEFT,
6344     MV_NONE,
6345     MV_RIGHT,
6346     MV_NONE,
6347   };
6348
6349   int element = Tile[x][y];
6350   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6351   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6352   int belt_dir = belt_move_dir[belt_dir_nr];
6353   int xx, yy, i;
6354
6355   if (!IS_BELT_SWITCH(element))
6356     return;
6357
6358   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6359   game.belt_dir[belt_nr] = belt_dir;
6360
6361   if (belt_dir_nr == 3)
6362     belt_dir_nr = 1;
6363
6364   // set frame order for belt animation graphic according to belt direction
6365   for (i = 0; i < NUM_BELT_PARTS; i++)
6366   {
6367     int element = belt_base_active_element[belt_nr] + i;
6368     int graphic_1 = el2img(element);
6369     int graphic_2 = el2panelimg(element);
6370
6371     if (belt_dir == MV_LEFT)
6372     {
6373       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6374       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6375     }
6376     else
6377     {
6378       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6379       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6380     }
6381   }
6382
6383   SCAN_PLAYFIELD(xx, yy)
6384   {
6385     int element = Tile[xx][yy];
6386
6387     if (IS_BELT_SWITCH(element))
6388     {
6389       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6390
6391       if (e_belt_nr == belt_nr)
6392       {
6393         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6394         TEST_DrawLevelField(xx, yy);
6395       }
6396     }
6397     else if (IS_BELT(element) && belt_dir != MV_NONE)
6398     {
6399       int e_belt_nr = getBeltNrFromBeltElement(element);
6400
6401       if (e_belt_nr == belt_nr)
6402       {
6403         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6404
6405         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6406         TEST_DrawLevelField(xx, yy);
6407       }
6408     }
6409     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6410     {
6411       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6412
6413       if (e_belt_nr == belt_nr)
6414       {
6415         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6416
6417         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6418         TEST_DrawLevelField(xx, yy);
6419       }
6420     }
6421   }
6422 }
6423
6424 static void ToggleSwitchgateSwitch(int x, int y)
6425 {
6426   int xx, yy;
6427
6428   game.switchgate_pos = !game.switchgate_pos;
6429
6430   SCAN_PLAYFIELD(xx, yy)
6431   {
6432     int element = Tile[xx][yy];
6433
6434     if (element == EL_SWITCHGATE_SWITCH_UP)
6435     {
6436       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6437       TEST_DrawLevelField(xx, yy);
6438     }
6439     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6440     {
6441       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6442       TEST_DrawLevelField(xx, yy);
6443     }
6444     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6445     {
6446       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6447       TEST_DrawLevelField(xx, yy);
6448     }
6449     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6450     {
6451       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6452       TEST_DrawLevelField(xx, yy);
6453     }
6454     else if (element == EL_SWITCHGATE_OPEN ||
6455              element == EL_SWITCHGATE_OPENING)
6456     {
6457       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6458
6459       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6460     }
6461     else if (element == EL_SWITCHGATE_CLOSED ||
6462              element == EL_SWITCHGATE_CLOSING)
6463     {
6464       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6465
6466       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6467     }
6468   }
6469 }
6470
6471 static int getInvisibleActiveFromInvisibleElement(int element)
6472 {
6473   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6474           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6475           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6476           element);
6477 }
6478
6479 static int getInvisibleFromInvisibleActiveElement(int element)
6480 {
6481   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6482           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6483           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6484           element);
6485 }
6486
6487 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6488 {
6489   int x, y;
6490
6491   SCAN_PLAYFIELD(x, y)
6492   {
6493     int element = Tile[x][y];
6494
6495     if (element == EL_LIGHT_SWITCH &&
6496         game.light_time_left > 0)
6497     {
6498       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6499       TEST_DrawLevelField(x, y);
6500     }
6501     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6502              game.light_time_left == 0)
6503     {
6504       Tile[x][y] = EL_LIGHT_SWITCH;
6505       TEST_DrawLevelField(x, y);
6506     }
6507     else if (element == EL_EMC_DRIPPER &&
6508              game.light_time_left > 0)
6509     {
6510       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6511       TEST_DrawLevelField(x, y);
6512     }
6513     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6514              game.light_time_left == 0)
6515     {
6516       Tile[x][y] = EL_EMC_DRIPPER;
6517       TEST_DrawLevelField(x, y);
6518     }
6519     else if (element == EL_INVISIBLE_STEELWALL ||
6520              element == EL_INVISIBLE_WALL ||
6521              element == EL_INVISIBLE_SAND)
6522     {
6523       if (game.light_time_left > 0)
6524         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6525
6526       TEST_DrawLevelField(x, y);
6527
6528       // uncrumble neighbour fields, if needed
6529       if (element == EL_INVISIBLE_SAND)
6530         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6531     }
6532     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6533              element == EL_INVISIBLE_WALL_ACTIVE ||
6534              element == EL_INVISIBLE_SAND_ACTIVE)
6535     {
6536       if (game.light_time_left == 0)
6537         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6538
6539       TEST_DrawLevelField(x, y);
6540
6541       // re-crumble neighbour fields, if needed
6542       if (element == EL_INVISIBLE_SAND)
6543         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6544     }
6545   }
6546 }
6547
6548 static void RedrawAllInvisibleElementsForLenses(void)
6549 {
6550   int x, y;
6551
6552   SCAN_PLAYFIELD(x, y)
6553   {
6554     int element = Tile[x][y];
6555
6556     if (element == EL_EMC_DRIPPER &&
6557         game.lenses_time_left > 0)
6558     {
6559       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6560       TEST_DrawLevelField(x, y);
6561     }
6562     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6563              game.lenses_time_left == 0)
6564     {
6565       Tile[x][y] = EL_EMC_DRIPPER;
6566       TEST_DrawLevelField(x, y);
6567     }
6568     else if (element == EL_INVISIBLE_STEELWALL ||
6569              element == EL_INVISIBLE_WALL ||
6570              element == EL_INVISIBLE_SAND)
6571     {
6572       if (game.lenses_time_left > 0)
6573         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6574
6575       TEST_DrawLevelField(x, y);
6576
6577       // uncrumble neighbour fields, if needed
6578       if (element == EL_INVISIBLE_SAND)
6579         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6580     }
6581     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6582              element == EL_INVISIBLE_WALL_ACTIVE ||
6583              element == EL_INVISIBLE_SAND_ACTIVE)
6584     {
6585       if (game.lenses_time_left == 0)
6586         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6587
6588       TEST_DrawLevelField(x, y);
6589
6590       // re-crumble neighbour fields, if needed
6591       if (element == EL_INVISIBLE_SAND)
6592         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6593     }
6594   }
6595 }
6596
6597 static void RedrawAllInvisibleElementsForMagnifier(void)
6598 {
6599   int x, y;
6600
6601   SCAN_PLAYFIELD(x, y)
6602   {
6603     int element = Tile[x][y];
6604
6605     if (element == EL_EMC_FAKE_GRASS &&
6606         game.magnify_time_left > 0)
6607     {
6608       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6609       TEST_DrawLevelField(x, y);
6610     }
6611     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6612              game.magnify_time_left == 0)
6613     {
6614       Tile[x][y] = EL_EMC_FAKE_GRASS;
6615       TEST_DrawLevelField(x, y);
6616     }
6617     else if (IS_GATE_GRAY(element) &&
6618              game.magnify_time_left > 0)
6619     {
6620       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6621                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6622                     IS_EM_GATE_GRAY(element) ?
6623                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6624                     IS_EMC_GATE_GRAY(element) ?
6625                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6626                     IS_DC_GATE_GRAY(element) ?
6627                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6628                     element);
6629       TEST_DrawLevelField(x, y);
6630     }
6631     else if (IS_GATE_GRAY_ACTIVE(element) &&
6632              game.magnify_time_left == 0)
6633     {
6634       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6635                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6636                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6637                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6638                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6639                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6640                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6641                     EL_DC_GATE_WHITE_GRAY :
6642                     element);
6643       TEST_DrawLevelField(x, y);
6644     }
6645   }
6646 }
6647
6648 static void ToggleLightSwitch(int x, int y)
6649 {
6650   int element = Tile[x][y];
6651
6652   game.light_time_left =
6653     (element == EL_LIGHT_SWITCH ?
6654      level.time_light * FRAMES_PER_SECOND : 0);
6655
6656   RedrawAllLightSwitchesAndInvisibleElements();
6657 }
6658
6659 static void ActivateTimegateSwitch(int x, int y)
6660 {
6661   int xx, yy;
6662
6663   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6664
6665   SCAN_PLAYFIELD(xx, yy)
6666   {
6667     int element = Tile[xx][yy];
6668
6669     if (element == EL_TIMEGATE_CLOSED ||
6670         element == EL_TIMEGATE_CLOSING)
6671     {
6672       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6673       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6674     }
6675
6676     /*
6677     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6678     {
6679       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6680       TEST_DrawLevelField(xx, yy);
6681     }
6682     */
6683
6684   }
6685
6686   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6687                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6688 }
6689
6690 static void Impact(int x, int y)
6691 {
6692   boolean last_line = (y == lev_fieldy - 1);
6693   boolean object_hit = FALSE;
6694   boolean impact = (last_line || object_hit);
6695   int element = Tile[x][y];
6696   int smashed = EL_STEELWALL;
6697
6698   if (!last_line)       // check if element below was hit
6699   {
6700     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6701       return;
6702
6703     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6704                                          MovDir[x][y + 1] != MV_DOWN ||
6705                                          MovPos[x][y + 1] <= TILEY / 2));
6706
6707     // do not smash moving elements that left the smashed field in time
6708     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6709         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6710       object_hit = FALSE;
6711
6712 #if USE_QUICKSAND_IMPACT_BUGFIX
6713     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6714     {
6715       RemoveMovingField(x, y + 1);
6716       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6717       Tile[x][y + 2] = EL_ROCK;
6718       TEST_DrawLevelField(x, y + 2);
6719
6720       object_hit = TRUE;
6721     }
6722
6723     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6724     {
6725       RemoveMovingField(x, y + 1);
6726       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6727       Tile[x][y + 2] = EL_ROCK;
6728       TEST_DrawLevelField(x, y + 2);
6729
6730       object_hit = TRUE;
6731     }
6732 #endif
6733
6734     if (object_hit)
6735       smashed = MovingOrBlocked2Element(x, y + 1);
6736
6737     impact = (last_line || object_hit);
6738   }
6739
6740   if (!last_line && smashed == EL_ACID) // element falls into acid
6741   {
6742     SplashAcid(x, y + 1);
6743     return;
6744   }
6745
6746   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6747   // only reset graphic animation if graphic really changes after impact
6748   if (impact &&
6749       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6750   {
6751     ResetGfxAnimation(x, y);
6752     TEST_DrawLevelField(x, y);
6753   }
6754
6755   if (impact && CAN_EXPLODE_IMPACT(element))
6756   {
6757     Bang(x, y);
6758     return;
6759   }
6760   else if (impact && element == EL_PEARL &&
6761            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6762   {
6763     ResetGfxAnimation(x, y);
6764
6765     Tile[x][y] = EL_PEARL_BREAKING;
6766     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6767     return;
6768   }
6769   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6770   {
6771     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6772
6773     return;
6774   }
6775
6776   if (impact && element == EL_AMOEBA_DROP)
6777   {
6778     if (object_hit && IS_PLAYER(x, y + 1))
6779       KillPlayerUnlessEnemyProtected(x, y + 1);
6780     else if (object_hit && smashed == EL_PENGUIN)
6781       Bang(x, y + 1);
6782     else
6783     {
6784       Tile[x][y] = EL_AMOEBA_GROWING;
6785       Store[x][y] = EL_AMOEBA_WET;
6786
6787       ResetRandomAnimationValue(x, y);
6788     }
6789     return;
6790   }
6791
6792   if (object_hit)               // check which object was hit
6793   {
6794     if ((CAN_PASS_MAGIC_WALL(element) && 
6795          (smashed == EL_MAGIC_WALL ||
6796           smashed == EL_BD_MAGIC_WALL)) ||
6797         (CAN_PASS_DC_MAGIC_WALL(element) &&
6798          smashed == EL_DC_MAGIC_WALL))
6799     {
6800       int xx, yy;
6801       int activated_magic_wall =
6802         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6803          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6804          EL_DC_MAGIC_WALL_ACTIVE);
6805
6806       // activate magic wall / mill
6807       SCAN_PLAYFIELD(xx, yy)
6808       {
6809         if (Tile[xx][yy] == smashed)
6810           Tile[xx][yy] = activated_magic_wall;
6811       }
6812
6813       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6814       game.magic_wall_active = TRUE;
6815
6816       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6817                             SND_MAGIC_WALL_ACTIVATING :
6818                             smashed == EL_BD_MAGIC_WALL ?
6819                             SND_BD_MAGIC_WALL_ACTIVATING :
6820                             SND_DC_MAGIC_WALL_ACTIVATING));
6821     }
6822
6823     if (IS_PLAYER(x, y + 1))
6824     {
6825       if (CAN_SMASH_PLAYER(element))
6826       {
6827         KillPlayerUnlessEnemyProtected(x, y + 1);
6828         return;
6829       }
6830     }
6831     else if (smashed == EL_PENGUIN)
6832     {
6833       if (CAN_SMASH_PLAYER(element))
6834       {
6835         Bang(x, y + 1);
6836         return;
6837       }
6838     }
6839     else if (element == EL_BD_DIAMOND)
6840     {
6841       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6842       {
6843         Bang(x, y + 1);
6844         return;
6845       }
6846     }
6847     else if (((element == EL_SP_INFOTRON ||
6848                element == EL_SP_ZONK) &&
6849               (smashed == EL_SP_SNIKSNAK ||
6850                smashed == EL_SP_ELECTRON ||
6851                smashed == EL_SP_DISK_ORANGE)) ||
6852              (element == EL_SP_INFOTRON &&
6853               smashed == EL_SP_DISK_YELLOW))
6854     {
6855       Bang(x, y + 1);
6856       return;
6857     }
6858     else if (CAN_SMASH_EVERYTHING(element))
6859     {
6860       if (IS_CLASSIC_ENEMY(smashed) ||
6861           CAN_EXPLODE_SMASHED(smashed))
6862       {
6863         Bang(x, y + 1);
6864         return;
6865       }
6866       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6867       {
6868         if (smashed == EL_LAMP ||
6869             smashed == EL_LAMP_ACTIVE)
6870         {
6871           Bang(x, y + 1);
6872           return;
6873         }
6874         else if (smashed == EL_NUT)
6875         {
6876           Tile[x][y + 1] = EL_NUT_BREAKING;
6877           PlayLevelSound(x, y, SND_NUT_BREAKING);
6878           RaiseScoreElement(EL_NUT);
6879           return;
6880         }
6881         else if (smashed == EL_PEARL)
6882         {
6883           ResetGfxAnimation(x, y);
6884
6885           Tile[x][y + 1] = EL_PEARL_BREAKING;
6886           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6887           return;
6888         }
6889         else if (smashed == EL_DIAMOND)
6890         {
6891           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6892           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6893           return;
6894         }
6895         else if (IS_BELT_SWITCH(smashed))
6896         {
6897           ToggleBeltSwitch(x, y + 1);
6898         }
6899         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6900                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6901                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6902                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6903         {
6904           ToggleSwitchgateSwitch(x, y + 1);
6905         }
6906         else if (smashed == EL_LIGHT_SWITCH ||
6907                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6908         {
6909           ToggleLightSwitch(x, y + 1);
6910         }
6911         else
6912         {
6913           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6914
6915           CheckElementChangeBySide(x, y + 1, smashed, element,
6916                                    CE_SWITCHED, CH_SIDE_TOP);
6917           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6918                                             CH_SIDE_TOP);
6919         }
6920       }
6921       else
6922       {
6923         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6924       }
6925     }
6926   }
6927
6928   // play sound of magic wall / mill
6929   if (!last_line &&
6930       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6931        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6932        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6933   {
6934     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6935       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6936     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6937       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6938     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6939       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6940
6941     return;
6942   }
6943
6944   // play sound of object that hits the ground
6945   if (last_line || object_hit)
6946     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6947 }
6948
6949 static void TurnRoundExt(int x, int y)
6950 {
6951   static struct
6952   {
6953     int dx, dy;
6954   } move_xy[] =
6955   {
6956     {  0,  0 },
6957     { -1,  0 },
6958     { +1,  0 },
6959     {  0,  0 },
6960     {  0, -1 },
6961     {  0,  0 }, { 0, 0 }, { 0, 0 },
6962     {  0, +1 }
6963   };
6964   static struct
6965   {
6966     int left, right, back;
6967   } turn[] =
6968   {
6969     { 0,        0,              0        },
6970     { MV_DOWN,  MV_UP,          MV_RIGHT },
6971     { MV_UP,    MV_DOWN,        MV_LEFT  },
6972     { 0,        0,              0        },
6973     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6974     { 0,        0,              0        },
6975     { 0,        0,              0        },
6976     { 0,        0,              0        },
6977     { MV_RIGHT, MV_LEFT,        MV_UP    }
6978   };
6979
6980   int element = Tile[x][y];
6981   int move_pattern = element_info[element].move_pattern;
6982
6983   int old_move_dir = MovDir[x][y];
6984   int left_dir  = turn[old_move_dir].left;
6985   int right_dir = turn[old_move_dir].right;
6986   int back_dir  = turn[old_move_dir].back;
6987
6988   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6989   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6990   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6991   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6992
6993   int left_x  = x + left_dx,  left_y  = y + left_dy;
6994   int right_x = x + right_dx, right_y = y + right_dy;
6995   int move_x  = x + move_dx,  move_y  = y + move_dy;
6996
6997   int xx, yy;
6998
6999   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
7000   {
7001     TestIfBadThingTouchesOtherBadThing(x, y);
7002
7003     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
7004       MovDir[x][y] = right_dir;
7005     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7006       MovDir[x][y] = left_dir;
7007
7008     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
7009       MovDelay[x][y] = 9;
7010     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
7011       MovDelay[x][y] = 1;
7012   }
7013   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
7014   {
7015     TestIfBadThingTouchesOtherBadThing(x, y);
7016
7017     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
7018       MovDir[x][y] = left_dir;
7019     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
7020       MovDir[x][y] = right_dir;
7021
7022     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
7023       MovDelay[x][y] = 9;
7024     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
7025       MovDelay[x][y] = 1;
7026   }
7027   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
7028   {
7029     TestIfBadThingTouchesOtherBadThing(x, y);
7030
7031     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
7032       MovDir[x][y] = left_dir;
7033     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
7034       MovDir[x][y] = right_dir;
7035
7036     if (MovDir[x][y] != old_move_dir)
7037       MovDelay[x][y] = 9;
7038   }
7039   else if (element == EL_YAMYAM)
7040   {
7041     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
7042     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
7043
7044     if (can_turn_left && can_turn_right)
7045       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7046     else if (can_turn_left)
7047       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7048     else if (can_turn_right)
7049       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7050     else
7051       MovDir[x][y] = back_dir;
7052
7053     MovDelay[x][y] = 16 + 16 * RND(3);
7054   }
7055   else if (element == EL_DARK_YAMYAM)
7056   {
7057     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7058                                                          left_x, left_y);
7059     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
7060                                                          right_x, right_y);
7061
7062     if (can_turn_left && can_turn_right)
7063       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7064     else if (can_turn_left)
7065       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7066     else if (can_turn_right)
7067       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7068     else
7069       MovDir[x][y] = back_dir;
7070
7071     MovDelay[x][y] = 16 + 16 * RND(3);
7072   }
7073   else if (element == EL_PACMAN)
7074   {
7075     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
7076     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
7077
7078     if (can_turn_left && can_turn_right)
7079       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7080     else if (can_turn_left)
7081       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7082     else if (can_turn_right)
7083       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7084     else
7085       MovDir[x][y] = back_dir;
7086
7087     MovDelay[x][y] = 6 + RND(40);
7088   }
7089   else if (element == EL_PIG)
7090   {
7091     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
7092     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
7093     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
7094     boolean should_turn_left, should_turn_right, should_move_on;
7095     int rnd_value = 24;
7096     int rnd = RND(rnd_value);
7097
7098     should_turn_left = (can_turn_left &&
7099                         (!can_move_on ||
7100                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
7101                                                    y + back_dy + left_dy)));
7102     should_turn_right = (can_turn_right &&
7103                          (!can_move_on ||
7104                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
7105                                                     y + back_dy + right_dy)));
7106     should_move_on = (can_move_on &&
7107                       (!can_turn_left ||
7108                        !can_turn_right ||
7109                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
7110                                                  y + move_dy + left_dy) ||
7111                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
7112                                                  y + move_dy + right_dy)));
7113
7114     if (should_turn_left || should_turn_right || should_move_on)
7115     {
7116       if (should_turn_left && should_turn_right && should_move_on)
7117         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
7118                         rnd < 2 * rnd_value / 3 ? right_dir :
7119                         old_move_dir);
7120       else if (should_turn_left && should_turn_right)
7121         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7122       else if (should_turn_left && should_move_on)
7123         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
7124       else if (should_turn_right && should_move_on)
7125         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
7126       else if (should_turn_left)
7127         MovDir[x][y] = left_dir;
7128       else if (should_turn_right)
7129         MovDir[x][y] = right_dir;
7130       else if (should_move_on)
7131         MovDir[x][y] = old_move_dir;
7132     }
7133     else if (can_move_on && rnd > rnd_value / 8)
7134       MovDir[x][y] = old_move_dir;
7135     else if (can_turn_left && can_turn_right)
7136       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7137     else if (can_turn_left && rnd > rnd_value / 8)
7138       MovDir[x][y] = left_dir;
7139     else if (can_turn_right && rnd > rnd_value/8)
7140       MovDir[x][y] = right_dir;
7141     else
7142       MovDir[x][y] = back_dir;
7143
7144     xx = x + move_xy[MovDir[x][y]].dx;
7145     yy = y + move_xy[MovDir[x][y]].dy;
7146
7147     if (!IN_LEV_FIELD(xx, yy) ||
7148         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7149       MovDir[x][y] = old_move_dir;
7150
7151     MovDelay[x][y] = 0;
7152   }
7153   else if (element == EL_DRAGON)
7154   {
7155     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7156     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7157     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7158     int rnd_value = 24;
7159     int rnd = RND(rnd_value);
7160
7161     if (can_move_on && rnd > rnd_value / 8)
7162       MovDir[x][y] = old_move_dir;
7163     else if (can_turn_left && can_turn_right)
7164       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7165     else if (can_turn_left && rnd > rnd_value / 8)
7166       MovDir[x][y] = left_dir;
7167     else if (can_turn_right && rnd > rnd_value / 8)
7168       MovDir[x][y] = right_dir;
7169     else
7170       MovDir[x][y] = back_dir;
7171
7172     xx = x + move_xy[MovDir[x][y]].dx;
7173     yy = y + move_xy[MovDir[x][y]].dy;
7174
7175     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7176       MovDir[x][y] = old_move_dir;
7177
7178     MovDelay[x][y] = 0;
7179   }
7180   else if (element == EL_MOLE)
7181   {
7182     boolean can_move_on =
7183       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7184                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7185                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7186     if (!can_move_on)
7187     {
7188       boolean can_turn_left =
7189         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7190                               IS_AMOEBOID(Tile[left_x][left_y])));
7191
7192       boolean can_turn_right =
7193         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7194                               IS_AMOEBOID(Tile[right_x][right_y])));
7195
7196       if (can_turn_left && can_turn_right)
7197         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7198       else if (can_turn_left)
7199         MovDir[x][y] = left_dir;
7200       else
7201         MovDir[x][y] = right_dir;
7202     }
7203
7204     if (MovDir[x][y] != old_move_dir)
7205       MovDelay[x][y] = 9;
7206   }
7207   else if (element == EL_BALLOON)
7208   {
7209     MovDir[x][y] = game.wind_direction;
7210     MovDelay[x][y] = 0;
7211   }
7212   else if (element == EL_SPRING)
7213   {
7214     if (MovDir[x][y] & MV_HORIZONTAL)
7215     {
7216       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7217           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7218       {
7219         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7220         ResetGfxAnimation(move_x, move_y);
7221         TEST_DrawLevelField(move_x, move_y);
7222
7223         MovDir[x][y] = back_dir;
7224       }
7225       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7226                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7227         MovDir[x][y] = MV_NONE;
7228     }
7229
7230     MovDelay[x][y] = 0;
7231   }
7232   else if (element == EL_ROBOT ||
7233            element == EL_SATELLITE ||
7234            element == EL_PENGUIN ||
7235            element == EL_EMC_ANDROID)
7236   {
7237     int attr_x = -1, attr_y = -1;
7238
7239     if (game.all_players_gone)
7240     {
7241       attr_x = game.exit_x;
7242       attr_y = game.exit_y;
7243     }
7244     else
7245     {
7246       int i;
7247
7248       for (i = 0; i < MAX_PLAYERS; i++)
7249       {
7250         struct PlayerInfo *player = &stored_player[i];
7251         int jx = player->jx, jy = player->jy;
7252
7253         if (!player->active)
7254           continue;
7255
7256         if (attr_x == -1 ||
7257             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7258         {
7259           attr_x = jx;
7260           attr_y = jy;
7261         }
7262       }
7263     }
7264
7265     if (element == EL_ROBOT &&
7266         game.robot_wheel_x >= 0 &&
7267         game.robot_wheel_y >= 0 &&
7268         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7269          game.engine_version < VERSION_IDENT(3,1,0,0)))
7270     {
7271       attr_x = game.robot_wheel_x;
7272       attr_y = game.robot_wheel_y;
7273     }
7274
7275     if (element == EL_PENGUIN)
7276     {
7277       int i;
7278       struct XY *xy = xy_topdown;
7279
7280       for (i = 0; i < NUM_DIRECTIONS; i++)
7281       {
7282         int ex = x + xy[i].x;
7283         int ey = y + xy[i].y;
7284
7285         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7286                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7287                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7288                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7289         {
7290           attr_x = ex;
7291           attr_y = ey;
7292           break;
7293         }
7294       }
7295     }
7296
7297     MovDir[x][y] = MV_NONE;
7298     if (attr_x < x)
7299       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7300     else if (attr_x > x)
7301       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7302     if (attr_y < y)
7303       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7304     else if (attr_y > y)
7305       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7306
7307     if (element == EL_ROBOT)
7308     {
7309       int newx, newy;
7310
7311       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7312         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7313       Moving2Blocked(x, y, &newx, &newy);
7314
7315       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7316         MovDelay[x][y] = 8 + 8 * !RND(3);
7317       else
7318         MovDelay[x][y] = 16;
7319     }
7320     else if (element == EL_PENGUIN)
7321     {
7322       int newx, newy;
7323
7324       MovDelay[x][y] = 1;
7325
7326       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7327       {
7328         boolean first_horiz = RND(2);
7329         int new_move_dir = MovDir[x][y];
7330
7331         MovDir[x][y] =
7332           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7333         Moving2Blocked(x, y, &newx, &newy);
7334
7335         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7336           return;
7337
7338         MovDir[x][y] =
7339           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7340         Moving2Blocked(x, y, &newx, &newy);
7341
7342         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7343           return;
7344
7345         MovDir[x][y] = old_move_dir;
7346         return;
7347       }
7348     }
7349     else if (element == EL_SATELLITE)
7350     {
7351       int newx, newy;
7352
7353       MovDelay[x][y] = 1;
7354
7355       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7356       {
7357         boolean first_horiz = RND(2);
7358         int new_move_dir = MovDir[x][y];
7359
7360         MovDir[x][y] =
7361           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7362         Moving2Blocked(x, y, &newx, &newy);
7363
7364         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7365           return;
7366
7367         MovDir[x][y] =
7368           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7369         Moving2Blocked(x, y, &newx, &newy);
7370
7371         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7372           return;
7373
7374         MovDir[x][y] = old_move_dir;
7375         return;
7376       }
7377     }
7378     else if (element == EL_EMC_ANDROID)
7379     {
7380       static int check_pos[16] =
7381       {
7382         -1,             //  0 => (invalid)
7383         7,              //  1 => MV_LEFT
7384         3,              //  2 => MV_RIGHT
7385         -1,             //  3 => (invalid)
7386         1,              //  4 =>            MV_UP
7387         0,              //  5 => MV_LEFT  | MV_UP
7388         2,              //  6 => MV_RIGHT | MV_UP
7389         -1,             //  7 => (invalid)
7390         5,              //  8 =>            MV_DOWN
7391         6,              //  9 => MV_LEFT  | MV_DOWN
7392         4,              // 10 => MV_RIGHT | MV_DOWN
7393         -1,             // 11 => (invalid)
7394         -1,             // 12 => (invalid)
7395         -1,             // 13 => (invalid)
7396         -1,             // 14 => (invalid)
7397         -1,             // 15 => (invalid)
7398       };
7399       static struct
7400       {
7401         int dx, dy;
7402         int dir;
7403       } check_xy[8] =
7404       {
7405         { -1, -1,       MV_LEFT  | MV_UP   },
7406         {  0, -1,                  MV_UP   },
7407         { +1, -1,       MV_RIGHT | MV_UP   },
7408         { +1,  0,       MV_RIGHT           },
7409         { +1, +1,       MV_RIGHT | MV_DOWN },
7410         {  0, +1,                  MV_DOWN },
7411         { -1, +1,       MV_LEFT  | MV_DOWN },
7412         { -1,  0,       MV_LEFT            },
7413       };
7414       int start_pos, check_order;
7415       boolean can_clone = FALSE;
7416       int i;
7417
7418       // check if there is any free field around current position
7419       for (i = 0; i < 8; i++)
7420       {
7421         int newx = x + check_xy[i].dx;
7422         int newy = y + check_xy[i].dy;
7423
7424         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7425         {
7426           can_clone = TRUE;
7427
7428           break;
7429         }
7430       }
7431
7432       if (can_clone)            // randomly find an element to clone
7433       {
7434         can_clone = FALSE;
7435
7436         start_pos = check_pos[RND(8)];
7437         check_order = (RND(2) ? -1 : +1);
7438
7439         for (i = 0; i < 8; i++)
7440         {
7441           int pos_raw = start_pos + i * check_order;
7442           int pos = (pos_raw + 8) % 8;
7443           int newx = x + check_xy[pos].dx;
7444           int newy = y + check_xy[pos].dy;
7445
7446           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7447           {
7448             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7449             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7450
7451             Store[x][y] = Tile[newx][newy];
7452
7453             can_clone = TRUE;
7454
7455             break;
7456           }
7457         }
7458       }
7459
7460       if (can_clone)            // randomly find a direction to move
7461       {
7462         can_clone = FALSE;
7463
7464         start_pos = check_pos[RND(8)];
7465         check_order = (RND(2) ? -1 : +1);
7466
7467         for (i = 0; i < 8; i++)
7468         {
7469           int pos_raw = start_pos + i * check_order;
7470           int pos = (pos_raw + 8) % 8;
7471           int newx = x + check_xy[pos].dx;
7472           int newy = y + check_xy[pos].dy;
7473           int new_move_dir = check_xy[pos].dir;
7474
7475           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7476           {
7477             MovDir[x][y] = new_move_dir;
7478             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7479
7480             can_clone = TRUE;
7481
7482             break;
7483           }
7484         }
7485       }
7486
7487       if (can_clone)            // cloning and moving successful
7488         return;
7489
7490       // cannot clone -- try to move towards player
7491
7492       start_pos = check_pos[MovDir[x][y] & 0x0f];
7493       check_order = (RND(2) ? -1 : +1);
7494
7495       for (i = 0; i < 3; i++)
7496       {
7497         // first check start_pos, then previous/next or (next/previous) pos
7498         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7499         int pos = (pos_raw + 8) % 8;
7500         int newx = x + check_xy[pos].dx;
7501         int newy = y + check_xy[pos].dy;
7502         int new_move_dir = check_xy[pos].dir;
7503
7504         if (IS_PLAYER(newx, newy))
7505           break;
7506
7507         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7508         {
7509           MovDir[x][y] = new_move_dir;
7510           MovDelay[x][y] = level.android_move_time * 8 + 1;
7511
7512           break;
7513         }
7514       }
7515     }
7516   }
7517   else if (move_pattern == MV_TURNING_LEFT ||
7518            move_pattern == MV_TURNING_RIGHT ||
7519            move_pattern == MV_TURNING_LEFT_RIGHT ||
7520            move_pattern == MV_TURNING_RIGHT_LEFT ||
7521            move_pattern == MV_TURNING_RANDOM ||
7522            move_pattern == MV_ALL_DIRECTIONS)
7523   {
7524     boolean can_turn_left =
7525       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7526     boolean can_turn_right =
7527       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7528
7529     if (element_info[element].move_stepsize == 0)       // "not moving"
7530       return;
7531
7532     if (move_pattern == MV_TURNING_LEFT)
7533       MovDir[x][y] = left_dir;
7534     else if (move_pattern == MV_TURNING_RIGHT)
7535       MovDir[x][y] = right_dir;
7536     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7537       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7538     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7539       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7540     else if (move_pattern == MV_TURNING_RANDOM)
7541       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7542                       can_turn_right && !can_turn_left ? right_dir :
7543                       RND(2) ? left_dir : right_dir);
7544     else if (can_turn_left && can_turn_right)
7545       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7546     else if (can_turn_left)
7547       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7548     else if (can_turn_right)
7549       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7550     else
7551       MovDir[x][y] = back_dir;
7552
7553     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7554   }
7555   else if (move_pattern == MV_HORIZONTAL ||
7556            move_pattern == MV_VERTICAL)
7557   {
7558     if (move_pattern & old_move_dir)
7559       MovDir[x][y] = back_dir;
7560     else if (move_pattern == MV_HORIZONTAL)
7561       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7562     else if (move_pattern == MV_VERTICAL)
7563       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7564
7565     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7566   }
7567   else if (move_pattern & MV_ANY_DIRECTION)
7568   {
7569     MovDir[x][y] = move_pattern;
7570     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7571   }
7572   else if (move_pattern & MV_WIND_DIRECTION)
7573   {
7574     MovDir[x][y] = game.wind_direction;
7575     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7576   }
7577   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7578   {
7579     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7580       MovDir[x][y] = left_dir;
7581     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7582       MovDir[x][y] = right_dir;
7583
7584     if (MovDir[x][y] != old_move_dir)
7585       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7586   }
7587   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7588   {
7589     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7590       MovDir[x][y] = right_dir;
7591     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7592       MovDir[x][y] = left_dir;
7593
7594     if (MovDir[x][y] != old_move_dir)
7595       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7596   }
7597   else if (move_pattern == MV_TOWARDS_PLAYER ||
7598            move_pattern == MV_AWAY_FROM_PLAYER)
7599   {
7600     int attr_x = -1, attr_y = -1;
7601     int newx, newy;
7602     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7603
7604     if (game.all_players_gone)
7605     {
7606       attr_x = game.exit_x;
7607       attr_y = game.exit_y;
7608     }
7609     else
7610     {
7611       int i;
7612
7613       for (i = 0; i < MAX_PLAYERS; i++)
7614       {
7615         struct PlayerInfo *player = &stored_player[i];
7616         int jx = player->jx, jy = player->jy;
7617
7618         if (!player->active)
7619           continue;
7620
7621         if (attr_x == -1 ||
7622             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7623         {
7624           attr_x = jx;
7625           attr_y = jy;
7626         }
7627       }
7628     }
7629
7630     MovDir[x][y] = MV_NONE;
7631     if (attr_x < x)
7632       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7633     else if (attr_x > x)
7634       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7635     if (attr_y < y)
7636       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7637     else if (attr_y > y)
7638       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7639
7640     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7641
7642     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7643     {
7644       boolean first_horiz = RND(2);
7645       int new_move_dir = MovDir[x][y];
7646
7647       if (element_info[element].move_stepsize == 0)     // "not moving"
7648       {
7649         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7650         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7651
7652         return;
7653       }
7654
7655       MovDir[x][y] =
7656         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7657       Moving2Blocked(x, y, &newx, &newy);
7658
7659       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7660         return;
7661
7662       MovDir[x][y] =
7663         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7664       Moving2Blocked(x, y, &newx, &newy);
7665
7666       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7667         return;
7668
7669       MovDir[x][y] = old_move_dir;
7670     }
7671   }
7672   else if (move_pattern == MV_WHEN_PUSHED ||
7673            move_pattern == MV_WHEN_DROPPED)
7674   {
7675     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7676       MovDir[x][y] = MV_NONE;
7677
7678     MovDelay[x][y] = 0;
7679   }
7680   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7681   {
7682     struct XY *test_xy = xy_topdown;
7683     static int test_dir[4] =
7684     {
7685       MV_UP,
7686       MV_LEFT,
7687       MV_RIGHT,
7688       MV_DOWN
7689     };
7690     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7691     int move_preference = -1000000;     // start with very low preference
7692     int new_move_dir = MV_NONE;
7693     int start_test = RND(4);
7694     int i;
7695
7696     for (i = 0; i < NUM_DIRECTIONS; i++)
7697     {
7698       int j = (start_test + i) % 4;
7699       int move_dir = test_dir[j];
7700       int move_dir_preference;
7701
7702       xx = x + test_xy[j].x;
7703       yy = y + test_xy[j].y;
7704
7705       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7706           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7707       {
7708         new_move_dir = move_dir;
7709
7710         break;
7711       }
7712
7713       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7714         continue;
7715
7716       move_dir_preference = -1 * RunnerVisit[xx][yy];
7717       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7718         move_dir_preference = PlayerVisit[xx][yy];
7719
7720       if (move_dir_preference > move_preference)
7721       {
7722         // prefer field that has not been visited for the longest time
7723         move_preference = move_dir_preference;
7724         new_move_dir = move_dir;
7725       }
7726       else if (move_dir_preference == move_preference &&
7727                move_dir == old_move_dir)
7728       {
7729         // prefer last direction when all directions are preferred equally
7730         move_preference = move_dir_preference;
7731         new_move_dir = move_dir;
7732       }
7733     }
7734
7735     MovDir[x][y] = new_move_dir;
7736     if (old_move_dir != new_move_dir)
7737       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7738   }
7739 }
7740
7741 static void TurnRound(int x, int y)
7742 {
7743   int direction = MovDir[x][y];
7744
7745   TurnRoundExt(x, y);
7746
7747   GfxDir[x][y] = MovDir[x][y];
7748
7749   if (direction != MovDir[x][y])
7750     GfxFrame[x][y] = 0;
7751
7752   if (MovDelay[x][y])
7753     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7754
7755   ResetGfxFrame(x, y);
7756 }
7757
7758 static boolean JustBeingPushed(int x, int y)
7759 {
7760   int i;
7761
7762   for (i = 0; i < MAX_PLAYERS; i++)
7763   {
7764     struct PlayerInfo *player = &stored_player[i];
7765
7766     if (player->active && player->is_pushing && player->MovPos)
7767     {
7768       int next_jx = player->jx + (player->jx - player->last_jx);
7769       int next_jy = player->jy + (player->jy - player->last_jy);
7770
7771       if (x == next_jx && y == next_jy)
7772         return TRUE;
7773     }
7774   }
7775
7776   return FALSE;
7777 }
7778
7779 static void StartMoving(int x, int y)
7780 {
7781   boolean started_moving = FALSE;       // some elements can fall _and_ move
7782   int element = Tile[x][y];
7783
7784   if (Stop[x][y])
7785     return;
7786
7787   if (MovDelay[x][y] == 0)
7788     GfxAction[x][y] = ACTION_DEFAULT;
7789
7790   if (CAN_FALL(element) && y < lev_fieldy - 1)
7791   {
7792     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7793         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7794       if (JustBeingPushed(x, y))
7795         return;
7796
7797     if (element == EL_QUICKSAND_FULL)
7798     {
7799       if (IS_FREE(x, y + 1))
7800       {
7801         InitMovingField(x, y, MV_DOWN);
7802         started_moving = TRUE;
7803
7804         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7805 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7806         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7807           Store[x][y] = EL_ROCK;
7808 #else
7809         Store[x][y] = EL_ROCK;
7810 #endif
7811
7812         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7813       }
7814       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7815       {
7816         if (!MovDelay[x][y])
7817         {
7818           MovDelay[x][y] = TILEY + 1;
7819
7820           ResetGfxAnimation(x, y);
7821           ResetGfxAnimation(x, y + 1);
7822         }
7823
7824         if (MovDelay[x][y])
7825         {
7826           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7827           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7828
7829           MovDelay[x][y]--;
7830           if (MovDelay[x][y])
7831             return;
7832         }
7833
7834         Tile[x][y] = EL_QUICKSAND_EMPTY;
7835         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7836         Store[x][y + 1] = Store[x][y];
7837         Store[x][y] = 0;
7838
7839         PlayLevelSoundAction(x, y, ACTION_FILLING);
7840       }
7841       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7842       {
7843         if (!MovDelay[x][y])
7844         {
7845           MovDelay[x][y] = TILEY + 1;
7846
7847           ResetGfxAnimation(x, y);
7848           ResetGfxAnimation(x, y + 1);
7849         }
7850
7851         if (MovDelay[x][y])
7852         {
7853           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7854           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7855
7856           MovDelay[x][y]--;
7857           if (MovDelay[x][y])
7858             return;
7859         }
7860
7861         Tile[x][y] = EL_QUICKSAND_EMPTY;
7862         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7863         Store[x][y + 1] = Store[x][y];
7864         Store[x][y] = 0;
7865
7866         PlayLevelSoundAction(x, y, ACTION_FILLING);
7867       }
7868     }
7869     else if (element == EL_QUICKSAND_FAST_FULL)
7870     {
7871       if (IS_FREE(x, y + 1))
7872       {
7873         InitMovingField(x, y, MV_DOWN);
7874         started_moving = TRUE;
7875
7876         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7877 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7878         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7879           Store[x][y] = EL_ROCK;
7880 #else
7881         Store[x][y] = EL_ROCK;
7882 #endif
7883
7884         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7885       }
7886       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7887       {
7888         if (!MovDelay[x][y])
7889         {
7890           MovDelay[x][y] = TILEY + 1;
7891
7892           ResetGfxAnimation(x, y);
7893           ResetGfxAnimation(x, y + 1);
7894         }
7895
7896         if (MovDelay[x][y])
7897         {
7898           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7899           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7900
7901           MovDelay[x][y]--;
7902           if (MovDelay[x][y])
7903             return;
7904         }
7905
7906         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7907         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7908         Store[x][y + 1] = Store[x][y];
7909         Store[x][y] = 0;
7910
7911         PlayLevelSoundAction(x, y, ACTION_FILLING);
7912       }
7913       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7914       {
7915         if (!MovDelay[x][y])
7916         {
7917           MovDelay[x][y] = TILEY + 1;
7918
7919           ResetGfxAnimation(x, y);
7920           ResetGfxAnimation(x, y + 1);
7921         }
7922
7923         if (MovDelay[x][y])
7924         {
7925           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7926           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7927
7928           MovDelay[x][y]--;
7929           if (MovDelay[x][y])
7930             return;
7931         }
7932
7933         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7934         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7935         Store[x][y + 1] = Store[x][y];
7936         Store[x][y] = 0;
7937
7938         PlayLevelSoundAction(x, y, ACTION_FILLING);
7939       }
7940     }
7941     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7942              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7943     {
7944       InitMovingField(x, y, MV_DOWN);
7945       started_moving = TRUE;
7946
7947       Tile[x][y] = EL_QUICKSAND_FILLING;
7948       Store[x][y] = element;
7949
7950       PlayLevelSoundAction(x, y, ACTION_FILLING);
7951     }
7952     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7953              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7954     {
7955       InitMovingField(x, y, MV_DOWN);
7956       started_moving = TRUE;
7957
7958       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7959       Store[x][y] = element;
7960
7961       PlayLevelSoundAction(x, y, ACTION_FILLING);
7962     }
7963     else if (element == EL_MAGIC_WALL_FULL)
7964     {
7965       if (IS_FREE(x, y + 1))
7966       {
7967         InitMovingField(x, y, MV_DOWN);
7968         started_moving = TRUE;
7969
7970         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7971         Store[x][y] = EL_CHANGED(Store[x][y]);
7972       }
7973       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7974       {
7975         if (!MovDelay[x][y])
7976           MovDelay[x][y] = TILEY / 4 + 1;
7977
7978         if (MovDelay[x][y])
7979         {
7980           MovDelay[x][y]--;
7981           if (MovDelay[x][y])
7982             return;
7983         }
7984
7985         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7986         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7987         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7988         Store[x][y] = 0;
7989       }
7990     }
7991     else if (element == EL_BD_MAGIC_WALL_FULL)
7992     {
7993       if (IS_FREE(x, y + 1))
7994       {
7995         InitMovingField(x, y, MV_DOWN);
7996         started_moving = TRUE;
7997
7998         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7999         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
8000       }
8001       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
8002       {
8003         if (!MovDelay[x][y])
8004           MovDelay[x][y] = TILEY / 4 + 1;
8005
8006         if (MovDelay[x][y])
8007         {
8008           MovDelay[x][y]--;
8009           if (MovDelay[x][y])
8010             return;
8011         }
8012
8013         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
8014         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
8015         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
8016         Store[x][y] = 0;
8017       }
8018     }
8019     else if (element == EL_DC_MAGIC_WALL_FULL)
8020     {
8021       if (IS_FREE(x, y + 1))
8022       {
8023         InitMovingField(x, y, MV_DOWN);
8024         started_moving = TRUE;
8025
8026         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
8027         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
8028       }
8029       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
8030       {
8031         if (!MovDelay[x][y])
8032           MovDelay[x][y] = TILEY / 4 + 1;
8033
8034         if (MovDelay[x][y])
8035         {
8036           MovDelay[x][y]--;
8037           if (MovDelay[x][y])
8038             return;
8039         }
8040
8041         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
8042         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
8043         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
8044         Store[x][y] = 0;
8045       }
8046     }
8047     else if ((CAN_PASS_MAGIC_WALL(element) &&
8048               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
8049                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
8050              (CAN_PASS_DC_MAGIC_WALL(element) &&
8051               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
8052
8053     {
8054       InitMovingField(x, y, MV_DOWN);
8055       started_moving = TRUE;
8056
8057       Tile[x][y] =
8058         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
8059          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
8060          EL_DC_MAGIC_WALL_FILLING);
8061       Store[x][y] = element;
8062     }
8063     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
8064     {
8065       SplashAcid(x, y + 1);
8066
8067       InitMovingField(x, y, MV_DOWN);
8068       started_moving = TRUE;
8069
8070       Store[x][y] = EL_ACID;
8071     }
8072     else if (
8073              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8074               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
8075              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
8076               CAN_FALL(element) && WasJustFalling[x][y] &&
8077               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
8078
8079              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
8080               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
8081               (Tile[x][y + 1] == EL_BLOCKED)))
8082     {
8083       /* this is needed for a special case not covered by calling "Impact()"
8084          from "ContinueMoving()": if an element moves to a tile directly below
8085          another element which was just falling on that tile (which was empty
8086          in the previous frame), the falling element above would just stop
8087          instead of smashing the element below (in previous version, the above
8088          element was just checked for "moving" instead of "falling", resulting
8089          in incorrect smashes caused by horizontal movement of the above
8090          element; also, the case of the player being the element to smash was
8091          simply not covered here... :-/ ) */
8092
8093       CheckCollision[x][y] = 0;
8094       CheckImpact[x][y] = 0;
8095
8096       Impact(x, y);
8097     }
8098     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
8099     {
8100       if (MovDir[x][y] == MV_NONE)
8101       {
8102         InitMovingField(x, y, MV_DOWN);
8103         started_moving = TRUE;
8104       }
8105     }
8106     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
8107     {
8108       if (WasJustFalling[x][y]) // prevent animation from being restarted
8109         MovDir[x][y] = MV_DOWN;
8110
8111       InitMovingField(x, y, MV_DOWN);
8112       started_moving = TRUE;
8113     }
8114     else if (element == EL_AMOEBA_DROP)
8115     {
8116       Tile[x][y] = EL_AMOEBA_GROWING;
8117       Store[x][y] = EL_AMOEBA_WET;
8118     }
8119     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
8120               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
8121              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
8122              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
8123     {
8124       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8125                                 (IS_FREE(x - 1, y + 1) ||
8126                                  Tile[x - 1][y + 1] == EL_ACID));
8127       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8128                                 (IS_FREE(x + 1, y + 1) ||
8129                                  Tile[x + 1][y + 1] == EL_ACID));
8130       boolean can_fall_any  = (can_fall_left || can_fall_right);
8131       boolean can_fall_both = (can_fall_left && can_fall_right);
8132       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8133
8134       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8135       {
8136         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8137           can_fall_right = FALSE;
8138         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8139           can_fall_left = FALSE;
8140         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8141           can_fall_right = FALSE;
8142         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8143           can_fall_left = FALSE;
8144
8145         can_fall_any  = (can_fall_left || can_fall_right);
8146         can_fall_both = FALSE;
8147       }
8148
8149       if (can_fall_both)
8150       {
8151         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8152           can_fall_right = FALSE;       // slip down on left side
8153         else
8154           can_fall_left = !(can_fall_right = RND(2));
8155
8156         can_fall_both = FALSE;
8157       }
8158
8159       if (can_fall_any)
8160       {
8161         // if not determined otherwise, prefer left side for slipping down
8162         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8163         started_moving = TRUE;
8164       }
8165     }
8166     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8167     {
8168       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8169       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8170       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8171       int belt_dir = game.belt_dir[belt_nr];
8172
8173       if ((belt_dir == MV_LEFT  && left_is_free) ||
8174           (belt_dir == MV_RIGHT && right_is_free))
8175       {
8176         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8177
8178         InitMovingField(x, y, belt_dir);
8179         started_moving = TRUE;
8180
8181         Pushed[x][y] = TRUE;
8182         Pushed[nextx][y] = TRUE;
8183
8184         GfxAction[x][y] = ACTION_DEFAULT;
8185       }
8186       else
8187       {
8188         MovDir[x][y] = 0;       // if element was moving, stop it
8189       }
8190     }
8191   }
8192
8193   // not "else if" because of elements that can fall and move (EL_SPRING)
8194   if (CAN_MOVE(element) && !started_moving)
8195   {
8196     int move_pattern = element_info[element].move_pattern;
8197     int newx, newy;
8198
8199     Moving2Blocked(x, y, &newx, &newy);
8200
8201     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8202       return;
8203
8204     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8205         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8206     {
8207       WasJustMoving[x][y] = 0;
8208       CheckCollision[x][y] = 0;
8209
8210       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8211
8212       if (Tile[x][y] != element)        // element has changed
8213         return;
8214     }
8215
8216     if (!MovDelay[x][y])        // start new movement phase
8217     {
8218       // all objects that can change their move direction after each step
8219       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8220
8221       if (element != EL_YAMYAM &&
8222           element != EL_DARK_YAMYAM &&
8223           element != EL_PACMAN &&
8224           !(move_pattern & MV_ANY_DIRECTION) &&
8225           move_pattern != MV_TURNING_LEFT &&
8226           move_pattern != MV_TURNING_RIGHT &&
8227           move_pattern != MV_TURNING_LEFT_RIGHT &&
8228           move_pattern != MV_TURNING_RIGHT_LEFT &&
8229           move_pattern != MV_TURNING_RANDOM)
8230       {
8231         TurnRound(x, y);
8232
8233         if (MovDelay[x][y] && (element == EL_BUG ||
8234                                element == EL_SPACESHIP ||
8235                                element == EL_SP_SNIKSNAK ||
8236                                element == EL_SP_ELECTRON ||
8237                                element == EL_MOLE))
8238           TEST_DrawLevelField(x, y);
8239       }
8240     }
8241
8242     if (MovDelay[x][y])         // wait some time before next movement
8243     {
8244       MovDelay[x][y]--;
8245
8246       if (element == EL_ROBOT ||
8247           element == EL_YAMYAM ||
8248           element == EL_DARK_YAMYAM)
8249       {
8250         DrawLevelElementAnimationIfNeeded(x, y, element);
8251         PlayLevelSoundAction(x, y, ACTION_WAITING);
8252       }
8253       else if (element == EL_SP_ELECTRON)
8254         DrawLevelElementAnimationIfNeeded(x, y, element);
8255       else if (element == EL_DRAGON)
8256       {
8257         int i;
8258         int dir = MovDir[x][y];
8259         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8260         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8261         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8262                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8263                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8264                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8265         int frame = getGraphicAnimationFrameXY(graphic, x, y);
8266
8267         GfxAction[x][y] = ACTION_ATTACKING;
8268
8269         if (IS_PLAYER(x, y))
8270           DrawPlayerField(x, y);
8271         else
8272           TEST_DrawLevelField(x, y);
8273
8274         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8275
8276         for (i = 1; i <= 3; i++)
8277         {
8278           int xx = x + i * dx;
8279           int yy = y + i * dy;
8280           int sx = SCREENX(xx);
8281           int sy = SCREENY(yy);
8282           int flame_graphic = graphic + (i - 1);
8283
8284           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8285             break;
8286
8287           if (MovDelay[x][y])
8288           {
8289             int flamed = MovingOrBlocked2Element(xx, yy);
8290
8291             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8292               Bang(xx, yy);
8293             else
8294               RemoveMovingField(xx, yy);
8295
8296             ChangeDelay[xx][yy] = 0;
8297
8298             Tile[xx][yy] = EL_FLAMES;
8299
8300             if (IN_SCR_FIELD(sx, sy))
8301             {
8302               TEST_DrawLevelFieldCrumbled(xx, yy);
8303               DrawScreenGraphic(sx, sy, flame_graphic, frame);
8304             }
8305           }
8306           else
8307           {
8308             if (Tile[xx][yy] == EL_FLAMES)
8309               Tile[xx][yy] = EL_EMPTY;
8310             TEST_DrawLevelField(xx, yy);
8311           }
8312         }
8313       }
8314
8315       if (MovDelay[x][y])       // element still has to wait some time
8316       {
8317         PlayLevelSoundAction(x, y, ACTION_WAITING);
8318
8319         return;
8320       }
8321     }
8322
8323     // now make next step
8324
8325     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8326
8327     if (DONT_COLLIDE_WITH(element) &&
8328         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8329         !PLAYER_ENEMY_PROTECTED(newx, newy))
8330     {
8331       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8332
8333       return;
8334     }
8335
8336     else if (CAN_MOVE_INTO_ACID(element) &&
8337              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8338              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8339              (MovDir[x][y] == MV_DOWN ||
8340               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8341     {
8342       SplashAcid(newx, newy);
8343       Store[x][y] = EL_ACID;
8344     }
8345     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8346     {
8347       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8348           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8349           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8350           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8351       {
8352         RemoveField(x, y);
8353         TEST_DrawLevelField(x, y);
8354
8355         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8356         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8357           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8358
8359         game.friends_still_needed--;
8360         if (!game.friends_still_needed &&
8361             !game.GameOver &&
8362             game.all_players_gone)
8363           LevelSolved();
8364
8365         return;
8366       }
8367       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8368       {
8369         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8370           TEST_DrawLevelField(newx, newy);
8371         else
8372           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8373       }
8374       else if (!IS_FREE(newx, newy))
8375       {
8376         GfxAction[x][y] = ACTION_WAITING;
8377
8378         if (IS_PLAYER(x, y))
8379           DrawPlayerField(x, y);
8380         else
8381           TEST_DrawLevelField(x, y);
8382
8383         return;
8384       }
8385     }
8386     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8387     {
8388       if (IS_FOOD_PIG(Tile[newx][newy]))
8389       {
8390         if (IS_MOVING(newx, newy))
8391           RemoveMovingField(newx, newy);
8392         else
8393         {
8394           Tile[newx][newy] = EL_EMPTY;
8395           TEST_DrawLevelField(newx, newy);
8396         }
8397
8398         PlayLevelSound(x, y, SND_PIG_DIGGING);
8399       }
8400       else if (!IS_FREE(newx, newy))
8401       {
8402         if (IS_PLAYER(x, y))
8403           DrawPlayerField(x, y);
8404         else
8405           TEST_DrawLevelField(x, y);
8406
8407         return;
8408       }
8409     }
8410     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8411     {
8412       if (Store[x][y] != EL_EMPTY)
8413       {
8414         boolean can_clone = FALSE;
8415         int xx, yy;
8416
8417         // check if element to clone is still there
8418         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8419         {
8420           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8421           {
8422             can_clone = TRUE;
8423
8424             break;
8425           }
8426         }
8427
8428         // cannot clone or target field not free anymore -- do not clone
8429         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8430           Store[x][y] = EL_EMPTY;
8431       }
8432
8433       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8434       {
8435         if (IS_MV_DIAGONAL(MovDir[x][y]))
8436         {
8437           int diagonal_move_dir = MovDir[x][y];
8438           int stored = Store[x][y];
8439           int change_delay = 8;
8440           int graphic;
8441
8442           // android is moving diagonally
8443
8444           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8445
8446           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8447           GfxElement[x][y] = EL_EMC_ANDROID;
8448           GfxAction[x][y] = ACTION_SHRINKING;
8449           GfxDir[x][y] = diagonal_move_dir;
8450           ChangeDelay[x][y] = change_delay;
8451
8452           if (Store[x][y] == EL_EMPTY)
8453             Store[x][y] = GfxElementEmpty[x][y];
8454
8455           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8456                                    GfxDir[x][y]);
8457
8458           DrawLevelGraphicAnimation(x, y, graphic);
8459           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8460
8461           if (Tile[newx][newy] == EL_ACID)
8462           {
8463             SplashAcid(newx, newy);
8464
8465             return;
8466           }
8467
8468           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8469
8470           Store[newx][newy] = EL_EMC_ANDROID;
8471           GfxElement[newx][newy] = EL_EMC_ANDROID;
8472           GfxAction[newx][newy] = ACTION_GROWING;
8473           GfxDir[newx][newy] = diagonal_move_dir;
8474           ChangeDelay[newx][newy] = change_delay;
8475
8476           graphic = el_act_dir2img(GfxElement[newx][newy],
8477                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8478
8479           DrawLevelGraphicAnimation(newx, newy, graphic);
8480           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8481
8482           return;
8483         }
8484         else
8485         {
8486           Tile[newx][newy] = EL_EMPTY;
8487           TEST_DrawLevelField(newx, newy);
8488
8489           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8490         }
8491       }
8492       else if (!IS_FREE(newx, newy))
8493       {
8494         return;
8495       }
8496     }
8497     else if (IS_CUSTOM_ELEMENT(element) &&
8498              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8499     {
8500       if (!DigFieldByCE(newx, newy, element))
8501         return;
8502
8503       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8504       {
8505         RunnerVisit[x][y] = FrameCounter;
8506         PlayerVisit[x][y] /= 8;         // expire player visit path
8507       }
8508     }
8509     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8510     {
8511       if (!IS_FREE(newx, newy))
8512       {
8513         if (IS_PLAYER(x, y))
8514           DrawPlayerField(x, y);
8515         else
8516           TEST_DrawLevelField(x, y);
8517
8518         return;
8519       }
8520       else
8521       {
8522         boolean wanna_flame = !RND(10);
8523         int dx = newx - x, dy = newy - y;
8524         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8525         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8526         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8527                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8528         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8529                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8530
8531         if ((wanna_flame ||
8532              IS_CLASSIC_ENEMY(element1) ||
8533              IS_CLASSIC_ENEMY(element2)) &&
8534             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8535             element1 != EL_FLAMES && element2 != EL_FLAMES)
8536         {
8537           ResetGfxAnimation(x, y);
8538           GfxAction[x][y] = ACTION_ATTACKING;
8539
8540           if (IS_PLAYER(x, y))
8541             DrawPlayerField(x, y);
8542           else
8543             TEST_DrawLevelField(x, y);
8544
8545           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8546
8547           MovDelay[x][y] = 50;
8548
8549           Tile[newx][newy] = EL_FLAMES;
8550           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8551             Tile[newx1][newy1] = EL_FLAMES;
8552           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8553             Tile[newx2][newy2] = EL_FLAMES;
8554
8555           return;
8556         }
8557       }
8558     }
8559     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8560              Tile[newx][newy] == EL_DIAMOND)
8561     {
8562       if (IS_MOVING(newx, newy))
8563         RemoveMovingField(newx, newy);
8564       else
8565       {
8566         Tile[newx][newy] = EL_EMPTY;
8567         TEST_DrawLevelField(newx, newy);
8568       }
8569
8570       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8571     }
8572     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8573              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8574     {
8575       if (AmoebaNr[newx][newy])
8576       {
8577         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8578         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8579             Tile[newx][newy] == EL_BD_AMOEBA)
8580           AmoebaCnt[AmoebaNr[newx][newy]]--;
8581       }
8582
8583       if (IS_MOVING(newx, newy))
8584       {
8585         RemoveMovingField(newx, newy);
8586       }
8587       else
8588       {
8589         Tile[newx][newy] = EL_EMPTY;
8590         TEST_DrawLevelField(newx, newy);
8591       }
8592
8593       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8594     }
8595     else if ((element == EL_PACMAN || element == EL_MOLE)
8596              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8597     {
8598       if (AmoebaNr[newx][newy])
8599       {
8600         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8601         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8602             Tile[newx][newy] == EL_BD_AMOEBA)
8603           AmoebaCnt[AmoebaNr[newx][newy]]--;
8604       }
8605
8606       if (element == EL_MOLE)
8607       {
8608         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8609         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8610
8611         ResetGfxAnimation(x, y);
8612         GfxAction[x][y] = ACTION_DIGGING;
8613         TEST_DrawLevelField(x, y);
8614
8615         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8616
8617         return;                         // wait for shrinking amoeba
8618       }
8619       else      // element == EL_PACMAN
8620       {
8621         Tile[newx][newy] = EL_EMPTY;
8622         TEST_DrawLevelField(newx, newy);
8623         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8624       }
8625     }
8626     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8627              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8628               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8629     {
8630       // wait for shrinking amoeba to completely disappear
8631       return;
8632     }
8633     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8634     {
8635       // object was running against a wall
8636
8637       TurnRound(x, y);
8638
8639       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8640         DrawLevelElementAnimation(x, y, element);
8641
8642       if (DONT_TOUCH(element))
8643         TestIfBadThingTouchesPlayer(x, y);
8644
8645       return;
8646     }
8647
8648     InitMovingField(x, y, MovDir[x][y]);
8649
8650     PlayLevelSoundAction(x, y, ACTION_MOVING);
8651   }
8652
8653   if (MovDir[x][y])
8654     ContinueMoving(x, y);
8655 }
8656
8657 void ContinueMoving(int x, int y)
8658 {
8659   int element = Tile[x][y];
8660   struct ElementInfo *ei = &element_info[element];
8661   int direction = MovDir[x][y];
8662   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8663   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8664   int newx = x + dx, newy = y + dy;
8665   int stored = Store[x][y];
8666   int stored_new = Store[newx][newy];
8667   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8668   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8669   boolean last_line = (newy == lev_fieldy - 1);
8670   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8671
8672   if (pushed_by_player)         // special case: moving object pushed by player
8673   {
8674     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8675   }
8676   else if (use_step_delay)      // special case: moving object has step delay
8677   {
8678     if (!MovDelay[x][y])
8679       MovPos[x][y] += getElementMoveStepsize(x, y);
8680
8681     if (MovDelay[x][y])
8682       MovDelay[x][y]--;
8683     else
8684       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8685
8686     if (MovDelay[x][y])
8687     {
8688       TEST_DrawLevelField(x, y);
8689
8690       return;   // element is still waiting
8691     }
8692   }
8693   else                          // normal case: generically moving object
8694   {
8695     MovPos[x][y] += getElementMoveStepsize(x, y);
8696   }
8697
8698   if (ABS(MovPos[x][y]) < TILEX)
8699   {
8700     TEST_DrawLevelField(x, y);
8701
8702     return;     // element is still moving
8703   }
8704
8705   // element reached destination field
8706
8707   Tile[x][y] = EL_EMPTY;
8708   Tile[newx][newy] = element;
8709   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8710
8711   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8712   {
8713     element = Tile[newx][newy] = EL_ACID;
8714   }
8715   else if (element == EL_MOLE)
8716   {
8717     Tile[x][y] = EL_SAND;
8718
8719     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8720   }
8721   else if (element == EL_QUICKSAND_FILLING)
8722   {
8723     element = Tile[newx][newy] = get_next_element(element);
8724     Store[newx][newy] = Store[x][y];
8725   }
8726   else if (element == EL_QUICKSAND_EMPTYING)
8727   {
8728     Tile[x][y] = get_next_element(element);
8729     element = Tile[newx][newy] = Store[x][y];
8730   }
8731   else if (element == EL_QUICKSAND_FAST_FILLING)
8732   {
8733     element = Tile[newx][newy] = get_next_element(element);
8734     Store[newx][newy] = Store[x][y];
8735   }
8736   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8737   {
8738     Tile[x][y] = get_next_element(element);
8739     element = Tile[newx][newy] = Store[x][y];
8740   }
8741   else if (element == EL_MAGIC_WALL_FILLING)
8742   {
8743     element = Tile[newx][newy] = get_next_element(element);
8744     if (!game.magic_wall_active)
8745       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8746     Store[newx][newy] = Store[x][y];
8747   }
8748   else if (element == EL_MAGIC_WALL_EMPTYING)
8749   {
8750     Tile[x][y] = get_next_element(element);
8751     if (!game.magic_wall_active)
8752       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8753     element = Tile[newx][newy] = Store[x][y];
8754
8755     InitField(newx, newy, FALSE);
8756   }
8757   else if (element == EL_BD_MAGIC_WALL_FILLING)
8758   {
8759     element = Tile[newx][newy] = get_next_element(element);
8760     if (!game.magic_wall_active)
8761       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8762     Store[newx][newy] = Store[x][y];
8763   }
8764   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8765   {
8766     Tile[x][y] = get_next_element(element);
8767     if (!game.magic_wall_active)
8768       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8769     element = Tile[newx][newy] = Store[x][y];
8770
8771     InitField(newx, newy, FALSE);
8772   }
8773   else if (element == EL_DC_MAGIC_WALL_FILLING)
8774   {
8775     element = Tile[newx][newy] = get_next_element(element);
8776     if (!game.magic_wall_active)
8777       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8778     Store[newx][newy] = Store[x][y];
8779   }
8780   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8781   {
8782     Tile[x][y] = get_next_element(element);
8783     if (!game.magic_wall_active)
8784       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8785     element = Tile[newx][newy] = Store[x][y];
8786
8787     InitField(newx, newy, FALSE);
8788   }
8789   else if (element == EL_AMOEBA_DROPPING)
8790   {
8791     Tile[x][y] = get_next_element(element);
8792     element = Tile[newx][newy] = Store[x][y];
8793   }
8794   else if (element == EL_SOKOBAN_OBJECT)
8795   {
8796     if (Back[x][y])
8797       Tile[x][y] = Back[x][y];
8798
8799     if (Back[newx][newy])
8800       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8801
8802     Back[x][y] = Back[newx][newy] = 0;
8803   }
8804
8805   Store[x][y] = EL_EMPTY;
8806   MovPos[x][y] = 0;
8807   MovDir[x][y] = 0;
8808   MovDelay[x][y] = 0;
8809
8810   MovDelay[newx][newy] = 0;
8811
8812   if (CAN_CHANGE_OR_HAS_ACTION(element))
8813   {
8814     // copy element change control values to new field
8815     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8816     ChangePage[newx][newy]  = ChangePage[x][y];
8817     ChangeCount[newx][newy] = ChangeCount[x][y];
8818     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8819   }
8820
8821   CustomValue[newx][newy] = CustomValue[x][y];
8822
8823   ChangeDelay[x][y] = 0;
8824   ChangePage[x][y] = -1;
8825   ChangeCount[x][y] = 0;
8826   ChangeEvent[x][y] = -1;
8827
8828   CustomValue[x][y] = 0;
8829
8830   // copy animation control values to new field
8831   GfxFrame[newx][newy]  = GfxFrame[x][y];
8832   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8833   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8834   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8835
8836   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8837
8838   // some elements can leave other elements behind after moving
8839   if (ei->move_leave_element != EL_EMPTY &&
8840       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8841       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8842   {
8843     int move_leave_element = ei->move_leave_element;
8844
8845     // this makes it possible to leave the removed element again
8846     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8847       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8848
8849     Tile[x][y] = move_leave_element;
8850
8851     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8852       MovDir[x][y] = direction;
8853
8854     InitField(x, y, FALSE);
8855
8856     if (GFX_CRUMBLED(Tile[x][y]))
8857       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8858
8859     if (IS_PLAYER_ELEMENT(move_leave_element))
8860       RelocatePlayer(x, y, move_leave_element);
8861   }
8862
8863   // do this after checking for left-behind element
8864   ResetGfxAnimation(x, y);      // reset animation values for old field
8865
8866   if (!CAN_MOVE(element) ||
8867       (CAN_FALL(element) && direction == MV_DOWN &&
8868        (element == EL_SPRING ||
8869         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8870         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8871     GfxDir[x][y] = MovDir[newx][newy] = 0;
8872
8873   TEST_DrawLevelField(x, y);
8874   TEST_DrawLevelField(newx, newy);
8875
8876   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8877
8878   // prevent pushed element from moving on in pushed direction
8879   if (pushed_by_player && CAN_MOVE(element) &&
8880       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8881       !(element_info[element].move_pattern & direction))
8882     TurnRound(newx, newy);
8883
8884   // prevent elements on conveyor belt from moving on in last direction
8885   if (pushed_by_conveyor && CAN_FALL(element) &&
8886       direction & MV_HORIZONTAL)
8887     MovDir[newx][newy] = 0;
8888
8889   if (!pushed_by_player)
8890   {
8891     int nextx = newx + dx, nexty = newy + dy;
8892     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8893
8894     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8895
8896     if (CAN_FALL(element) && direction == MV_DOWN)
8897       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8898
8899     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8900       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8901
8902     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8903       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8904   }
8905
8906   if (DONT_TOUCH(element))      // object may be nasty to player or others
8907   {
8908     TestIfBadThingTouchesPlayer(newx, newy);
8909     TestIfBadThingTouchesFriend(newx, newy);
8910
8911     if (!IS_CUSTOM_ELEMENT(element))
8912       TestIfBadThingTouchesOtherBadThing(newx, newy);
8913   }
8914   else if (element == EL_PENGUIN)
8915     TestIfFriendTouchesBadThing(newx, newy);
8916
8917   if (DONT_GET_HIT_BY(element))
8918   {
8919     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8920   }
8921
8922   // give the player one last chance (one more frame) to move away
8923   if (CAN_FALL(element) && direction == MV_DOWN &&
8924       (last_line || (!IS_FREE(x, newy + 1) &&
8925                      (!IS_PLAYER(x, newy + 1) ||
8926                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8927     Impact(x, newy);
8928
8929   if (pushed_by_player && !game.use_change_when_pushing_bug)
8930   {
8931     int push_side = MV_DIR_OPPOSITE(direction);
8932     struct PlayerInfo *player = PLAYERINFO(x, y);
8933
8934     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8935                                player->index_bit, push_side);
8936     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8937                                         player->index_bit, push_side);
8938   }
8939
8940   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8941     MovDelay[newx][newy] = 1;
8942
8943   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8944
8945   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8946   TestIfElementHitsCustomElement(newx, newy, direction);
8947   TestIfPlayerTouchesCustomElement(newx, newy);
8948   TestIfElementTouchesCustomElement(newx, newy);
8949
8950   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8951       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8952     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8953                              MV_DIR_OPPOSITE(direction));
8954 }
8955
8956 int AmoebaNeighbourNr(int ax, int ay)
8957 {
8958   int i;
8959   int element = Tile[ax][ay];
8960   int group_nr = 0;
8961   struct XY *xy = xy_topdown;
8962
8963   for (i = 0; i < NUM_DIRECTIONS; i++)
8964   {
8965     int x = ax + xy[i].x;
8966     int y = ay + xy[i].y;
8967
8968     if (!IN_LEV_FIELD(x, y))
8969       continue;
8970
8971     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8972       group_nr = AmoebaNr[x][y];
8973   }
8974
8975   return group_nr;
8976 }
8977
8978 static void AmoebaMerge(int ax, int ay)
8979 {
8980   int i, x, y, xx, yy;
8981   int new_group_nr = AmoebaNr[ax][ay];
8982   struct XY *xy = xy_topdown;
8983
8984   if (new_group_nr == 0)
8985     return;
8986
8987   for (i = 0; i < NUM_DIRECTIONS; i++)
8988   {
8989     x = ax + xy[i].x;
8990     y = ay + xy[i].y;
8991
8992     if (!IN_LEV_FIELD(x, y))
8993       continue;
8994
8995     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8996          Tile[x][y] == EL_BD_AMOEBA ||
8997          Tile[x][y] == EL_AMOEBA_DEAD) &&
8998         AmoebaNr[x][y] != new_group_nr)
8999     {
9000       int old_group_nr = AmoebaNr[x][y];
9001
9002       if (old_group_nr == 0)
9003         return;
9004
9005       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
9006       AmoebaCnt[old_group_nr] = 0;
9007       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
9008       AmoebaCnt2[old_group_nr] = 0;
9009
9010       SCAN_PLAYFIELD(xx, yy)
9011       {
9012         if (AmoebaNr[xx][yy] == old_group_nr)
9013           AmoebaNr[xx][yy] = new_group_nr;
9014       }
9015     }
9016   }
9017 }
9018
9019 void AmoebaToDiamond(int ax, int ay)
9020 {
9021   int i, x, y;
9022
9023   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
9024   {
9025     int group_nr = AmoebaNr[ax][ay];
9026
9027 #ifdef DEBUG
9028     if (group_nr == 0)
9029     {
9030       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
9031       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
9032
9033       return;
9034     }
9035 #endif
9036
9037     SCAN_PLAYFIELD(x, y)
9038     {
9039       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
9040       {
9041         AmoebaNr[x][y] = 0;
9042         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
9043       }
9044     }
9045
9046     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
9047                             SND_AMOEBA_TURNING_TO_GEM :
9048                             SND_AMOEBA_TURNING_TO_ROCK));
9049     Bang(ax, ay);
9050   }
9051   else
9052   {
9053     struct XY *xy = xy_topdown;
9054
9055     for (i = 0; i < NUM_DIRECTIONS; i++)
9056     {
9057       x = ax + xy[i].x;
9058       y = ay + xy[i].y;
9059
9060       if (!IN_LEV_FIELD(x, y))
9061         continue;
9062
9063       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
9064       {
9065         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
9066                               SND_AMOEBA_TURNING_TO_GEM :
9067                               SND_AMOEBA_TURNING_TO_ROCK));
9068         Bang(x, y);
9069       }
9070     }
9071   }
9072 }
9073
9074 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
9075 {
9076   int x, y;
9077   int group_nr = AmoebaNr[ax][ay];
9078   boolean done = FALSE;
9079
9080 #ifdef DEBUG
9081   if (group_nr == 0)
9082   {
9083     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
9084     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
9085
9086     return;
9087   }
9088 #endif
9089
9090   SCAN_PLAYFIELD(x, y)
9091   {
9092     if (AmoebaNr[x][y] == group_nr &&
9093         (Tile[x][y] == EL_AMOEBA_DEAD ||
9094          Tile[x][y] == EL_BD_AMOEBA ||
9095          Tile[x][y] == EL_AMOEBA_GROWING))
9096     {
9097       AmoebaNr[x][y] = 0;
9098       Tile[x][y] = new_element;
9099       InitField(x, y, FALSE);
9100       TEST_DrawLevelField(x, y);
9101       done = TRUE;
9102     }
9103   }
9104
9105   if (done)
9106     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
9107                             SND_BD_AMOEBA_TURNING_TO_ROCK :
9108                             SND_BD_AMOEBA_TURNING_TO_GEM));
9109 }
9110
9111 static void AmoebaGrowing(int x, int y)
9112 {
9113   static DelayCounter sound_delay = { 0 };
9114
9115   if (!MovDelay[x][y])          // start new growing cycle
9116   {
9117     MovDelay[x][y] = 7;
9118
9119     if (DelayReached(&sound_delay))
9120     {
9121       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9122       sound_delay.value = 30;
9123     }
9124   }
9125
9126   if (MovDelay[x][y])           // wait some time before growing bigger
9127   {
9128     MovDelay[x][y]--;
9129     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9130     {
9131       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9132                                            6 - MovDelay[x][y]);
9133
9134       DrawLevelGraphic(x, y, IMG_AMOEBA_GROWING, frame);
9135     }
9136
9137     if (!MovDelay[x][y])
9138     {
9139       Tile[x][y] = Store[x][y];
9140       Store[x][y] = 0;
9141       TEST_DrawLevelField(x, y);
9142     }
9143   }
9144 }
9145
9146 static void AmoebaShrinking(int x, int y)
9147 {
9148   static DelayCounter sound_delay = { 0 };
9149
9150   if (!MovDelay[x][y])          // start new shrinking cycle
9151   {
9152     MovDelay[x][y] = 7;
9153
9154     if (DelayReached(&sound_delay))
9155       sound_delay.value = 30;
9156   }
9157
9158   if (MovDelay[x][y])           // wait some time before shrinking
9159   {
9160     MovDelay[x][y]--;
9161     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9162     {
9163       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9164                                            6 - MovDelay[x][y]);
9165
9166       DrawLevelGraphic(x, y, IMG_AMOEBA_SHRINKING, frame);
9167     }
9168
9169     if (!MovDelay[x][y])
9170     {
9171       Tile[x][y] = EL_EMPTY;
9172       TEST_DrawLevelField(x, y);
9173
9174       // don't let mole enter this field in this cycle;
9175       // (give priority to objects falling to this field from above)
9176       Stop[x][y] = TRUE;
9177     }
9178   }
9179 }
9180
9181 static void AmoebaReproduce(int ax, int ay)
9182 {
9183   int i;
9184   int element = Tile[ax][ay];
9185   int graphic = el2img(element);
9186   int newax = ax, neway = ay;
9187   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9188   struct XY *xy = xy_topdown;
9189
9190   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9191   {
9192     Tile[ax][ay] = EL_AMOEBA_DEAD;
9193     TEST_DrawLevelField(ax, ay);
9194     return;
9195   }
9196
9197   if (IS_ANIMATED(graphic))
9198     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9199
9200   if (!MovDelay[ax][ay])        // start making new amoeba field
9201     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9202
9203   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9204   {
9205     MovDelay[ax][ay]--;
9206     if (MovDelay[ax][ay])
9207       return;
9208   }
9209
9210   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9211   {
9212     int start = RND(4);
9213     int x = ax + xy[start].x;
9214     int y = ay + xy[start].y;
9215
9216     if (!IN_LEV_FIELD(x, y))
9217       return;
9218
9219     if (IS_FREE(x, y) ||
9220         CAN_GROW_INTO(Tile[x][y]) ||
9221         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9222         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9223     {
9224       newax = x;
9225       neway = y;
9226     }
9227
9228     if (newax == ax && neway == ay)
9229       return;
9230   }
9231   else                          // normal or "filled" (BD style) amoeba
9232   {
9233     int start = RND(4);
9234     boolean waiting_for_player = FALSE;
9235
9236     for (i = 0; i < NUM_DIRECTIONS; i++)
9237     {
9238       int j = (start + i) % 4;
9239       int x = ax + xy[j].x;
9240       int y = ay + xy[j].y;
9241
9242       if (!IN_LEV_FIELD(x, y))
9243         continue;
9244
9245       if (IS_FREE(x, y) ||
9246           CAN_GROW_INTO(Tile[x][y]) ||
9247           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9248           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9249       {
9250         newax = x;
9251         neway = y;
9252         break;
9253       }
9254       else if (IS_PLAYER(x, y))
9255         waiting_for_player = TRUE;
9256     }
9257
9258     if (newax == ax && neway == ay)             // amoeba cannot grow
9259     {
9260       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9261       {
9262         Tile[ax][ay] = EL_AMOEBA_DEAD;
9263         TEST_DrawLevelField(ax, ay);
9264         AmoebaCnt[AmoebaNr[ax][ay]]--;
9265
9266         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9267         {
9268           if (element == EL_AMOEBA_FULL)
9269             AmoebaToDiamond(ax, ay);
9270           else if (element == EL_BD_AMOEBA)
9271             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9272         }
9273       }
9274       return;
9275     }
9276     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9277     {
9278       // amoeba gets larger by growing in some direction
9279
9280       int new_group_nr = AmoebaNr[ax][ay];
9281
9282 #ifdef DEBUG
9283   if (new_group_nr == 0)
9284   {
9285     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9286           newax, neway);
9287     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9288
9289     return;
9290   }
9291 #endif
9292
9293       AmoebaNr[newax][neway] = new_group_nr;
9294       AmoebaCnt[new_group_nr]++;
9295       AmoebaCnt2[new_group_nr]++;
9296
9297       // if amoeba touches other amoeba(s) after growing, unify them
9298       AmoebaMerge(newax, neway);
9299
9300       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9301       {
9302         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9303         return;
9304       }
9305     }
9306   }
9307
9308   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9309       (neway == lev_fieldy - 1 && newax != ax))
9310   {
9311     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9312     Store[newax][neway] = element;
9313   }
9314   else if (neway == ay || element == EL_EMC_DRIPPER)
9315   {
9316     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9317
9318     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9319   }
9320   else
9321   {
9322     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9323     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9324     Store[ax][ay] = EL_AMOEBA_DROP;
9325     ContinueMoving(ax, ay);
9326     return;
9327   }
9328
9329   TEST_DrawLevelField(newax, neway);
9330 }
9331
9332 static void Life(int ax, int ay)
9333 {
9334   int x1, y1, x2, y2;
9335   int life_time = 40;
9336   int element = Tile[ax][ay];
9337   int graphic = el2img(element);
9338   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9339                          level.biomaze);
9340   boolean changed = FALSE;
9341
9342   if (IS_ANIMATED(graphic))
9343     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9344
9345   if (Stop[ax][ay])
9346     return;
9347
9348   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9349     MovDelay[ax][ay] = life_time;
9350
9351   if (MovDelay[ax][ay])         // wait some time before next cycle
9352   {
9353     MovDelay[ax][ay]--;
9354     if (MovDelay[ax][ay])
9355       return;
9356   }
9357
9358   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9359   {
9360     int xx = ax+x1, yy = ay+y1;
9361     int old_element = Tile[xx][yy];
9362     int num_neighbours = 0;
9363
9364     if (!IN_LEV_FIELD(xx, yy))
9365       continue;
9366
9367     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9368     {
9369       int x = xx+x2, y = yy+y2;
9370
9371       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9372         continue;
9373
9374       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9375       boolean is_neighbour = FALSE;
9376
9377       if (level.use_life_bugs)
9378         is_neighbour =
9379           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9380            (IS_FREE(x, y)                             &&  Stop[x][y]));
9381       else
9382         is_neighbour =
9383           (Last[x][y] == element || is_player_cell);
9384
9385       if (is_neighbour)
9386         num_neighbours++;
9387     }
9388
9389     boolean is_free = FALSE;
9390
9391     if (level.use_life_bugs)
9392       is_free = (IS_FREE(xx, yy));
9393     else
9394       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9395
9396     if (xx == ax && yy == ay)           // field in the middle
9397     {
9398       if (num_neighbours < life_parameter[0] ||
9399           num_neighbours > life_parameter[1])
9400       {
9401         Tile[xx][yy] = EL_EMPTY;
9402         if (Tile[xx][yy] != old_element)
9403           TEST_DrawLevelField(xx, yy);
9404         Stop[xx][yy] = TRUE;
9405         changed = TRUE;
9406       }
9407     }
9408     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9409     {                                   // free border field
9410       if (num_neighbours >= life_parameter[2] &&
9411           num_neighbours <= life_parameter[3])
9412       {
9413         Tile[xx][yy] = element;
9414         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time - 1);
9415         if (Tile[xx][yy] != old_element)
9416           TEST_DrawLevelField(xx, yy);
9417         Stop[xx][yy] = TRUE;
9418         changed = TRUE;
9419       }
9420     }
9421   }
9422
9423   if (changed)
9424     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9425                    SND_GAME_OF_LIFE_GROWING);
9426 }
9427
9428 static void InitRobotWheel(int x, int y)
9429 {
9430   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9431 }
9432
9433 static void RunRobotWheel(int x, int y)
9434 {
9435   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9436 }
9437
9438 static void StopRobotWheel(int x, int y)
9439 {
9440   if (game.robot_wheel_x == x &&
9441       game.robot_wheel_y == y)
9442   {
9443     game.robot_wheel_x = -1;
9444     game.robot_wheel_y = -1;
9445     game.robot_wheel_active = FALSE;
9446   }
9447 }
9448
9449 static void InitTimegateWheel(int x, int y)
9450 {
9451   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9452 }
9453
9454 static void RunTimegateWheel(int x, int y)
9455 {
9456   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9457 }
9458
9459 static void InitMagicBallDelay(int x, int y)
9460 {
9461   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9462 }
9463
9464 static void ActivateMagicBall(int bx, int by)
9465 {
9466   int x, y;
9467
9468   if (level.ball_random)
9469   {
9470     int pos_border = RND(8);    // select one of the eight border elements
9471     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9472     int xx = pos_content % 3;
9473     int yy = pos_content / 3;
9474
9475     x = bx - 1 + xx;
9476     y = by - 1 + yy;
9477
9478     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9479       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9480   }
9481   else
9482   {
9483     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9484     {
9485       int xx = x - bx + 1;
9486       int yy = y - by + 1;
9487
9488       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9489         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9490     }
9491   }
9492
9493   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9494 }
9495
9496 static void CheckExit(int x, int y)
9497 {
9498   if (game.gems_still_needed > 0 ||
9499       game.sokoban_fields_still_needed > 0 ||
9500       game.sokoban_objects_still_needed > 0 ||
9501       game.lights_still_needed > 0)
9502   {
9503     int element = Tile[x][y];
9504     int graphic = el2img(element);
9505
9506     if (IS_ANIMATED(graphic))
9507       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9508
9509     return;
9510   }
9511
9512   // do not re-open exit door closed after last player
9513   if (game.all_players_gone)
9514     return;
9515
9516   Tile[x][y] = EL_EXIT_OPENING;
9517
9518   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9519 }
9520
9521 static void CheckExitEM(int x, int y)
9522 {
9523   if (game.gems_still_needed > 0 ||
9524       game.sokoban_fields_still_needed > 0 ||
9525       game.sokoban_objects_still_needed > 0 ||
9526       game.lights_still_needed > 0)
9527   {
9528     int element = Tile[x][y];
9529     int graphic = el2img(element);
9530
9531     if (IS_ANIMATED(graphic))
9532       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9533
9534     return;
9535   }
9536
9537   // do not re-open exit door closed after last player
9538   if (game.all_players_gone)
9539     return;
9540
9541   Tile[x][y] = EL_EM_EXIT_OPENING;
9542
9543   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9544 }
9545
9546 static void CheckExitSteel(int x, int y)
9547 {
9548   if (game.gems_still_needed > 0 ||
9549       game.sokoban_fields_still_needed > 0 ||
9550       game.sokoban_objects_still_needed > 0 ||
9551       game.lights_still_needed > 0)
9552   {
9553     int element = Tile[x][y];
9554     int graphic = el2img(element);
9555
9556     if (IS_ANIMATED(graphic))
9557       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9558
9559     return;
9560   }
9561
9562   // do not re-open exit door closed after last player
9563   if (game.all_players_gone)
9564     return;
9565
9566   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9567
9568   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9569 }
9570
9571 static void CheckExitSteelEM(int x, int y)
9572 {
9573   if (game.gems_still_needed > 0 ||
9574       game.sokoban_fields_still_needed > 0 ||
9575       game.sokoban_objects_still_needed > 0 ||
9576       game.lights_still_needed > 0)
9577   {
9578     int element = Tile[x][y];
9579     int graphic = el2img(element);
9580
9581     if (IS_ANIMATED(graphic))
9582       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9583
9584     return;
9585   }
9586
9587   // do not re-open exit door closed after last player
9588   if (game.all_players_gone)
9589     return;
9590
9591   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9592
9593   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9594 }
9595
9596 static void CheckExitSP(int x, int y)
9597 {
9598   if (game.gems_still_needed > 0)
9599   {
9600     int element = Tile[x][y];
9601     int graphic = el2img(element);
9602
9603     if (IS_ANIMATED(graphic))
9604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9605
9606     return;
9607   }
9608
9609   // do not re-open exit door closed after last player
9610   if (game.all_players_gone)
9611     return;
9612
9613   Tile[x][y] = EL_SP_EXIT_OPENING;
9614
9615   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9616 }
9617
9618 static void CloseAllOpenTimegates(void)
9619 {
9620   int x, y;
9621
9622   SCAN_PLAYFIELD(x, y)
9623   {
9624     int element = Tile[x][y];
9625
9626     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9627     {
9628       Tile[x][y] = EL_TIMEGATE_CLOSING;
9629
9630       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9631     }
9632   }
9633 }
9634
9635 static void DrawTwinkleOnField(int x, int y)
9636 {
9637   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9638     return;
9639
9640   if (Tile[x][y] == EL_BD_DIAMOND)
9641     return;
9642
9643   if (MovDelay[x][y] == 0)      // next animation frame
9644     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9645
9646   if (MovDelay[x][y] != 0)      // wait some time before next frame
9647   {
9648     MovDelay[x][y]--;
9649
9650     DrawLevelElementAnimation(x, y, Tile[x][y]);
9651
9652     if (MovDelay[x][y] != 0)
9653     {
9654       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9655                                            10 - MovDelay[x][y]);
9656
9657       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9658     }
9659   }
9660 }
9661
9662 static void WallGrowing(int x, int y)
9663 {
9664   int delay = 6;
9665
9666   if (!MovDelay[x][y])          // next animation frame
9667     MovDelay[x][y] = 3 * delay;
9668
9669   if (MovDelay[x][y])           // wait some time before next frame
9670   {
9671     MovDelay[x][y]--;
9672
9673     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9674     {
9675       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9676       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9677
9678       DrawLevelGraphic(x, y, graphic, frame);
9679     }
9680
9681     if (!MovDelay[x][y])
9682     {
9683       if (MovDir[x][y] == MV_LEFT)
9684       {
9685         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9686           TEST_DrawLevelField(x - 1, y);
9687       }
9688       else if (MovDir[x][y] == MV_RIGHT)
9689       {
9690         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9691           TEST_DrawLevelField(x + 1, y);
9692       }
9693       else if (MovDir[x][y] == MV_UP)
9694       {
9695         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9696           TEST_DrawLevelField(x, y - 1);
9697       }
9698       else
9699       {
9700         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9701           TEST_DrawLevelField(x, y + 1);
9702       }
9703
9704       Tile[x][y] = Store[x][y];
9705       Store[x][y] = 0;
9706       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9707       TEST_DrawLevelField(x, y);
9708     }
9709   }
9710 }
9711
9712 static void CheckWallGrowing(int ax, int ay)
9713 {
9714   int element = Tile[ax][ay];
9715   int graphic = el2img(element);
9716   boolean free_top    = FALSE;
9717   boolean free_bottom = FALSE;
9718   boolean free_left   = FALSE;
9719   boolean free_right  = FALSE;
9720   boolean stop_top    = FALSE;
9721   boolean stop_bottom = FALSE;
9722   boolean stop_left   = FALSE;
9723   boolean stop_right  = FALSE;
9724   boolean new_wall    = FALSE;
9725
9726   boolean is_steelwall  = (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9727                            element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9728                            element == EL_EXPANDABLE_STEELWALL_ANY);
9729
9730   boolean grow_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9731                              element == EL_EXPANDABLE_WALL_ANY ||
9732                              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9733                              element == EL_EXPANDABLE_STEELWALL_ANY);
9734
9735   boolean grow_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9736                              element == EL_EXPANDABLE_WALL_ANY ||
9737                              element == EL_EXPANDABLE_WALL ||
9738                              element == EL_BD_EXPANDABLE_WALL ||
9739                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9740                              element == EL_EXPANDABLE_STEELWALL_ANY);
9741
9742   boolean stop_vertical   = (element == EL_EXPANDABLE_WALL_VERTICAL ||
9743                              element == EL_EXPANDABLE_STEELWALL_VERTICAL);
9744
9745   boolean stop_horizontal = (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9746                              element == EL_EXPANDABLE_WALL ||
9747                              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL);
9748
9749   int wall_growing = (is_steelwall ?
9750                       EL_EXPANDABLE_STEELWALL_GROWING :
9751                       EL_EXPANDABLE_WALL_GROWING);
9752
9753   int gfx_wall_growing_up    = (is_steelwall ?
9754                                 IMG_EXPANDABLE_STEELWALL_GROWING_UP :
9755                                 IMG_EXPANDABLE_WALL_GROWING_UP);
9756   int gfx_wall_growing_down  = (is_steelwall ?
9757                                 IMG_EXPANDABLE_STEELWALL_GROWING_DOWN :
9758                                 IMG_EXPANDABLE_WALL_GROWING_DOWN);
9759   int gfx_wall_growing_left  = (is_steelwall ?
9760                                 IMG_EXPANDABLE_STEELWALL_GROWING_LEFT :
9761                                 IMG_EXPANDABLE_WALL_GROWING_LEFT);
9762   int gfx_wall_growing_right = (is_steelwall ?
9763                                 IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT :
9764                                 IMG_EXPANDABLE_WALL_GROWING_RIGHT);
9765
9766   if (IS_ANIMATED(graphic))
9767     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9768
9769   if (!MovDelay[ax][ay])        // start building new wall
9770     MovDelay[ax][ay] = 6;
9771
9772   if (MovDelay[ax][ay])         // wait some time before building new wall
9773   {
9774     MovDelay[ax][ay]--;
9775     if (MovDelay[ax][ay])
9776       return;
9777   }
9778
9779   if (IN_LEV_FIELD(ax, ay - 1) && IS_FREE(ax, ay - 1))
9780     free_top = TRUE;
9781   if (IN_LEV_FIELD(ax, ay + 1) && IS_FREE(ax, ay + 1))
9782     free_bottom = TRUE;
9783   if (IN_LEV_FIELD(ax - 1, ay) && IS_FREE(ax - 1, ay))
9784     free_left = TRUE;
9785   if (IN_LEV_FIELD(ax + 1, ay) && IS_FREE(ax + 1, ay))
9786     free_right = TRUE;
9787
9788   if (grow_vertical)
9789   {
9790     if (free_top)
9791     {
9792       Tile[ax][ay - 1] = wall_growing;
9793       Store[ax][ay - 1] = element;
9794       GfxDir[ax][ay - 1] = MovDir[ax][ay - 1] = MV_UP;
9795
9796       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay - 1)))
9797         DrawLevelGraphic(ax, ay - 1, gfx_wall_growing_up, 0);
9798
9799       new_wall = TRUE;
9800     }
9801
9802     if (free_bottom)
9803     {
9804       Tile[ax][ay + 1] = wall_growing;
9805       Store[ax][ay + 1] = element;
9806       GfxDir[ax][ay + 1] = MovDir[ax][ay + 1] = MV_DOWN;
9807
9808       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay + 1)))
9809         DrawLevelGraphic(ax, ay + 1, gfx_wall_growing_down, 0);
9810
9811       new_wall = TRUE;
9812     }
9813   }
9814
9815   if (grow_horizontal)
9816   {
9817     if (free_left)
9818     {
9819       Tile[ax - 1][ay] = wall_growing;
9820       Store[ax - 1][ay] = element;
9821       GfxDir[ax - 1][ay] = MovDir[ax - 1][ay] = MV_LEFT;
9822
9823       if (IN_SCR_FIELD(SCREENX(ax - 1), SCREENY(ay)))
9824         DrawLevelGraphic(ax - 1, ay, gfx_wall_growing_left, 0);
9825
9826       new_wall = TRUE;
9827     }
9828
9829     if (free_right)
9830     {
9831       Tile[ax + 1][ay] = wall_growing;
9832       Store[ax + 1][ay] = element;
9833       GfxDir[ax + 1][ay] = MovDir[ax + 1][ay] = MV_RIGHT;
9834
9835       if (IN_SCR_FIELD(SCREENX(ax + 1), SCREENY(ay)))
9836         DrawLevelGraphic(ax + 1, ay, gfx_wall_growing_right, 0);
9837
9838       new_wall = TRUE;
9839     }
9840   }
9841
9842   if (element == EL_EXPANDABLE_WALL && (free_left || free_right))
9843     TEST_DrawLevelField(ax, ay);
9844
9845   if (!IN_LEV_FIELD(ax, ay - 1) || IS_WALL(Tile[ax][ay - 1]))
9846     stop_top = TRUE;
9847   if (!IN_LEV_FIELD(ax, ay + 1) || IS_WALL(Tile[ax][ay + 1]))
9848     stop_bottom = TRUE;
9849   if (!IN_LEV_FIELD(ax - 1, ay) || IS_WALL(Tile[ax - 1][ay]))
9850     stop_left = TRUE;
9851   if (!IN_LEV_FIELD(ax + 1, ay) || IS_WALL(Tile[ax + 1][ay]))
9852     stop_right = TRUE;
9853
9854   if (((stop_top && stop_bottom) || stop_horizontal) &&
9855       ((stop_left && stop_right) || stop_vertical))
9856     Tile[ax][ay] = EL_WALL;
9857
9858   if (new_wall)
9859     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9860 }
9861
9862 static void CheckForDragon(int x, int y)
9863 {
9864   int i, j;
9865   boolean dragon_found = FALSE;
9866   struct XY *xy = xy_topdown;
9867
9868   for (i = 0; i < NUM_DIRECTIONS; i++)
9869   {
9870     for (j = 0; j < 4; j++)
9871     {
9872       int xx = x + j * xy[i].x;
9873       int yy = y + j * xy[i].y;
9874
9875       if (IN_LEV_FIELD(xx, yy) &&
9876           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9877       {
9878         if (Tile[xx][yy] == EL_DRAGON)
9879           dragon_found = TRUE;
9880       }
9881       else
9882         break;
9883     }
9884   }
9885
9886   if (!dragon_found)
9887   {
9888     for (i = 0; i < NUM_DIRECTIONS; i++)
9889     {
9890       for (j = 0; j < 3; j++)
9891       {
9892         int xx = x + j * xy[i].x;
9893         int yy = y + j * xy[i].y;
9894
9895         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9896         {
9897           Tile[xx][yy] = EL_EMPTY;
9898           TEST_DrawLevelField(xx, yy);
9899         }
9900         else
9901           break;
9902       }
9903     }
9904   }
9905 }
9906
9907 static void InitBuggyBase(int x, int y)
9908 {
9909   int element = Tile[x][y];
9910   int activating_delay = FRAMES_PER_SECOND / 4;
9911
9912   ChangeDelay[x][y] =
9913     (element == EL_SP_BUGGY_BASE ?
9914      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9915      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9916      activating_delay :
9917      element == EL_SP_BUGGY_BASE_ACTIVE ?
9918      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9919 }
9920
9921 static void WarnBuggyBase(int x, int y)
9922 {
9923   int i;
9924   struct XY *xy = xy_topdown;
9925
9926   for (i = 0; i < NUM_DIRECTIONS; i++)
9927   {
9928     int xx = x + xy[i].x;
9929     int yy = y + xy[i].y;
9930
9931     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9932     {
9933       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9934
9935       break;
9936     }
9937   }
9938 }
9939
9940 static void InitTrap(int x, int y)
9941 {
9942   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9943 }
9944
9945 static void ActivateTrap(int x, int y)
9946 {
9947   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9948 }
9949
9950 static void ChangeActiveTrap(int x, int y)
9951 {
9952   int graphic = IMG_TRAP_ACTIVE;
9953
9954   // if new animation frame was drawn, correct crumbled sand border
9955   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9956     TEST_DrawLevelFieldCrumbled(x, y);
9957 }
9958
9959 static int getSpecialActionElement(int element, int number, int base_element)
9960 {
9961   return (element != EL_EMPTY ? element :
9962           number != -1 ? base_element + number - 1 :
9963           EL_EMPTY);
9964 }
9965
9966 static int getModifiedActionNumber(int value_old, int operator, int operand,
9967                                    int value_min, int value_max)
9968 {
9969   int value_new = (operator == CA_MODE_SET      ? operand :
9970                    operator == CA_MODE_ADD      ? value_old + operand :
9971                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9972                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9973                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9974                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9975                    value_old);
9976
9977   return (value_new < value_min ? value_min :
9978           value_new > value_max ? value_max :
9979           value_new);
9980 }
9981
9982 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9983 {
9984   struct ElementInfo *ei = &element_info[element];
9985   struct ElementChangeInfo *change = &ei->change_page[page];
9986   int target_element = change->target_element;
9987   int action_type = change->action_type;
9988   int action_mode = change->action_mode;
9989   int action_arg = change->action_arg;
9990   int action_element = change->action_element;
9991   int i;
9992
9993   if (!change->has_action)
9994     return;
9995
9996   // ---------- determine action paramater values -----------------------------
9997
9998   int level_time_value =
9999     (level.time > 0 ? TimeLeft :
10000      TimePlayed);
10001
10002   int action_arg_element_raw =
10003     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
10004      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
10005      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
10006      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
10007      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
10008      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
10009      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
10010      EL_EMPTY);
10011   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
10012
10013   int action_arg_direction =
10014     (action_arg >= CA_ARG_DIRECTION_LEFT &&
10015      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
10016      action_arg == CA_ARG_DIRECTION_TRIGGER ?
10017      change->actual_trigger_side :
10018      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
10019      MV_DIR_OPPOSITE(change->actual_trigger_side) :
10020      MV_NONE);
10021
10022   int action_arg_number_min =
10023     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
10024      CA_ARG_MIN);
10025
10026   int action_arg_number_max =
10027     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
10028      action_type == CA_SET_LEVEL_GEMS ? 999 :
10029      action_type == CA_SET_LEVEL_TIME ? 9999 :
10030      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10031      action_type == CA_SET_CE_VALUE ? 9999 :
10032      action_type == CA_SET_CE_SCORE ? 9999 :
10033      CA_ARG_MAX);
10034
10035   int action_arg_number_reset =
10036     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10037      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10038      action_type == CA_SET_LEVEL_TIME ? level.time :
10039      action_type == CA_SET_LEVEL_SCORE ? 0 :
10040      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10041      action_type == CA_SET_CE_SCORE ? 0 :
10042      0);
10043
10044   int action_arg_number =
10045     (action_arg <= CA_ARG_MAX ? action_arg :
10046      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10047      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10048      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10049      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10050      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10051      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10052      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10053      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10054      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10055      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10056      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10057      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10058      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10059      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10060      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10061      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10062      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10063      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10064      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10065      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10066      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10067      -1);
10068
10069   int action_arg_number_old =
10070     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10071      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10072      action_type == CA_SET_LEVEL_SCORE ? game.score :
10073      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10074      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10075      0);
10076
10077   int action_arg_number_new =
10078     getModifiedActionNumber(action_arg_number_old,
10079                             action_mode, action_arg_number,
10080                             action_arg_number_min, action_arg_number_max);
10081
10082   int trigger_player_bits =
10083     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10084      change->actual_trigger_player_bits : change->trigger_player);
10085
10086   int action_arg_player_bits =
10087     (action_arg >= CA_ARG_PLAYER_1 &&
10088      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10089      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10090      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10091      PLAYER_BITS_ANY);
10092
10093   // ---------- execute action  -----------------------------------------------
10094
10095   switch (action_type)
10096   {
10097     case CA_NO_ACTION:
10098     {
10099       return;
10100     }
10101
10102     // ---------- level actions  ----------------------------------------------
10103
10104     case CA_RESTART_LEVEL:
10105     {
10106       game.restart_level = TRUE;
10107
10108       break;
10109     }
10110
10111     case CA_SHOW_ENVELOPE:
10112     {
10113       int element = getSpecialActionElement(action_arg_element,
10114                                             action_arg_number, EL_ENVELOPE_1);
10115
10116       if (IS_ENVELOPE(element))
10117         local_player->show_envelope = element;
10118
10119       break;
10120     }
10121
10122     case CA_SET_LEVEL_TIME:
10123     {
10124       if (level.time > 0)       // only modify limited time value
10125       {
10126         TimeLeft = action_arg_number_new;
10127
10128         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10129
10130         DisplayGameControlValues();
10131
10132         if (!TimeLeft && game.time_limit)
10133           for (i = 0; i < MAX_PLAYERS; i++)
10134             KillPlayer(&stored_player[i]);
10135       }
10136
10137       break;
10138     }
10139
10140     case CA_SET_LEVEL_SCORE:
10141     {
10142       game.score = action_arg_number_new;
10143
10144       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10145
10146       DisplayGameControlValues();
10147
10148       break;
10149     }
10150
10151     case CA_SET_LEVEL_GEMS:
10152     {
10153       game.gems_still_needed = action_arg_number_new;
10154
10155       game.snapshot.collected_item = TRUE;
10156
10157       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10158
10159       DisplayGameControlValues();
10160
10161       break;
10162     }
10163
10164     case CA_SET_LEVEL_WIND:
10165     {
10166       game.wind_direction = action_arg_direction;
10167
10168       break;
10169     }
10170
10171     case CA_SET_LEVEL_RANDOM_SEED:
10172     {
10173       // ensure that setting a new random seed while playing is predictable
10174       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10175
10176       break;
10177     }
10178
10179     // ---------- player actions  ---------------------------------------------
10180
10181     case CA_MOVE_PLAYER:
10182     case CA_MOVE_PLAYER_NEW:
10183     {
10184       // automatically move to the next field in specified direction
10185       for (i = 0; i < MAX_PLAYERS; i++)
10186         if (trigger_player_bits & (1 << i))
10187           if (action_type == CA_MOVE_PLAYER ||
10188               stored_player[i].MovPos == 0)
10189             stored_player[i].programmed_action = action_arg_direction;
10190
10191       break;
10192     }
10193
10194     case CA_EXIT_PLAYER:
10195     {
10196       for (i = 0; i < MAX_PLAYERS; i++)
10197         if (action_arg_player_bits & (1 << i))
10198           ExitPlayer(&stored_player[i]);
10199
10200       if (game.players_still_needed == 0)
10201         LevelSolved();
10202
10203       break;
10204     }
10205
10206     case CA_KILL_PLAYER:
10207     {
10208       for (i = 0; i < MAX_PLAYERS; i++)
10209         if (action_arg_player_bits & (1 << i))
10210           KillPlayer(&stored_player[i]);
10211
10212       break;
10213     }
10214
10215     case CA_SET_PLAYER_KEYS:
10216     {
10217       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10218       int element = getSpecialActionElement(action_arg_element,
10219                                             action_arg_number, EL_KEY_1);
10220
10221       if (IS_KEY(element))
10222       {
10223         for (i = 0; i < MAX_PLAYERS; i++)
10224         {
10225           if (trigger_player_bits & (1 << i))
10226           {
10227             stored_player[i].key[KEY_NR(element)] = key_state;
10228
10229             DrawGameDoorValues();
10230           }
10231         }
10232       }
10233
10234       break;
10235     }
10236
10237     case CA_SET_PLAYER_SPEED:
10238     {
10239       for (i = 0; i < MAX_PLAYERS; i++)
10240       {
10241         if (trigger_player_bits & (1 << i))
10242         {
10243           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10244
10245           if (action_arg == CA_ARG_SPEED_FASTER &&
10246               stored_player[i].cannot_move)
10247           {
10248             action_arg_number = STEPSIZE_VERY_SLOW;
10249           }
10250           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10251                    action_arg == CA_ARG_SPEED_FASTER)
10252           {
10253             action_arg_number = 2;
10254             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10255                            CA_MODE_MULTIPLY);
10256           }
10257           else if (action_arg == CA_ARG_NUMBER_RESET)
10258           {
10259             action_arg_number = level.initial_player_stepsize[i];
10260           }
10261
10262           move_stepsize =
10263             getModifiedActionNumber(move_stepsize,
10264                                     action_mode,
10265                                     action_arg_number,
10266                                     action_arg_number_min,
10267                                     action_arg_number_max);
10268
10269           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10270         }
10271       }
10272
10273       break;
10274     }
10275
10276     case CA_SET_PLAYER_SHIELD:
10277     {
10278       for (i = 0; i < MAX_PLAYERS; i++)
10279       {
10280         if (trigger_player_bits & (1 << i))
10281         {
10282           if (action_arg == CA_ARG_SHIELD_OFF)
10283           {
10284             stored_player[i].shield_normal_time_left = 0;
10285             stored_player[i].shield_deadly_time_left = 0;
10286           }
10287           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10288           {
10289             stored_player[i].shield_normal_time_left = 999999;
10290           }
10291           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10292           {
10293             stored_player[i].shield_normal_time_left = 999999;
10294             stored_player[i].shield_deadly_time_left = 999999;
10295           }
10296         }
10297       }
10298
10299       break;
10300     }
10301
10302     case CA_SET_PLAYER_GRAVITY:
10303     {
10304       for (i = 0; i < MAX_PLAYERS; i++)
10305       {
10306         if (trigger_player_bits & (1 << i))
10307         {
10308           stored_player[i].gravity =
10309             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10310              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10311              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10312              stored_player[i].gravity);
10313         }
10314       }
10315
10316       break;
10317     }
10318
10319     case CA_SET_PLAYER_ARTWORK:
10320     {
10321       for (i = 0; i < MAX_PLAYERS; i++)
10322       {
10323         if (trigger_player_bits & (1 << i))
10324         {
10325           int artwork_element = action_arg_element;
10326
10327           if (action_arg == CA_ARG_ELEMENT_RESET)
10328             artwork_element =
10329               (level.use_artwork_element[i] ? level.artwork_element[i] :
10330                stored_player[i].element_nr);
10331
10332           if (stored_player[i].artwork_element != artwork_element)
10333             stored_player[i].Frame = 0;
10334
10335           stored_player[i].artwork_element = artwork_element;
10336
10337           SetPlayerWaiting(&stored_player[i], FALSE);
10338
10339           // set number of special actions for bored and sleeping animation
10340           stored_player[i].num_special_action_bored =
10341             get_num_special_action(artwork_element,
10342                                    ACTION_BORING_1, ACTION_BORING_LAST);
10343           stored_player[i].num_special_action_sleeping =
10344             get_num_special_action(artwork_element,
10345                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10346         }
10347       }
10348
10349       break;
10350     }
10351
10352     case CA_SET_PLAYER_INVENTORY:
10353     {
10354       for (i = 0; i < MAX_PLAYERS; i++)
10355       {
10356         struct PlayerInfo *player = &stored_player[i];
10357         int j, k;
10358
10359         if (trigger_player_bits & (1 << i))
10360         {
10361           int inventory_element = action_arg_element;
10362
10363           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10364               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10365               action_arg == CA_ARG_ELEMENT_ACTION)
10366           {
10367             int element = inventory_element;
10368             int collect_count = element_info[element].collect_count_initial;
10369
10370             if (!IS_CUSTOM_ELEMENT(element))
10371               collect_count = 1;
10372
10373             if (collect_count == 0)
10374               player->inventory_infinite_element = element;
10375             else
10376               for (k = 0; k < collect_count; k++)
10377                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10378                   player->inventory_element[player->inventory_size++] =
10379                     element;
10380           }
10381           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10382                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10383                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10384           {
10385             if (player->inventory_infinite_element != EL_UNDEFINED &&
10386                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10387                                      action_arg_element_raw))
10388               player->inventory_infinite_element = EL_UNDEFINED;
10389
10390             for (k = 0, j = 0; j < player->inventory_size; j++)
10391             {
10392               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10393                                         action_arg_element_raw))
10394                 player->inventory_element[k++] = player->inventory_element[j];
10395             }
10396
10397             player->inventory_size = k;
10398           }
10399           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10400           {
10401             if (player->inventory_size > 0)
10402             {
10403               for (j = 0; j < player->inventory_size - 1; j++)
10404                 player->inventory_element[j] = player->inventory_element[j + 1];
10405
10406               player->inventory_size--;
10407             }
10408           }
10409           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10410           {
10411             if (player->inventory_size > 0)
10412               player->inventory_size--;
10413           }
10414           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10415           {
10416             player->inventory_infinite_element = EL_UNDEFINED;
10417             player->inventory_size = 0;
10418           }
10419           else if (action_arg == CA_ARG_INVENTORY_RESET)
10420           {
10421             player->inventory_infinite_element = EL_UNDEFINED;
10422             player->inventory_size = 0;
10423
10424             if (level.use_initial_inventory[i])
10425             {
10426               for (j = 0; j < level.initial_inventory_size[i]; j++)
10427               {
10428                 int element = level.initial_inventory_content[i][j];
10429                 int collect_count = element_info[element].collect_count_initial;
10430
10431                 if (!IS_CUSTOM_ELEMENT(element))
10432                   collect_count = 1;
10433
10434                 if (collect_count == 0)
10435                   player->inventory_infinite_element = element;
10436                 else
10437                   for (k = 0; k < collect_count; k++)
10438                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10439                       player->inventory_element[player->inventory_size++] =
10440                         element;
10441               }
10442             }
10443           }
10444         }
10445       }
10446
10447       break;
10448     }
10449
10450     // ---------- CE actions  -------------------------------------------------
10451
10452     case CA_SET_CE_VALUE:
10453     {
10454       int last_ce_value = CustomValue[x][y];
10455
10456       CustomValue[x][y] = action_arg_number_new;
10457
10458       if (CustomValue[x][y] != last_ce_value)
10459       {
10460         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10461         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10462
10463         if (CustomValue[x][y] == 0)
10464         {
10465           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10466           ChangeCount[x][y] = 0;        // allow at least one more change
10467
10468           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10469           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10470         }
10471       }
10472
10473       break;
10474     }
10475
10476     case CA_SET_CE_SCORE:
10477     {
10478       int last_ce_score = ei->collect_score;
10479
10480       ei->collect_score = action_arg_number_new;
10481
10482       if (ei->collect_score != last_ce_score)
10483       {
10484         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10485         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10486
10487         if (ei->collect_score == 0)
10488         {
10489           int xx, yy;
10490
10491           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10492           ChangeCount[x][y] = 0;        // allow at least one more change
10493
10494           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10495           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10496
10497           /*
10498             This is a very special case that seems to be a mixture between
10499             CheckElementChange() and CheckTriggeredElementChange(): while
10500             the first one only affects single elements that are triggered
10501             directly, the second one affects multiple elements in the playfield
10502             that are triggered indirectly by another element. This is a third
10503             case: Changing the CE score always affects multiple identical CEs,
10504             so every affected CE must be checked, not only the single CE for
10505             which the CE score was changed in the first place (as every instance
10506             of that CE shares the same CE score, and therefore also can change)!
10507           */
10508           SCAN_PLAYFIELD(xx, yy)
10509           {
10510             if (Tile[xx][yy] == element)
10511               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10512                                  CE_SCORE_GETS_ZERO);
10513           }
10514         }
10515       }
10516
10517       break;
10518     }
10519
10520     case CA_SET_CE_ARTWORK:
10521     {
10522       int artwork_element = action_arg_element;
10523       boolean reset_frame = FALSE;
10524       int xx, yy;
10525
10526       if (action_arg == CA_ARG_ELEMENT_RESET)
10527         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10528                            element);
10529
10530       if (ei->gfx_element != artwork_element)
10531         reset_frame = TRUE;
10532
10533       ei->gfx_element = artwork_element;
10534
10535       SCAN_PLAYFIELD(xx, yy)
10536       {
10537         if (Tile[xx][yy] == element)
10538         {
10539           if (reset_frame)
10540           {
10541             ResetGfxAnimation(xx, yy);
10542             ResetRandomAnimationValue(xx, yy);
10543           }
10544
10545           TEST_DrawLevelField(xx, yy);
10546         }
10547       }
10548
10549       break;
10550     }
10551
10552     // ---------- engine actions  ---------------------------------------------
10553
10554     case CA_SET_ENGINE_SCAN_MODE:
10555     {
10556       InitPlayfieldScanMode(action_arg);
10557
10558       break;
10559     }
10560
10561     default:
10562       break;
10563   }
10564 }
10565
10566 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10567 {
10568   int old_element = Tile[x][y];
10569   int new_element = GetElementFromGroupElement(element);
10570   int previous_move_direction = MovDir[x][y];
10571   int last_ce_value = CustomValue[x][y];
10572   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10573   boolean new_element_is_player = IS_PLAYER_ELEMENT(new_element);
10574   boolean add_player_onto_element = (new_element_is_player &&
10575                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10576                                      IS_WALKABLE(old_element));
10577
10578   if (!add_player_onto_element)
10579   {
10580     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10581       RemoveMovingField(x, y);
10582     else
10583       RemoveField(x, y);
10584
10585     Tile[x][y] = new_element;
10586
10587     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10588       MovDir[x][y] = previous_move_direction;
10589
10590     if (element_info[new_element].use_last_ce_value)
10591       CustomValue[x][y] = last_ce_value;
10592
10593     InitField_WithBug1(x, y, FALSE);
10594
10595     new_element = Tile[x][y];   // element may have changed
10596
10597     ResetGfxAnimation(x, y);
10598     ResetRandomAnimationValue(x, y);
10599
10600     TEST_DrawLevelField(x, y);
10601
10602     if (GFX_CRUMBLED(new_element))
10603       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10604   }
10605
10606   // check if element under the player changes from accessible to unaccessible
10607   // (needed for special case of dropping element which then changes)
10608   // (must be checked after creating new element for walkable group elements)
10609   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10610       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10611   {
10612     Bang(x, y);
10613
10614     return;
10615   }
10616
10617   // "ChangeCount" not set yet to allow "entered by player" change one time
10618   if (new_element_is_player)
10619     RelocatePlayer(x, y, new_element);
10620
10621   if (is_change)
10622     ChangeCount[x][y]++;        // count number of changes in the same frame
10623
10624   TestIfBadThingTouchesPlayer(x, y);
10625   TestIfPlayerTouchesCustomElement(x, y);
10626   TestIfElementTouchesCustomElement(x, y);
10627 }
10628
10629 static void CreateField(int x, int y, int element)
10630 {
10631   CreateFieldExt(x, y, element, FALSE);
10632 }
10633
10634 static void CreateElementFromChange(int x, int y, int element)
10635 {
10636   element = GET_VALID_RUNTIME_ELEMENT(element);
10637
10638   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10639   {
10640     int old_element = Tile[x][y];
10641
10642     // prevent changed element from moving in same engine frame
10643     // unless both old and new element can either fall or move
10644     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10645         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10646       Stop[x][y] = TRUE;
10647   }
10648
10649   CreateFieldExt(x, y, element, TRUE);
10650 }
10651
10652 static boolean ChangeElement(int x, int y, int element, int page)
10653 {
10654   struct ElementInfo *ei = &element_info[element];
10655   struct ElementChangeInfo *change = &ei->change_page[page];
10656   int ce_value = CustomValue[x][y];
10657   int ce_score = ei->collect_score;
10658   int target_element;
10659   int old_element = Tile[x][y];
10660
10661   // always use default change event to prevent running into a loop
10662   if (ChangeEvent[x][y] == -1)
10663     ChangeEvent[x][y] = CE_DELAY;
10664
10665   if (ChangeEvent[x][y] == CE_DELAY)
10666   {
10667     // reset actual trigger element, trigger player and action element
10668     change->actual_trigger_element = EL_EMPTY;
10669     change->actual_trigger_player = EL_EMPTY;
10670     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10671     change->actual_trigger_side = CH_SIDE_NONE;
10672     change->actual_trigger_ce_value = 0;
10673     change->actual_trigger_ce_score = 0;
10674   }
10675
10676   // do not change elements more than a specified maximum number of changes
10677   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10678     return FALSE;
10679
10680   ChangeCount[x][y]++;          // count number of changes in the same frame
10681
10682   if (change->explode)
10683   {
10684     Bang(x, y);
10685
10686     return TRUE;
10687   }
10688
10689   if (change->use_target_content)
10690   {
10691     boolean complete_replace = TRUE;
10692     boolean can_replace[3][3];
10693     int xx, yy;
10694
10695     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10696     {
10697       boolean is_empty;
10698       boolean is_walkable;
10699       boolean is_diggable;
10700       boolean is_collectible;
10701       boolean is_removable;
10702       boolean is_destructible;
10703       int ex = x + xx - 1;
10704       int ey = y + yy - 1;
10705       int content_element = change->target_content.e[xx][yy];
10706       int e;
10707
10708       can_replace[xx][yy] = TRUE;
10709
10710       if (ex == x && ey == y)   // do not check changing element itself
10711         continue;
10712
10713       if (content_element == EL_EMPTY_SPACE)
10714       {
10715         can_replace[xx][yy] = FALSE;    // do not replace border with space
10716
10717         continue;
10718       }
10719
10720       if (!IN_LEV_FIELD(ex, ey))
10721       {
10722         can_replace[xx][yy] = FALSE;
10723         complete_replace = FALSE;
10724
10725         continue;
10726       }
10727
10728       e = Tile[ex][ey];
10729
10730       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10731         e = MovingOrBlocked2Element(ex, ey);
10732
10733       is_empty = (IS_FREE(ex, ey) ||
10734                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10735
10736       is_walkable     = (is_empty || IS_WALKABLE(e));
10737       is_diggable     = (is_empty || IS_DIGGABLE(e));
10738       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10739       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10740       is_removable    = (is_diggable || is_collectible);
10741
10742       can_replace[xx][yy] =
10743         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10744           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10745           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10746           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10747           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10748           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10749          !(IS_PLAYER(ex, ey) && IS_PLAYER_ELEMENT(content_element)));
10750
10751       if (!can_replace[xx][yy])
10752         complete_replace = FALSE;
10753     }
10754
10755     if (!change->only_if_complete || complete_replace)
10756     {
10757       boolean something_has_changed = FALSE;
10758
10759       if (change->only_if_complete && change->use_random_replace &&
10760           RND(100) < change->random_percentage)
10761         return FALSE;
10762
10763       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10764       {
10765         int ex = x + xx - 1;
10766         int ey = y + yy - 1;
10767         int content_element;
10768
10769         if (can_replace[xx][yy] && (!change->use_random_replace ||
10770                                     RND(100) < change->random_percentage))
10771         {
10772           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10773             RemoveMovingField(ex, ey);
10774
10775           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10776
10777           content_element = change->target_content.e[xx][yy];
10778           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10779                                               ce_value, ce_score);
10780
10781           CreateElementFromChange(ex, ey, target_element);
10782
10783           something_has_changed = TRUE;
10784
10785           // for symmetry reasons, freeze newly created border elements
10786           if (ex != x || ey != y)
10787             Stop[ex][ey] = TRUE;        // no more moving in this frame
10788         }
10789       }
10790
10791       if (something_has_changed)
10792       {
10793         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10794         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10795       }
10796     }
10797   }
10798   else
10799   {
10800     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10801                                         ce_value, ce_score);
10802
10803     if (element == EL_DIAGONAL_GROWING ||
10804         element == EL_DIAGONAL_SHRINKING)
10805     {
10806       target_element = Store[x][y];
10807
10808       Store[x][y] = EL_EMPTY;
10809     }
10810
10811     // special case: element changes to player (and may be kept if walkable)
10812     if (IS_PLAYER_ELEMENT(target_element) && !level.keep_walkable_ce)
10813       CreateElementFromChange(x, y, EL_EMPTY);
10814
10815     CreateElementFromChange(x, y, target_element);
10816
10817     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10818     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10819   }
10820
10821   // this uses direct change before indirect change
10822   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10823
10824   return TRUE;
10825 }
10826
10827 static void HandleElementChange(int x, int y, int page)
10828 {
10829   int element = MovingOrBlocked2Element(x, y);
10830   struct ElementInfo *ei = &element_info[element];
10831   struct ElementChangeInfo *change = &ei->change_page[page];
10832   boolean handle_action_before_change = FALSE;
10833
10834 #ifdef DEBUG
10835   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10836       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10837   {
10838     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10839           x, y, element, element_info[element].token_name);
10840     Debug("game:playing:HandleElementChange", "This should never happen!");
10841   }
10842 #endif
10843
10844   // this can happen with classic bombs on walkable, changing elements
10845   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10846   {
10847     return;
10848   }
10849
10850   if (ChangeDelay[x][y] == 0)           // initialize element change
10851   {
10852     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10853
10854     if (change->can_change)
10855     {
10856       // !!! not clear why graphic animation should be reset at all here !!!
10857       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10858       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10859
10860       /*
10861         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10862
10863         When using an animation frame delay of 1 (this only happens with
10864         "sp_zonk.moving.left/right" in the classic graphics), the default
10865         (non-moving) animation shows wrong animation frames (while the
10866         moving animation, like "sp_zonk.moving.left/right", is correct,
10867         so this graphical bug never shows up with the classic graphics).
10868         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10869         be drawn instead of the correct frames 0,1,2,3. This is caused by
10870         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10871         an element change: First when the change delay ("ChangeDelay[][]")
10872         counter has reached zero after decrementing, then a second time in
10873         the next frame (after "GfxFrame[][]" was already incremented) when
10874         "ChangeDelay[][]" is reset to the initial delay value again.
10875
10876         This causes frame 0 to be drawn twice, while the last frame won't
10877         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10878
10879         As some animations may already be cleverly designed around this bug
10880         (at least the "Snake Bite" snake tail animation does this), it cannot
10881         simply be fixed here without breaking such existing animations.
10882         Unfortunately, it cannot easily be detected if a graphics set was
10883         designed "before" or "after" the bug was fixed. As a workaround,
10884         a new graphics set option "game.graphics_engine_version" was added
10885         to be able to specify the game's major release version for which the
10886         graphics set was designed, which can then be used to decide if the
10887         bugfix should be used (version 4 and above) or not (version 3 or
10888         below, or if no version was specified at all, as with old sets).
10889
10890         (The wrong/fixed animation frames can be tested with the test level set
10891         "test_gfxframe" and level "000", which contains a specially prepared
10892         custom element at level position (x/y) == (11/9) which uses the zonk
10893         animation mentioned above. Using "game.graphics_engine_version: 4"
10894         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10895         This can also be seen from the debug output for this test element.)
10896       */
10897
10898       // when a custom element is about to change (for example by change delay),
10899       // do not reset graphic animation when the custom element is moving
10900       if (game.graphics_engine_version < 4 &&
10901           !IS_MOVING(x, y))
10902       {
10903         ResetGfxAnimation(x, y);
10904         ResetRandomAnimationValue(x, y);
10905       }
10906
10907       if (change->pre_change_function)
10908         change->pre_change_function(x, y);
10909     }
10910   }
10911
10912   ChangeDelay[x][y]--;
10913
10914   if (ChangeDelay[x][y] != 0)           // continue element change
10915   {
10916     if (change->can_change)
10917     {
10918       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10919
10920       if (IS_ANIMATED(graphic))
10921         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10922
10923       if (change->change_function)
10924         change->change_function(x, y);
10925     }
10926   }
10927   else                                  // finish element change
10928   {
10929     if (ChangePage[x][y] != -1)         // remember page from delayed change
10930     {
10931       page = ChangePage[x][y];
10932       ChangePage[x][y] = -1;
10933
10934       change = &ei->change_page[page];
10935     }
10936
10937     if (IS_MOVING(x, y))                // never change a running system ;-)
10938     {
10939       ChangeDelay[x][y] = 1;            // try change after next move step
10940       ChangePage[x][y] = page;          // remember page to use for change
10941
10942       return;
10943     }
10944
10945     // special case: set new level random seed before changing element
10946     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10947       handle_action_before_change = TRUE;
10948
10949     if (change->has_action && handle_action_before_change)
10950       ExecuteCustomElementAction(x, y, element, page);
10951
10952     if (change->can_change)
10953     {
10954       if (ChangeElement(x, y, element, page))
10955       {
10956         if (change->post_change_function)
10957           change->post_change_function(x, y);
10958       }
10959     }
10960
10961     if (change->has_action && !handle_action_before_change)
10962       ExecuteCustomElementAction(x, y, element, page);
10963   }
10964 }
10965
10966 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10967                                               int trigger_element,
10968                                               int trigger_event,
10969                                               int trigger_player,
10970                                               int trigger_side,
10971                                               int trigger_page)
10972 {
10973   boolean change_done_any = FALSE;
10974   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10975   int i;
10976
10977   if (!(trigger_events[trigger_element][trigger_event]))
10978     return FALSE;
10979
10980   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10981
10982   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10983   {
10984     int element = EL_CUSTOM_START + i;
10985     boolean change_done = FALSE;
10986     int p;
10987
10988     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10989         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10990       continue;
10991
10992     for (p = 0; p < element_info[element].num_change_pages; p++)
10993     {
10994       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10995
10996       if (change->can_change_or_has_action &&
10997           change->has_event[trigger_event] &&
10998           change->trigger_side & trigger_side &&
10999           change->trigger_player & trigger_player &&
11000           change->trigger_page & trigger_page_bits &&
11001           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
11002       {
11003         change->actual_trigger_element = trigger_element;
11004         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11005         change->actual_trigger_player_bits = trigger_player;
11006         change->actual_trigger_side = trigger_side;
11007         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
11008         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11009
11010         if ((change->can_change && !change_done) || change->has_action)
11011         {
11012           int x, y;
11013
11014           SCAN_PLAYFIELD(x, y)
11015           {
11016             if (Tile[x][y] == element)
11017             {
11018               if (change->can_change && !change_done)
11019               {
11020                 // if element already changed in this frame, not only prevent
11021                 // another element change (checked in ChangeElement()), but
11022                 // also prevent additional element actions for this element
11023
11024                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11025                     !level.use_action_after_change_bug)
11026                   continue;
11027
11028                 ChangeDelay[x][y] = 1;
11029                 ChangeEvent[x][y] = trigger_event;
11030
11031                 HandleElementChange(x, y, p);
11032               }
11033               else if (change->has_action)
11034               {
11035                 // if element already changed in this frame, not only prevent
11036                 // another element change (checked in ChangeElement()), but
11037                 // also prevent additional element actions for this element
11038
11039                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11040                     !level.use_action_after_change_bug)
11041                   continue;
11042
11043                 ExecuteCustomElementAction(x, y, element, p);
11044                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11045               }
11046             }
11047           }
11048
11049           if (change->can_change)
11050           {
11051             change_done = TRUE;
11052             change_done_any = TRUE;
11053           }
11054         }
11055       }
11056     }
11057   }
11058
11059   RECURSION_LOOP_DETECTION_END();
11060
11061   return change_done_any;
11062 }
11063
11064 static boolean CheckElementChangeExt(int x, int y,
11065                                      int element,
11066                                      int trigger_element,
11067                                      int trigger_event,
11068                                      int trigger_player,
11069                                      int trigger_side)
11070 {
11071   boolean change_done = FALSE;
11072   int p;
11073
11074   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11075       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11076     return FALSE;
11077
11078   if (Tile[x][y] == EL_BLOCKED)
11079   {
11080     Blocked2Moving(x, y, &x, &y);
11081     element = Tile[x][y];
11082   }
11083
11084   // check if element has already changed or is about to change after moving
11085   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11086        Tile[x][y] != element) ||
11087
11088       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11089        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11090         ChangePage[x][y] != -1)))
11091     return FALSE;
11092
11093   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11094
11095   for (p = 0; p < element_info[element].num_change_pages; p++)
11096   {
11097     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11098
11099     /* check trigger element for all events where the element that is checked
11100        for changing interacts with a directly adjacent element -- this is
11101        different to element changes that affect other elements to change on the
11102        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11103     boolean check_trigger_element =
11104       (trigger_event == CE_NEXT_TO_X ||
11105        trigger_event == CE_TOUCHING_X ||
11106        trigger_event == CE_HITTING_X ||
11107        trigger_event == CE_HIT_BY_X ||
11108        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11109
11110     if (change->can_change_or_has_action &&
11111         change->has_event[trigger_event] &&
11112         change->trigger_side & trigger_side &&
11113         change->trigger_player & trigger_player &&
11114         (!check_trigger_element ||
11115          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11116     {
11117       change->actual_trigger_element = trigger_element;
11118       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11119       change->actual_trigger_player_bits = trigger_player;
11120       change->actual_trigger_side = trigger_side;
11121       change->actual_trigger_ce_value = CustomValue[x][y];
11122       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11123
11124       // special case: trigger element not at (x,y) position for some events
11125       if (check_trigger_element)
11126       {
11127         static struct
11128         {
11129           int dx, dy;
11130         } move_xy[] =
11131           {
11132             {  0,  0 },
11133             { -1,  0 },
11134             { +1,  0 },
11135             {  0,  0 },
11136             {  0, -1 },
11137             {  0,  0 }, { 0, 0 }, { 0, 0 },
11138             {  0, +1 }
11139           };
11140
11141         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11142         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11143
11144         change->actual_trigger_ce_value = CustomValue[xx][yy];
11145         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11146       }
11147
11148       if (change->can_change && !change_done)
11149       {
11150         ChangeDelay[x][y] = 1;
11151         ChangeEvent[x][y] = trigger_event;
11152
11153         HandleElementChange(x, y, p);
11154
11155         change_done = TRUE;
11156       }
11157       else if (change->has_action)
11158       {
11159         ExecuteCustomElementAction(x, y, element, p);
11160         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11161       }
11162     }
11163   }
11164
11165   RECURSION_LOOP_DETECTION_END();
11166
11167   return change_done;
11168 }
11169
11170 static void PlayPlayerSound(struct PlayerInfo *player)
11171 {
11172   int jx = player->jx, jy = player->jy;
11173   int sound_element = player->artwork_element;
11174   int last_action = player->last_action_waiting;
11175   int action = player->action_waiting;
11176
11177   if (player->is_waiting)
11178   {
11179     if (action != last_action)
11180       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11181     else
11182       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11183   }
11184   else
11185   {
11186     if (action != last_action)
11187       StopSound(element_info[sound_element].sound[last_action]);
11188
11189     if (last_action == ACTION_SLEEPING)
11190       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11191   }
11192 }
11193
11194 static void PlayAllPlayersSound(void)
11195 {
11196   int i;
11197
11198   for (i = 0; i < MAX_PLAYERS; i++)
11199     if (stored_player[i].active)
11200       PlayPlayerSound(&stored_player[i]);
11201 }
11202
11203 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11204 {
11205   boolean last_waiting = player->is_waiting;
11206   int move_dir = player->MovDir;
11207
11208   player->dir_waiting = move_dir;
11209   player->last_action_waiting = player->action_waiting;
11210
11211   if (is_waiting)
11212   {
11213     if (!last_waiting)          // not waiting -> waiting
11214     {
11215       player->is_waiting = TRUE;
11216
11217       player->frame_counter_bored =
11218         FrameCounter +
11219         game.player_boring_delay_fixed +
11220         GetSimpleRandom(game.player_boring_delay_random);
11221       player->frame_counter_sleeping =
11222         FrameCounter +
11223         game.player_sleeping_delay_fixed +
11224         GetSimpleRandom(game.player_sleeping_delay_random);
11225
11226       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11227     }
11228
11229     if (game.player_sleeping_delay_fixed +
11230         game.player_sleeping_delay_random > 0 &&
11231         player->anim_delay_counter == 0 &&
11232         player->post_delay_counter == 0 &&
11233         FrameCounter >= player->frame_counter_sleeping)
11234       player->is_sleeping = TRUE;
11235     else if (game.player_boring_delay_fixed +
11236              game.player_boring_delay_random > 0 &&
11237              FrameCounter >= player->frame_counter_bored)
11238       player->is_bored = TRUE;
11239
11240     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11241                               player->is_bored ? ACTION_BORING :
11242                               ACTION_WAITING);
11243
11244     if (player->is_sleeping && player->use_murphy)
11245     {
11246       // special case for sleeping Murphy when leaning against non-free tile
11247
11248       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11249           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11250            !IS_MOVING(player->jx - 1, player->jy)))
11251         move_dir = MV_LEFT;
11252       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11253                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11254                 !IS_MOVING(player->jx + 1, player->jy)))
11255         move_dir = MV_RIGHT;
11256       else
11257         player->is_sleeping = FALSE;
11258
11259       player->dir_waiting = move_dir;
11260     }
11261
11262     if (player->is_sleeping)
11263     {
11264       if (player->num_special_action_sleeping > 0)
11265       {
11266         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11267         {
11268           int last_special_action = player->special_action_sleeping;
11269           int num_special_action = player->num_special_action_sleeping;
11270           int special_action =
11271             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11272              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11273              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11274              last_special_action + 1 : ACTION_SLEEPING);
11275           int special_graphic =
11276             el_act_dir2img(player->artwork_element, special_action, move_dir);
11277
11278           player->anim_delay_counter =
11279             graphic_info[special_graphic].anim_delay_fixed +
11280             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11281           player->post_delay_counter =
11282             graphic_info[special_graphic].post_delay_fixed +
11283             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11284
11285           player->special_action_sleeping = special_action;
11286         }
11287
11288         if (player->anim_delay_counter > 0)
11289         {
11290           player->action_waiting = player->special_action_sleeping;
11291           player->anim_delay_counter--;
11292         }
11293         else if (player->post_delay_counter > 0)
11294         {
11295           player->post_delay_counter--;
11296         }
11297       }
11298     }
11299     else if (player->is_bored)
11300     {
11301       if (player->num_special_action_bored > 0)
11302       {
11303         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11304         {
11305           int special_action =
11306             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11307           int special_graphic =
11308             el_act_dir2img(player->artwork_element, special_action, move_dir);
11309
11310           player->anim_delay_counter =
11311             graphic_info[special_graphic].anim_delay_fixed +
11312             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11313           player->post_delay_counter =
11314             graphic_info[special_graphic].post_delay_fixed +
11315             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11316
11317           player->special_action_bored = special_action;
11318         }
11319
11320         if (player->anim_delay_counter > 0)
11321         {
11322           player->action_waiting = player->special_action_bored;
11323           player->anim_delay_counter--;
11324         }
11325         else if (player->post_delay_counter > 0)
11326         {
11327           player->post_delay_counter--;
11328         }
11329       }
11330     }
11331   }
11332   else if (last_waiting)        // waiting -> not waiting
11333   {
11334     player->is_waiting = FALSE;
11335     player->is_bored = FALSE;
11336     player->is_sleeping = FALSE;
11337
11338     player->frame_counter_bored = -1;
11339     player->frame_counter_sleeping = -1;
11340
11341     player->anim_delay_counter = 0;
11342     player->post_delay_counter = 0;
11343
11344     player->dir_waiting = player->MovDir;
11345     player->action_waiting = ACTION_DEFAULT;
11346
11347     player->special_action_bored = ACTION_DEFAULT;
11348     player->special_action_sleeping = ACTION_DEFAULT;
11349   }
11350 }
11351
11352 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11353 {
11354   if ((!player->is_moving  && player->was_moving) ||
11355       (player->MovPos == 0 && player->was_moving) ||
11356       (player->is_snapping && !player->was_snapping) ||
11357       (player->is_dropping && !player->was_dropping))
11358   {
11359     if (!CheckSaveEngineSnapshotToList())
11360       return;
11361
11362     player->was_moving = FALSE;
11363     player->was_snapping = TRUE;
11364     player->was_dropping = TRUE;
11365   }
11366   else
11367   {
11368     if (player->is_moving)
11369       player->was_moving = TRUE;
11370
11371     if (!player->is_snapping)
11372       player->was_snapping = FALSE;
11373
11374     if (!player->is_dropping)
11375       player->was_dropping = FALSE;
11376   }
11377
11378   static struct MouseActionInfo mouse_action_last = { 0 };
11379   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11380   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11381
11382   if (new_released)
11383     CheckSaveEngineSnapshotToList();
11384
11385   mouse_action_last = mouse_action;
11386 }
11387
11388 static void CheckSingleStepMode(struct PlayerInfo *player)
11389 {
11390   if (tape.single_step && tape.recording && !tape.pausing)
11391   {
11392     // as it is called "single step mode", just return to pause mode when the
11393     // player stopped moving after one tile (or never starts moving at all)
11394     // (reverse logic needed here in case single step mode used in team mode)
11395     if (player->is_moving ||
11396         player->is_pushing ||
11397         player->is_dropping_pressed ||
11398         player->effective_mouse_action.button)
11399       game.enter_single_step_mode = FALSE;
11400   }
11401
11402   CheckSaveEngineSnapshot(player);
11403 }
11404
11405 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11406 {
11407   int left      = player_action & JOY_LEFT;
11408   int right     = player_action & JOY_RIGHT;
11409   int up        = player_action & JOY_UP;
11410   int down      = player_action & JOY_DOWN;
11411   int button1   = player_action & JOY_BUTTON_1;
11412   int button2   = player_action & JOY_BUTTON_2;
11413   int dx        = (left ? -1 : right ? 1 : 0);
11414   int dy        = (up   ? -1 : down  ? 1 : 0);
11415
11416   if (!player->active || tape.pausing)
11417     return 0;
11418
11419   if (player_action)
11420   {
11421     if (button1)
11422       SnapField(player, dx, dy);
11423     else
11424     {
11425       if (button2)
11426         DropElement(player);
11427
11428       MovePlayer(player, dx, dy);
11429     }
11430
11431     CheckSingleStepMode(player);
11432
11433     SetPlayerWaiting(player, FALSE);
11434
11435     return player_action;
11436   }
11437   else
11438   {
11439     // no actions for this player (no input at player's configured device)
11440
11441     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11442     SnapField(player, 0, 0);
11443     CheckGravityMovementWhenNotMoving(player);
11444
11445     if (player->MovPos == 0)
11446       SetPlayerWaiting(player, TRUE);
11447
11448     if (player->MovPos == 0)    // needed for tape.playing
11449       player->is_moving = FALSE;
11450
11451     player->is_dropping = FALSE;
11452     player->is_dropping_pressed = FALSE;
11453     player->drop_pressed_delay = 0;
11454
11455     CheckSingleStepMode(player);
11456
11457     return 0;
11458   }
11459 }
11460
11461 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11462                                          byte *tape_action)
11463 {
11464   if (!tape.use_mouse_actions)
11465     return;
11466
11467   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11468   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11469   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11470 }
11471
11472 static void SetTapeActionFromMouseAction(byte *tape_action,
11473                                          struct MouseActionInfo *mouse_action)
11474 {
11475   if (!tape.use_mouse_actions)
11476     return;
11477
11478   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11479   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11480   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11481 }
11482
11483 static void CheckLevelSolved(void)
11484 {
11485   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11486   {
11487     if (game_em.level_solved &&
11488         !game_em.game_over)                             // game won
11489     {
11490       LevelSolved();
11491
11492       game_em.game_over = TRUE;
11493
11494       game.all_players_gone = TRUE;
11495     }
11496
11497     if (game_em.game_over)                              // game lost
11498       game.all_players_gone = TRUE;
11499   }
11500   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11501   {
11502     if (game_sp.level_solved &&
11503         !game_sp.game_over)                             // game won
11504     {
11505       LevelSolved();
11506
11507       game_sp.game_over = TRUE;
11508
11509       game.all_players_gone = TRUE;
11510     }
11511
11512     if (game_sp.game_over)                              // game lost
11513       game.all_players_gone = TRUE;
11514   }
11515   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11516   {
11517     if (game_mm.level_solved &&
11518         !game_mm.game_over)                             // game won
11519     {
11520       LevelSolved();
11521
11522       game_mm.game_over = TRUE;
11523
11524       game.all_players_gone = TRUE;
11525     }
11526
11527     if (game_mm.game_over)                              // game lost
11528       game.all_players_gone = TRUE;
11529   }
11530 }
11531
11532 static void CheckLevelTime_StepCounter(void)
11533 {
11534   int i;
11535
11536   TimePlayed++;
11537
11538   if (TimeLeft > 0)
11539   {
11540     TimeLeft--;
11541
11542     if (TimeLeft <= 10 && game.time_limit && !game.LevelSolved)
11543       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11544
11545     game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11546
11547     DisplayGameControlValues();
11548
11549     if (!TimeLeft && game.time_limit && !game.LevelSolved)
11550       for (i = 0; i < MAX_PLAYERS; i++)
11551         KillPlayer(&stored_player[i]);
11552   }
11553   else if (game.no_level_time_limit && !game.all_players_gone)
11554   {
11555     game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11556
11557     DisplayGameControlValues();
11558   }
11559 }
11560
11561 static void CheckLevelTime(void)
11562 {
11563   int i;
11564
11565   if (TimeFrames >= FRAMES_PER_SECOND)
11566   {
11567     TimeFrames = 0;
11568     TapeTime++;
11569
11570     for (i = 0; i < MAX_PLAYERS; i++)
11571     {
11572       struct PlayerInfo *player = &stored_player[i];
11573
11574       if (SHIELD_ON(player))
11575       {
11576         player->shield_normal_time_left--;
11577
11578         if (player->shield_deadly_time_left > 0)
11579           player->shield_deadly_time_left--;
11580       }
11581     }
11582
11583     if (!game.LevelSolved && !level.use_step_counter)
11584     {
11585       TimePlayed++;
11586
11587       if (TimeLeft > 0)
11588       {
11589         TimeLeft--;
11590
11591         if (TimeLeft <= 10 && game.time_limit)
11592           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11593
11594         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11595            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11596
11597         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11598
11599         if (!TimeLeft && game.time_limit)
11600         {
11601           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11602             game_em.lev->killed_out_of_time = TRUE;
11603           else
11604             for (i = 0; i < MAX_PLAYERS; i++)
11605               KillPlayer(&stored_player[i]);
11606         }
11607       }
11608       else if (game.no_level_time_limit && !game.all_players_gone)
11609       {
11610         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11611       }
11612
11613       game_em.lev->time = (game.no_level_time_limit ? TimePlayed : TimeLeft);
11614     }
11615
11616     if (tape.recording || tape.playing)
11617       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11618   }
11619
11620   if (tape.recording || tape.playing)
11621     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11622
11623   UpdateAndDisplayGameControlValues();
11624 }
11625
11626 void AdvanceFrameAndPlayerCounters(int player_nr)
11627 {
11628   int i;
11629
11630   // advance frame counters (global frame counter and time frame counter)
11631   FrameCounter++;
11632   TimeFrames++;
11633
11634   // advance player counters (counters for move delay, move animation etc.)
11635   for (i = 0; i < MAX_PLAYERS; i++)
11636   {
11637     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11638     int move_delay_value = stored_player[i].move_delay_value;
11639     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11640
11641     if (!advance_player_counters)       // not all players may be affected
11642       continue;
11643
11644     if (move_frames == 0)       // less than one move per game frame
11645     {
11646       int stepsize = TILEX / move_delay_value;
11647       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11648       int count = (stored_player[i].is_moving ?
11649                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11650
11651       if (count % delay == 0)
11652         move_frames = 1;
11653     }
11654
11655     stored_player[i].Frame += move_frames;
11656
11657     if (stored_player[i].MovPos != 0)
11658       stored_player[i].StepFrame += move_frames;
11659
11660     if (stored_player[i].move_delay > 0)
11661       stored_player[i].move_delay--;
11662
11663     // due to bugs in previous versions, counter must count up, not down
11664     if (stored_player[i].push_delay != -1)
11665       stored_player[i].push_delay++;
11666
11667     if (stored_player[i].drop_delay > 0)
11668       stored_player[i].drop_delay--;
11669
11670     if (stored_player[i].is_dropping_pressed)
11671       stored_player[i].drop_pressed_delay++;
11672   }
11673 }
11674
11675 void StartGameActions(boolean init_network_game, boolean record_tape,
11676                       int random_seed)
11677 {
11678   unsigned int new_random_seed = InitRND(random_seed);
11679
11680   if (record_tape)
11681     TapeStartRecording(new_random_seed);
11682
11683   if (setup.auto_pause_on_start && !tape.pausing)
11684     TapeTogglePause(TAPE_TOGGLE_MANUAL);
11685
11686   if (init_network_game)
11687   {
11688     SendToServer_LevelFile();
11689     SendToServer_StartPlaying();
11690
11691     return;
11692   }
11693
11694   InitGame();
11695 }
11696
11697 static void GameActionsExt(void)
11698 {
11699 #if 0
11700   static unsigned int game_frame_delay = 0;
11701 #endif
11702   unsigned int game_frame_delay_value;
11703   byte *recorded_player_action;
11704   byte summarized_player_action = 0;
11705   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11706   int i;
11707
11708   // detect endless loops, caused by custom element programming
11709   if (recursion_loop_detected && recursion_loop_depth == 0)
11710   {
11711     char *message = getStringCat3("Internal Error! Element ",
11712                                   EL_NAME(recursion_loop_element),
11713                                   " caused endless loop! Quit the game?");
11714
11715     Warn("element '%s' caused endless loop in game engine",
11716          EL_NAME(recursion_loop_element));
11717
11718     RequestQuitGameExt(program.headless, level_editor_test_game, message);
11719
11720     recursion_loop_detected = FALSE;    // if game should be continued
11721
11722     free(message);
11723
11724     return;
11725   }
11726
11727   if (game.restart_level)
11728     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11729
11730   CheckLevelSolved();
11731
11732   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11733     GameWon();
11734
11735   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11736     TapeStop();
11737
11738   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11739     return;
11740
11741   game_frame_delay_value =
11742     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11743
11744   if (tape.playing && tape.warp_forward && !tape.pausing)
11745     game_frame_delay_value = 0;
11746
11747   SetVideoFrameDelay(game_frame_delay_value);
11748
11749   // (de)activate virtual buttons depending on current game status
11750   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11751   {
11752     if (game.all_players_gone)  // if no players there to be controlled anymore
11753       SetOverlayActive(FALSE);
11754     else if (!tape.playing)     // if game continues after tape stopped playing
11755       SetOverlayActive(TRUE);
11756   }
11757
11758 #if 0
11759 #if 0
11760   // ---------- main game synchronization point ----------
11761
11762   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11763
11764   Debug("game:playing:skip", "skip == %d", skip);
11765
11766 #else
11767   // ---------- main game synchronization point ----------
11768
11769   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11770 #endif
11771 #endif
11772
11773   if (network_playing && !network_player_action_received)
11774   {
11775     // try to get network player actions in time
11776
11777     // last chance to get network player actions without main loop delay
11778     HandleNetworking();
11779
11780     // game was quit by network peer
11781     if (game_status != GAME_MODE_PLAYING)
11782       return;
11783
11784     // check if network player actions still missing and game still running
11785     if (!network_player_action_received && !checkGameEnded())
11786       return;           // failed to get network player actions in time
11787
11788     // do not yet reset "network_player_action_received" (for tape.pausing)
11789   }
11790
11791   if (tape.pausing)
11792     return;
11793
11794   // at this point we know that we really continue executing the game
11795
11796   network_player_action_received = FALSE;
11797
11798   // when playing tape, read previously recorded player input from tape data
11799   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11800
11801   local_player->effective_mouse_action = local_player->mouse_action;
11802
11803   if (recorded_player_action != NULL)
11804     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11805                                  recorded_player_action);
11806
11807   // TapePlayAction() may return NULL when toggling to "pause before death"
11808   if (tape.pausing)
11809     return;
11810
11811   if (tape.set_centered_player)
11812   {
11813     game.centered_player_nr_next = tape.centered_player_nr_next;
11814     game.set_centered_player = TRUE;
11815   }
11816
11817   for (i = 0; i < MAX_PLAYERS; i++)
11818   {
11819     summarized_player_action |= stored_player[i].action;
11820
11821     if (!network_playing && (game.team_mode || tape.playing))
11822       stored_player[i].effective_action = stored_player[i].action;
11823   }
11824
11825   if (network_playing && !checkGameEnded())
11826     SendToServer_MovePlayer(summarized_player_action);
11827
11828   // summarize all actions at local players mapped input device position
11829   // (this allows using different input devices in single player mode)
11830   if (!network.enabled && !game.team_mode)
11831     stored_player[map_player_action[local_player->index_nr]].effective_action =
11832       summarized_player_action;
11833
11834   // summarize all actions at centered player in local team mode
11835   if (tape.recording &&
11836       setup.team_mode && !network.enabled &&
11837       setup.input_on_focus &&
11838       game.centered_player_nr != -1)
11839   {
11840     for (i = 0; i < MAX_PLAYERS; i++)
11841       stored_player[map_player_action[i]].effective_action =
11842         (i == game.centered_player_nr ? summarized_player_action : 0);
11843   }
11844
11845   if (recorded_player_action != NULL)
11846     for (i = 0; i < MAX_PLAYERS; i++)
11847       stored_player[i].effective_action = recorded_player_action[i];
11848
11849   for (i = 0; i < MAX_PLAYERS; i++)
11850   {
11851     tape_action[i] = stored_player[i].effective_action;
11852
11853     /* (this may happen in the RND game engine if a player was not present on
11854        the playfield on level start, but appeared later from a custom element */
11855     if (setup.team_mode &&
11856         tape.recording &&
11857         tape_action[i] &&
11858         !tape.player_participates[i])
11859       tape.player_participates[i] = TRUE;
11860   }
11861
11862   SetTapeActionFromMouseAction(tape_action,
11863                                &local_player->effective_mouse_action);
11864
11865   // only record actions from input devices, but not programmed actions
11866   if (tape.recording)
11867     TapeRecordAction(tape_action);
11868
11869   // remember if game was played (especially after tape stopped playing)
11870   if (!tape.playing && summarized_player_action)
11871     game.GamePlayed = TRUE;
11872
11873 #if USE_NEW_PLAYER_ASSIGNMENTS
11874   // !!! also map player actions in single player mode !!!
11875   // if (game.team_mode)
11876   if (1)
11877   {
11878     byte mapped_action[MAX_PLAYERS];
11879
11880 #if DEBUG_PLAYER_ACTIONS
11881     for (i = 0; i < MAX_PLAYERS; i++)
11882       DebugContinued("", "%d, ", stored_player[i].effective_action);
11883 #endif
11884
11885     for (i = 0; i < MAX_PLAYERS; i++)
11886       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11887
11888     for (i = 0; i < MAX_PLAYERS; i++)
11889       stored_player[i].effective_action = mapped_action[i];
11890
11891 #if DEBUG_PLAYER_ACTIONS
11892     DebugContinued("", "=> ");
11893     for (i = 0; i < MAX_PLAYERS; i++)
11894       DebugContinued("", "%d, ", stored_player[i].effective_action);
11895     DebugContinued("game:playing:player", "\n");
11896 #endif
11897   }
11898 #if DEBUG_PLAYER_ACTIONS
11899   else
11900   {
11901     for (i = 0; i < MAX_PLAYERS; i++)
11902       DebugContinued("", "%d, ", stored_player[i].effective_action);
11903     DebugContinued("game:playing:player", "\n");
11904   }
11905 #endif
11906 #endif
11907
11908   for (i = 0; i < MAX_PLAYERS; i++)
11909   {
11910     // allow engine snapshot in case of changed movement attempt
11911     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11912         (stored_player[i].effective_action & KEY_MOTION))
11913       game.snapshot.changed_action = TRUE;
11914
11915     // allow engine snapshot in case of snapping/dropping attempt
11916     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11917         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11918       game.snapshot.changed_action = TRUE;
11919
11920     game.snapshot.last_action[i] = stored_player[i].effective_action;
11921   }
11922
11923   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11924   {
11925     GameActions_EM_Main();
11926   }
11927   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11928   {
11929     GameActions_SP_Main();
11930   }
11931   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11932   {
11933     GameActions_MM_Main();
11934   }
11935   else
11936   {
11937     GameActions_RND_Main();
11938   }
11939
11940   BlitScreenToBitmap(backbuffer);
11941
11942   CheckLevelSolved();
11943   CheckLevelTime();
11944
11945   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11946
11947   if (global.show_frames_per_second)
11948   {
11949     static unsigned int fps_counter = 0;
11950     static int fps_frames = 0;
11951     unsigned int fps_delay_ms = Counter() - fps_counter;
11952
11953     fps_frames++;
11954
11955     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11956     {
11957       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11958
11959       fps_frames = 0;
11960       fps_counter = Counter();
11961
11962       // always draw FPS to screen after FPS value was updated
11963       redraw_mask |= REDRAW_FPS;
11964     }
11965
11966     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11967     if (GetDrawDeactivationMask() == REDRAW_NONE)
11968       redraw_mask |= REDRAW_FPS;
11969   }
11970 }
11971
11972 static void GameActions_CheckSaveEngineSnapshot(void)
11973 {
11974   if (!game.snapshot.save_snapshot)
11975     return;
11976
11977   // clear flag for saving snapshot _before_ saving snapshot
11978   game.snapshot.save_snapshot = FALSE;
11979
11980   SaveEngineSnapshotToList();
11981 }
11982
11983 void GameActions(void)
11984 {
11985   GameActionsExt();
11986
11987   GameActions_CheckSaveEngineSnapshot();
11988 }
11989
11990 void GameActions_EM_Main(void)
11991 {
11992   byte effective_action[MAX_PLAYERS];
11993   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11994   int i;
11995
11996   for (i = 0; i < MAX_PLAYERS; i++)
11997     effective_action[i] = stored_player[i].effective_action;
11998
11999   GameActions_EM(effective_action, warp_mode);
12000 }
12001
12002 void GameActions_SP_Main(void)
12003 {
12004   byte effective_action[MAX_PLAYERS];
12005   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12006   int i;
12007
12008   for (i = 0; i < MAX_PLAYERS; i++)
12009     effective_action[i] = stored_player[i].effective_action;
12010
12011   GameActions_SP(effective_action, warp_mode);
12012
12013   for (i = 0; i < MAX_PLAYERS; i++)
12014   {
12015     if (stored_player[i].force_dropping)
12016       stored_player[i].action |= KEY_BUTTON_DROP;
12017
12018     stored_player[i].force_dropping = FALSE;
12019   }
12020 }
12021
12022 void GameActions_MM_Main(void)
12023 {
12024   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
12025
12026   GameActions_MM(local_player->effective_mouse_action, warp_mode);
12027 }
12028
12029 void GameActions_RND_Main(void)
12030 {
12031   GameActions_RND();
12032 }
12033
12034 void GameActions_RND(void)
12035 {
12036   static struct MouseActionInfo mouse_action_last = { 0 };
12037   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
12038   int magic_wall_x = 0, magic_wall_y = 0;
12039   int i, x, y, element, graphic, last_gfx_frame;
12040
12041   InitPlayfieldScanModeVars();
12042
12043   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
12044   {
12045     SCAN_PLAYFIELD(x, y)
12046     {
12047       ChangeCount[x][y] = 0;
12048       ChangeEvent[x][y] = -1;
12049     }
12050   }
12051
12052   if (game.set_centered_player)
12053   {
12054     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
12055
12056     // switching to "all players" only possible if all players fit to screen
12057     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
12058     {
12059       game.centered_player_nr_next = game.centered_player_nr;
12060       game.set_centered_player = FALSE;
12061     }
12062
12063     // do not switch focus to non-existing (or non-active) player
12064     if (game.centered_player_nr_next >= 0 &&
12065         !stored_player[game.centered_player_nr_next].active)
12066     {
12067       game.centered_player_nr_next = game.centered_player_nr;
12068       game.set_centered_player = FALSE;
12069     }
12070   }
12071
12072   if (game.set_centered_player &&
12073       ScreenMovPos == 0)        // screen currently aligned at tile position
12074   {
12075     int sx, sy;
12076
12077     if (game.centered_player_nr_next == -1)
12078     {
12079       setScreenCenteredToAllPlayers(&sx, &sy);
12080     }
12081     else
12082     {
12083       sx = stored_player[game.centered_player_nr_next].jx;
12084       sy = stored_player[game.centered_player_nr_next].jy;
12085     }
12086
12087     game.centered_player_nr = game.centered_player_nr_next;
12088     game.set_centered_player = FALSE;
12089
12090     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12091     DrawGameDoorValues();
12092   }
12093
12094   // check single step mode (set flag and clear again if any player is active)
12095   game.enter_single_step_mode =
12096     (tape.single_step && tape.recording && !tape.pausing);
12097
12098   for (i = 0; i < MAX_PLAYERS; i++)
12099   {
12100     int actual_player_action = stored_player[i].effective_action;
12101
12102 #if 1
12103     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12104        - rnd_equinox_tetrachloride 048
12105        - rnd_equinox_tetrachloride_ii 096
12106        - rnd_emanuel_schmieg 002
12107        - doctor_sloan_ww 001, 020
12108     */
12109     if (stored_player[i].MovPos == 0)
12110       CheckGravityMovement(&stored_player[i]);
12111 #endif
12112
12113     // overwrite programmed action with tape action
12114     if (stored_player[i].programmed_action)
12115       actual_player_action = stored_player[i].programmed_action;
12116
12117     PlayerActions(&stored_player[i], actual_player_action);
12118
12119     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12120   }
12121
12122   // single step pause mode may already have been toggled by "ScrollPlayer()"
12123   if (game.enter_single_step_mode && !tape.pausing)
12124     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12125
12126   ScrollScreen(NULL, SCROLL_GO_ON);
12127
12128   /* for backwards compatibility, the following code emulates a fixed bug that
12129      occured when pushing elements (causing elements that just made their last
12130      pushing step to already (if possible) make their first falling step in the
12131      same game frame, which is bad); this code is also needed to use the famous
12132      "spring push bug" which is used in older levels and might be wanted to be
12133      used also in newer levels, but in this case the buggy pushing code is only
12134      affecting the "spring" element and no other elements */
12135
12136   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12137   {
12138     for (i = 0; i < MAX_PLAYERS; i++)
12139     {
12140       struct PlayerInfo *player = &stored_player[i];
12141       int x = player->jx;
12142       int y = player->jy;
12143
12144       if (player->active && player->is_pushing && player->is_moving &&
12145           IS_MOVING(x, y) &&
12146           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12147            Tile[x][y] == EL_SPRING))
12148       {
12149         ContinueMoving(x, y);
12150
12151         // continue moving after pushing (this is actually a bug)
12152         if (!IS_MOVING(x, y))
12153           Stop[x][y] = FALSE;
12154       }
12155     }
12156   }
12157
12158   SCAN_PLAYFIELD(x, y)
12159   {
12160     Last[x][y] = Tile[x][y];
12161
12162     ChangeCount[x][y] = 0;
12163     ChangeEvent[x][y] = -1;
12164
12165     // this must be handled before main playfield loop
12166     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12167     {
12168       MovDelay[x][y]--;
12169       if (MovDelay[x][y] <= 0)
12170         RemoveField(x, y);
12171     }
12172
12173     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12174     {
12175       MovDelay[x][y]--;
12176       if (MovDelay[x][y] <= 0)
12177       {
12178         int element = Store[x][y];
12179         int move_direction = MovDir[x][y];
12180         int player_index_bit = Store2[x][y];
12181
12182         Store[x][y] = 0;
12183         Store2[x][y] = 0;
12184
12185         RemoveField(x, y);
12186         TEST_DrawLevelField(x, y);
12187
12188         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12189
12190         if (IS_ENVELOPE(element))
12191           local_player->show_envelope = element;
12192       }
12193     }
12194
12195 #if DEBUG
12196     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12197     {
12198       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12199             x, y);
12200       Debug("game:playing:GameActions_RND", "This should never happen!");
12201
12202       ChangePage[x][y] = -1;
12203     }
12204 #endif
12205
12206     Stop[x][y] = FALSE;
12207     if (WasJustMoving[x][y] > 0)
12208       WasJustMoving[x][y]--;
12209     if (WasJustFalling[x][y] > 0)
12210       WasJustFalling[x][y]--;
12211     if (CheckCollision[x][y] > 0)
12212       CheckCollision[x][y]--;
12213     if (CheckImpact[x][y] > 0)
12214       CheckImpact[x][y]--;
12215
12216     GfxFrame[x][y]++;
12217
12218     /* reset finished pushing action (not done in ContinueMoving() to allow
12219        continuous pushing animation for elements with zero push delay) */
12220     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12221     {
12222       ResetGfxAnimation(x, y);
12223       TEST_DrawLevelField(x, y);
12224     }
12225
12226 #if DEBUG
12227     if (IS_BLOCKED(x, y))
12228     {
12229       int oldx, oldy;
12230
12231       Blocked2Moving(x, y, &oldx, &oldy);
12232       if (!IS_MOVING(oldx, oldy))
12233       {
12234         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12235         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12236         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12237         Debug("game:playing:GameActions_RND", "This should never happen!");
12238       }
12239     }
12240 #endif
12241   }
12242
12243   if (mouse_action.button)
12244   {
12245     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12246     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12247
12248     x = mouse_action.lx;
12249     y = mouse_action.ly;
12250     element = Tile[x][y];
12251
12252     if (new_button)
12253     {
12254       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12255       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12256                                          ch_button);
12257     }
12258
12259     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12260     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12261                                        ch_button);
12262
12263     if (level.use_step_counter)
12264     {
12265       boolean counted_click = FALSE;
12266
12267       // element clicked that can change when clicked/pressed
12268       if (CAN_CHANGE_OR_HAS_ACTION(element) &&
12269           (HAS_ANY_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
12270            HAS_ANY_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE)))
12271         counted_click = TRUE;
12272
12273       // element clicked that can trigger change when clicked/pressed
12274       if (trigger_events[element][CE_MOUSE_CLICKED_ON_X] ||
12275           trigger_events[element][CE_MOUSE_PRESSED_ON_X])
12276         counted_click = TRUE;
12277
12278       if (new_button && counted_click)
12279         CheckLevelTime_StepCounter();
12280     }
12281   }
12282
12283   SCAN_PLAYFIELD(x, y)
12284   {
12285     element = Tile[x][y];
12286     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12287     last_gfx_frame = GfxFrame[x][y];
12288
12289     if (element == EL_EMPTY)
12290       graphic = el2img(GfxElementEmpty[x][y]);
12291
12292     ResetGfxFrame(x, y);
12293
12294     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12295       DrawLevelGraphicAnimation(x, y, graphic);
12296
12297     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12298         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12299       ResetRandomAnimationValue(x, y);
12300
12301     SetRandomAnimationValue(x, y);
12302
12303     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12304
12305     if (IS_INACTIVE(element))
12306     {
12307       if (IS_ANIMATED(graphic))
12308         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12309
12310       continue;
12311     }
12312
12313     // this may take place after moving, so 'element' may have changed
12314     if (IS_CHANGING(x, y) &&
12315         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12316     {
12317       int page = element_info[element].event_page_nr[CE_DELAY];
12318
12319       HandleElementChange(x, y, page);
12320
12321       element = Tile[x][y];
12322       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12323     }
12324
12325     CheckNextToConditions(x, y);
12326
12327     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12328     {
12329       StartMoving(x, y);
12330
12331       element = Tile[x][y];
12332       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12333
12334       if (IS_ANIMATED(graphic) &&
12335           !IS_MOVING(x, y) &&
12336           !Stop[x][y])
12337         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12338
12339       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12340         TEST_DrawTwinkleOnField(x, y);
12341     }
12342     else if (element == EL_ACID)
12343     {
12344       if (!Stop[x][y])
12345         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12346     }
12347     else if ((element == EL_EXIT_OPEN ||
12348               element == EL_EM_EXIT_OPEN ||
12349               element == EL_SP_EXIT_OPEN ||
12350               element == EL_STEEL_EXIT_OPEN ||
12351               element == EL_EM_STEEL_EXIT_OPEN ||
12352               element == EL_SP_TERMINAL ||
12353               element == EL_SP_TERMINAL_ACTIVE ||
12354               element == EL_EXTRA_TIME ||
12355               element == EL_SHIELD_NORMAL ||
12356               element == EL_SHIELD_DEADLY) &&
12357              IS_ANIMATED(graphic))
12358       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12359     else if (IS_MOVING(x, y))
12360       ContinueMoving(x, y);
12361     else if (IS_ACTIVE_BOMB(element))
12362       CheckDynamite(x, y);
12363     else if (element == EL_AMOEBA_GROWING)
12364       AmoebaGrowing(x, y);
12365     else if (element == EL_AMOEBA_SHRINKING)
12366       AmoebaShrinking(x, y);
12367
12368 #if !USE_NEW_AMOEBA_CODE
12369     else if (IS_AMOEBALIVE(element))
12370       AmoebaReproduce(x, y);
12371 #endif
12372
12373     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12374       Life(x, y);
12375     else if (element == EL_EXIT_CLOSED)
12376       CheckExit(x, y);
12377     else if (element == EL_EM_EXIT_CLOSED)
12378       CheckExitEM(x, y);
12379     else if (element == EL_STEEL_EXIT_CLOSED)
12380       CheckExitSteel(x, y);
12381     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12382       CheckExitSteelEM(x, y);
12383     else if (element == EL_SP_EXIT_CLOSED)
12384       CheckExitSP(x, y);
12385     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12386              element == EL_EXPANDABLE_STEELWALL_GROWING)
12387       WallGrowing(x, y);
12388     else if (element == EL_EXPANDABLE_WALL ||
12389              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12390              element == EL_EXPANDABLE_WALL_VERTICAL ||
12391              element == EL_EXPANDABLE_WALL_ANY ||
12392              element == EL_BD_EXPANDABLE_WALL ||
12393              element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12394              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12395              element == EL_EXPANDABLE_STEELWALL_ANY)
12396       CheckWallGrowing(x, y);
12397     else if (element == EL_FLAMES)
12398       CheckForDragon(x, y);
12399     else if (element == EL_EXPLOSION)
12400       ; // drawing of correct explosion animation is handled separately
12401     else if (element == EL_ELEMENT_SNAPPING ||
12402              element == EL_DIAGONAL_SHRINKING ||
12403              element == EL_DIAGONAL_GROWING)
12404     {
12405       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12406
12407       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12408     }
12409     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12410       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12411
12412     if (IS_BELT_ACTIVE(element))
12413       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12414
12415     if (game.magic_wall_active)
12416     {
12417       int jx = local_player->jx, jy = local_player->jy;
12418
12419       // play the element sound at the position nearest to the player
12420       if ((element == EL_MAGIC_WALL_FULL ||
12421            element == EL_MAGIC_WALL_ACTIVE ||
12422            element == EL_MAGIC_WALL_EMPTYING ||
12423            element == EL_BD_MAGIC_WALL_FULL ||
12424            element == EL_BD_MAGIC_WALL_ACTIVE ||
12425            element == EL_BD_MAGIC_WALL_EMPTYING ||
12426            element == EL_DC_MAGIC_WALL_FULL ||
12427            element == EL_DC_MAGIC_WALL_ACTIVE ||
12428            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12429           ABS(x - jx) + ABS(y - jy) <
12430           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12431       {
12432         magic_wall_x = x;
12433         magic_wall_y = y;
12434       }
12435     }
12436   }
12437
12438 #if USE_NEW_AMOEBA_CODE
12439   // new experimental amoeba growth stuff
12440   if (!(FrameCounter % 8))
12441   {
12442     static unsigned int random = 1684108901;
12443
12444     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12445     {
12446       x = RND(lev_fieldx);
12447       y = RND(lev_fieldy);
12448       element = Tile[x][y];
12449
12450       if (!IS_PLAYER(x,y) &&
12451           (element == EL_EMPTY ||
12452            CAN_GROW_INTO(element) ||
12453            element == EL_QUICKSAND_EMPTY ||
12454            element == EL_QUICKSAND_FAST_EMPTY ||
12455            element == EL_ACID_SPLASH_LEFT ||
12456            element == EL_ACID_SPLASH_RIGHT))
12457       {
12458         if ((IN_LEV_FIELD(x, y - 1) && Tile[x][y - 1] == EL_AMOEBA_WET) ||
12459             (IN_LEV_FIELD(x - 1, y) && Tile[x - 1][y] == EL_AMOEBA_WET) ||
12460             (IN_LEV_FIELD(x + 1, y) && Tile[x + 1][y] == EL_AMOEBA_WET) ||
12461             (IN_LEV_FIELD(x, y + 1) && Tile[x][y + 1] == EL_AMOEBA_WET))
12462           Tile[x][y] = EL_AMOEBA_DROP;
12463       }
12464
12465       random = random * 129 + 1;
12466     }
12467   }
12468 #endif
12469
12470   game.explosions_delayed = FALSE;
12471
12472   SCAN_PLAYFIELD(x, y)
12473   {
12474     element = Tile[x][y];
12475
12476     if (ExplodeField[x][y])
12477       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12478     else if (element == EL_EXPLOSION)
12479       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12480
12481     ExplodeField[x][y] = EX_TYPE_NONE;
12482   }
12483
12484   game.explosions_delayed = TRUE;
12485
12486   if (game.magic_wall_active)
12487   {
12488     if (!(game.magic_wall_time_left % 4))
12489     {
12490       int element = Tile[magic_wall_x][magic_wall_y];
12491
12492       if (element == EL_BD_MAGIC_WALL_FULL ||
12493           element == EL_BD_MAGIC_WALL_ACTIVE ||
12494           element == EL_BD_MAGIC_WALL_EMPTYING)
12495         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12496       else if (element == EL_DC_MAGIC_WALL_FULL ||
12497                element == EL_DC_MAGIC_WALL_ACTIVE ||
12498                element == EL_DC_MAGIC_WALL_EMPTYING)
12499         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12500       else
12501         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12502     }
12503
12504     if (game.magic_wall_time_left > 0)
12505     {
12506       game.magic_wall_time_left--;
12507
12508       if (!game.magic_wall_time_left)
12509       {
12510         SCAN_PLAYFIELD(x, y)
12511         {
12512           element = Tile[x][y];
12513
12514           if (element == EL_MAGIC_WALL_ACTIVE ||
12515               element == EL_MAGIC_WALL_FULL)
12516           {
12517             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12518             TEST_DrawLevelField(x, y);
12519           }
12520           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12521                    element == EL_BD_MAGIC_WALL_FULL)
12522           {
12523             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12524             TEST_DrawLevelField(x, y);
12525           }
12526           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12527                    element == EL_DC_MAGIC_WALL_FULL)
12528           {
12529             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12530             TEST_DrawLevelField(x, y);
12531           }
12532         }
12533
12534         game.magic_wall_active = FALSE;
12535       }
12536     }
12537   }
12538
12539   if (game.light_time_left > 0)
12540   {
12541     game.light_time_left--;
12542
12543     if (game.light_time_left == 0)
12544       RedrawAllLightSwitchesAndInvisibleElements();
12545   }
12546
12547   if (game.timegate_time_left > 0)
12548   {
12549     game.timegate_time_left--;
12550
12551     if (game.timegate_time_left == 0)
12552       CloseAllOpenTimegates();
12553   }
12554
12555   if (game.lenses_time_left > 0)
12556   {
12557     game.lenses_time_left--;
12558
12559     if (game.lenses_time_left == 0)
12560       RedrawAllInvisibleElementsForLenses();
12561   }
12562
12563   if (game.magnify_time_left > 0)
12564   {
12565     game.magnify_time_left--;
12566
12567     if (game.magnify_time_left == 0)
12568       RedrawAllInvisibleElementsForMagnifier();
12569   }
12570
12571   for (i = 0; i < MAX_PLAYERS; i++)
12572   {
12573     struct PlayerInfo *player = &stored_player[i];
12574
12575     if (SHIELD_ON(player))
12576     {
12577       if (player->shield_deadly_time_left)
12578         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12579       else if (player->shield_normal_time_left)
12580         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12581     }
12582   }
12583
12584 #if USE_DELAYED_GFX_REDRAW
12585   SCAN_PLAYFIELD(x, y)
12586   {
12587     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12588     {
12589       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12590          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12591
12592       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12593         DrawLevelField(x, y);
12594
12595       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12596         DrawLevelFieldCrumbled(x, y);
12597
12598       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12599         DrawLevelFieldCrumbledNeighbours(x, y);
12600
12601       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12602         DrawTwinkleOnField(x, y);
12603     }
12604
12605     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12606   }
12607 #endif
12608
12609   DrawAllPlayers();
12610   PlayAllPlayersSound();
12611
12612   for (i = 0; i < MAX_PLAYERS; i++)
12613   {
12614     struct PlayerInfo *player = &stored_player[i];
12615
12616     if (player->show_envelope != 0 && (!player->active ||
12617                                        player->MovPos == 0))
12618     {
12619       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12620
12621       player->show_envelope = 0;
12622     }
12623   }
12624
12625   // use random number generator in every frame to make it less predictable
12626   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12627     RND(1);
12628
12629   mouse_action_last = mouse_action;
12630 }
12631
12632 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12633 {
12634   int min_x = x, min_y = y, max_x = x, max_y = y;
12635   int scr_fieldx = getScreenFieldSizeX();
12636   int scr_fieldy = getScreenFieldSizeY();
12637   int i;
12638
12639   for (i = 0; i < MAX_PLAYERS; i++)
12640   {
12641     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12642
12643     if (!stored_player[i].active || &stored_player[i] == player)
12644       continue;
12645
12646     min_x = MIN(min_x, jx);
12647     min_y = MIN(min_y, jy);
12648     max_x = MAX(max_x, jx);
12649     max_y = MAX(max_y, jy);
12650   }
12651
12652   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12653 }
12654
12655 static boolean AllPlayersInVisibleScreen(void)
12656 {
12657   int i;
12658
12659   for (i = 0; i < MAX_PLAYERS; i++)
12660   {
12661     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12662
12663     if (!stored_player[i].active)
12664       continue;
12665
12666     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12667       return FALSE;
12668   }
12669
12670   return TRUE;
12671 }
12672
12673 void ScrollLevel(int dx, int dy)
12674 {
12675   int scroll_offset = 2 * TILEX_VAR;
12676   int x, y;
12677
12678   BlitBitmap(drawto_field, drawto_field,
12679              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12680              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12681              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12682              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12683              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12684              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12685
12686   if (dx != 0)
12687   {
12688     x = (dx == 1 ? BX1 : BX2);
12689     for (y = BY1; y <= BY2; y++)
12690       DrawScreenField(x, y);
12691   }
12692
12693   if (dy != 0)
12694   {
12695     y = (dy == 1 ? BY1 : BY2);
12696     for (x = BX1; x <= BX2; x++)
12697       DrawScreenField(x, y);
12698   }
12699
12700   redraw_mask |= REDRAW_FIELD;
12701 }
12702
12703 static boolean canFallDown(struct PlayerInfo *player)
12704 {
12705   int jx = player->jx, jy = player->jy;
12706
12707   return (IN_LEV_FIELD(jx, jy + 1) &&
12708           (IS_FREE(jx, jy + 1) ||
12709            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12710           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12711           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12712 }
12713
12714 static boolean canPassField(int x, int y, int move_dir)
12715 {
12716   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12717   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12718   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12719   int nextx = x + dx;
12720   int nexty = y + dy;
12721   int element = Tile[x][y];
12722
12723   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12724           !CAN_MOVE(element) &&
12725           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12726           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12727           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12728 }
12729
12730 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12731 {
12732   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12733   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12734   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12735   int newx = x + dx;
12736   int newy = y + dy;
12737
12738   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12739           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12740           (IS_DIGGABLE(Tile[newx][newy]) ||
12741            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12742            canPassField(newx, newy, move_dir)));
12743 }
12744
12745 static void CheckGravityMovement(struct PlayerInfo *player)
12746 {
12747   if (player->gravity && !player->programmed_action)
12748   {
12749     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12750     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12751     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12752     int jx = player->jx, jy = player->jy;
12753     boolean player_is_moving_to_valid_field =
12754       (!player_is_snapping &&
12755        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12756         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12757     boolean player_can_fall_down = canFallDown(player);
12758
12759     if (player_can_fall_down &&
12760         !player_is_moving_to_valid_field)
12761       player->programmed_action = MV_DOWN;
12762   }
12763 }
12764
12765 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12766 {
12767   return CheckGravityMovement(player);
12768
12769   if (player->gravity && !player->programmed_action)
12770   {
12771     int jx = player->jx, jy = player->jy;
12772     boolean field_under_player_is_free =
12773       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12774     boolean player_is_standing_on_valid_field =
12775       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12776        (IS_WALKABLE(Tile[jx][jy]) &&
12777         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12778
12779     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12780       player->programmed_action = MV_DOWN;
12781   }
12782 }
12783
12784 /*
12785   MovePlayerOneStep()
12786   -----------------------------------------------------------------------------
12787   dx, dy:               direction (non-diagonal) to try to move the player to
12788   real_dx, real_dy:     direction as read from input device (can be diagonal)
12789 */
12790
12791 boolean MovePlayerOneStep(struct PlayerInfo *player,
12792                           int dx, int dy, int real_dx, int real_dy)
12793 {
12794   int jx = player->jx, jy = player->jy;
12795   int new_jx = jx + dx, new_jy = jy + dy;
12796   int can_move;
12797   boolean player_can_move = !player->cannot_move;
12798
12799   if (!player->active || (!dx && !dy))
12800     return MP_NO_ACTION;
12801
12802   player->MovDir = (dx < 0 ? MV_LEFT :
12803                     dx > 0 ? MV_RIGHT :
12804                     dy < 0 ? MV_UP :
12805                     dy > 0 ? MV_DOWN :  MV_NONE);
12806
12807   if (!IN_LEV_FIELD(new_jx, new_jy))
12808     return MP_NO_ACTION;
12809
12810   if (!player_can_move)
12811   {
12812     if (player->MovPos == 0)
12813     {
12814       player->is_moving = FALSE;
12815       player->is_digging = FALSE;
12816       player->is_collecting = FALSE;
12817       player->is_snapping = FALSE;
12818       player->is_pushing = FALSE;
12819     }
12820   }
12821
12822   if (!network.enabled && game.centered_player_nr == -1 &&
12823       !AllPlayersInSight(player, new_jx, new_jy))
12824     return MP_NO_ACTION;
12825
12826   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12827   if (can_move != MP_MOVING)
12828     return can_move;
12829
12830   // check if DigField() has caused relocation of the player
12831   if (player->jx != jx || player->jy != jy)
12832     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12833
12834   StorePlayer[jx][jy] = 0;
12835   player->last_jx = jx;
12836   player->last_jy = jy;
12837   player->jx = new_jx;
12838   player->jy = new_jy;
12839   StorePlayer[new_jx][new_jy] = player->element_nr;
12840
12841   if (player->move_delay_value_next != -1)
12842   {
12843     player->move_delay_value = player->move_delay_value_next;
12844     player->move_delay_value_next = -1;
12845   }
12846
12847   player->MovPos =
12848     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12849
12850   player->step_counter++;
12851
12852   PlayerVisit[jx][jy] = FrameCounter;
12853
12854   player->is_moving = TRUE;
12855
12856 #if 1
12857   // should better be called in MovePlayer(), but this breaks some tapes
12858   ScrollPlayer(player, SCROLL_INIT);
12859 #endif
12860
12861   return MP_MOVING;
12862 }
12863
12864 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12865 {
12866   int jx = player->jx, jy = player->jy;
12867   int old_jx = jx, old_jy = jy;
12868   int moved = MP_NO_ACTION;
12869
12870   if (!player->active)
12871     return FALSE;
12872
12873   if (!dx && !dy)
12874   {
12875     if (player->MovPos == 0)
12876     {
12877       player->is_moving = FALSE;
12878       player->is_digging = FALSE;
12879       player->is_collecting = FALSE;
12880       player->is_snapping = FALSE;
12881       player->is_pushing = FALSE;
12882     }
12883
12884     return FALSE;
12885   }
12886
12887   if (player->move_delay > 0)
12888     return FALSE;
12889
12890   player->move_delay = -1;              // set to "uninitialized" value
12891
12892   // store if player is automatically moved to next field
12893   player->is_auto_moving = (player->programmed_action != MV_NONE);
12894
12895   // remove the last programmed player action
12896   player->programmed_action = 0;
12897
12898   if (player->MovPos)
12899   {
12900     // should only happen if pre-1.2 tape recordings are played
12901     // this is only for backward compatibility
12902
12903     int original_move_delay_value = player->move_delay_value;
12904
12905 #if DEBUG
12906     Debug("game:playing:MovePlayer",
12907           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12908           tape.counter);
12909 #endif
12910
12911     // scroll remaining steps with finest movement resolution
12912     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12913
12914     while (player->MovPos)
12915     {
12916       ScrollPlayer(player, SCROLL_GO_ON);
12917       ScrollScreen(NULL, SCROLL_GO_ON);
12918
12919       AdvanceFrameAndPlayerCounters(player->index_nr);
12920
12921       DrawAllPlayers();
12922       BackToFront_WithFrameDelay(0);
12923     }
12924
12925     player->move_delay_value = original_move_delay_value;
12926   }
12927
12928   player->is_active = FALSE;
12929
12930   if (player->last_move_dir & MV_HORIZONTAL)
12931   {
12932     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12933       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12934   }
12935   else
12936   {
12937     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12938       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12939   }
12940
12941   if (!moved && !player->is_active)
12942   {
12943     player->is_moving = FALSE;
12944     player->is_digging = FALSE;
12945     player->is_collecting = FALSE;
12946     player->is_snapping = FALSE;
12947     player->is_pushing = FALSE;
12948   }
12949
12950   jx = player->jx;
12951   jy = player->jy;
12952
12953   if (moved & MP_MOVING && !ScreenMovPos &&
12954       (player->index_nr == game.centered_player_nr ||
12955        game.centered_player_nr == -1))
12956   {
12957     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12958
12959     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12960     {
12961       // actual player has left the screen -- scroll in that direction
12962       if (jx != old_jx)         // player has moved horizontally
12963         scroll_x += (jx - old_jx);
12964       else                      // player has moved vertically
12965         scroll_y += (jy - old_jy);
12966     }
12967     else
12968     {
12969       int offset_raw = game.scroll_delay_value;
12970
12971       if (jx != old_jx)         // player has moved horizontally
12972       {
12973         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12974         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12975         int new_scroll_x = jx - MIDPOSX + offset_x;
12976
12977         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12978             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12979           scroll_x = new_scroll_x;
12980
12981         // don't scroll over playfield boundaries
12982         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12983
12984         // don't scroll more than one field at a time
12985         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12986
12987         // don't scroll against the player's moving direction
12988         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12989             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12990           scroll_x = old_scroll_x;
12991       }
12992       else                      // player has moved vertically
12993       {
12994         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12995         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12996         int new_scroll_y = jy - MIDPOSY + offset_y;
12997
12998         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12999             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
13000           scroll_y = new_scroll_y;
13001
13002         // don't scroll over playfield boundaries
13003         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
13004
13005         // don't scroll more than one field at a time
13006         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
13007
13008         // don't scroll against the player's moving direction
13009         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
13010             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
13011           scroll_y = old_scroll_y;
13012       }
13013     }
13014
13015     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
13016     {
13017       if (!network.enabled && game.centered_player_nr == -1 &&
13018           !AllPlayersInVisibleScreen())
13019       {
13020         scroll_x = old_scroll_x;
13021         scroll_y = old_scroll_y;
13022       }
13023       else
13024       {
13025         ScrollScreen(player, SCROLL_INIT);
13026         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
13027       }
13028     }
13029   }
13030
13031   player->StepFrame = 0;
13032
13033   if (moved & MP_MOVING)
13034   {
13035     if (old_jx != jx && old_jy == jy)
13036       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
13037     else if (old_jx == jx && old_jy != jy)
13038       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
13039
13040     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
13041
13042     player->last_move_dir = player->MovDir;
13043     player->is_moving = TRUE;
13044     player->is_snapping = FALSE;
13045     player->is_switching = FALSE;
13046     player->is_dropping = FALSE;
13047     player->is_dropping_pressed = FALSE;
13048     player->drop_pressed_delay = 0;
13049
13050 #if 0
13051     // should better be called here than above, but this breaks some tapes
13052     ScrollPlayer(player, SCROLL_INIT);
13053 #endif
13054   }
13055   else
13056   {
13057     CheckGravityMovementWhenNotMoving(player);
13058
13059     player->is_moving = FALSE;
13060
13061     /* at this point, the player is allowed to move, but cannot move right now
13062        (e.g. because of something blocking the way) -- ensure that the player
13063        is also allowed to move in the next frame (in old versions before 3.1.1,
13064        the player was forced to wait again for eight frames before next try) */
13065
13066     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
13067       player->move_delay = 0;   // allow direct movement in the next frame
13068   }
13069
13070   if (player->move_delay == -1)         // not yet initialized by DigField()
13071     player->move_delay = player->move_delay_value;
13072
13073   if (game.engine_version < VERSION_IDENT(3,0,7,0))
13074   {
13075     TestIfPlayerTouchesBadThing(jx, jy);
13076     TestIfPlayerTouchesCustomElement(jx, jy);
13077   }
13078
13079   if (!player->active)
13080     RemovePlayer(player);
13081
13082   return moved;
13083 }
13084
13085 void ScrollPlayer(struct PlayerInfo *player, int mode)
13086 {
13087   int jx = player->jx, jy = player->jy;
13088   int last_jx = player->last_jx, last_jy = player->last_jy;
13089   int move_stepsize = TILEX / player->move_delay_value;
13090
13091   if (!player->active)
13092     return;
13093
13094   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13095     return;
13096
13097   if (mode == SCROLL_INIT)
13098   {
13099     player->actual_frame_counter.count = FrameCounter;
13100     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13101
13102     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13103         Tile[last_jx][last_jy] == EL_EMPTY)
13104     {
13105       int last_field_block_delay = 0;   // start with no blocking at all
13106       int block_delay_adjustment = player->block_delay_adjustment;
13107
13108       // if player blocks last field, add delay for exactly one move
13109       if (player->block_last_field)
13110       {
13111         last_field_block_delay += player->move_delay_value;
13112
13113         // when blocking enabled, prevent moving up despite gravity
13114         if (player->gravity && player->MovDir == MV_UP)
13115           block_delay_adjustment = -1;
13116       }
13117
13118       // add block delay adjustment (also possible when not blocking)
13119       last_field_block_delay += block_delay_adjustment;
13120
13121       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13122       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13123     }
13124
13125     if (player->MovPos != 0)    // player has not yet reached destination
13126       return;
13127   }
13128   else if (!FrameReached(&player->actual_frame_counter))
13129     return;
13130
13131   if (player->MovPos != 0)
13132   {
13133     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13134     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13135
13136     // before DrawPlayer() to draw correct player graphic for this case
13137     if (player->MovPos == 0)
13138       CheckGravityMovement(player);
13139   }
13140
13141   if (player->MovPos == 0)      // player reached destination field
13142   {
13143     if (player->move_delay_reset_counter > 0)
13144     {
13145       player->move_delay_reset_counter--;
13146
13147       if (player->move_delay_reset_counter == 0)
13148       {
13149         // continue with normal speed after quickly moving through gate
13150         HALVE_PLAYER_SPEED(player);
13151
13152         // be able to make the next move without delay
13153         player->move_delay = 0;
13154       }
13155     }
13156
13157     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13158         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13159         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13160         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13161         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13162         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13163         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13164         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13165     {
13166       ExitPlayer(player);
13167
13168       if (game.players_still_needed == 0 &&
13169           (game.friends_still_needed == 0 ||
13170            IS_SP_ELEMENT(Tile[jx][jy])))
13171         LevelSolved();
13172     }
13173
13174     player->last_jx = jx;
13175     player->last_jy = jy;
13176
13177     // this breaks one level: "machine", level 000
13178     {
13179       int move_direction = player->MovDir;
13180       int enter_side = MV_DIR_OPPOSITE(move_direction);
13181       int leave_side = move_direction;
13182       int old_jx = last_jx;
13183       int old_jy = last_jy;
13184       int old_element = Tile[old_jx][old_jy];
13185       int new_element = Tile[jx][jy];
13186
13187       if (IS_CUSTOM_ELEMENT(old_element))
13188         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13189                                    CE_LEFT_BY_PLAYER,
13190                                    player->index_bit, leave_side);
13191
13192       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13193                                           CE_PLAYER_LEAVES_X,
13194                                           player->index_bit, leave_side);
13195
13196       if (IS_CUSTOM_ELEMENT(new_element))
13197         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13198                                    player->index_bit, enter_side);
13199
13200       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13201                                           CE_PLAYER_ENTERS_X,
13202                                           player->index_bit, enter_side);
13203
13204       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13205                                         CE_MOVE_OF_X, move_direction);
13206     }
13207
13208     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13209     {
13210       TestIfPlayerTouchesBadThing(jx, jy);
13211       TestIfPlayerTouchesCustomElement(jx, jy);
13212
13213       /* needed because pushed element has not yet reached its destination,
13214          so it would trigger a change event at its previous field location */
13215       if (!player->is_pushing)
13216         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13217
13218       if (level.finish_dig_collect &&
13219           (player->is_digging || player->is_collecting))
13220       {
13221         int last_element = player->last_removed_element;
13222         int move_direction = player->MovDir;
13223         int enter_side = MV_DIR_OPPOSITE(move_direction);
13224         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13225                             CE_PLAYER_COLLECTS_X);
13226
13227         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13228                                             player->index_bit, enter_side);
13229
13230         player->last_removed_element = EL_UNDEFINED;
13231       }
13232
13233       if (!player->active)
13234         RemovePlayer(player);
13235     }
13236
13237     if (level.use_step_counter)
13238       CheckLevelTime_StepCounter();
13239
13240     if (tape.single_step && tape.recording && !tape.pausing &&
13241         !player->programmed_action)
13242       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13243
13244     if (!player->programmed_action)
13245       CheckSaveEngineSnapshot(player);
13246   }
13247 }
13248
13249 void ScrollScreen(struct PlayerInfo *player, int mode)
13250 {
13251   static DelayCounter screen_frame_counter = { 0 };
13252
13253   if (mode == SCROLL_INIT)
13254   {
13255     // set scrolling step size according to actual player's moving speed
13256     ScrollStepSize = TILEX / player->move_delay_value;
13257
13258     screen_frame_counter.count = FrameCounter;
13259     screen_frame_counter.value = 1;
13260
13261     ScreenMovDir = player->MovDir;
13262     ScreenMovPos = player->MovPos;
13263     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13264     return;
13265   }
13266   else if (!FrameReached(&screen_frame_counter))
13267     return;
13268
13269   if (ScreenMovPos)
13270   {
13271     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13272     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13273     redraw_mask |= REDRAW_FIELD;
13274   }
13275   else
13276     ScreenMovDir = MV_NONE;
13277 }
13278
13279 void CheckNextToConditions(int x, int y)
13280 {
13281   int element = Tile[x][y];
13282
13283   if (IS_PLAYER(x, y))
13284     TestIfPlayerNextToCustomElement(x, y);
13285
13286   if (CAN_CHANGE_OR_HAS_ACTION(element) &&
13287       HAS_ANY_CHANGE_EVENT(element, CE_NEXT_TO_X))
13288     TestIfElementNextToCustomElement(x, y);
13289 }
13290
13291 void TestIfPlayerNextToCustomElement(int x, int y)
13292 {
13293   struct XY *xy = xy_topdown;
13294   static int trigger_sides[4][2] =
13295   {
13296     // center side       border side
13297     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13298     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13299     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13300     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13301   };
13302   int i;
13303
13304   if (!IS_PLAYER(x, y))
13305     return;
13306
13307   struct PlayerInfo *player = PLAYERINFO(x, y);
13308
13309   if (player->is_moving)
13310     return;
13311
13312   for (i = 0; i < NUM_DIRECTIONS; i++)
13313   {
13314     int xx = x + xy[i].x;
13315     int yy = y + xy[i].y;
13316     int border_side = trigger_sides[i][1];
13317     int border_element;
13318
13319     if (!IN_LEV_FIELD(xx, yy))
13320       continue;
13321
13322     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13323       continue;         // center and border element not connected
13324
13325     border_element = Tile[xx][yy];
13326
13327     CheckElementChangeByPlayer(xx, yy, border_element, CE_NEXT_TO_PLAYER,
13328                                player->index_bit, border_side);
13329     CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13330                                         CE_PLAYER_NEXT_TO_X,
13331                                         player->index_bit, border_side);
13332
13333     /* use player element that is initially defined in the level playfield,
13334        not the player element that corresponds to the runtime player number
13335        (example: a level that contains EL_PLAYER_3 as the only player would
13336        incorrectly give EL_PLAYER_1 for "player->element_nr") */
13337
13338     CheckElementChangeBySide(xx, yy, border_element, player->initial_element,
13339                              CE_NEXT_TO_X, border_side);
13340   }
13341 }
13342
13343 void TestIfPlayerTouchesCustomElement(int x, int y)
13344 {
13345   struct XY *xy = xy_topdown;
13346   static int trigger_sides[4][2] =
13347   {
13348     // center side       border side
13349     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13350     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13351     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13352     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13353   };
13354   static int touch_dir[4] =
13355   {
13356     MV_LEFT | MV_RIGHT,
13357     MV_UP   | MV_DOWN,
13358     MV_UP   | MV_DOWN,
13359     MV_LEFT | MV_RIGHT
13360   };
13361   int center_element = Tile[x][y];      // should always be non-moving!
13362   int i;
13363
13364   for (i = 0; i < NUM_DIRECTIONS; i++)
13365   {
13366     int xx = x + xy[i].x;
13367     int yy = y + xy[i].y;
13368     int center_side = trigger_sides[i][0];
13369     int border_side = trigger_sides[i][1];
13370     int border_element;
13371
13372     if (!IN_LEV_FIELD(xx, yy))
13373       continue;
13374
13375     if (IS_PLAYER(x, y))                // player found at center element
13376     {
13377       struct PlayerInfo *player = PLAYERINFO(x, y);
13378
13379       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13380         border_element = Tile[xx][yy];          // may be moving!
13381       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13382         border_element = Tile[xx][yy];
13383       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13384         border_element = MovingOrBlocked2Element(xx, yy);
13385       else
13386         continue;               // center and border element do not touch
13387
13388       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13389                                  player->index_bit, border_side);
13390       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13391                                           CE_PLAYER_TOUCHES_X,
13392                                           player->index_bit, border_side);
13393
13394       {
13395         /* use player element that is initially defined in the level playfield,
13396            not the player element that corresponds to the runtime player number
13397            (example: a level that contains EL_PLAYER_3 as the only player would
13398            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13399         int player_element = PLAYERINFO(x, y)->initial_element;
13400
13401         CheckElementChangeBySide(xx, yy, border_element, player_element,
13402                                  CE_TOUCHING_X, border_side);
13403       }
13404     }
13405     else if (IS_PLAYER(xx, yy))         // player found at border element
13406     {
13407       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13408
13409       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13410       {
13411         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13412           continue;             // center and border element do not touch
13413       }
13414
13415       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13416                                  player->index_bit, center_side);
13417       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13418                                           CE_PLAYER_TOUCHES_X,
13419                                           player->index_bit, center_side);
13420
13421       {
13422         /* use player element that is initially defined in the level playfield,
13423            not the player element that corresponds to the runtime player number
13424            (example: a level that contains EL_PLAYER_3 as the only player would
13425            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13426         int player_element = PLAYERINFO(xx, yy)->initial_element;
13427
13428         CheckElementChangeBySide(x, y, center_element, player_element,
13429                                  CE_TOUCHING_X, center_side);
13430       }
13431
13432       break;
13433     }
13434   }
13435 }
13436
13437 void TestIfElementNextToCustomElement(int x, int y)
13438 {
13439   struct XY *xy = xy_topdown;
13440   static int trigger_sides[4][2] =
13441   {
13442     // center side      border side
13443     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13444     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13445     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13446     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13447   };
13448   int center_element = Tile[x][y];      // should always be non-moving!
13449   int i;
13450
13451   if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
13452     return;
13453
13454   for (i = 0; i < NUM_DIRECTIONS; i++)
13455   {
13456     int xx = x + xy[i].x;
13457     int yy = y + xy[i].y;
13458     int border_side = trigger_sides[i][1];
13459     int border_element;
13460
13461     if (!IN_LEV_FIELD(xx, yy))
13462       continue;
13463
13464     if (IS_MOVING(xx, yy) || IS_BLOCKED(xx, yy))
13465       continue;                 // center and border element not connected
13466
13467     border_element = Tile[xx][yy];
13468
13469     // check for change of center element (but change it only once)
13470     if (CheckElementChangeBySide(x, y, center_element, border_element,
13471                                  CE_NEXT_TO_X, border_side))
13472       break;
13473   }
13474 }
13475
13476 void TestIfElementTouchesCustomElement(int x, int y)
13477 {
13478   struct XY *xy = xy_topdown;
13479   static int trigger_sides[4][2] =
13480   {
13481     // center side      border side
13482     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13483     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13484     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13485     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13486   };
13487   static int touch_dir[4] =
13488   {
13489     MV_LEFT | MV_RIGHT,
13490     MV_UP   | MV_DOWN,
13491     MV_UP   | MV_DOWN,
13492     MV_LEFT | MV_RIGHT
13493   };
13494   boolean change_center_element = FALSE;
13495   int center_element = Tile[x][y];      // should always be non-moving!
13496   int border_element_old[NUM_DIRECTIONS];
13497   int i;
13498
13499   for (i = 0; i < NUM_DIRECTIONS; i++)
13500   {
13501     int xx = x + xy[i].x;
13502     int yy = y + xy[i].y;
13503     int border_element;
13504
13505     border_element_old[i] = -1;
13506
13507     if (!IN_LEV_FIELD(xx, yy))
13508       continue;
13509
13510     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13511       border_element = Tile[xx][yy];    // may be moving!
13512     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13513       border_element = Tile[xx][yy];
13514     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13515       border_element = MovingOrBlocked2Element(xx, yy);
13516     else
13517       continue;                 // center and border element do not touch
13518
13519     border_element_old[i] = border_element;
13520   }
13521
13522   for (i = 0; i < NUM_DIRECTIONS; i++)
13523   {
13524     int xx = x + xy[i].x;
13525     int yy = y + xy[i].y;
13526     int center_side = trigger_sides[i][0];
13527     int border_element = border_element_old[i];
13528
13529     if (border_element == -1)
13530       continue;
13531
13532     // check for change of border element
13533     CheckElementChangeBySide(xx, yy, border_element, center_element,
13534                              CE_TOUCHING_X, center_side);
13535
13536     // (center element cannot be player, so we dont have to check this here)
13537   }
13538
13539   for (i = 0; i < NUM_DIRECTIONS; i++)
13540   {
13541     int xx = x + xy[i].x;
13542     int yy = y + xy[i].y;
13543     int border_side = trigger_sides[i][1];
13544     int border_element = border_element_old[i];
13545
13546     if (border_element == -1)
13547       continue;
13548
13549     // check for change of center element (but change it only once)
13550     if (!change_center_element)
13551       change_center_element =
13552         CheckElementChangeBySide(x, y, center_element, border_element,
13553                                  CE_TOUCHING_X, border_side);
13554
13555     if (IS_PLAYER(xx, yy))
13556     {
13557       /* use player element that is initially defined in the level playfield,
13558          not the player element that corresponds to the runtime player number
13559          (example: a level that contains EL_PLAYER_3 as the only player would
13560          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13561       int player_element = PLAYERINFO(xx, yy)->initial_element;
13562
13563       CheckElementChangeBySide(x, y, center_element, player_element,
13564                                CE_TOUCHING_X, border_side);
13565     }
13566   }
13567 }
13568
13569 void TestIfElementHitsCustomElement(int x, int y, int direction)
13570 {
13571   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13572   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13573   int hitx = x + dx, hity = y + dy;
13574   int hitting_element = Tile[x][y];
13575   int touched_element;
13576
13577   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13578     return;
13579
13580   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13581                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13582
13583   if (IN_LEV_FIELD(hitx, hity))
13584   {
13585     int opposite_direction = MV_DIR_OPPOSITE(direction);
13586     int hitting_side = direction;
13587     int touched_side = opposite_direction;
13588     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13589                           MovDir[hitx][hity] != direction ||
13590                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13591
13592     object_hit = TRUE;
13593
13594     if (object_hit)
13595     {
13596       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13597                                CE_HITTING_X, touched_side);
13598
13599       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13600                                CE_HIT_BY_X, hitting_side);
13601
13602       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13603                                CE_HIT_BY_SOMETHING, opposite_direction);
13604
13605       if (IS_PLAYER(hitx, hity))
13606       {
13607         /* use player element that is initially defined in the level playfield,
13608            not the player element that corresponds to the runtime player number
13609            (example: a level that contains EL_PLAYER_3 as the only player would
13610            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13611         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13612
13613         CheckElementChangeBySide(x, y, hitting_element, player_element,
13614                                  CE_HITTING_X, touched_side);
13615       }
13616     }
13617   }
13618
13619   // "hitting something" is also true when hitting the playfield border
13620   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13621                            CE_HITTING_SOMETHING, direction);
13622 }
13623
13624 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13625 {
13626   int i, kill_x = -1, kill_y = -1;
13627
13628   int bad_element = -1;
13629   struct XY *test_xy = xy_topdown;
13630   static int test_dir[4] =
13631   {
13632     MV_UP,
13633     MV_LEFT,
13634     MV_RIGHT,
13635     MV_DOWN
13636   };
13637
13638   for (i = 0; i < NUM_DIRECTIONS; i++)
13639   {
13640     int test_x, test_y, test_move_dir, test_element;
13641
13642     test_x = good_x + test_xy[i].x;
13643     test_y = good_y + test_xy[i].y;
13644
13645     if (!IN_LEV_FIELD(test_x, test_y))
13646       continue;
13647
13648     test_move_dir =
13649       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13650
13651     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13652
13653     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13654        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13655     */
13656     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13657         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13658     {
13659       kill_x = test_x;
13660       kill_y = test_y;
13661       bad_element = test_element;
13662
13663       break;
13664     }
13665   }
13666
13667   if (kill_x != -1 || kill_y != -1)
13668   {
13669     if (IS_PLAYER(good_x, good_y))
13670     {
13671       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13672
13673       if (player->shield_deadly_time_left > 0 &&
13674           !IS_INDESTRUCTIBLE(bad_element))
13675         Bang(kill_x, kill_y);
13676       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13677         KillPlayer(player);
13678     }
13679     else
13680       Bang(good_x, good_y);
13681   }
13682 }
13683
13684 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13685 {
13686   int i, kill_x = -1, kill_y = -1;
13687   int bad_element = Tile[bad_x][bad_y];
13688   struct XY *test_xy = xy_topdown;
13689   static int touch_dir[4] =
13690   {
13691     MV_LEFT | MV_RIGHT,
13692     MV_UP   | MV_DOWN,
13693     MV_UP   | MV_DOWN,
13694     MV_LEFT | MV_RIGHT
13695   };
13696   static int test_dir[4] =
13697   {
13698     MV_UP,
13699     MV_LEFT,
13700     MV_RIGHT,
13701     MV_DOWN
13702   };
13703
13704   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13705     return;
13706
13707   for (i = 0; i < NUM_DIRECTIONS; i++)
13708   {
13709     int test_x, test_y, test_move_dir, test_element;
13710
13711     test_x = bad_x + test_xy[i].x;
13712     test_y = bad_y + test_xy[i].y;
13713
13714     if (!IN_LEV_FIELD(test_x, test_y))
13715       continue;
13716
13717     test_move_dir =
13718       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13719
13720     test_element = Tile[test_x][test_y];
13721
13722     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13723        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13724     */
13725     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13726         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13727     {
13728       // good thing is player or penguin that does not move away
13729       if (IS_PLAYER(test_x, test_y))
13730       {
13731         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13732
13733         if (bad_element == EL_ROBOT && player->is_moving)
13734           continue;     // robot does not kill player if he is moving
13735
13736         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13737         {
13738           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13739             continue;           // center and border element do not touch
13740         }
13741
13742         kill_x = test_x;
13743         kill_y = test_y;
13744
13745         break;
13746       }
13747       else if (test_element == EL_PENGUIN)
13748       {
13749         kill_x = test_x;
13750         kill_y = test_y;
13751
13752         break;
13753       }
13754     }
13755   }
13756
13757   if (kill_x != -1 || kill_y != -1)
13758   {
13759     if (IS_PLAYER(kill_x, kill_y))
13760     {
13761       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13762
13763       if (player->shield_deadly_time_left > 0 &&
13764           !IS_INDESTRUCTIBLE(bad_element))
13765         Bang(bad_x, bad_y);
13766       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13767         KillPlayer(player);
13768     }
13769     else
13770       Bang(kill_x, kill_y);
13771   }
13772 }
13773
13774 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13775 {
13776   int bad_element = Tile[bad_x][bad_y];
13777   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13778   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13779   int test_x = bad_x + dx, test_y = bad_y + dy;
13780   int test_move_dir, test_element;
13781   int kill_x = -1, kill_y = -1;
13782
13783   if (!IN_LEV_FIELD(test_x, test_y))
13784     return;
13785
13786   test_move_dir =
13787     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13788
13789   test_element = Tile[test_x][test_y];
13790
13791   if (test_move_dir != bad_move_dir)
13792   {
13793     // good thing can be player or penguin that does not move away
13794     if (IS_PLAYER(test_x, test_y))
13795     {
13796       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13797
13798       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13799          player as being hit when he is moving towards the bad thing, because
13800          the "get hit by" condition would be lost after the player stops) */
13801       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13802         return;         // player moves away from bad thing
13803
13804       kill_x = test_x;
13805       kill_y = test_y;
13806     }
13807     else if (test_element == EL_PENGUIN)
13808     {
13809       kill_x = test_x;
13810       kill_y = test_y;
13811     }
13812   }
13813
13814   if (kill_x != -1 || kill_y != -1)
13815   {
13816     if (IS_PLAYER(kill_x, kill_y))
13817     {
13818       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13819
13820       if (player->shield_deadly_time_left > 0 &&
13821           !IS_INDESTRUCTIBLE(bad_element))
13822         Bang(bad_x, bad_y);
13823       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13824         KillPlayer(player);
13825     }
13826     else
13827       Bang(kill_x, kill_y);
13828   }
13829 }
13830
13831 void TestIfPlayerTouchesBadThing(int x, int y)
13832 {
13833   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13834 }
13835
13836 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13837 {
13838   TestIfGoodThingHitsBadThing(x, y, move_dir);
13839 }
13840
13841 void TestIfBadThingTouchesPlayer(int x, int y)
13842 {
13843   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13844 }
13845
13846 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13847 {
13848   TestIfBadThingHitsGoodThing(x, y, move_dir);
13849 }
13850
13851 void TestIfFriendTouchesBadThing(int x, int y)
13852 {
13853   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13854 }
13855
13856 void TestIfBadThingTouchesFriend(int x, int y)
13857 {
13858   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13859 }
13860
13861 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13862 {
13863   int i, kill_x = bad_x, kill_y = bad_y;
13864   struct XY *xy = xy_topdown;
13865
13866   for (i = 0; i < NUM_DIRECTIONS; i++)
13867   {
13868     int x, y, element;
13869
13870     x = bad_x + xy[i].x;
13871     y = bad_y + xy[i].y;
13872     if (!IN_LEV_FIELD(x, y))
13873       continue;
13874
13875     element = Tile[x][y];
13876     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13877         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13878     {
13879       kill_x = x;
13880       kill_y = y;
13881       break;
13882     }
13883   }
13884
13885   if (kill_x != bad_x || kill_y != bad_y)
13886     Bang(bad_x, bad_y);
13887 }
13888
13889 void KillPlayer(struct PlayerInfo *player)
13890 {
13891   int jx = player->jx, jy = player->jy;
13892
13893   if (!player->active)
13894     return;
13895
13896 #if 0
13897   Debug("game:playing:KillPlayer",
13898         "0: killed == %d, active == %d, reanimated == %d",
13899         player->killed, player->active, player->reanimated);
13900 #endif
13901
13902   /* the following code was introduced to prevent an infinite loop when calling
13903      -> Bang()
13904      -> CheckTriggeredElementChangeExt()
13905      -> ExecuteCustomElementAction()
13906      -> KillPlayer()
13907      -> (infinitely repeating the above sequence of function calls)
13908      which occurs when killing the player while having a CE with the setting
13909      "kill player X when explosion of <player X>"; the solution using a new
13910      field "player->killed" was chosen for backwards compatibility, although
13911      clever use of the fields "player->active" etc. would probably also work */
13912 #if 1
13913   if (player->killed)
13914     return;
13915 #endif
13916
13917   player->killed = TRUE;
13918
13919   // remove accessible field at the player's position
13920   Tile[jx][jy] = EL_EMPTY;
13921
13922   // deactivate shield (else Bang()/Explode() would not work right)
13923   player->shield_normal_time_left = 0;
13924   player->shield_deadly_time_left = 0;
13925
13926 #if 0
13927   Debug("game:playing:KillPlayer",
13928         "1: killed == %d, active == %d, reanimated == %d",
13929         player->killed, player->active, player->reanimated);
13930 #endif
13931
13932   Bang(jx, jy);
13933
13934 #if 0
13935   Debug("game:playing:KillPlayer",
13936         "2: killed == %d, active == %d, reanimated == %d",
13937         player->killed, player->active, player->reanimated);
13938 #endif
13939
13940   if (player->reanimated)       // killed player may have been reanimated
13941     player->killed = player->reanimated = FALSE;
13942   else
13943     BuryPlayer(player);
13944 }
13945
13946 static void KillPlayerUnlessEnemyProtected(int x, int y)
13947 {
13948   if (!PLAYER_ENEMY_PROTECTED(x, y))
13949     KillPlayer(PLAYERINFO(x, y));
13950 }
13951
13952 static void KillPlayerUnlessExplosionProtected(int x, int y)
13953 {
13954   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13955     KillPlayer(PLAYERINFO(x, y));
13956 }
13957
13958 void BuryPlayer(struct PlayerInfo *player)
13959 {
13960   int jx = player->jx, jy = player->jy;
13961
13962   if (!player->active)
13963     return;
13964
13965   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13966   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13967
13968   RemovePlayer(player);
13969
13970   player->buried = TRUE;
13971
13972   if (game.all_players_gone)
13973     game.GameOver = TRUE;
13974 }
13975
13976 void RemovePlayer(struct PlayerInfo *player)
13977 {
13978   int jx = player->jx, jy = player->jy;
13979   int i, found = FALSE;
13980
13981   player->present = FALSE;
13982   player->active = FALSE;
13983
13984   // required for some CE actions (even if the player is not active anymore)
13985   player->MovPos = 0;
13986
13987   if (!ExplodeField[jx][jy])
13988     StorePlayer[jx][jy] = 0;
13989
13990   if (player->is_moving)
13991     TEST_DrawLevelField(player->last_jx, player->last_jy);
13992
13993   for (i = 0; i < MAX_PLAYERS; i++)
13994     if (stored_player[i].active)
13995       found = TRUE;
13996
13997   if (!found)
13998   {
13999     game.all_players_gone = TRUE;
14000     game.GameOver = TRUE;
14001   }
14002
14003   game.exit_x = game.robot_wheel_x = jx;
14004   game.exit_y = game.robot_wheel_y = jy;
14005 }
14006
14007 void ExitPlayer(struct PlayerInfo *player)
14008 {
14009   DrawPlayer(player);   // needed here only to cleanup last field
14010   RemovePlayer(player);
14011
14012   if (game.players_still_needed > 0)
14013     game.players_still_needed--;
14014 }
14015
14016 static void SetFieldForSnapping(int x, int y, int element, int direction,
14017                                 int player_index_bit)
14018 {
14019   struct ElementInfo *ei = &element_info[element];
14020   int direction_bit = MV_DIR_TO_BIT(direction);
14021   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
14022   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
14023                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
14024
14025   Tile[x][y] = EL_ELEMENT_SNAPPING;
14026   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
14027   MovDir[x][y] = direction;
14028   Store[x][y] = element;
14029   Store2[x][y] = player_index_bit;
14030
14031   ResetGfxAnimation(x, y);
14032
14033   GfxElement[x][y] = element;
14034   GfxAction[x][y] = action;
14035   GfxDir[x][y] = direction;
14036   GfxFrame[x][y] = -1;
14037 }
14038
14039 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
14040                                    int player_index_bit)
14041 {
14042   TestIfElementTouchesCustomElement(x, y);      // for empty space
14043
14044   if (level.finish_dig_collect)
14045   {
14046     int dig_side = MV_DIR_OPPOSITE(direction);
14047     int change_event = (IS_DIGGABLE(element) ? CE_PLAYER_DIGS_X :
14048                         CE_PLAYER_COLLECTS_X);
14049
14050     CheckTriggeredElementChangeByPlayer(x, y, element, change_event,
14051                                         player_index_bit, dig_side);
14052     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14053                                         player_index_bit, dig_side);
14054   }
14055 }
14056
14057 /*
14058   =============================================================================
14059   checkDiagonalPushing()
14060   -----------------------------------------------------------------------------
14061   check if diagonal input device direction results in pushing of object
14062   (by checking if the alternative direction is walkable, diggable, ...)
14063   =============================================================================
14064 */
14065
14066 static boolean checkDiagonalPushing(struct PlayerInfo *player,
14067                                     int x, int y, int real_dx, int real_dy)
14068 {
14069   int jx, jy, dx, dy, xx, yy;
14070
14071   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
14072     return TRUE;
14073
14074   // diagonal direction: check alternative direction
14075   jx = player->jx;
14076   jy = player->jy;
14077   dx = x - jx;
14078   dy = y - jy;
14079   xx = jx + (dx == 0 ? real_dx : 0);
14080   yy = jy + (dy == 0 ? real_dy : 0);
14081
14082   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
14083 }
14084
14085 /*
14086   =============================================================================
14087   DigField()
14088   -----------------------------------------------------------------------------
14089   x, y:                 field next to player (non-diagonal) to try to dig to
14090   real_dx, real_dy:     direction as read from input device (can be diagonal)
14091   =============================================================================
14092 */
14093
14094 static int DigField(struct PlayerInfo *player,
14095                     int oldx, int oldy, int x, int y,
14096                     int real_dx, int real_dy, int mode)
14097 {
14098   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
14099   boolean player_was_pushing = player->is_pushing;
14100   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
14101   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
14102   int jx = oldx, jy = oldy;
14103   int dx = x - jx, dy = y - jy;
14104   int nextx = x + dx, nexty = y + dy;
14105   int move_direction = (dx == -1 ? MV_LEFT  :
14106                         dx == +1 ? MV_RIGHT :
14107                         dy == -1 ? MV_UP    :
14108                         dy == +1 ? MV_DOWN  : MV_NONE);
14109   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
14110   int dig_side = MV_DIR_OPPOSITE(move_direction);
14111   int old_element = Tile[jx][jy];
14112   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
14113   int collect_count;
14114
14115   if (is_player)                // function can also be called by EL_PENGUIN
14116   {
14117     if (player->MovPos == 0)
14118     {
14119       player->is_digging = FALSE;
14120       player->is_collecting = FALSE;
14121     }
14122
14123     if (player->MovPos == 0)    // last pushing move finished
14124       player->is_pushing = FALSE;
14125
14126     if (mode == DF_NO_PUSH)     // player just stopped pushing
14127     {
14128       player->is_switching = FALSE;
14129       player->push_delay = -1;
14130
14131       return MP_NO_ACTION;
14132     }
14133   }
14134   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
14135     old_element = Back[jx][jy];
14136
14137   // in case of element dropped at player position, check background
14138   else if (Back[jx][jy] != EL_EMPTY &&
14139            game.engine_version >= VERSION_IDENT(2,2,0,0))
14140     old_element = Back[jx][jy];
14141
14142   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
14143     return MP_NO_ACTION;        // field has no opening in this direction
14144
14145   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14146     return MP_NO_ACTION;        // field has no opening in this direction
14147
14148   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14149   {
14150     SplashAcid(x, y);
14151
14152     Tile[jx][jy] = player->artwork_element;
14153     InitMovingField(jx, jy, MV_DOWN);
14154     Store[jx][jy] = EL_ACID;
14155     ContinueMoving(jx, jy);
14156     BuryPlayer(player);
14157
14158     return MP_DONT_RUN_INTO;
14159   }
14160
14161   if (player_can_move && DONT_RUN_INTO(element))
14162   {
14163     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14164
14165     return MP_DONT_RUN_INTO;
14166   }
14167
14168   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14169     return MP_NO_ACTION;
14170
14171   collect_count = element_info[element].collect_count_initial;
14172
14173   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14174     return MP_NO_ACTION;
14175
14176   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14177     player_can_move = player_can_move_or_snap;
14178
14179   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14180       game.engine_version >= VERSION_IDENT(2,2,0,0))
14181   {
14182     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14183                                player->index_bit, dig_side);
14184     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14185                                         player->index_bit, dig_side);
14186
14187     if (element == EL_DC_LANDMINE)
14188       Bang(x, y);
14189
14190     if (Tile[x][y] != element)          // field changed by snapping
14191       return MP_ACTION;
14192
14193     return MP_NO_ACTION;
14194   }
14195
14196   if (player->gravity && is_player && !player->is_auto_moving &&
14197       canFallDown(player) && move_direction != MV_DOWN &&
14198       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14199     return MP_NO_ACTION;        // player cannot walk here due to gravity
14200
14201   if (player_can_move &&
14202       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14203   {
14204     int sound_element = SND_ELEMENT(element);
14205     int sound_action = ACTION_WALKING;
14206
14207     if (IS_RND_GATE(element))
14208     {
14209       if (!player->key[RND_GATE_NR(element)])
14210         return MP_NO_ACTION;
14211     }
14212     else if (IS_RND_GATE_GRAY(element))
14213     {
14214       if (!player->key[RND_GATE_GRAY_NR(element)])
14215         return MP_NO_ACTION;
14216     }
14217     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14218     {
14219       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14220         return MP_NO_ACTION;
14221     }
14222     else if (element == EL_EXIT_OPEN ||
14223              element == EL_EM_EXIT_OPEN ||
14224              element == EL_EM_EXIT_OPENING ||
14225              element == EL_STEEL_EXIT_OPEN ||
14226              element == EL_EM_STEEL_EXIT_OPEN ||
14227              element == EL_EM_STEEL_EXIT_OPENING ||
14228              element == EL_SP_EXIT_OPEN ||
14229              element == EL_SP_EXIT_OPENING)
14230     {
14231       sound_action = ACTION_PASSING;    // player is passing exit
14232     }
14233     else if (element == EL_EMPTY)
14234     {
14235       sound_action = ACTION_MOVING;             // nothing to walk on
14236     }
14237
14238     // play sound from background or player, whatever is available
14239     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14240       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14241     else
14242       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14243   }
14244   else if (player_can_move &&
14245            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14246   {
14247     if (!ACCESS_FROM(element, opposite_direction))
14248       return MP_NO_ACTION;      // field not accessible from this direction
14249
14250     if (CAN_MOVE(element))      // only fixed elements can be passed!
14251       return MP_NO_ACTION;
14252
14253     if (IS_EM_GATE(element))
14254     {
14255       if (!player->key[EM_GATE_NR(element)])
14256         return MP_NO_ACTION;
14257     }
14258     else if (IS_EM_GATE_GRAY(element))
14259     {
14260       if (!player->key[EM_GATE_GRAY_NR(element)])
14261         return MP_NO_ACTION;
14262     }
14263     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14264     {
14265       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14266         return MP_NO_ACTION;
14267     }
14268     else if (IS_EMC_GATE(element))
14269     {
14270       if (!player->key[EMC_GATE_NR(element)])
14271         return MP_NO_ACTION;
14272     }
14273     else if (IS_EMC_GATE_GRAY(element))
14274     {
14275       if (!player->key[EMC_GATE_GRAY_NR(element)])
14276         return MP_NO_ACTION;
14277     }
14278     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14279     {
14280       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14281         return MP_NO_ACTION;
14282     }
14283     else if (element == EL_DC_GATE_WHITE ||
14284              element == EL_DC_GATE_WHITE_GRAY ||
14285              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14286     {
14287       if (player->num_white_keys == 0)
14288         return MP_NO_ACTION;
14289
14290       player->num_white_keys--;
14291     }
14292     else if (IS_SP_PORT(element))
14293     {
14294       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14295           element == EL_SP_GRAVITY_PORT_RIGHT ||
14296           element == EL_SP_GRAVITY_PORT_UP ||
14297           element == EL_SP_GRAVITY_PORT_DOWN)
14298         player->gravity = !player->gravity;
14299       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14300                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14301                element == EL_SP_GRAVITY_ON_PORT_UP ||
14302                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14303         player->gravity = TRUE;
14304       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14305                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14306                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14307                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14308         player->gravity = FALSE;
14309     }
14310
14311     // automatically move to the next field with double speed
14312     player->programmed_action = move_direction;
14313
14314     if (player->move_delay_reset_counter == 0)
14315     {
14316       player->move_delay_reset_counter = 2;     // two double speed steps
14317
14318       DOUBLE_PLAYER_SPEED(player);
14319     }
14320
14321     PlayLevelSoundAction(x, y, ACTION_PASSING);
14322   }
14323   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14324   {
14325     RemoveField(x, y);
14326
14327     if (mode != DF_SNAP)
14328     {
14329       GfxElement[x][y] = GFX_ELEMENT(element);
14330       player->is_digging = TRUE;
14331     }
14332
14333     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14334
14335     // use old behaviour for old levels (digging)
14336     if (!level.finish_dig_collect)
14337     {
14338       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14339                                           player->index_bit, dig_side);
14340
14341       // if digging triggered player relocation, finish digging tile
14342       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14343         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14344     }
14345
14346     if (mode == DF_SNAP)
14347     {
14348       if (level.block_snap_field)
14349         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14350       else
14351         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14352
14353       // use old behaviour for old levels (snapping)
14354       if (!level.finish_dig_collect)
14355         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14356                                             player->index_bit, dig_side);
14357     }
14358   }
14359   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14360   {
14361     RemoveField(x, y);
14362
14363     if (is_player && mode != DF_SNAP)
14364     {
14365       GfxElement[x][y] = element;
14366       player->is_collecting = TRUE;
14367     }
14368
14369     if (element == EL_SPEED_PILL)
14370     {
14371       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14372     }
14373     else if (element == EL_EXTRA_TIME && level.time > 0)
14374     {
14375       TimeLeft += level.extra_time;
14376
14377       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14378
14379       DisplayGameControlValues();
14380     }
14381     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14382     {
14383       int shield_time = (element == EL_SHIELD_DEADLY ?
14384                          level.shield_deadly_time :
14385                          level.shield_normal_time);
14386
14387       player->shield_normal_time_left += shield_time;
14388       if (element == EL_SHIELD_DEADLY)
14389         player->shield_deadly_time_left += shield_time;
14390     }
14391     else if (element == EL_DYNAMITE ||
14392              element == EL_EM_DYNAMITE ||
14393              element == EL_SP_DISK_RED)
14394     {
14395       if (player->inventory_size < MAX_INVENTORY_SIZE)
14396         player->inventory_element[player->inventory_size++] = element;
14397
14398       DrawGameDoorValues();
14399     }
14400     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14401     {
14402       player->dynabomb_count++;
14403       player->dynabombs_left++;
14404     }
14405     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14406     {
14407       player->dynabomb_size++;
14408     }
14409     else if (element == EL_DYNABOMB_INCREASE_POWER)
14410     {
14411       player->dynabomb_xl = TRUE;
14412     }
14413     else if (IS_KEY(element))
14414     {
14415       player->key[KEY_NR(element)] = TRUE;
14416
14417       DrawGameDoorValues();
14418     }
14419     else if (element == EL_DC_KEY_WHITE)
14420     {
14421       player->num_white_keys++;
14422
14423       // display white keys?
14424       // DrawGameDoorValues();
14425     }
14426     else if (IS_ENVELOPE(element))
14427     {
14428       boolean wait_for_snapping = (mode == DF_SNAP && level.block_snap_field);
14429
14430       if (!wait_for_snapping)
14431         player->show_envelope = element;
14432     }
14433     else if (element == EL_EMC_LENSES)
14434     {
14435       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14436
14437       RedrawAllInvisibleElementsForLenses();
14438     }
14439     else if (element == EL_EMC_MAGNIFIER)
14440     {
14441       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14442
14443       RedrawAllInvisibleElementsForMagnifier();
14444     }
14445     else if (IS_DROPPABLE(element) ||
14446              IS_THROWABLE(element))     // can be collected and dropped
14447     {
14448       int i;
14449
14450       if (collect_count == 0)
14451         player->inventory_infinite_element = element;
14452       else
14453         for (i = 0; i < collect_count; i++)
14454           if (player->inventory_size < MAX_INVENTORY_SIZE)
14455             player->inventory_element[player->inventory_size++] = element;
14456
14457       DrawGameDoorValues();
14458     }
14459     else if (collect_count > 0)
14460     {
14461       game.gems_still_needed -= collect_count;
14462       if (game.gems_still_needed < 0)
14463         game.gems_still_needed = 0;
14464
14465       game.snapshot.collected_item = TRUE;
14466
14467       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14468
14469       DisplayGameControlValues();
14470     }
14471
14472     RaiseScoreElement(element);
14473     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14474
14475     // use old behaviour for old levels (collecting)
14476     if (!level.finish_dig_collect && is_player)
14477     {
14478       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14479                                           player->index_bit, dig_side);
14480
14481       // if collecting triggered player relocation, finish collecting tile
14482       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14483         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14484     }
14485
14486     if (mode == DF_SNAP)
14487     {
14488       if (level.block_snap_field)
14489         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14490       else
14491         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14492
14493       // use old behaviour for old levels (snapping)
14494       if (!level.finish_dig_collect)
14495         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14496                                             player->index_bit, dig_side);
14497     }
14498   }
14499   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14500   {
14501     if (mode == DF_SNAP && element != EL_BD_ROCK)
14502       return MP_NO_ACTION;
14503
14504     if (CAN_FALL(element) && dy)
14505       return MP_NO_ACTION;
14506
14507     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14508         !(element == EL_SPRING && level.use_spring_bug))
14509       return MP_NO_ACTION;
14510
14511     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14512         ((move_direction & MV_VERTICAL &&
14513           ((element_info[element].move_pattern & MV_LEFT &&
14514             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14515            (element_info[element].move_pattern & MV_RIGHT &&
14516             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14517          (move_direction & MV_HORIZONTAL &&
14518           ((element_info[element].move_pattern & MV_UP &&
14519             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14520            (element_info[element].move_pattern & MV_DOWN &&
14521             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14522       return MP_NO_ACTION;
14523
14524     // do not push elements already moving away faster than player
14525     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14526         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14527       return MP_NO_ACTION;
14528
14529     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14530     {
14531       if (player->push_delay_value == -1 || !player_was_pushing)
14532         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14533     }
14534     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14535     {
14536       if (player->push_delay_value == -1)
14537         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14538     }
14539     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14540     {
14541       if (!player->is_pushing)
14542         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14543     }
14544
14545     player->is_pushing = TRUE;
14546     player->is_active = TRUE;
14547
14548     if (!(IN_LEV_FIELD(nextx, nexty) &&
14549           (IS_FREE(nextx, nexty) ||
14550            (IS_SB_ELEMENT(element) &&
14551             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14552            (IS_CUSTOM_ELEMENT(element) &&
14553             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14554       return MP_NO_ACTION;
14555
14556     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14557       return MP_NO_ACTION;
14558
14559     if (player->push_delay == -1)       // new pushing; restart delay
14560       player->push_delay = 0;
14561
14562     if (player->push_delay < player->push_delay_value &&
14563         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14564         element != EL_SPRING && element != EL_BALLOON)
14565     {
14566       // make sure that there is no move delay before next try to push
14567       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14568         player->move_delay = 0;
14569
14570       return MP_NO_ACTION;
14571     }
14572
14573     if (IS_CUSTOM_ELEMENT(element) &&
14574         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14575     {
14576       if (!DigFieldByCE(nextx, nexty, element))
14577         return MP_NO_ACTION;
14578     }
14579
14580     if (IS_SB_ELEMENT(element))
14581     {
14582       boolean sokoban_task_solved = FALSE;
14583
14584       if (element == EL_SOKOBAN_FIELD_FULL)
14585       {
14586         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14587
14588         IncrementSokobanFieldsNeeded();
14589         IncrementSokobanObjectsNeeded();
14590       }
14591
14592       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14593       {
14594         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14595
14596         DecrementSokobanFieldsNeeded();
14597         DecrementSokobanObjectsNeeded();
14598
14599         // sokoban object was pushed from empty field to sokoban field
14600         if (Back[x][y] == EL_EMPTY)
14601           sokoban_task_solved = TRUE;
14602       }
14603
14604       Tile[x][y] = EL_SOKOBAN_OBJECT;
14605
14606       if (Back[x][y] == Back[nextx][nexty])
14607         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14608       else if (Back[x][y] != 0)
14609         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14610                                     ACTION_EMPTYING);
14611       else
14612         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14613                                     ACTION_FILLING);
14614
14615       if (sokoban_task_solved &&
14616           game.sokoban_fields_still_needed == 0 &&
14617           game.sokoban_objects_still_needed == 0 &&
14618           level.auto_exit_sokoban)
14619       {
14620         game.players_still_needed = 0;
14621
14622         LevelSolved();
14623
14624         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14625       }
14626     }
14627     else
14628       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14629
14630     InitMovingField(x, y, move_direction);
14631     GfxAction[x][y] = ACTION_PUSHING;
14632
14633     if (mode == DF_SNAP)
14634       ContinueMoving(x, y);
14635     else
14636       MovPos[x][y] = (dx != 0 ? dx : dy);
14637
14638     Pushed[x][y] = TRUE;
14639     Pushed[nextx][nexty] = TRUE;
14640
14641     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14642       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14643     else
14644       player->push_delay_value = -1;    // get new value later
14645
14646     // check for element change _after_ element has been pushed
14647     if (game.use_change_when_pushing_bug)
14648     {
14649       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14650                                  player->index_bit, dig_side);
14651       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14652                                           player->index_bit, dig_side);
14653     }
14654   }
14655   else if (IS_SWITCHABLE(element))
14656   {
14657     if (PLAYER_SWITCHING(player, x, y))
14658     {
14659       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14660                                           player->index_bit, dig_side);
14661
14662       return MP_ACTION;
14663     }
14664
14665     player->is_switching = TRUE;
14666     player->switch_x = x;
14667     player->switch_y = y;
14668
14669     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14670
14671     if (element == EL_ROBOT_WHEEL)
14672     {
14673       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14674
14675       game.robot_wheel_x = x;
14676       game.robot_wheel_y = y;
14677       game.robot_wheel_active = TRUE;
14678
14679       TEST_DrawLevelField(x, y);
14680     }
14681     else if (element == EL_SP_TERMINAL)
14682     {
14683       int xx, yy;
14684
14685       SCAN_PLAYFIELD(xx, yy)
14686       {
14687         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14688         {
14689           Bang(xx, yy);
14690         }
14691         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14692         {
14693           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14694
14695           ResetGfxAnimation(xx, yy);
14696           TEST_DrawLevelField(xx, yy);
14697         }
14698       }
14699     }
14700     else if (IS_BELT_SWITCH(element))
14701     {
14702       ToggleBeltSwitch(x, y);
14703     }
14704     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14705              element == EL_SWITCHGATE_SWITCH_DOWN ||
14706              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14707              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14708     {
14709       ToggleSwitchgateSwitch(x, y);
14710     }
14711     else if (element == EL_LIGHT_SWITCH ||
14712              element == EL_LIGHT_SWITCH_ACTIVE)
14713     {
14714       ToggleLightSwitch(x, y);
14715     }
14716     else if (element == EL_TIMEGATE_SWITCH ||
14717              element == EL_DC_TIMEGATE_SWITCH)
14718     {
14719       ActivateTimegateSwitch(x, y);
14720     }
14721     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14722              element == EL_BALLOON_SWITCH_RIGHT ||
14723              element == EL_BALLOON_SWITCH_UP    ||
14724              element == EL_BALLOON_SWITCH_DOWN  ||
14725              element == EL_BALLOON_SWITCH_NONE  ||
14726              element == EL_BALLOON_SWITCH_ANY)
14727     {
14728       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14729                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14730                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14731                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14732                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14733                              move_direction);
14734     }
14735     else if (element == EL_LAMP)
14736     {
14737       Tile[x][y] = EL_LAMP_ACTIVE;
14738       game.lights_still_needed--;
14739
14740       ResetGfxAnimation(x, y);
14741       TEST_DrawLevelField(x, y);
14742     }
14743     else if (element == EL_TIME_ORB_FULL)
14744     {
14745       Tile[x][y] = EL_TIME_ORB_EMPTY;
14746
14747       if (level.time > 0 || level.use_time_orb_bug)
14748       {
14749         TimeLeft += level.time_orb_time;
14750         game.no_level_time_limit = FALSE;
14751
14752         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14753
14754         DisplayGameControlValues();
14755       }
14756
14757       ResetGfxAnimation(x, y);
14758       TEST_DrawLevelField(x, y);
14759     }
14760     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14761              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14762     {
14763       int xx, yy;
14764
14765       game.ball_active = !game.ball_active;
14766
14767       SCAN_PLAYFIELD(xx, yy)
14768       {
14769         int e = Tile[xx][yy];
14770
14771         if (game.ball_active)
14772         {
14773           if (e == EL_EMC_MAGIC_BALL)
14774             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14775           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14776             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14777         }
14778         else
14779         {
14780           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14781             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14782           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14783             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14784         }
14785       }
14786     }
14787
14788     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14789                                         player->index_bit, dig_side);
14790
14791     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14792                                         player->index_bit, dig_side);
14793
14794     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14795                                         player->index_bit, dig_side);
14796
14797     return MP_ACTION;
14798   }
14799   else
14800   {
14801     if (!PLAYER_SWITCHING(player, x, y))
14802     {
14803       player->is_switching = TRUE;
14804       player->switch_x = x;
14805       player->switch_y = y;
14806
14807       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14808                                  player->index_bit, dig_side);
14809       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14810                                           player->index_bit, dig_side);
14811
14812       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14813                                  player->index_bit, dig_side);
14814       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14815                                           player->index_bit, dig_side);
14816     }
14817
14818     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14819                                player->index_bit, dig_side);
14820     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14821                                         player->index_bit, dig_side);
14822
14823     return MP_NO_ACTION;
14824   }
14825
14826   player->push_delay = -1;
14827
14828   if (is_player)                // function can also be called by EL_PENGUIN
14829   {
14830     if (Tile[x][y] != element)          // really digged/collected something
14831     {
14832       player->is_collecting = !player->is_digging;
14833       player->is_active = TRUE;
14834
14835       player->last_removed_element = element;
14836     }
14837   }
14838
14839   return MP_MOVING;
14840 }
14841
14842 static boolean DigFieldByCE(int x, int y, int digging_element)
14843 {
14844   int element = Tile[x][y];
14845
14846   if (!IS_FREE(x, y))
14847   {
14848     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14849                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14850                   ACTION_BREAKING);
14851
14852     // no element can dig solid indestructible elements
14853     if (IS_INDESTRUCTIBLE(element) &&
14854         !IS_DIGGABLE(element) &&
14855         !IS_COLLECTIBLE(element))
14856       return FALSE;
14857
14858     if (AmoebaNr[x][y] &&
14859         (element == EL_AMOEBA_FULL ||
14860          element == EL_BD_AMOEBA ||
14861          element == EL_AMOEBA_GROWING))
14862     {
14863       AmoebaCnt[AmoebaNr[x][y]]--;
14864       AmoebaCnt2[AmoebaNr[x][y]]--;
14865     }
14866
14867     if (IS_MOVING(x, y))
14868       RemoveMovingField(x, y);
14869     else
14870     {
14871       RemoveField(x, y);
14872       TEST_DrawLevelField(x, y);
14873     }
14874
14875     // if digged element was about to explode, prevent the explosion
14876     ExplodeField[x][y] = EX_TYPE_NONE;
14877
14878     PlayLevelSoundAction(x, y, action);
14879   }
14880
14881   Store[x][y] = EL_EMPTY;
14882
14883   // this makes it possible to leave the removed element again
14884   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14885     Store[x][y] = element;
14886
14887   return TRUE;
14888 }
14889
14890 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14891 {
14892   int jx = player->jx, jy = player->jy;
14893   int x = jx + dx, y = jy + dy;
14894   int snap_direction = (dx == -1 ? MV_LEFT  :
14895                         dx == +1 ? MV_RIGHT :
14896                         dy == -1 ? MV_UP    :
14897                         dy == +1 ? MV_DOWN  : MV_NONE);
14898   boolean can_continue_snapping = (level.continuous_snapping &&
14899                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14900
14901   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14902     return FALSE;
14903
14904   if (!player->active || !IN_LEV_FIELD(x, y))
14905     return FALSE;
14906
14907   if (dx && dy)
14908     return FALSE;
14909
14910   if (!dx && !dy)
14911   {
14912     if (player->MovPos == 0)
14913       player->is_pushing = FALSE;
14914
14915     player->is_snapping = FALSE;
14916
14917     if (player->MovPos == 0)
14918     {
14919       player->is_moving = FALSE;
14920       player->is_digging = FALSE;
14921       player->is_collecting = FALSE;
14922     }
14923
14924     return FALSE;
14925   }
14926
14927   // prevent snapping with already pressed snap key when not allowed
14928   if (player->is_snapping && !can_continue_snapping)
14929     return FALSE;
14930
14931   player->MovDir = snap_direction;
14932
14933   if (player->MovPos == 0)
14934   {
14935     player->is_moving = FALSE;
14936     player->is_digging = FALSE;
14937     player->is_collecting = FALSE;
14938   }
14939
14940   player->is_dropping = FALSE;
14941   player->is_dropping_pressed = FALSE;
14942   player->drop_pressed_delay = 0;
14943
14944   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14945     return FALSE;
14946
14947   player->is_snapping = TRUE;
14948   player->is_active = TRUE;
14949
14950   if (player->MovPos == 0)
14951   {
14952     player->is_moving = FALSE;
14953     player->is_digging = FALSE;
14954     player->is_collecting = FALSE;
14955   }
14956
14957   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14958     TEST_DrawLevelField(player->last_jx, player->last_jy);
14959
14960   TEST_DrawLevelField(x, y);
14961
14962   return TRUE;
14963 }
14964
14965 static boolean DropElement(struct PlayerInfo *player)
14966 {
14967   int old_element, new_element;
14968   int dropx = player->jx, dropy = player->jy;
14969   int drop_direction = player->MovDir;
14970   int drop_side = drop_direction;
14971   int drop_element = get_next_dropped_element(player);
14972
14973   /* do not drop an element on top of another element; when holding drop key
14974      pressed without moving, dropped element must move away before the next
14975      element can be dropped (this is especially important if the next element
14976      is dynamite, which can be placed on background for historical reasons) */
14977   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14978     return MP_ACTION;
14979
14980   if (IS_THROWABLE(drop_element))
14981   {
14982     dropx += GET_DX_FROM_DIR(drop_direction);
14983     dropy += GET_DY_FROM_DIR(drop_direction);
14984
14985     if (!IN_LEV_FIELD(dropx, dropy))
14986       return FALSE;
14987   }
14988
14989   old_element = Tile[dropx][dropy];     // old element at dropping position
14990   new_element = drop_element;           // default: no change when dropping
14991
14992   // check if player is active, not moving and ready to drop
14993   if (!player->active || player->MovPos || player->drop_delay > 0)
14994     return FALSE;
14995
14996   // check if player has anything that can be dropped
14997   if (new_element == EL_UNDEFINED)
14998     return FALSE;
14999
15000   // only set if player has anything that can be dropped
15001   player->is_dropping_pressed = TRUE;
15002
15003   // check if drop key was pressed long enough for EM style dynamite
15004   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
15005     return FALSE;
15006
15007   // check if anything can be dropped at the current position
15008   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
15009     return FALSE;
15010
15011   // collected custom elements can only be dropped on empty fields
15012   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
15013     return FALSE;
15014
15015   if (old_element != EL_EMPTY)
15016     Back[dropx][dropy] = old_element;   // store old element on this field
15017
15018   ResetGfxAnimation(dropx, dropy);
15019   ResetRandomAnimationValue(dropx, dropy);
15020
15021   if (player->inventory_size > 0 ||
15022       player->inventory_infinite_element != EL_UNDEFINED)
15023   {
15024     if (player->inventory_size > 0)
15025     {
15026       player->inventory_size--;
15027
15028       DrawGameDoorValues();
15029
15030       if (new_element == EL_DYNAMITE)
15031         new_element = EL_DYNAMITE_ACTIVE;
15032       else if (new_element == EL_EM_DYNAMITE)
15033         new_element = EL_EM_DYNAMITE_ACTIVE;
15034       else if (new_element == EL_SP_DISK_RED)
15035         new_element = EL_SP_DISK_RED_ACTIVE;
15036     }
15037
15038     Tile[dropx][dropy] = new_element;
15039
15040     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15041       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15042                           el2img(Tile[dropx][dropy]), 0);
15043
15044     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15045
15046     // needed if previous element just changed to "empty" in the last frame
15047     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15048
15049     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
15050                                player->index_bit, drop_side);
15051     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
15052                                         CE_PLAYER_DROPS_X,
15053                                         player->index_bit, drop_side);
15054
15055     TestIfElementTouchesCustomElement(dropx, dropy);
15056   }
15057   else          // player is dropping a dyna bomb
15058   {
15059     player->dynabombs_left--;
15060
15061     Tile[dropx][dropy] = new_element;
15062
15063     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
15064       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
15065                           el2img(Tile[dropx][dropy]), 0);
15066
15067     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
15068   }
15069
15070   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
15071     InitField_WithBug1(dropx, dropy, FALSE);
15072
15073   new_element = Tile[dropx][dropy];     // element might have changed
15074
15075   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
15076       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
15077   {
15078     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
15079       MovDir[dropx][dropy] = drop_direction;
15080
15081     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
15082
15083     // do not cause impact style collision by dropping elements that can fall
15084     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
15085   }
15086
15087   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
15088   player->is_dropping = TRUE;
15089
15090   player->drop_pressed_delay = 0;
15091   player->is_dropping_pressed = FALSE;
15092
15093   player->drop_x = dropx;
15094   player->drop_y = dropy;
15095
15096   return TRUE;
15097 }
15098
15099 // ----------------------------------------------------------------------------
15100 // game sound playing functions
15101 // ----------------------------------------------------------------------------
15102
15103 static int *loop_sound_frame = NULL;
15104 static int *loop_sound_volume = NULL;
15105
15106 void InitPlayLevelSound(void)
15107 {
15108   int num_sounds = getSoundListSize();
15109
15110   checked_free(loop_sound_frame);
15111   checked_free(loop_sound_volume);
15112
15113   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
15114   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
15115 }
15116
15117 static void PlayLevelSound(int x, int y, int nr)
15118 {
15119   int sx = SCREENX(x), sy = SCREENY(y);
15120   int volume, stereo_position;
15121   int max_distance = 8;
15122   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
15123
15124   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
15125       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
15126     return;
15127
15128   if (!IN_LEV_FIELD(x, y) ||
15129       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
15130       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
15131     return;
15132
15133   volume = SOUND_MAX_VOLUME;
15134
15135   if (!IN_SCR_FIELD(sx, sy))
15136   {
15137     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
15138     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
15139
15140     volume -= volume * (dx > dy ? dx : dy) / max_distance;
15141   }
15142
15143   stereo_position = (SOUND_MAX_LEFT +
15144                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
15145                      (SCR_FIELDX + 2 * max_distance));
15146
15147   if (IS_LOOP_SOUND(nr))
15148   {
15149     /* This assures that quieter loop sounds do not overwrite louder ones,
15150        while restarting sound volume comparison with each new game frame. */
15151
15152     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15153       return;
15154
15155     loop_sound_volume[nr] = volume;
15156     loop_sound_frame[nr] = FrameCounter;
15157   }
15158
15159   PlaySoundExt(nr, volume, stereo_position, type);
15160 }
15161
15162 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15163 {
15164   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15165                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15166                  y < LEVELY(BY1) ? LEVELY(BY1) :
15167                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15168                  sound_action);
15169 }
15170
15171 static void PlayLevelSoundAction(int x, int y, int action)
15172 {
15173   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15174 }
15175
15176 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15177 {
15178   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15179
15180   if (sound_effect != SND_UNDEFINED)
15181     PlayLevelSound(x, y, sound_effect);
15182 }
15183
15184 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15185                                               int action)
15186 {
15187   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15188
15189   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15190     PlayLevelSound(x, y, sound_effect);
15191 }
15192
15193 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15194 {
15195   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15196
15197   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15198     PlayLevelSound(x, y, sound_effect);
15199 }
15200
15201 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15202 {
15203   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15204
15205   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15206     StopSound(sound_effect);
15207 }
15208
15209 static int getLevelMusicNr(void)
15210 {
15211   if (levelset.music[level_nr] != MUS_UNDEFINED)
15212     return levelset.music[level_nr];            // from config file
15213   else
15214     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15215 }
15216
15217 static void FadeLevelSounds(void)
15218 {
15219   FadeSounds();
15220 }
15221
15222 static void FadeLevelMusic(void)
15223 {
15224   int music_nr = getLevelMusicNr();
15225   char *curr_music = getCurrentlyPlayingMusicFilename();
15226   char *next_music = getMusicInfoEntryFilename(music_nr);
15227
15228   if (!strEqual(curr_music, next_music))
15229     FadeMusic();
15230 }
15231
15232 void FadeLevelSoundsAndMusic(void)
15233 {
15234   FadeLevelSounds();
15235   FadeLevelMusic();
15236 }
15237
15238 static void PlayLevelMusic(void)
15239 {
15240   int music_nr = getLevelMusicNr();
15241   char *curr_music = getCurrentlyPlayingMusicFilename();
15242   char *next_music = getMusicInfoEntryFilename(music_nr);
15243
15244   if (!strEqual(curr_music, next_music))
15245     PlayMusicLoop(music_nr);
15246 }
15247
15248 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15249 {
15250   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15251   int offset = 0;
15252   int x = xx - offset;
15253   int y = yy - offset;
15254
15255   switch (sample)
15256   {
15257     case SOUND_blank:
15258       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15259       break;
15260
15261     case SOUND_roll:
15262       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15263       break;
15264
15265     case SOUND_stone:
15266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15267       break;
15268
15269     case SOUND_nut:
15270       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15271       break;
15272
15273     case SOUND_crack:
15274       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15275       break;
15276
15277     case SOUND_bug:
15278       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15279       break;
15280
15281     case SOUND_tank:
15282       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15283       break;
15284
15285     case SOUND_android_clone:
15286       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15287       break;
15288
15289     case SOUND_android_move:
15290       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15291       break;
15292
15293     case SOUND_spring:
15294       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15295       break;
15296
15297     case SOUND_slurp:
15298       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15299       break;
15300
15301     case SOUND_eater:
15302       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15303       break;
15304
15305     case SOUND_eater_eat:
15306       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15307       break;
15308
15309     case SOUND_alien:
15310       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15311       break;
15312
15313     case SOUND_collect:
15314       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15315       break;
15316
15317     case SOUND_diamond:
15318       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15319       break;
15320
15321     case SOUND_squash:
15322       // !!! CHECK THIS !!!
15323 #if 1
15324       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15325 #else
15326       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15327 #endif
15328       break;
15329
15330     case SOUND_wonderfall:
15331       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15332       break;
15333
15334     case SOUND_drip:
15335       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15336       break;
15337
15338     case SOUND_push:
15339       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15340       break;
15341
15342     case SOUND_dirt:
15343       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15344       break;
15345
15346     case SOUND_acid:
15347       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15348       break;
15349
15350     case SOUND_ball:
15351       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15352       break;
15353
15354     case SOUND_slide:
15355       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15356       break;
15357
15358     case SOUND_wonder:
15359       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15360       break;
15361
15362     case SOUND_door:
15363       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15364       break;
15365
15366     case SOUND_exit_open:
15367       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15368       break;
15369
15370     case SOUND_exit_leave:
15371       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15372       break;
15373
15374     case SOUND_dynamite:
15375       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15376       break;
15377
15378     case SOUND_tick:
15379       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15380       break;
15381
15382     case SOUND_press:
15383       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15384       break;
15385
15386     case SOUND_wheel:
15387       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15388       break;
15389
15390     case SOUND_boom:
15391       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15392       break;
15393
15394     case SOUND_die:
15395       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15396       break;
15397
15398     case SOUND_time:
15399       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15400       break;
15401
15402     default:
15403       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15404       break;
15405   }
15406 }
15407
15408 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15409 {
15410   int element = map_element_SP_to_RND(element_sp);
15411   int action = map_action_SP_to_RND(action_sp);
15412   int offset = (setup.sp_show_border_elements ? 0 : 1);
15413   int x = xx - offset;
15414   int y = yy - offset;
15415
15416   PlayLevelSoundElementAction(x, y, element, action);
15417 }
15418
15419 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15420 {
15421   int element = map_element_MM_to_RND(element_mm);
15422   int action = map_action_MM_to_RND(action_mm);
15423   int offset = 0;
15424   int x = xx - offset;
15425   int y = yy - offset;
15426
15427   if (!IS_MM_ELEMENT(element))
15428     element = EL_MM_DEFAULT;
15429
15430   PlayLevelSoundElementAction(x, y, element, action);
15431 }
15432
15433 void PlaySound_MM(int sound_mm)
15434 {
15435   int sound = map_sound_MM_to_RND(sound_mm);
15436
15437   if (sound == SND_UNDEFINED)
15438     return;
15439
15440   PlaySound(sound);
15441 }
15442
15443 void PlaySoundLoop_MM(int sound_mm)
15444 {
15445   int sound = map_sound_MM_to_RND(sound_mm);
15446
15447   if (sound == SND_UNDEFINED)
15448     return;
15449
15450   PlaySoundLoop(sound);
15451 }
15452
15453 void StopSound_MM(int sound_mm)
15454 {
15455   int sound = map_sound_MM_to_RND(sound_mm);
15456
15457   if (sound == SND_UNDEFINED)
15458     return;
15459
15460   StopSound(sound);
15461 }
15462
15463 void RaiseScore(int value)
15464 {
15465   game.score += value;
15466
15467   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15468
15469   DisplayGameControlValues();
15470 }
15471
15472 void RaiseScoreElement(int element)
15473 {
15474   switch (element)
15475   {
15476     case EL_EMERALD:
15477     case EL_BD_DIAMOND:
15478     case EL_EMERALD_YELLOW:
15479     case EL_EMERALD_RED:
15480     case EL_EMERALD_PURPLE:
15481     case EL_SP_INFOTRON:
15482       RaiseScore(level.score[SC_EMERALD]);
15483       break;
15484     case EL_DIAMOND:
15485       RaiseScore(level.score[SC_DIAMOND]);
15486       break;
15487     case EL_CRYSTAL:
15488       RaiseScore(level.score[SC_CRYSTAL]);
15489       break;
15490     case EL_PEARL:
15491       RaiseScore(level.score[SC_PEARL]);
15492       break;
15493     case EL_BUG:
15494     case EL_BD_BUTTERFLY:
15495     case EL_SP_ELECTRON:
15496       RaiseScore(level.score[SC_BUG]);
15497       break;
15498     case EL_SPACESHIP:
15499     case EL_BD_FIREFLY:
15500     case EL_SP_SNIKSNAK:
15501       RaiseScore(level.score[SC_SPACESHIP]);
15502       break;
15503     case EL_YAMYAM:
15504     case EL_DARK_YAMYAM:
15505       RaiseScore(level.score[SC_YAMYAM]);
15506       break;
15507     case EL_ROBOT:
15508       RaiseScore(level.score[SC_ROBOT]);
15509       break;
15510     case EL_PACMAN:
15511       RaiseScore(level.score[SC_PACMAN]);
15512       break;
15513     case EL_NUT:
15514       RaiseScore(level.score[SC_NUT]);
15515       break;
15516     case EL_DYNAMITE:
15517     case EL_EM_DYNAMITE:
15518     case EL_SP_DISK_RED:
15519     case EL_DYNABOMB_INCREASE_NUMBER:
15520     case EL_DYNABOMB_INCREASE_SIZE:
15521     case EL_DYNABOMB_INCREASE_POWER:
15522       RaiseScore(level.score[SC_DYNAMITE]);
15523       break;
15524     case EL_SHIELD_NORMAL:
15525     case EL_SHIELD_DEADLY:
15526       RaiseScore(level.score[SC_SHIELD]);
15527       break;
15528     case EL_EXTRA_TIME:
15529       RaiseScore(level.extra_time_score);
15530       break;
15531     case EL_KEY_1:
15532     case EL_KEY_2:
15533     case EL_KEY_3:
15534     case EL_KEY_4:
15535     case EL_EM_KEY_1:
15536     case EL_EM_KEY_2:
15537     case EL_EM_KEY_3:
15538     case EL_EM_KEY_4:
15539     case EL_EMC_KEY_5:
15540     case EL_EMC_KEY_6:
15541     case EL_EMC_KEY_7:
15542     case EL_EMC_KEY_8:
15543     case EL_DC_KEY_WHITE:
15544       RaiseScore(level.score[SC_KEY]);
15545       break;
15546     default:
15547       RaiseScore(element_info[element].collect_score);
15548       break;
15549   }
15550 }
15551
15552 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15553 {
15554   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15555   {
15556     if (!quick_quit)
15557     {
15558       // prevent short reactivation of overlay buttons while closing door
15559       SetOverlayActive(FALSE);
15560       UnmapGameButtons();
15561
15562       // door may still be open due to skipped or envelope style request
15563       CloseDoor(score_info_tape_play ? DOOR_CLOSE_ALL : DOOR_CLOSE_1);
15564     }
15565
15566     if (network.enabled)
15567       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15568     else
15569     {
15570       if (quick_quit)
15571         FadeSkipNextFadeIn();
15572
15573       SetGameStatus(GAME_MODE_MAIN);
15574
15575       DrawMainMenu();
15576     }
15577   }
15578   else          // continue playing the game
15579   {
15580     if (tape.playing && tape.deactivate_display)
15581       TapeDeactivateDisplayOff(TRUE);
15582
15583     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15584
15585     if (tape.playing && tape.deactivate_display)
15586       TapeDeactivateDisplayOn();
15587   }
15588 }
15589
15590 void RequestQuitGame(boolean escape_key_pressed)
15591 {
15592   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15593   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15594                         level_editor_test_game);
15595   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15596                           quick_quit || score_info_tape_play);
15597
15598   RequestQuitGameExt(skip_request, quick_quit,
15599                      "Do you really want to quit the game?");
15600 }
15601
15602 void RequestRestartGame(char *message)
15603 {
15604   game.restart_game_message = NULL;
15605
15606   boolean has_started_game = hasStartedNetworkGame();
15607   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15608
15609   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15610   {
15611     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15612   }
15613   else
15614   {
15615     // needed in case of envelope request to close game panel
15616     CloseDoor(DOOR_CLOSE_1);
15617
15618     SetGameStatus(GAME_MODE_MAIN);
15619
15620     DrawMainMenu();
15621   }
15622 }
15623
15624 void CheckGameOver(void)
15625 {
15626   static boolean last_game_over = FALSE;
15627   static int game_over_delay = 0;
15628   int game_over_delay_value = 50;
15629   boolean game_over = checkGameFailed();
15630
15631   // do not handle game over if request dialog is already active
15632   if (game.request_active)
15633     return;
15634
15635   // do not ask to play again if game was never actually played
15636   if (!game.GamePlayed)
15637     return;
15638
15639   if (!game_over)
15640   {
15641     last_game_over = FALSE;
15642     game_over_delay = game_over_delay_value;
15643
15644     return;
15645   }
15646
15647   if (game_over_delay > 0)
15648   {
15649     game_over_delay--;
15650
15651     return;
15652   }
15653
15654   if (last_game_over != game_over)
15655     game.restart_game_message = (hasStartedNetworkGame() ?
15656                                  "Game over! Play it again?" :
15657                                  "Game over!");
15658
15659   last_game_over = game_over;
15660 }
15661
15662 boolean checkGameSolved(void)
15663 {
15664   // set for all game engines if level was solved
15665   return game.LevelSolved_GameEnd;
15666 }
15667
15668 boolean checkGameFailed(void)
15669 {
15670   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15671     return (game_em.game_over && !game_em.level_solved);
15672   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15673     return (game_sp.game_over && !game_sp.level_solved);
15674   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15675     return (game_mm.game_over && !game_mm.level_solved);
15676   else                          // GAME_ENGINE_TYPE_RND
15677     return (game.GameOver && !game.LevelSolved);
15678 }
15679
15680 boolean checkGameEnded(void)
15681 {
15682   return (checkGameSolved() || checkGameFailed());
15683 }
15684
15685
15686 // ----------------------------------------------------------------------------
15687 // random generator functions
15688 // ----------------------------------------------------------------------------
15689
15690 unsigned int InitEngineRandom_RND(int seed)
15691 {
15692   game.num_random_calls = 0;
15693
15694   return InitEngineRandom(seed);
15695 }
15696
15697 unsigned int RND(int max)
15698 {
15699   if (max > 0)
15700   {
15701     game.num_random_calls++;
15702
15703     return GetEngineRandom(max);
15704   }
15705
15706   return 0;
15707 }
15708
15709
15710 // ----------------------------------------------------------------------------
15711 // game engine snapshot handling functions
15712 // ----------------------------------------------------------------------------
15713
15714 struct EngineSnapshotInfo
15715 {
15716   // runtime values for custom element collect score
15717   int collect_score[NUM_CUSTOM_ELEMENTS];
15718
15719   // runtime values for group element choice position
15720   int choice_pos[NUM_GROUP_ELEMENTS];
15721
15722   // runtime values for belt position animations
15723   int belt_graphic[4][NUM_BELT_PARTS];
15724   int belt_anim_mode[4][NUM_BELT_PARTS];
15725 };
15726
15727 static struct EngineSnapshotInfo engine_snapshot_rnd;
15728 static char *snapshot_level_identifier = NULL;
15729 static int snapshot_level_nr = -1;
15730
15731 static void SaveEngineSnapshotValues_RND(void)
15732 {
15733   static int belt_base_active_element[4] =
15734   {
15735     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15736     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15737     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15738     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15739   };
15740   int i, j;
15741
15742   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15743   {
15744     int element = EL_CUSTOM_START + i;
15745
15746     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15747   }
15748
15749   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15750   {
15751     int element = EL_GROUP_START + i;
15752
15753     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15754   }
15755
15756   for (i = 0; i < 4; i++)
15757   {
15758     for (j = 0; j < NUM_BELT_PARTS; j++)
15759     {
15760       int element = belt_base_active_element[i] + j;
15761       int graphic = el2img(element);
15762       int anim_mode = graphic_info[graphic].anim_mode;
15763
15764       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15765       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15766     }
15767   }
15768 }
15769
15770 static void LoadEngineSnapshotValues_RND(void)
15771 {
15772   unsigned int num_random_calls = game.num_random_calls;
15773   int i, j;
15774
15775   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15776   {
15777     int element = EL_CUSTOM_START + i;
15778
15779     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15780   }
15781
15782   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15783   {
15784     int element = EL_GROUP_START + i;
15785
15786     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15787   }
15788
15789   for (i = 0; i < 4; i++)
15790   {
15791     for (j = 0; j < NUM_BELT_PARTS; j++)
15792     {
15793       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15794       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15795
15796       graphic_info[graphic].anim_mode = anim_mode;
15797     }
15798   }
15799
15800   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15801   {
15802     InitRND(tape.random_seed);
15803     for (i = 0; i < num_random_calls; i++)
15804       RND(1);
15805   }
15806
15807   if (game.num_random_calls != num_random_calls)
15808   {
15809     Error("number of random calls out of sync");
15810     Error("number of random calls should be %d", num_random_calls);
15811     Error("number of random calls is %d", game.num_random_calls);
15812
15813     Fail("this should not happen -- please debug");
15814   }
15815 }
15816
15817 void FreeEngineSnapshotSingle(void)
15818 {
15819   FreeSnapshotSingle();
15820
15821   setString(&snapshot_level_identifier, NULL);
15822   snapshot_level_nr = -1;
15823 }
15824
15825 void FreeEngineSnapshotList(void)
15826 {
15827   FreeSnapshotList();
15828 }
15829
15830 static ListNode *SaveEngineSnapshotBuffers(void)
15831 {
15832   ListNode *buffers = NULL;
15833
15834   // copy some special values to a structure better suited for the snapshot
15835
15836   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15837     SaveEngineSnapshotValues_RND();
15838   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15839     SaveEngineSnapshotValues_EM();
15840   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15841     SaveEngineSnapshotValues_SP(&buffers);
15842   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15843     SaveEngineSnapshotValues_MM(&buffers);
15844
15845   // save values stored in special snapshot structure
15846
15847   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15848     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15849   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15850     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15851   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15852     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15853   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15854     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15855
15856   // save further RND engine values
15857
15858   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15859   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15860   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15861
15862   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15863   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15864   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15865   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15866   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15867
15868   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15869   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15870   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15871
15872   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15873
15874   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15875   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15876
15877   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15878   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15879   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15880   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15881   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15882   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15883   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15884   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15885   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15886   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15887   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15888   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15889   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15890   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15891   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15892   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15893   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15894   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15895
15896   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15897   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15898
15899   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15900   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15901   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15902
15903   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15904   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15905
15906   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15907   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15908   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandomStatic));
15909   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15910   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15911   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15912
15913   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15914   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15915
15916 #if 0
15917   ListNode *node = engine_snapshot_list_rnd;
15918   int num_bytes = 0;
15919
15920   while (node != NULL)
15921   {
15922     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15923
15924     node = node->next;
15925   }
15926
15927   Debug("game:playing:SaveEngineSnapshotBuffers",
15928         "size of engine snapshot: %d bytes", num_bytes);
15929 #endif
15930
15931   return buffers;
15932 }
15933
15934 void SaveEngineSnapshotSingle(void)
15935 {
15936   ListNode *buffers = SaveEngineSnapshotBuffers();
15937
15938   // finally save all snapshot buffers to single snapshot
15939   SaveSnapshotSingle(buffers);
15940
15941   // save level identification information
15942   setString(&snapshot_level_identifier, leveldir_current->identifier);
15943   snapshot_level_nr = level_nr;
15944 }
15945
15946 boolean CheckSaveEngineSnapshotToList(void)
15947 {
15948   boolean save_snapshot =
15949     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15950      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15951       game.snapshot.changed_action) ||
15952      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15953       game.snapshot.collected_item));
15954
15955   game.snapshot.changed_action = FALSE;
15956   game.snapshot.collected_item = FALSE;
15957   game.snapshot.save_snapshot = save_snapshot;
15958
15959   return save_snapshot;
15960 }
15961
15962 void SaveEngineSnapshotToList(void)
15963 {
15964   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15965       tape.quick_resume)
15966     return;
15967
15968   ListNode *buffers = SaveEngineSnapshotBuffers();
15969
15970   // finally save all snapshot buffers to snapshot list
15971   SaveSnapshotToList(buffers);
15972 }
15973
15974 void SaveEngineSnapshotToListInitial(void)
15975 {
15976   FreeEngineSnapshotList();
15977
15978   SaveEngineSnapshotToList();
15979 }
15980
15981 static void LoadEngineSnapshotValues(void)
15982 {
15983   // restore special values from snapshot structure
15984
15985   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15986     LoadEngineSnapshotValues_RND();
15987   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15988     LoadEngineSnapshotValues_EM();
15989   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15990     LoadEngineSnapshotValues_SP();
15991   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15992     LoadEngineSnapshotValues_MM();
15993 }
15994
15995 void LoadEngineSnapshotSingle(void)
15996 {
15997   LoadSnapshotSingle();
15998
15999   LoadEngineSnapshotValues();
16000 }
16001
16002 static void LoadEngineSnapshot_Undo(int steps)
16003 {
16004   LoadSnapshotFromList_Older(steps);
16005
16006   LoadEngineSnapshotValues();
16007 }
16008
16009 static void LoadEngineSnapshot_Redo(int steps)
16010 {
16011   LoadSnapshotFromList_Newer(steps);
16012
16013   LoadEngineSnapshotValues();
16014 }
16015
16016 boolean CheckEngineSnapshotSingle(void)
16017 {
16018   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
16019           snapshot_level_nr == level_nr);
16020 }
16021
16022 boolean CheckEngineSnapshotList(void)
16023 {
16024   return CheckSnapshotList();
16025 }
16026
16027
16028 // ---------- new game button stuff -------------------------------------------
16029
16030 static struct
16031 {
16032   int graphic;
16033   struct XY *pos;
16034   int gadget_id;
16035   boolean *setup_value;
16036   boolean allowed_on_tape;
16037   boolean is_touch_button;
16038   char *infotext;
16039 } gamebutton_info[NUM_GAME_BUTTONS] =
16040 {
16041   {
16042     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
16043     GAME_CTRL_ID_STOP,                          NULL,
16044     TRUE, FALSE,                                "stop game"
16045   },
16046   {
16047     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
16048     GAME_CTRL_ID_PAUSE,                         NULL,
16049     TRUE, FALSE,                                "pause game"
16050   },
16051   {
16052     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
16053     GAME_CTRL_ID_PLAY,                          NULL,
16054     TRUE, FALSE,                                "play game"
16055   },
16056   {
16057     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
16058     GAME_CTRL_ID_UNDO,                          NULL,
16059     TRUE, FALSE,                                "undo step"
16060   },
16061   {
16062     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
16063     GAME_CTRL_ID_REDO,                          NULL,
16064     TRUE, FALSE,                                "redo step"
16065   },
16066   {
16067     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
16068     GAME_CTRL_ID_SAVE,                          NULL,
16069     TRUE, FALSE,                                "save game"
16070   },
16071   {
16072     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
16073     GAME_CTRL_ID_PAUSE2,                        NULL,
16074     TRUE, FALSE,                                "pause game"
16075   },
16076   {
16077     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
16078     GAME_CTRL_ID_LOAD,                          NULL,
16079     TRUE, FALSE,                                "load game"
16080   },
16081   {
16082     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
16083     GAME_CTRL_ID_PANEL_STOP,                    NULL,
16084     FALSE, FALSE,                               "stop game"
16085   },
16086   {
16087     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
16088     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
16089     FALSE, FALSE,                               "pause game"
16090   },
16091   {
16092     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
16093     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
16094     FALSE, FALSE,                               "play game"
16095   },
16096   {
16097     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
16098     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
16099     FALSE, TRUE,                                "stop game"
16100   },
16101   {
16102     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
16103     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
16104     FALSE, TRUE,                                "pause game"
16105   },
16106   {
16107     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
16108     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
16109     TRUE, FALSE,                                "background music on/off"
16110   },
16111   {
16112     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
16113     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
16114     TRUE, FALSE,                                "sound loops on/off"
16115   },
16116   {
16117     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
16118     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
16119     TRUE, FALSE,                                "normal sounds on/off"
16120   },
16121   {
16122     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
16123     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
16124     FALSE, FALSE,                               "background music on/off"
16125   },
16126   {
16127     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
16128     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
16129     FALSE, FALSE,                               "sound loops on/off"
16130   },
16131   {
16132     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
16133     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
16134     FALSE, FALSE,                               "normal sounds on/off"
16135   }
16136 };
16137
16138 void CreateGameButtons(void)
16139 {
16140   int i;
16141
16142   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16143   {
16144     int graphic = gamebutton_info[i].graphic;
16145     struct GraphicInfo *gfx = &graphic_info[graphic];
16146     struct XY *pos = gamebutton_info[i].pos;
16147     struct GadgetInfo *gi;
16148     int button_type;
16149     boolean checked;
16150     unsigned int event_mask;
16151     boolean is_touch_button = gamebutton_info[i].is_touch_button;
16152     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
16153     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
16154     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16155     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16156     int gd_x   = gfx->src_x;
16157     int gd_y   = gfx->src_y;
16158     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16159     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16160     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16161     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16162     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16163     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16164     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16165     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16166     int id = i;
16167
16168     // do not use touch buttons if overlay touch buttons are disabled
16169     if (is_touch_button && !setup.touch.overlay_buttons)
16170       continue;
16171
16172     if (gfx->bitmap == NULL)
16173     {
16174       game_gadget[id] = NULL;
16175
16176       continue;
16177     }
16178
16179     if (id == GAME_CTRL_ID_STOP ||
16180         id == GAME_CTRL_ID_PANEL_STOP ||
16181         id == GAME_CTRL_ID_TOUCH_STOP ||
16182         id == GAME_CTRL_ID_PLAY ||
16183         id == GAME_CTRL_ID_PANEL_PLAY ||
16184         id == GAME_CTRL_ID_SAVE ||
16185         id == GAME_CTRL_ID_LOAD)
16186     {
16187       button_type = GD_TYPE_NORMAL_BUTTON;
16188       checked = FALSE;
16189       event_mask = GD_EVENT_RELEASED;
16190     }
16191     else if (id == GAME_CTRL_ID_UNDO ||
16192              id == GAME_CTRL_ID_REDO)
16193     {
16194       button_type = GD_TYPE_NORMAL_BUTTON;
16195       checked = FALSE;
16196       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16197     }
16198     else
16199     {
16200       button_type = GD_TYPE_CHECK_BUTTON;
16201       checked = (gamebutton_info[i].setup_value != NULL ?
16202                  *gamebutton_info[i].setup_value : FALSE);
16203       event_mask = GD_EVENT_PRESSED;
16204     }
16205
16206     gi = CreateGadget(GDI_CUSTOM_ID, id,
16207                       GDI_IMAGE_ID, graphic,
16208                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16209                       GDI_X, base_x + x,
16210                       GDI_Y, base_y + y,
16211                       GDI_WIDTH, gfx->width,
16212                       GDI_HEIGHT, gfx->height,
16213                       GDI_TYPE, button_type,
16214                       GDI_STATE, GD_BUTTON_UNPRESSED,
16215                       GDI_CHECKED, checked,
16216                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16217                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16218                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16219                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16220                       GDI_DIRECT_DRAW, FALSE,
16221                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16222                       GDI_EVENT_MASK, event_mask,
16223                       GDI_CALLBACK_ACTION, HandleGameButtons,
16224                       GDI_END);
16225
16226     if (gi == NULL)
16227       Fail("cannot create gadget");
16228
16229     game_gadget[id] = gi;
16230   }
16231 }
16232
16233 void FreeGameButtons(void)
16234 {
16235   int i;
16236
16237   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16238     FreeGadget(game_gadget[i]);
16239 }
16240
16241 static void UnmapGameButtonsAtSamePosition(int id)
16242 {
16243   int i;
16244
16245   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16246     if (i != id &&
16247         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16248         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16249       UnmapGadget(game_gadget[i]);
16250 }
16251
16252 static void UnmapGameButtonsAtSamePosition_All(void)
16253 {
16254   if (setup.show_load_save_buttons)
16255   {
16256     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16257     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16258     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16259   }
16260   else if (setup.show_undo_redo_buttons)
16261   {
16262     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16263     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16264     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16265   }
16266   else
16267   {
16268     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16269     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16270     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16271
16272     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16273     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16274     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16275   }
16276 }
16277
16278 void MapLoadSaveButtons(void)
16279 {
16280   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16281   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16282
16283   MapGadget(game_gadget[GAME_CTRL_ID_LOAD]);
16284   MapGadget(game_gadget[GAME_CTRL_ID_SAVE]);
16285 }
16286
16287 void MapUndoRedoButtons(void)
16288 {
16289   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16290   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16291
16292   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16293   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16294 }
16295
16296 void ModifyPauseButtons(void)
16297 {
16298   static int ids[] =
16299   {
16300     GAME_CTRL_ID_PAUSE,
16301     GAME_CTRL_ID_PAUSE2,
16302     GAME_CTRL_ID_PANEL_PAUSE,
16303     GAME_CTRL_ID_TOUCH_PAUSE,
16304     -1
16305   };
16306   int i;
16307
16308   for (i = 0; ids[i] > -1; i++)
16309     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16310 }
16311
16312 static void MapGameButtonsExt(boolean on_tape)
16313 {
16314   int i;
16315
16316   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16317   {
16318     if ((i == GAME_CTRL_ID_UNDO ||
16319          i == GAME_CTRL_ID_REDO) &&
16320         game_status != GAME_MODE_PLAYING)
16321       continue;
16322
16323     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16324       MapGadget(game_gadget[i]);
16325   }
16326
16327   UnmapGameButtonsAtSamePosition_All();
16328
16329   RedrawGameButtons();
16330 }
16331
16332 static void UnmapGameButtonsExt(boolean on_tape)
16333 {
16334   int i;
16335
16336   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16337     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16338       UnmapGadget(game_gadget[i]);
16339 }
16340
16341 static void RedrawGameButtonsExt(boolean on_tape)
16342 {
16343   int i;
16344
16345   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16346     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16347       RedrawGadget(game_gadget[i]);
16348 }
16349
16350 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16351 {
16352   if (gi == NULL)
16353     return;
16354
16355   gi->checked = state;
16356 }
16357
16358 static void RedrawSoundButtonGadget(int id)
16359 {
16360   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16361              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16362              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16363              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16364              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16365              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16366              id);
16367
16368   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16369   RedrawGadget(game_gadget[id2]);
16370 }
16371
16372 void MapGameButtons(void)
16373 {
16374   MapGameButtonsExt(FALSE);
16375 }
16376
16377 void UnmapGameButtons(void)
16378 {
16379   UnmapGameButtonsExt(FALSE);
16380 }
16381
16382 void RedrawGameButtons(void)
16383 {
16384   RedrawGameButtonsExt(FALSE);
16385 }
16386
16387 void MapGameButtonsOnTape(void)
16388 {
16389   MapGameButtonsExt(TRUE);
16390 }
16391
16392 void UnmapGameButtonsOnTape(void)
16393 {
16394   UnmapGameButtonsExt(TRUE);
16395 }
16396
16397 void RedrawGameButtonsOnTape(void)
16398 {
16399   RedrawGameButtonsExt(TRUE);
16400 }
16401
16402 static void GameUndoRedoExt(void)
16403 {
16404   ClearPlayerAction();
16405
16406   tape.pausing = TRUE;
16407
16408   RedrawPlayfield();
16409   UpdateAndDisplayGameControlValues();
16410
16411   DrawCompleteVideoDisplay();
16412   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16413   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16414   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16415
16416   ModifyPauseButtons();
16417
16418   BackToFront();
16419 }
16420
16421 static void GameUndo(int steps)
16422 {
16423   if (!CheckEngineSnapshotList())
16424     return;
16425
16426   int tape_property_bits = tape.property_bits;
16427
16428   LoadEngineSnapshot_Undo(steps);
16429
16430   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16431
16432   GameUndoRedoExt();
16433 }
16434
16435 static void GameRedo(int steps)
16436 {
16437   if (!CheckEngineSnapshotList())
16438     return;
16439
16440   int tape_property_bits = tape.property_bits;
16441
16442   LoadEngineSnapshot_Redo(steps);
16443
16444   tape.property_bits |= tape_property_bits | TAPE_PROPERTY_SNAPSHOT;
16445
16446   GameUndoRedoExt();
16447 }
16448
16449 static void HandleGameButtonsExt(int id, int button)
16450 {
16451   static boolean game_undo_executed = FALSE;
16452   int steps = BUTTON_STEPSIZE(button);
16453   boolean handle_game_buttons =
16454     (game_status == GAME_MODE_PLAYING ||
16455      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16456
16457   if (!handle_game_buttons)
16458     return;
16459
16460   switch (id)
16461   {
16462     case GAME_CTRL_ID_STOP:
16463     case GAME_CTRL_ID_PANEL_STOP:
16464     case GAME_CTRL_ID_TOUCH_STOP:
16465       TapeStopGame();
16466
16467       break;
16468
16469     case GAME_CTRL_ID_PAUSE:
16470     case GAME_CTRL_ID_PAUSE2:
16471     case GAME_CTRL_ID_PANEL_PAUSE:
16472     case GAME_CTRL_ID_TOUCH_PAUSE:
16473       if (network.enabled && game_status == GAME_MODE_PLAYING)
16474       {
16475         if (tape.pausing)
16476           SendToServer_ContinuePlaying();
16477         else
16478           SendToServer_PausePlaying();
16479       }
16480       else
16481         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16482
16483       game_undo_executed = FALSE;
16484
16485       break;
16486
16487     case GAME_CTRL_ID_PLAY:
16488     case GAME_CTRL_ID_PANEL_PLAY:
16489       if (game_status == GAME_MODE_MAIN)
16490       {
16491         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16492       }
16493       else if (tape.pausing)
16494       {
16495         if (network.enabled)
16496           SendToServer_ContinuePlaying();
16497         else
16498           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16499       }
16500       break;
16501
16502     case GAME_CTRL_ID_UNDO:
16503       // Important: When using "save snapshot when collecting an item" mode,
16504       // load last (current) snapshot for first "undo" after pressing "pause"
16505       // (else the last-but-one snapshot would be loaded, because the snapshot
16506       // pointer already points to the last snapshot when pressing "pause",
16507       // which is fine for "every step/move" mode, but not for "every collect")
16508       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16509           !game_undo_executed)
16510         steps--;
16511
16512       game_undo_executed = TRUE;
16513
16514       GameUndo(steps);
16515       break;
16516
16517     case GAME_CTRL_ID_REDO:
16518       GameRedo(steps);
16519       break;
16520
16521     case GAME_CTRL_ID_SAVE:
16522       TapeQuickSave();
16523       break;
16524
16525     case GAME_CTRL_ID_LOAD:
16526       TapeQuickLoad();
16527       break;
16528
16529     case SOUND_CTRL_ID_MUSIC:
16530     case SOUND_CTRL_ID_PANEL_MUSIC:
16531       if (setup.sound_music)
16532       { 
16533         setup.sound_music = FALSE;
16534
16535         FadeMusic();
16536       }
16537       else if (audio.music_available)
16538       { 
16539         setup.sound = setup.sound_music = TRUE;
16540
16541         SetAudioMode(setup.sound);
16542
16543         if (game_status == GAME_MODE_PLAYING)
16544           PlayLevelMusic();
16545       }
16546
16547       RedrawSoundButtonGadget(id);
16548
16549       break;
16550
16551     case SOUND_CTRL_ID_LOOPS:
16552     case SOUND_CTRL_ID_PANEL_LOOPS:
16553       if (setup.sound_loops)
16554         setup.sound_loops = FALSE;
16555       else if (audio.loops_available)
16556       {
16557         setup.sound = setup.sound_loops = TRUE;
16558
16559         SetAudioMode(setup.sound);
16560       }
16561
16562       RedrawSoundButtonGadget(id);
16563
16564       break;
16565
16566     case SOUND_CTRL_ID_SIMPLE:
16567     case SOUND_CTRL_ID_PANEL_SIMPLE:
16568       if (setup.sound_simple)
16569         setup.sound_simple = FALSE;
16570       else if (audio.sound_available)
16571       {
16572         setup.sound = setup.sound_simple = TRUE;
16573
16574         SetAudioMode(setup.sound);
16575       }
16576
16577       RedrawSoundButtonGadget(id);
16578
16579       break;
16580
16581     default:
16582       break;
16583   }
16584 }
16585
16586 static void HandleGameButtons(struct GadgetInfo *gi)
16587 {
16588   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16589 }
16590
16591 void HandleSoundButtonKeys(Key key)
16592 {
16593   if (key == setup.shortcut.sound_simple)
16594     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16595   else if (key == setup.shortcut.sound_loops)
16596     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16597   else if (key == setup.shortcut.sound_music)
16598     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16599 }