added step delay option to custom element movement configuration
[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 TestIfPlayerTouchesCustomElement(int, int);
1062 static void TestIfElementTouchesCustomElement(int, int);
1063 static void TestIfElementHitsCustomElement(int, int, int);
1064
1065 static void HandleElementChange(int, int, int);
1066 static void ExecuteCustomElementAction(int, int, int, int);
1067 static boolean ChangeElement(int, int, int, int);
1068
1069 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1070 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1071         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1072 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1073         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1074 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1075         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1076 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1077         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1078
1079 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1080 #define CheckElementChange(x, y, e, te, ev)                             \
1081         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1082 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1083         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1084 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1085         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1086
1087 static void PlayLevelSound(int, int, int);
1088 static void PlayLevelSoundNearest(int, int, int);
1089 static void PlayLevelSoundAction(int, int, int);
1090 static void PlayLevelSoundElementAction(int, int, int, int);
1091 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1092 static void PlayLevelSoundActionIfLoop(int, int, int);
1093 static void StopLevelSoundActionIfLoop(int, int, int);
1094 static void PlayLevelMusic(void);
1095 static void FadeLevelSoundsAndMusic(void);
1096
1097 static void HandleGameButtons(struct GadgetInfo *);
1098
1099 int AmoebaNeighbourNr(int, int);
1100 void AmoebaToDiamond(int, int);
1101 void ContinueMoving(int, int);
1102 void Bang(int, int);
1103 void InitMovDir(int, int);
1104 void InitAmoebaNr(int, int);
1105 int NewHiScore(int);
1106
1107 void TestIfGoodThingHitsBadThing(int, int, int);
1108 void TestIfBadThingHitsGoodThing(int, int, int);
1109 void TestIfPlayerTouchesBadThing(int, int);
1110 void TestIfPlayerRunsIntoBadThing(int, int, int);
1111 void TestIfBadThingTouchesPlayer(int, int);
1112 void TestIfBadThingRunsIntoPlayer(int, int, int);
1113 void TestIfFriendTouchesBadThing(int, int);
1114 void TestIfBadThingTouchesFriend(int, int);
1115 void TestIfBadThingTouchesOtherBadThing(int, int);
1116 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1117
1118 void KillPlayer(struct PlayerInfo *);
1119 void BuryPlayer(struct PlayerInfo *);
1120 void RemovePlayer(struct PlayerInfo *);
1121 void ExitPlayer(struct PlayerInfo *);
1122
1123 static int getInvisibleActiveFromInvisibleElement(int);
1124 static int getInvisibleFromInvisibleActiveElement(int);
1125
1126 static void TestFieldAfterSnapping(int, int, int, int, int);
1127
1128 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1129
1130 // for detection of endless loops, caused by custom element programming
1131 // (using maximal playfield width x 10 is just a rough approximation)
1132 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1133
1134 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1135 {                                                                       \
1136   if (recursion_loop_detected)                                          \
1137     return (rc);                                                        \
1138                                                                         \
1139   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1140   {                                                                     \
1141     recursion_loop_detected = TRUE;                                     \
1142     recursion_loop_element = (e);                                       \
1143   }                                                                     \
1144                                                                         \
1145   recursion_loop_depth++;                                               \
1146 }
1147
1148 #define RECURSION_LOOP_DETECTION_END()                                  \
1149 {                                                                       \
1150   recursion_loop_depth--;                                               \
1151 }
1152
1153 static int recursion_loop_depth;
1154 static boolean recursion_loop_detected;
1155 static boolean recursion_loop_element;
1156
1157 static int map_player_action[MAX_PLAYERS];
1158
1159
1160 // ----------------------------------------------------------------------------
1161 // definition of elements that automatically change to other elements after
1162 // a specified time, eventually calling a function when changing
1163 // ----------------------------------------------------------------------------
1164
1165 // forward declaration for changer functions
1166 static void InitBuggyBase(int, int);
1167 static void WarnBuggyBase(int, int);
1168
1169 static void InitTrap(int, int);
1170 static void ActivateTrap(int, int);
1171 static void ChangeActiveTrap(int, int);
1172
1173 static void InitRobotWheel(int, int);
1174 static void RunRobotWheel(int, int);
1175 static void StopRobotWheel(int, int);
1176
1177 static void InitTimegateWheel(int, int);
1178 static void RunTimegateWheel(int, int);
1179
1180 static void InitMagicBallDelay(int, int);
1181 static void ActivateMagicBall(int, int);
1182
1183 struct ChangingElementInfo
1184 {
1185   int element;
1186   int target_element;
1187   int change_delay;
1188   void (*pre_change_function)(int x, int y);
1189   void (*change_function)(int x, int y);
1190   void (*post_change_function)(int x, int y);
1191 };
1192
1193 static struct ChangingElementInfo change_delay_list[] =
1194 {
1195   {
1196     EL_NUT_BREAKING,
1197     EL_EMERALD,
1198     6,
1199     NULL,
1200     NULL,
1201     NULL
1202   },
1203   {
1204     EL_PEARL_BREAKING,
1205     EL_EMPTY,
1206     8,
1207     NULL,
1208     NULL,
1209     NULL
1210   },
1211   {
1212     EL_EXIT_OPENING,
1213     EL_EXIT_OPEN,
1214     29,
1215     NULL,
1216     NULL,
1217     NULL
1218   },
1219   {
1220     EL_EXIT_CLOSING,
1221     EL_EXIT_CLOSED,
1222     29,
1223     NULL,
1224     NULL,
1225     NULL
1226   },
1227   {
1228     EL_STEEL_EXIT_OPENING,
1229     EL_STEEL_EXIT_OPEN,
1230     29,
1231     NULL,
1232     NULL,
1233     NULL
1234   },
1235   {
1236     EL_STEEL_EXIT_CLOSING,
1237     EL_STEEL_EXIT_CLOSED,
1238     29,
1239     NULL,
1240     NULL,
1241     NULL
1242   },
1243   {
1244     EL_EM_EXIT_OPENING,
1245     EL_EM_EXIT_OPEN,
1246     29,
1247     NULL,
1248     NULL,
1249     NULL
1250   },
1251   {
1252     EL_EM_EXIT_CLOSING,
1253     EL_EMPTY,
1254     29,
1255     NULL,
1256     NULL,
1257     NULL
1258   },
1259   {
1260     EL_EM_STEEL_EXIT_OPENING,
1261     EL_EM_STEEL_EXIT_OPEN,
1262     29,
1263     NULL,
1264     NULL,
1265     NULL
1266   },
1267   {
1268     EL_EM_STEEL_EXIT_CLOSING,
1269     EL_STEELWALL,
1270     29,
1271     NULL,
1272     NULL,
1273     NULL
1274   },
1275   {
1276     EL_SP_EXIT_OPENING,
1277     EL_SP_EXIT_OPEN,
1278     29,
1279     NULL,
1280     NULL,
1281     NULL
1282   },
1283   {
1284     EL_SP_EXIT_CLOSING,
1285     EL_SP_EXIT_CLOSED,
1286     29,
1287     NULL,
1288     NULL,
1289     NULL
1290   },
1291   {
1292     EL_SWITCHGATE_OPENING,
1293     EL_SWITCHGATE_OPEN,
1294     29,
1295     NULL,
1296     NULL,
1297     NULL
1298   },
1299   {
1300     EL_SWITCHGATE_CLOSING,
1301     EL_SWITCHGATE_CLOSED,
1302     29,
1303     NULL,
1304     NULL,
1305     NULL
1306   },
1307   {
1308     EL_TIMEGATE_OPENING,
1309     EL_TIMEGATE_OPEN,
1310     29,
1311     NULL,
1312     NULL,
1313     NULL
1314   },
1315   {
1316     EL_TIMEGATE_CLOSING,
1317     EL_TIMEGATE_CLOSED,
1318     29,
1319     NULL,
1320     NULL,
1321     NULL
1322   },
1323
1324   {
1325     EL_ACID_SPLASH_LEFT,
1326     EL_EMPTY,
1327     8,
1328     NULL,
1329     NULL,
1330     NULL
1331   },
1332   {
1333     EL_ACID_SPLASH_RIGHT,
1334     EL_EMPTY,
1335     8,
1336     NULL,
1337     NULL,
1338     NULL
1339   },
1340   {
1341     EL_SP_BUGGY_BASE,
1342     EL_SP_BUGGY_BASE_ACTIVATING,
1343     0,
1344     InitBuggyBase,
1345     NULL,
1346     NULL
1347   },
1348   {
1349     EL_SP_BUGGY_BASE_ACTIVATING,
1350     EL_SP_BUGGY_BASE_ACTIVE,
1351     0,
1352     InitBuggyBase,
1353     NULL,
1354     NULL
1355   },
1356   {
1357     EL_SP_BUGGY_BASE_ACTIVE,
1358     EL_SP_BUGGY_BASE,
1359     0,
1360     InitBuggyBase,
1361     WarnBuggyBase,
1362     NULL
1363   },
1364   {
1365     EL_TRAP,
1366     EL_TRAP_ACTIVE,
1367     0,
1368     InitTrap,
1369     NULL,
1370     ActivateTrap
1371   },
1372   {
1373     EL_TRAP_ACTIVE,
1374     EL_TRAP,
1375     31,
1376     NULL,
1377     ChangeActiveTrap,
1378     NULL
1379   },
1380   {
1381     EL_ROBOT_WHEEL_ACTIVE,
1382     EL_ROBOT_WHEEL,
1383     0,
1384     InitRobotWheel,
1385     RunRobotWheel,
1386     StopRobotWheel
1387   },
1388   {
1389     EL_TIMEGATE_SWITCH_ACTIVE,
1390     EL_TIMEGATE_SWITCH,
1391     0,
1392     InitTimegateWheel,
1393     RunTimegateWheel,
1394     NULL
1395   },
1396   {
1397     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1398     EL_DC_TIMEGATE_SWITCH,
1399     0,
1400     InitTimegateWheel,
1401     RunTimegateWheel,
1402     NULL
1403   },
1404   {
1405     EL_EMC_MAGIC_BALL_ACTIVE,
1406     EL_EMC_MAGIC_BALL_ACTIVE,
1407     0,
1408     InitMagicBallDelay,
1409     NULL,
1410     ActivateMagicBall
1411   },
1412   {
1413     EL_EMC_SPRING_BUMPER_ACTIVE,
1414     EL_EMC_SPRING_BUMPER,
1415     8,
1416     NULL,
1417     NULL,
1418     NULL
1419   },
1420   {
1421     EL_DIAGONAL_SHRINKING,
1422     EL_UNDEFINED,
1423     0,
1424     NULL,
1425     NULL,
1426     NULL
1427   },
1428   {
1429     EL_DIAGONAL_GROWING,
1430     EL_UNDEFINED,
1431     0,
1432     NULL,
1433     NULL,
1434     NULL,
1435   },
1436
1437   {
1438     EL_UNDEFINED,
1439     EL_UNDEFINED,
1440     -1,
1441     NULL,
1442     NULL,
1443     NULL
1444   }
1445 };
1446
1447 struct
1448 {
1449   int element;
1450   int push_delay_fixed, push_delay_random;
1451 }
1452 push_delay_list[] =
1453 {
1454   { EL_SPRING,                  0, 0 },
1455   { EL_BALLOON,                 0, 0 },
1456
1457   { EL_SOKOBAN_OBJECT,          2, 0 },
1458   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1459   { EL_SATELLITE,               2, 0 },
1460   { EL_SP_DISK_YELLOW,          2, 0 },
1461
1462   { EL_UNDEFINED,               0, 0 },
1463 };
1464
1465 struct
1466 {
1467   int element;
1468   int move_stepsize;
1469 }
1470 move_stepsize_list[] =
1471 {
1472   { EL_AMOEBA_DROP,             2 },
1473   { EL_AMOEBA_DROPPING,         2 },
1474   { EL_QUICKSAND_FILLING,       1 },
1475   { EL_QUICKSAND_EMPTYING,      1 },
1476   { EL_QUICKSAND_FAST_FILLING,  2 },
1477   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1478   { EL_MAGIC_WALL_FILLING,      2 },
1479   { EL_MAGIC_WALL_EMPTYING,     2 },
1480   { EL_BD_MAGIC_WALL_FILLING,   2 },
1481   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1482   { EL_DC_MAGIC_WALL_FILLING,   2 },
1483   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1484
1485   { EL_UNDEFINED,               0 },
1486 };
1487
1488 struct
1489 {
1490   int element;
1491   int count;
1492 }
1493 collect_count_list[] =
1494 {
1495   { EL_EMERALD,                 1 },
1496   { EL_BD_DIAMOND,              1 },
1497   { EL_EMERALD_YELLOW,          1 },
1498   { EL_EMERALD_RED,             1 },
1499   { EL_EMERALD_PURPLE,          1 },
1500   { EL_DIAMOND,                 3 },
1501   { EL_SP_INFOTRON,             1 },
1502   { EL_PEARL,                   5 },
1503   { EL_CRYSTAL,                 8 },
1504
1505   { EL_UNDEFINED,               0 },
1506 };
1507
1508 struct
1509 {
1510   int element;
1511   int direction;
1512 }
1513 access_direction_list[] =
1514 {
1515   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1516   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1517   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1518   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1519   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1521   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1522   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1523   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1524   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1525   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1526
1527   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1528   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1529   { EL_SP_PORT_UP,                                                   MV_DOWN },
1530   { EL_SP_PORT_DOWN,                                         MV_UP           },
1531   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1532   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1533   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1534   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1535   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1536   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1537   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1538   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1539   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1540   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1541   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1542   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1543   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1544   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1545   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1546
1547   { EL_UNDEFINED,                       MV_NONE                              }
1548 };
1549
1550 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1551
1552 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1553 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1554 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1555                                  IS_JUST_CHANGING(x, y))
1556
1557 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1558
1559 // static variables for playfield scan mode (scanning forward or backward)
1560 static int playfield_scan_start_x = 0;
1561 static int playfield_scan_start_y = 0;
1562 static int playfield_scan_delta_x = 1;
1563 static int playfield_scan_delta_y = 1;
1564
1565 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1566                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1567                                      (y) += playfield_scan_delta_y)     \
1568                                 for ((x) = playfield_scan_start_x;      \
1569                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1570                                      (x) += playfield_scan_delta_x)
1571
1572 #ifdef DEBUG
1573 void DEBUG_SetMaximumDynamite(void)
1574 {
1575   int i;
1576
1577   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1578     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1579       local_player->inventory_element[local_player->inventory_size++] =
1580         EL_DYNAMITE;
1581 }
1582 #endif
1583
1584 static void InitPlayfieldScanModeVars(void)
1585 {
1586   if (game.use_reverse_scan_direction)
1587   {
1588     playfield_scan_start_x = lev_fieldx - 1;
1589     playfield_scan_start_y = lev_fieldy - 1;
1590
1591     playfield_scan_delta_x = -1;
1592     playfield_scan_delta_y = -1;
1593   }
1594   else
1595   {
1596     playfield_scan_start_x = 0;
1597     playfield_scan_start_y = 0;
1598
1599     playfield_scan_delta_x = 1;
1600     playfield_scan_delta_y = 1;
1601   }
1602 }
1603
1604 static void InitPlayfieldScanMode(int mode)
1605 {
1606   game.use_reverse_scan_direction =
1607     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1608
1609   InitPlayfieldScanModeVars();
1610 }
1611
1612 static int get_move_delay_from_stepsize(int move_stepsize)
1613 {
1614   move_stepsize =
1615     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1616
1617   // make sure that stepsize value is always a power of 2
1618   move_stepsize = (1 << log_2(move_stepsize));
1619
1620   return TILEX / move_stepsize;
1621 }
1622
1623 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1624                                boolean init_game)
1625 {
1626   int player_nr = player->index_nr;
1627   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1628   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1629
1630   // do no immediately change move delay -- the player might just be moving
1631   player->move_delay_value_next = move_delay;
1632
1633   // information if player can move must be set separately
1634   player->cannot_move = cannot_move;
1635
1636   if (init_game)
1637   {
1638     player->move_delay       = game.initial_move_delay[player_nr];
1639     player->move_delay_value = game.initial_move_delay_value[player_nr];
1640
1641     player->move_delay_value_next = -1;
1642
1643     player->move_delay_reset_counter = 0;
1644   }
1645 }
1646
1647 void GetPlayerConfig(void)
1648 {
1649   GameFrameDelay = setup.game_frame_delay;
1650
1651   if (!audio.sound_available)
1652     setup.sound_simple = FALSE;
1653
1654   if (!audio.loops_available)
1655     setup.sound_loops = FALSE;
1656
1657   if (!audio.music_available)
1658     setup.sound_music = FALSE;
1659
1660   if (!video.fullscreen_available)
1661     setup.fullscreen = FALSE;
1662
1663   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1664
1665   SetAudioMode(setup.sound);
1666 }
1667
1668 int GetElementFromGroupElement(int element)
1669 {
1670   if (IS_GROUP_ELEMENT(element))
1671   {
1672     struct ElementGroupInfo *group = element_info[element].group;
1673     int last_anim_random_frame = gfx.anim_random_frame;
1674     int element_pos;
1675
1676     if (group->choice_mode == ANIM_RANDOM)
1677       gfx.anim_random_frame = RND(group->num_elements_resolved);
1678
1679     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1680                                     group->choice_mode, 0,
1681                                     group->choice_pos);
1682
1683     if (group->choice_mode == ANIM_RANDOM)
1684       gfx.anim_random_frame = last_anim_random_frame;
1685
1686     group->choice_pos++;
1687
1688     element = group->element_resolved[element_pos];
1689   }
1690
1691   return element;
1692 }
1693
1694 static void IncrementSokobanFieldsNeeded(void)
1695 {
1696   if (level.sb_fields_needed)
1697     game.sokoban_fields_still_needed++;
1698 }
1699
1700 static void IncrementSokobanObjectsNeeded(void)
1701 {
1702   if (level.sb_objects_needed)
1703     game.sokoban_objects_still_needed++;
1704 }
1705
1706 static void DecrementSokobanFieldsNeeded(void)
1707 {
1708   if (game.sokoban_fields_still_needed > 0)
1709     game.sokoban_fields_still_needed--;
1710 }
1711
1712 static void DecrementSokobanObjectsNeeded(void)
1713 {
1714   if (game.sokoban_objects_still_needed > 0)
1715     game.sokoban_objects_still_needed--;
1716 }
1717
1718 static void InitPlayerField(int x, int y, int element, boolean init_game)
1719 {
1720   if (element == EL_SP_MURPHY)
1721   {
1722     if (init_game)
1723     {
1724       if (stored_player[0].present)
1725       {
1726         Tile[x][y] = EL_SP_MURPHY_CLONE;
1727
1728         return;
1729       }
1730       else
1731       {
1732         stored_player[0].initial_element = element;
1733         stored_player[0].use_murphy = TRUE;
1734
1735         if (!level.use_artwork_element[0])
1736           stored_player[0].artwork_element = EL_SP_MURPHY;
1737       }
1738
1739       Tile[x][y] = EL_PLAYER_1;
1740     }
1741   }
1742
1743   if (init_game)
1744   {
1745     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1746     int jx = player->jx, jy = player->jy;
1747
1748     player->present = TRUE;
1749
1750     player->block_last_field = (element == EL_SP_MURPHY ?
1751                                 level.sp_block_last_field :
1752                                 level.block_last_field);
1753
1754     // ---------- initialize player's last field block delay ------------------
1755
1756     // always start with reliable default value (no adjustment needed)
1757     player->block_delay_adjustment = 0;
1758
1759     // special case 1: in Supaplex, Murphy blocks last field one more frame
1760     if (player->block_last_field && element == EL_SP_MURPHY)
1761       player->block_delay_adjustment = 1;
1762
1763     // special case 2: in game engines before 3.1.1, blocking was different
1764     if (game.use_block_last_field_bug)
1765       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1766
1767     if (!network.enabled || player->connected_network)
1768     {
1769       player->active = TRUE;
1770
1771       // remove potentially duplicate players
1772       if (StorePlayer[jx][jy] == Tile[x][y])
1773         StorePlayer[jx][jy] = 0;
1774
1775       StorePlayer[x][y] = Tile[x][y];
1776
1777 #if DEBUG_INIT_PLAYER
1778       Debug("game:init:player", "- player element %d activated",
1779             player->element_nr);
1780       Debug("game:init:player", "  (local player is %d and currently %s)",
1781             local_player->element_nr,
1782             local_player->active ? "active" : "not active");
1783     }
1784 #endif
1785
1786     Tile[x][y] = EL_EMPTY;
1787
1788     player->jx = player->last_jx = x;
1789     player->jy = player->last_jy = y;
1790   }
1791
1792   // always check if player was just killed and should be reanimated
1793   {
1794     int player_nr = GET_PLAYER_NR(element);
1795     struct PlayerInfo *player = &stored_player[player_nr];
1796
1797     if (player->active && player->killed)
1798       player->reanimated = TRUE; // if player was just killed, reanimate him
1799   }
1800 }
1801
1802 static void InitField(int x, int y, boolean init_game)
1803 {
1804   int element = Tile[x][y];
1805
1806   switch (element)
1807   {
1808     case EL_SP_MURPHY:
1809     case EL_PLAYER_1:
1810     case EL_PLAYER_2:
1811     case EL_PLAYER_3:
1812     case EL_PLAYER_4:
1813       InitPlayerField(x, y, element, init_game);
1814       break;
1815
1816     case EL_SOKOBAN_FIELD_PLAYER:
1817       element = Tile[x][y] = EL_PLAYER_1;
1818       InitField(x, y, init_game);
1819
1820       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1821       InitField(x, y, init_game);
1822       break;
1823
1824     case EL_SOKOBAN_FIELD_EMPTY:
1825       IncrementSokobanFieldsNeeded();
1826       break;
1827
1828     case EL_SOKOBAN_OBJECT:
1829       IncrementSokobanObjectsNeeded();
1830       break;
1831
1832     case EL_STONEBLOCK:
1833       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1834         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1835       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1836         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1837       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1838         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1839       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1843       break;
1844
1845     case EL_BUG:
1846     case EL_BUG_RIGHT:
1847     case EL_BUG_UP:
1848     case EL_BUG_LEFT:
1849     case EL_BUG_DOWN:
1850     case EL_SPACESHIP:
1851     case EL_SPACESHIP_RIGHT:
1852     case EL_SPACESHIP_UP:
1853     case EL_SPACESHIP_LEFT:
1854     case EL_SPACESHIP_DOWN:
1855     case EL_BD_BUTTERFLY:
1856     case EL_BD_BUTTERFLY_RIGHT:
1857     case EL_BD_BUTTERFLY_UP:
1858     case EL_BD_BUTTERFLY_LEFT:
1859     case EL_BD_BUTTERFLY_DOWN:
1860     case EL_BD_FIREFLY:
1861     case EL_BD_FIREFLY_RIGHT:
1862     case EL_BD_FIREFLY_UP:
1863     case EL_BD_FIREFLY_LEFT:
1864     case EL_BD_FIREFLY_DOWN:
1865     case EL_PACMAN_RIGHT:
1866     case EL_PACMAN_UP:
1867     case EL_PACMAN_LEFT:
1868     case EL_PACMAN_DOWN:
1869     case EL_YAMYAM:
1870     case EL_YAMYAM_LEFT:
1871     case EL_YAMYAM_RIGHT:
1872     case EL_YAMYAM_UP:
1873     case EL_YAMYAM_DOWN:
1874     case EL_DARK_YAMYAM:
1875     case EL_ROBOT:
1876     case EL_PACMAN:
1877     case EL_SP_SNIKSNAK:
1878     case EL_SP_ELECTRON:
1879     case EL_MOLE:
1880     case EL_MOLE_LEFT:
1881     case EL_MOLE_RIGHT:
1882     case EL_MOLE_UP:
1883     case EL_MOLE_DOWN:
1884     case EL_SPRING_LEFT:
1885     case EL_SPRING_RIGHT:
1886       InitMovDir(x, y);
1887       break;
1888
1889     case EL_AMOEBA_FULL:
1890     case EL_BD_AMOEBA:
1891       InitAmoebaNr(x, y);
1892       break;
1893
1894     case EL_AMOEBA_DROP:
1895       if (y == lev_fieldy - 1)
1896       {
1897         Tile[x][y] = EL_AMOEBA_GROWING;
1898         Store[x][y] = EL_AMOEBA_WET;
1899       }
1900       break;
1901
1902     case EL_DYNAMITE_ACTIVE:
1903     case EL_SP_DISK_RED_ACTIVE:
1904     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1905     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1906     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1907     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1908       MovDelay[x][y] = 96;
1909       break;
1910
1911     case EL_EM_DYNAMITE_ACTIVE:
1912       MovDelay[x][y] = 32;
1913       break;
1914
1915     case EL_LAMP:
1916       game.lights_still_needed++;
1917       break;
1918
1919     case EL_PENGUIN:
1920       game.friends_still_needed++;
1921       break;
1922
1923     case EL_PIG:
1924     case EL_DRAGON:
1925       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1926       break;
1927
1928     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1929     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1930     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1931     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1932     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1933     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1934     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1935     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1936     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1937     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1938     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1939     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1940       if (init_game)
1941       {
1942         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1943         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1944         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1945
1946         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1947         {
1948           game.belt_dir[belt_nr] = belt_dir;
1949           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1950         }
1951         else    // more than one switch -- set it like the first switch
1952         {
1953           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1954         }
1955       }
1956       break;
1957
1958     case EL_LIGHT_SWITCH_ACTIVE:
1959       if (init_game)
1960         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1961       break;
1962
1963     case EL_INVISIBLE_STEELWALL:
1964     case EL_INVISIBLE_WALL:
1965     case EL_INVISIBLE_SAND:
1966       if (game.light_time_left > 0 ||
1967           game.lenses_time_left > 0)
1968         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1969       break;
1970
1971     case EL_EMC_MAGIC_BALL:
1972       if (game.ball_active)
1973         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1974       break;
1975
1976     case EL_EMC_MAGIC_BALL_SWITCH:
1977       if (game.ball_active)
1978         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1979       break;
1980
1981     case EL_TRIGGER_PLAYER:
1982     case EL_TRIGGER_ELEMENT:
1983     case EL_TRIGGER_CE_VALUE:
1984     case EL_TRIGGER_CE_SCORE:
1985     case EL_SELF:
1986     case EL_ANY_ELEMENT:
1987     case EL_CURRENT_CE_VALUE:
1988     case EL_CURRENT_CE_SCORE:
1989     case EL_PREV_CE_1:
1990     case EL_PREV_CE_2:
1991     case EL_PREV_CE_3:
1992     case EL_PREV_CE_4:
1993     case EL_PREV_CE_5:
1994     case EL_PREV_CE_6:
1995     case EL_PREV_CE_7:
1996     case EL_PREV_CE_8:
1997     case EL_NEXT_CE_1:
1998     case EL_NEXT_CE_2:
1999     case EL_NEXT_CE_3:
2000     case EL_NEXT_CE_4:
2001     case EL_NEXT_CE_5:
2002     case EL_NEXT_CE_6:
2003     case EL_NEXT_CE_7:
2004     case EL_NEXT_CE_8:
2005       // reference elements should not be used on the playfield
2006       Tile[x][y] = EL_EMPTY;
2007       break;
2008
2009     default:
2010       if (IS_CUSTOM_ELEMENT(element))
2011       {
2012         if (CAN_MOVE(element))
2013           InitMovDir(x, y);
2014
2015         if (!element_info[element].use_last_ce_value || init_game)
2016           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2017       }
2018       else if (IS_GROUP_ELEMENT(element))
2019       {
2020         Tile[x][y] = GetElementFromGroupElement(element);
2021
2022         InitField(x, y, init_game);
2023       }
2024
2025       break;
2026   }
2027
2028   if (!init_game)
2029     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2030 }
2031
2032 static void InitField_WithBug1(int x, int y, boolean init_game)
2033 {
2034   InitField(x, y, init_game);
2035
2036   // not needed to call InitMovDir() -- already done by InitField()!
2037   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2038       CAN_MOVE(Tile[x][y]))
2039     InitMovDir(x, y);
2040 }
2041
2042 static void InitField_WithBug2(int x, int y, boolean init_game)
2043 {
2044   int old_element = Tile[x][y];
2045
2046   InitField(x, y, init_game);
2047
2048   // not needed to call InitMovDir() -- already done by InitField()!
2049   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2050       CAN_MOVE(old_element) &&
2051       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2052     InitMovDir(x, y);
2053
2054   /* this case is in fact a combination of not less than three bugs:
2055      first, it calls InitMovDir() for elements that can move, although this is
2056      already done by InitField(); then, it checks the element that was at this
2057      field _before_ the call to InitField() (which can change it); lastly, it
2058      was not called for "mole with direction" elements, which were treated as
2059      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2060   */
2061 }
2062
2063 static int get_key_element_from_nr(int key_nr)
2064 {
2065   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2066                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2067                           EL_EM_KEY_1 : EL_KEY_1);
2068
2069   return key_base_element + key_nr;
2070 }
2071
2072 static int get_next_dropped_element(struct PlayerInfo *player)
2073 {
2074   return (player->inventory_size > 0 ?
2075           player->inventory_element[player->inventory_size - 1] :
2076           player->inventory_infinite_element != EL_UNDEFINED ?
2077           player->inventory_infinite_element :
2078           player->dynabombs_left > 0 ?
2079           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2080           EL_UNDEFINED);
2081 }
2082
2083 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2084 {
2085   // pos >= 0: get element from bottom of the stack;
2086   // pos <  0: get element from top of the stack
2087
2088   if (pos < 0)
2089   {
2090     int min_inventory_size = -pos;
2091     int inventory_pos = player->inventory_size - min_inventory_size;
2092     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2093
2094     return (player->inventory_size >= min_inventory_size ?
2095             player->inventory_element[inventory_pos] :
2096             player->inventory_infinite_element != EL_UNDEFINED ?
2097             player->inventory_infinite_element :
2098             player->dynabombs_left >= min_dynabombs_left ?
2099             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2100             EL_UNDEFINED);
2101   }
2102   else
2103   {
2104     int min_dynabombs_left = pos + 1;
2105     int min_inventory_size = pos + 1 - player->dynabombs_left;
2106     int inventory_pos = pos - player->dynabombs_left;
2107
2108     return (player->inventory_infinite_element != EL_UNDEFINED ?
2109             player->inventory_infinite_element :
2110             player->dynabombs_left >= min_dynabombs_left ?
2111             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2112             player->inventory_size >= min_inventory_size ?
2113             player->inventory_element[inventory_pos] :
2114             EL_UNDEFINED);
2115   }
2116 }
2117
2118 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2119 {
2120   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2121   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2122   int compare_result;
2123
2124   if (gpo1->sort_priority != gpo2->sort_priority)
2125     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2126   else
2127     compare_result = gpo1->nr - gpo2->nr;
2128
2129   return compare_result;
2130 }
2131
2132 int getPlayerInventorySize(int player_nr)
2133 {
2134   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2135     return game_em.ply[player_nr]->dynamite;
2136   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2137     return game_sp.red_disk_count;
2138   else
2139     return stored_player[player_nr].inventory_size;
2140 }
2141
2142 static void InitGameControlValues(void)
2143 {
2144   int i;
2145
2146   for (i = 0; game_panel_controls[i].nr != -1; i++)
2147   {
2148     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2149     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2150     struct TextPosInfo *pos = gpc->pos;
2151     int nr = gpc->nr;
2152     int type = gpc->type;
2153
2154     if (nr != i)
2155     {
2156       Error("'game_panel_controls' structure corrupted at %d", i);
2157
2158       Fail("this should not happen -- please debug");
2159     }
2160
2161     // force update of game controls after initialization
2162     gpc->value = gpc->last_value = -1;
2163     gpc->frame = gpc->last_frame = -1;
2164     gpc->gfx_frame = -1;
2165
2166     // determine panel value width for later calculation of alignment
2167     if (type == TYPE_INTEGER || type == TYPE_STRING)
2168     {
2169       pos->width = pos->size * getFontWidth(pos->font);
2170       pos->height = getFontHeight(pos->font);
2171     }
2172     else if (type == TYPE_ELEMENT)
2173     {
2174       pos->width = pos->size;
2175       pos->height = pos->size;
2176     }
2177
2178     // fill structure for game panel draw order
2179     gpo->nr = gpc->nr;
2180     gpo->sort_priority = pos->sort_priority;
2181   }
2182
2183   // sort game panel controls according to sort_priority and control number
2184   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2185         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2186 }
2187
2188 static void UpdatePlayfieldElementCount(void)
2189 {
2190   boolean use_element_count = FALSE;
2191   int i, j, x, y;
2192
2193   // first check if it is needed at all to calculate playfield element count
2194   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2195     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2196       use_element_count = TRUE;
2197
2198   if (!use_element_count)
2199     return;
2200
2201   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2202     element_info[i].element_count = 0;
2203
2204   SCAN_PLAYFIELD(x, y)
2205   {
2206     element_info[Tile[x][y]].element_count++;
2207   }
2208
2209   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2210     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2211       if (IS_IN_GROUP(j, i))
2212         element_info[EL_GROUP_START + i].element_count +=
2213           element_info[j].element_count;
2214 }
2215
2216 static void UpdateGameControlValues(void)
2217 {
2218   int i, k;
2219   int time = (game.LevelSolved ?
2220               game.LevelSolved_CountingTime :
2221               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2222               game_em.lev->time :
2223               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2224               game_sp.time_played :
2225               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2226               game_mm.energy_left :
2227               game.no_time_limit ? TimePlayed : TimeLeft);
2228   int score = (game.LevelSolved ?
2229                game.LevelSolved_CountingScore :
2230                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2231                game_em.lev->score :
2232                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2233                game_sp.score :
2234                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2235                game_mm.score :
2236                game.score);
2237   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2238               game_em.lev->gems_needed :
2239               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2240               game_sp.infotrons_still_needed :
2241               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2242               game_mm.kettles_still_needed :
2243               game.gems_still_needed);
2244   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2245                      game_em.lev->gems_needed > 0 :
2246                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2247                      game_sp.infotrons_still_needed > 0 :
2248                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2249                      game_mm.kettles_still_needed > 0 ||
2250                      game_mm.lights_still_needed > 0 :
2251                      game.gems_still_needed > 0 ||
2252                      game.sokoban_fields_still_needed > 0 ||
2253                      game.sokoban_objects_still_needed > 0 ||
2254                      game.lights_still_needed > 0);
2255   int health = (game.LevelSolved ?
2256                 game.LevelSolved_CountingHealth :
2257                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2258                 MM_HEALTH(game_mm.laser_overload_value) :
2259                 game.health);
2260   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2261
2262   UpdatePlayfieldElementCount();
2263
2264   // update game panel control values
2265
2266   // used instead of "level_nr" (for network games)
2267   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2268   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2269
2270   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2271   for (i = 0; i < MAX_NUM_KEYS; i++)
2272     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2273   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2274   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2275
2276   if (game.centered_player_nr == -1)
2277   {
2278     for (i = 0; i < MAX_PLAYERS; i++)
2279     {
2280       // only one player in Supaplex game engine
2281       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2282         break;
2283
2284       for (k = 0; k < MAX_NUM_KEYS; k++)
2285       {
2286         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2287         {
2288           if (game_em.ply[i]->keys & (1 << k))
2289             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2290               get_key_element_from_nr(k);
2291         }
2292         else if (stored_player[i].key[k])
2293           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294             get_key_element_from_nr(k);
2295       }
2296
2297       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2298         getPlayerInventorySize(i);
2299
2300       if (stored_player[i].num_white_keys > 0)
2301         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2302           EL_DC_KEY_WHITE;
2303
2304       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2305         stored_player[i].num_white_keys;
2306     }
2307   }
2308   else
2309   {
2310     int player_nr = game.centered_player_nr;
2311
2312     for (k = 0; k < MAX_NUM_KEYS; k++)
2313     {
2314       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2315       {
2316         if (game_em.ply[player_nr]->keys & (1 << k))
2317           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2318             get_key_element_from_nr(k);
2319       }
2320       else if (stored_player[player_nr].key[k])
2321         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322           get_key_element_from_nr(k);
2323     }
2324
2325     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2326       getPlayerInventorySize(player_nr);
2327
2328     if (stored_player[player_nr].num_white_keys > 0)
2329       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2330
2331     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2332       stored_player[player_nr].num_white_keys;
2333   }
2334
2335   // re-arrange keys on game panel, if needed or if defined by style settings
2336   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2337   {
2338     int nr = GAME_PANEL_KEY_1 + i;
2339     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2340     struct TextPosInfo *pos = gpc->pos;
2341
2342     // skip check if key is not in the player's inventory
2343     if (gpc->value == EL_EMPTY)
2344       continue;
2345
2346     // check if keys should be arranged on panel from left to right
2347     if (pos->style == STYLE_LEFTMOST_POSITION)
2348     {
2349       // check previous key positions (left from current key)
2350       for (k = 0; k < i; k++)
2351       {
2352         int nr_new = GAME_PANEL_KEY_1 + k;
2353
2354         if (game_panel_controls[nr_new].value == EL_EMPTY)
2355         {
2356           game_panel_controls[nr_new].value = gpc->value;
2357           gpc->value = EL_EMPTY;
2358
2359           break;
2360         }
2361       }
2362     }
2363
2364     // check if "undefined" keys can be placed at some other position
2365     if (pos->x == -1 && pos->y == -1)
2366     {
2367       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2368
2369       // 1st try: display key at the same position as normal or EM keys
2370       if (game_panel_controls[nr_new].value == EL_EMPTY)
2371       {
2372         game_panel_controls[nr_new].value = gpc->value;
2373       }
2374       else
2375       {
2376         // 2nd try: display key at the next free position in the key panel
2377         for (k = 0; k < STD_NUM_KEYS; k++)
2378         {
2379           nr_new = GAME_PANEL_KEY_1 + k;
2380
2381           if (game_panel_controls[nr_new].value == EL_EMPTY)
2382           {
2383             game_panel_controls[nr_new].value = gpc->value;
2384
2385             break;
2386           }
2387         }
2388       }
2389     }
2390   }
2391
2392   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2393   {
2394     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2395       get_inventory_element_from_pos(local_player, i);
2396     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2397       get_inventory_element_from_pos(local_player, -i - 1);
2398   }
2399
2400   game_panel_controls[GAME_PANEL_SCORE].value = score;
2401   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2402
2403   game_panel_controls[GAME_PANEL_TIME].value = time;
2404
2405   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2406   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2407   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2408
2409   if (level.time == 0)
2410     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2411   else
2412     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2413
2414   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2415   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2416
2417   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2418
2419   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2420     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2421      EL_EMPTY);
2422   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2423     local_player->shield_normal_time_left;
2424   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2425     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2426      EL_EMPTY);
2427   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2428     local_player->shield_deadly_time_left;
2429
2430   game_panel_controls[GAME_PANEL_EXIT].value =
2431     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2432
2433   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2434     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2435   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2436     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2437      EL_EMC_MAGIC_BALL_SWITCH);
2438
2439   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2440     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2441   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2442     game.light_time_left;
2443
2444   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2445     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2446   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2447     game.timegate_time_left;
2448
2449   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2450     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2451
2452   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2453     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2454   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2455     game.lenses_time_left;
2456
2457   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2458     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2459   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2460     game.magnify_time_left;
2461
2462   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2463     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2464      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2465      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2466      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2467      EL_BALLOON_SWITCH_NONE);
2468
2469   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2470     local_player->dynabomb_count;
2471   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2472     local_player->dynabomb_size;
2473   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2474     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2475
2476   game_panel_controls[GAME_PANEL_PENGUINS].value =
2477     game.friends_still_needed;
2478
2479   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2480     game.sokoban_objects_still_needed;
2481   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2482     game.sokoban_fields_still_needed;
2483
2484   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2485     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2486
2487   for (i = 0; i < NUM_BELTS; i++)
2488   {
2489     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2490       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2491        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2492     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2493       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2494   }
2495
2496   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2497     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2498   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2499     game.magic_wall_time_left;
2500
2501   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2502     local_player->gravity;
2503
2504   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2505     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2506
2507   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2508     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2509       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2510        game.panel.element[i].id : EL_UNDEFINED);
2511
2512   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2513     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2514       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2515        element_info[game.panel.element_count[i].id].element_count : 0);
2516
2517   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2518     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2519       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2520        element_info[game.panel.ce_score[i].id].collect_score : 0);
2521
2522   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2523     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2524       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2525        element_info[game.panel.ce_score_element[i].id].collect_score :
2526        EL_UNDEFINED);
2527
2528   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2529   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2530   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2531
2532   // update game panel control frames
2533
2534   for (i = 0; game_panel_controls[i].nr != -1; i++)
2535   {
2536     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2537
2538     if (gpc->type == TYPE_ELEMENT)
2539     {
2540       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2541       {
2542         int last_anim_random_frame = gfx.anim_random_frame;
2543         int element = gpc->value;
2544         int graphic = el2panelimg(element);
2545         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2546                                sync_random_frame : INIT_GFX_RANDOM());
2547
2548         if (gpc->value != gpc->last_value)
2549         {
2550           gpc->gfx_frame = 0;
2551           gpc->gfx_random = init_gfx_random;
2552         }
2553         else
2554         {
2555           gpc->gfx_frame++;
2556
2557           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2558               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2559             gpc->gfx_random = init_gfx_random;
2560         }
2561
2562         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2563           gfx.anim_random_frame = gpc->gfx_random;
2564
2565         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2566           gpc->gfx_frame = element_info[element].collect_score;
2567
2568         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2569
2570         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2571           gfx.anim_random_frame = last_anim_random_frame;
2572       }
2573     }
2574     else if (gpc->type == TYPE_GRAPHIC)
2575     {
2576       if (gpc->graphic != IMG_UNDEFINED)
2577       {
2578         int last_anim_random_frame = gfx.anim_random_frame;
2579         int graphic = gpc->graphic;
2580         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2581                                sync_random_frame : INIT_GFX_RANDOM());
2582
2583         if (gpc->value != gpc->last_value)
2584         {
2585           gpc->gfx_frame = 0;
2586           gpc->gfx_random = init_gfx_random;
2587         }
2588         else
2589         {
2590           gpc->gfx_frame++;
2591
2592           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2593               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2594             gpc->gfx_random = init_gfx_random;
2595         }
2596
2597         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2598           gfx.anim_random_frame = gpc->gfx_random;
2599
2600         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2601
2602         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2603           gfx.anim_random_frame = last_anim_random_frame;
2604       }
2605     }
2606   }
2607 }
2608
2609 static void DisplayGameControlValues(void)
2610 {
2611   boolean redraw_panel = FALSE;
2612   int i;
2613
2614   for (i = 0; game_panel_controls[i].nr != -1; i++)
2615   {
2616     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2617
2618     if (PANEL_DEACTIVATED(gpc->pos))
2619       continue;
2620
2621     if (gpc->value == gpc->last_value &&
2622         gpc->frame == gpc->last_frame)
2623       continue;
2624
2625     redraw_panel = TRUE;
2626   }
2627
2628   if (!redraw_panel)
2629     return;
2630
2631   // copy default game door content to main double buffer
2632
2633   // !!! CHECK AGAIN !!!
2634   SetPanelBackground();
2635   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2636   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2637
2638   // redraw game control buttons
2639   RedrawGameButtons();
2640
2641   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2642
2643   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2644   {
2645     int nr = game_panel_order[i].nr;
2646     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2647     struct TextPosInfo *pos = gpc->pos;
2648     int type = gpc->type;
2649     int value = gpc->value;
2650     int frame = gpc->frame;
2651     int size = pos->size;
2652     int font = pos->font;
2653     boolean draw_masked = pos->draw_masked;
2654     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2655
2656     if (PANEL_DEACTIVATED(pos))
2657       continue;
2658
2659     if (pos->class == get_hash_from_key("extra_panel_items") &&
2660         !setup.prefer_extra_panel_items)
2661       continue;
2662
2663     gpc->last_value = value;
2664     gpc->last_frame = frame;
2665
2666     if (type == TYPE_INTEGER)
2667     {
2668       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2669           nr == GAME_PANEL_TIME)
2670       {
2671         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2672
2673         if (use_dynamic_size)           // use dynamic number of digits
2674         {
2675           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2676           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2677           int size2 = size1 + 1;
2678           int font1 = pos->font;
2679           int font2 = pos->font_alt;
2680
2681           size = (value < value_change ? size1 : size2);
2682           font = (value < value_change ? font1 : font2);
2683         }
2684       }
2685
2686       // correct text size if "digits" is zero or less
2687       if (size <= 0)
2688         size = strlen(int2str(value, size));
2689
2690       // dynamically correct text alignment
2691       pos->width = size * getFontWidth(font);
2692
2693       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2694                   int2str(value, size), font, mask_mode);
2695     }
2696     else if (type == TYPE_ELEMENT)
2697     {
2698       int element, graphic;
2699       Bitmap *src_bitmap;
2700       int src_x, src_y;
2701       int width, height;
2702       int dst_x = PANEL_XPOS(pos);
2703       int dst_y = PANEL_YPOS(pos);
2704
2705       if (value != EL_UNDEFINED && value != EL_EMPTY)
2706       {
2707         element = value;
2708         graphic = el2panelimg(value);
2709
2710 #if 0
2711         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2712               element, EL_NAME(element), size);
2713 #endif
2714
2715         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2716           size = TILESIZE;
2717
2718         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2719                               &src_x, &src_y);
2720
2721         width  = graphic_info[graphic].width  * size / TILESIZE;
2722         height = graphic_info[graphic].height * size / TILESIZE;
2723
2724         if (draw_masked)
2725           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2726                            dst_x, dst_y);
2727         else
2728           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2729                      dst_x, dst_y);
2730       }
2731     }
2732     else if (type == TYPE_GRAPHIC)
2733     {
2734       int graphic        = gpc->graphic;
2735       int graphic_active = gpc->graphic_active;
2736       Bitmap *src_bitmap;
2737       int src_x, src_y;
2738       int width, height;
2739       int dst_x = PANEL_XPOS(pos);
2740       int dst_y = PANEL_YPOS(pos);
2741       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2742                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2743
2744       if (graphic != IMG_UNDEFINED && !skip)
2745       {
2746         if (pos->style == STYLE_REVERSE)
2747           value = 100 - value;
2748
2749         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2750
2751         if (pos->direction & MV_HORIZONTAL)
2752         {
2753           width  = graphic_info[graphic_active].width * value / 100;
2754           height = graphic_info[graphic_active].height;
2755
2756           if (pos->direction == MV_LEFT)
2757           {
2758             src_x += graphic_info[graphic_active].width - width;
2759             dst_x += graphic_info[graphic_active].width - width;
2760           }
2761         }
2762         else
2763         {
2764           width  = graphic_info[graphic_active].width;
2765           height = graphic_info[graphic_active].height * value / 100;
2766
2767           if (pos->direction == MV_UP)
2768           {
2769             src_y += graphic_info[graphic_active].height - height;
2770             dst_y += graphic_info[graphic_active].height - height;
2771           }
2772         }
2773
2774         if (draw_masked)
2775           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2776                            dst_x, dst_y);
2777         else
2778           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2779                      dst_x, dst_y);
2780
2781         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2782
2783         if (pos->direction & MV_HORIZONTAL)
2784         {
2785           if (pos->direction == MV_RIGHT)
2786           {
2787             src_x += width;
2788             dst_x += width;
2789           }
2790           else
2791           {
2792             dst_x = PANEL_XPOS(pos);
2793           }
2794
2795           width = graphic_info[graphic].width - width;
2796         }
2797         else
2798         {
2799           if (pos->direction == MV_DOWN)
2800           {
2801             src_y += height;
2802             dst_y += height;
2803           }
2804           else
2805           {
2806             dst_y = PANEL_YPOS(pos);
2807           }
2808
2809           height = graphic_info[graphic].height - height;
2810         }
2811
2812         if (draw_masked)
2813           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2814                            dst_x, dst_y);
2815         else
2816           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2817                      dst_x, dst_y);
2818       }
2819     }
2820     else if (type == TYPE_STRING)
2821     {
2822       boolean active = (value != 0);
2823       char *state_normal = "off";
2824       char *state_active = "on";
2825       char *state = (active ? state_active : state_normal);
2826       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2827                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2828                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2829                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2830
2831       if (nr == GAME_PANEL_GRAVITY_STATE)
2832       {
2833         int font1 = pos->font;          // (used for normal state)
2834         int font2 = pos->font_alt;      // (used for active state)
2835
2836         font = (active ? font2 : font1);
2837       }
2838
2839       if (s != NULL)
2840       {
2841         char *s_cut;
2842
2843         if (size <= 0)
2844         {
2845           // don't truncate output if "chars" is zero or less
2846           size = strlen(s);
2847
2848           // dynamically correct text alignment
2849           pos->width = size * getFontWidth(font);
2850         }
2851
2852         s_cut = getStringCopyN(s, size);
2853
2854         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2855                     s_cut, font, mask_mode);
2856
2857         free(s_cut);
2858       }
2859     }
2860
2861     redraw_mask |= REDRAW_DOOR_1;
2862   }
2863
2864   SetGameStatus(GAME_MODE_PLAYING);
2865 }
2866
2867 void UpdateAndDisplayGameControlValues(void)
2868 {
2869   if (tape.deactivate_display)
2870     return;
2871
2872   UpdateGameControlValues();
2873   DisplayGameControlValues();
2874 }
2875
2876 void UpdateGameDoorValues(void)
2877 {
2878   UpdateGameControlValues();
2879 }
2880
2881 void DrawGameDoorValues(void)
2882 {
2883   DisplayGameControlValues();
2884 }
2885
2886
2887 // ============================================================================
2888 // InitGameEngine()
2889 // ----------------------------------------------------------------------------
2890 // initialize game engine due to level / tape version number
2891 // ============================================================================
2892
2893 static void InitGameEngine(void)
2894 {
2895   int i, j, k, l, x, y;
2896
2897   // set game engine from tape file when re-playing, else from level file
2898   game.engine_version = (tape.playing ? tape.engine_version :
2899                          level.game_version);
2900
2901   // set single or multi-player game mode (needed for re-playing tapes)
2902   game.team_mode = setup.team_mode;
2903
2904   if (tape.playing)
2905   {
2906     int num_players = 0;
2907
2908     for (i = 0; i < MAX_PLAYERS; i++)
2909       if (tape.player_participates[i])
2910         num_players++;
2911
2912     // multi-player tapes contain input data for more than one player
2913     game.team_mode = (num_players > 1);
2914   }
2915
2916 #if 0
2917   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2918         level.game_version);
2919   Debug("game:init:level", "          tape.file_version   == %06d",
2920         tape.file_version);
2921   Debug("game:init:level", "          tape.game_version   == %06d",
2922         tape.game_version);
2923   Debug("game:init:level", "          tape.engine_version == %06d",
2924         tape.engine_version);
2925   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2926         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2927 #endif
2928
2929   // --------------------------------------------------------------------------
2930   // set flags for bugs and changes according to active game engine version
2931   // --------------------------------------------------------------------------
2932
2933   /*
2934     Summary of bugfix:
2935     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2936
2937     Bug was introduced in version:
2938     2.0.1
2939
2940     Bug was fixed in version:
2941     4.2.0.0
2942
2943     Description:
2944     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2945     but the property "can fall" was missing, which caused some levels to be
2946     unsolvable. This was fixed in version 4.2.0.0.
2947
2948     Affected levels/tapes:
2949     An example for a tape that was fixed by this bugfix is tape 029 from the
2950     level set "rnd_sam_bateman".
2951     The wrong behaviour will still be used for all levels or tapes that were
2952     created/recorded with it. An example for this is tape 023 from the level
2953     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2954   */
2955
2956   boolean use_amoeba_dropping_cannot_fall_bug =
2957     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2958       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2959      (tape.playing &&
2960       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2961       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2962
2963   /*
2964     Summary of bugfix/change:
2965     Fixed move speed of elements entering or leaving magic wall.
2966
2967     Fixed/changed in version:
2968     2.0.1
2969
2970     Description:
2971     Before 2.0.1, move speed of elements entering or leaving magic wall was
2972     twice as fast as it is now.
2973     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2974
2975     Affected levels/tapes:
2976     The first condition is generally needed for all levels/tapes before version
2977     2.0.1, which might use the old behaviour before it was changed; known tapes
2978     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2979     The second condition is an exception from the above case and is needed for
2980     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2981     above, but before it was known that this change would break tapes like the
2982     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2983     although the engine version while recording maybe was before 2.0.1. There
2984     are a lot of tapes that are affected by this exception, like tape 006 from
2985     the level set "rnd_conor_mancone".
2986   */
2987
2988   boolean use_old_move_stepsize_for_magic_wall =
2989     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2990      !(tape.playing &&
2991        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2992        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2993
2994   /*
2995     Summary of bugfix/change:
2996     Fixed handling for custom elements that change when pushed by the player.
2997
2998     Fixed/changed in version:
2999     3.1.0
3000
3001     Description:
3002     Before 3.1.0, custom elements that "change when pushing" changed directly
3003     after the player started pushing them (until then handled in "DigField()").
3004     Since 3.1.0, these custom elements are not changed until the "pushing"
3005     move of the element is finished (now handled in "ContinueMoving()").
3006
3007     Affected levels/tapes:
3008     The first condition is generally needed for all levels/tapes before version
3009     3.1.0, which might use the old behaviour before it was changed; known tapes
3010     that are affected are some tapes from the level set "Walpurgis Gardens" by
3011     Jamie Cullen.
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 3.1.0 or
3014     above (including some development versions of 3.1.0), but before it was
3015     known that this change would break tapes like the above and was fixed in
3016     3.1.1, so that the changed behaviour was active although the engine version
3017     while recording maybe was before 3.1.0. There is at least one tape that is
3018     affected by this exception, which is the tape for the one-level set "Bug
3019     Machine" by Juergen Bonhagen.
3020   */
3021
3022   game.use_change_when_pushing_bug =
3023     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3024      !(tape.playing &&
3025        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3026        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3027
3028   /*
3029     Summary of bugfix/change:
3030     Fixed handling for blocking the field the player leaves when moving.
3031
3032     Fixed/changed in version:
3033     3.1.1
3034
3035     Description:
3036     Before 3.1.1, when "block last field when moving" was enabled, the field
3037     the player is leaving when moving was blocked for the time of the move,
3038     and was directly unblocked afterwards. This resulted in the last field
3039     being blocked for exactly one less than the number of frames of one player
3040     move. Additionally, even when blocking was disabled, the last field was
3041     blocked for exactly one frame.
3042     Since 3.1.1, due to changes in player movement handling, the last field
3043     is not blocked at all when blocking is disabled. When blocking is enabled,
3044     the last field is blocked for exactly the number of frames of one player
3045     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3046     last field is blocked for exactly one more than the number of frames of
3047     one player move.
3048
3049     Affected levels/tapes:
3050     (!!! yet to be determined -- probably many !!!)
3051   */
3052
3053   game.use_block_last_field_bug =
3054     (game.engine_version < VERSION_IDENT(3,1,1,0));
3055
3056   /* various special flags and settings for native Emerald Mine game engine */
3057
3058   game_em.use_single_button =
3059     (game.engine_version > VERSION_IDENT(4,0,0,2));
3060
3061   game_em.use_snap_key_bug =
3062     (game.engine_version < VERSION_IDENT(4,0,1,0));
3063
3064   game_em.use_random_bug =
3065     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3066
3067   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3068
3069   game_em.use_old_explosions            = use_old_em_engine;
3070   game_em.use_old_android               = use_old_em_engine;
3071   game_em.use_old_push_elements         = use_old_em_engine;
3072   game_em.use_old_push_into_acid        = use_old_em_engine;
3073
3074   game_em.use_wrap_around               = !use_old_em_engine;
3075
3076   // --------------------------------------------------------------------------
3077
3078   // set maximal allowed number of custom element changes per game frame
3079   game.max_num_changes_per_frame = 1;
3080
3081   // default scan direction: scan playfield from top/left to bottom/right
3082   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3083
3084   // dynamically adjust element properties according to game engine version
3085   InitElementPropertiesEngine(game.engine_version);
3086
3087   // ---------- initialize special element properties -------------------------
3088
3089   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3090   if (use_amoeba_dropping_cannot_fall_bug)
3091     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3092
3093   // ---------- initialize player's initial move delay ------------------------
3094
3095   // dynamically adjust player properties according to level information
3096   for (i = 0; i < MAX_PLAYERS; i++)
3097     game.initial_move_delay_value[i] =
3098       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3099
3100   // dynamically adjust player properties according to game engine version
3101   for (i = 0; i < MAX_PLAYERS; i++)
3102     game.initial_move_delay[i] =
3103       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3104        game.initial_move_delay_value[i] : 0);
3105
3106   // ---------- initialize player's initial push delay ------------------------
3107
3108   // dynamically adjust player properties according to game engine version
3109   game.initial_push_delay_value =
3110     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3111
3112   // ---------- initialize changing elements ----------------------------------
3113
3114   // initialize changing elements information
3115   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3116   {
3117     struct ElementInfo *ei = &element_info[i];
3118
3119     // this pointer might have been changed in the level editor
3120     ei->change = &ei->change_page[0];
3121
3122     if (!IS_CUSTOM_ELEMENT(i))
3123     {
3124       ei->change->target_element = EL_EMPTY_SPACE;
3125       ei->change->delay_fixed = 0;
3126       ei->change->delay_random = 0;
3127       ei->change->delay_frames = 1;
3128     }
3129
3130     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3131     {
3132       ei->has_change_event[j] = FALSE;
3133
3134       ei->event_page_nr[j] = 0;
3135       ei->event_page[j] = &ei->change_page[0];
3136     }
3137   }
3138
3139   // add changing elements from pre-defined list
3140   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3141   {
3142     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3143     struct ElementInfo *ei = &element_info[ch_delay->element];
3144
3145     ei->change->target_element       = ch_delay->target_element;
3146     ei->change->delay_fixed          = ch_delay->change_delay;
3147
3148     ei->change->pre_change_function  = ch_delay->pre_change_function;
3149     ei->change->change_function      = ch_delay->change_function;
3150     ei->change->post_change_function = ch_delay->post_change_function;
3151
3152     ei->change->can_change = TRUE;
3153     ei->change->can_change_or_has_action = TRUE;
3154
3155     ei->has_change_event[CE_DELAY] = TRUE;
3156
3157     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3158     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3159   }
3160
3161   // ---------- initialize internal run-time variables ------------------------
3162
3163   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3164   {
3165     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3166
3167     for (j = 0; j < ei->num_change_pages; j++)
3168     {
3169       ei->change_page[j].can_change_or_has_action =
3170         (ei->change_page[j].can_change |
3171          ei->change_page[j].has_action);
3172     }
3173   }
3174
3175   // add change events from custom element configuration
3176   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3177   {
3178     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3179
3180     for (j = 0; j < ei->num_change_pages; j++)
3181     {
3182       if (!ei->change_page[j].can_change_or_has_action)
3183         continue;
3184
3185       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3186       {
3187         // only add event page for the first page found with this event
3188         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3189         {
3190           ei->has_change_event[k] = TRUE;
3191
3192           ei->event_page_nr[k] = j;
3193           ei->event_page[k] = &ei->change_page[j];
3194         }
3195       }
3196     }
3197   }
3198
3199   // ---------- initialize reference elements in change conditions ------------
3200
3201   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3202   {
3203     int element = EL_CUSTOM_START + i;
3204     struct ElementInfo *ei = &element_info[element];
3205
3206     for (j = 0; j < ei->num_change_pages; j++)
3207     {
3208       int trigger_element = ei->change_page[j].initial_trigger_element;
3209
3210       if (trigger_element >= EL_PREV_CE_8 &&
3211           trigger_element <= EL_NEXT_CE_8)
3212         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3213
3214       ei->change_page[j].trigger_element = trigger_element;
3215     }
3216   }
3217
3218   // ---------- initialize run-time trigger player and element ----------------
3219
3220   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3221   {
3222     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3223
3224     for (j = 0; j < ei->num_change_pages; j++)
3225     {
3226       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3227       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3228       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3229       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3230       ei->change_page[j].actual_trigger_ce_value = 0;
3231       ei->change_page[j].actual_trigger_ce_score = 0;
3232     }
3233   }
3234
3235   // ---------- initialize trigger events -------------------------------------
3236
3237   // initialize trigger events information
3238   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3239     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3240       trigger_events[i][j] = FALSE;
3241
3242   // add trigger events from element change event properties
3243   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3244   {
3245     struct ElementInfo *ei = &element_info[i];
3246
3247     for (j = 0; j < ei->num_change_pages; j++)
3248     {
3249       if (!ei->change_page[j].can_change_or_has_action)
3250         continue;
3251
3252       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3253       {
3254         int trigger_element = ei->change_page[j].trigger_element;
3255
3256         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3257         {
3258           if (ei->change_page[j].has_event[k])
3259           {
3260             if (IS_GROUP_ELEMENT(trigger_element))
3261             {
3262               struct ElementGroupInfo *group =
3263                 element_info[trigger_element].group;
3264
3265               for (l = 0; l < group->num_elements_resolved; l++)
3266                 trigger_events[group->element_resolved[l]][k] = TRUE;
3267             }
3268             else if (trigger_element == EL_ANY_ELEMENT)
3269               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3270                 trigger_events[l][k] = TRUE;
3271             else
3272               trigger_events[trigger_element][k] = TRUE;
3273           }
3274         }
3275       }
3276     }
3277   }
3278
3279   // ---------- initialize push delay -----------------------------------------
3280
3281   // initialize push delay values to default
3282   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3283   {
3284     if (!IS_CUSTOM_ELEMENT(i))
3285     {
3286       // set default push delay values (corrected since version 3.0.7-1)
3287       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3288       {
3289         element_info[i].push_delay_fixed = 2;
3290         element_info[i].push_delay_random = 8;
3291       }
3292       else
3293       {
3294         element_info[i].push_delay_fixed = 8;
3295         element_info[i].push_delay_random = 8;
3296       }
3297     }
3298   }
3299
3300   // set push delay value for certain elements from pre-defined list
3301   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3302   {
3303     int e = push_delay_list[i].element;
3304
3305     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3306     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3307   }
3308
3309   // set push delay value for Supaplex elements for newer engine versions
3310   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3311   {
3312     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3313     {
3314       if (IS_SP_ELEMENT(i))
3315       {
3316         // set SP push delay to just enough to push under a falling zonk
3317         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3318
3319         element_info[i].push_delay_fixed  = delay;
3320         element_info[i].push_delay_random = 0;
3321       }
3322     }
3323   }
3324
3325   // ---------- initialize move stepsize --------------------------------------
3326
3327   // initialize move stepsize values to default
3328   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3329     if (!IS_CUSTOM_ELEMENT(i))
3330       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3331
3332   // set move stepsize value for certain elements from pre-defined list
3333   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3334   {
3335     int e = move_stepsize_list[i].element;
3336
3337     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3338
3339     // set move stepsize value for certain elements for older engine versions
3340     if (use_old_move_stepsize_for_magic_wall)
3341     {
3342       if (e == EL_MAGIC_WALL_FILLING ||
3343           e == EL_MAGIC_WALL_EMPTYING ||
3344           e == EL_BD_MAGIC_WALL_FILLING ||
3345           e == EL_BD_MAGIC_WALL_EMPTYING)
3346         element_info[e].move_stepsize *= 2;
3347     }
3348   }
3349
3350   // ---------- initialize collect score --------------------------------------
3351
3352   // initialize collect score values for custom elements from initial value
3353   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3354     if (IS_CUSTOM_ELEMENT(i))
3355       element_info[i].collect_score = element_info[i].collect_score_initial;
3356
3357   // ---------- initialize collect count --------------------------------------
3358
3359   // initialize collect count values for non-custom elements
3360   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3361     if (!IS_CUSTOM_ELEMENT(i))
3362       element_info[i].collect_count_initial = 0;
3363
3364   // add collect count values for all elements from pre-defined list
3365   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3366     element_info[collect_count_list[i].element].collect_count_initial =
3367       collect_count_list[i].count;
3368
3369   // ---------- initialize access direction -----------------------------------
3370
3371   // initialize access direction values to default (access from every side)
3372   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3373     if (!IS_CUSTOM_ELEMENT(i))
3374       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3375
3376   // set access direction value for certain elements from pre-defined list
3377   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3378     element_info[access_direction_list[i].element].access_direction =
3379       access_direction_list[i].direction;
3380
3381   // ---------- initialize explosion content ----------------------------------
3382   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3383   {
3384     if (IS_CUSTOM_ELEMENT(i))
3385       continue;
3386
3387     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3388     {
3389       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3390
3391       element_info[i].content.e[x][y] =
3392         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3393          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3394          i == EL_PLAYER_3 ? EL_EMERALD :
3395          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3396          i == EL_MOLE ? EL_EMERALD_RED :
3397          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3398          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3399          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3400          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3401          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3402          i == EL_WALL_EMERALD ? EL_EMERALD :
3403          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3404          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3405          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3406          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3407          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3408          i == EL_WALL_PEARL ? EL_PEARL :
3409          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3410          EL_EMPTY);
3411     }
3412   }
3413
3414   // ---------- initialize recursion detection --------------------------------
3415   recursion_loop_depth = 0;
3416   recursion_loop_detected = FALSE;
3417   recursion_loop_element = EL_UNDEFINED;
3418
3419   // ---------- initialize graphics engine ------------------------------------
3420   game.scroll_delay_value =
3421     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3422      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3423      !setup.forced_scroll_delay           ? 0 :
3424      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3425   game.scroll_delay_value =
3426     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3427
3428   // ---------- initialize game engine snapshots ------------------------------
3429   for (i = 0; i < MAX_PLAYERS; i++)
3430     game.snapshot.last_action[i] = 0;
3431   game.snapshot.changed_action = FALSE;
3432   game.snapshot.collected_item = FALSE;
3433   game.snapshot.mode =
3434     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3435      SNAPSHOT_MODE_EVERY_STEP :
3436      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3437      SNAPSHOT_MODE_EVERY_MOVE :
3438      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3439      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3440   game.snapshot.save_snapshot = FALSE;
3441
3442   // ---------- initialize level time for Supaplex engine ---------------------
3443   // Supaplex levels with time limit currently unsupported -- should be added
3444   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3445     level.time = 0;
3446
3447   // ---------- initialize flags for handling game actions --------------------
3448
3449   // set flags for game actions to default values
3450   game.use_key_actions = TRUE;
3451   game.use_mouse_actions = FALSE;
3452
3453   // when using Mirror Magic game engine, handle mouse events only
3454   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3455   {
3456     game.use_key_actions = FALSE;
3457     game.use_mouse_actions = TRUE;
3458   }
3459
3460   // check for custom elements with mouse click events
3461   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3462   {
3463     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3464     {
3465       int element = EL_CUSTOM_START + i;
3466
3467       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3468           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3469           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3470           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3471         game.use_mouse_actions = TRUE;
3472     }
3473   }
3474 }
3475
3476 static int get_num_special_action(int element, int action_first,
3477                                   int action_last)
3478 {
3479   int num_special_action = 0;
3480   int i, j;
3481
3482   for (i = action_first; i <= action_last; i++)
3483   {
3484     boolean found = FALSE;
3485
3486     for (j = 0; j < NUM_DIRECTIONS; j++)
3487       if (el_act_dir2img(element, i, j) !=
3488           el_act_dir2img(element, ACTION_DEFAULT, j))
3489         found = TRUE;
3490
3491     if (found)
3492       num_special_action++;
3493     else
3494       break;
3495   }
3496
3497   return num_special_action;
3498 }
3499
3500
3501 // ============================================================================
3502 // InitGame()
3503 // ----------------------------------------------------------------------------
3504 // initialize and start new game
3505 // ============================================================================
3506
3507 #if DEBUG_INIT_PLAYER
3508 static void DebugPrintPlayerStatus(char *message)
3509 {
3510   int i;
3511
3512   if (!options.debug)
3513     return;
3514
3515   Debug("game:init:player", "%s:", message);
3516
3517   for (i = 0; i < MAX_PLAYERS; i++)
3518   {
3519     struct PlayerInfo *player = &stored_player[i];
3520
3521     Debug("game:init:player",
3522           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3523           i + 1,
3524           player->present,
3525           player->connected,
3526           player->connected_locally,
3527           player->connected_network,
3528           player->active,
3529           (local_player == player ? " (local player)" : ""));
3530   }
3531 }
3532 #endif
3533
3534 void InitGame(void)
3535 {
3536   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3537   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3538   int fade_mask = REDRAW_FIELD;
3539
3540   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3541   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3542   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3543   int initial_move_dir = MV_DOWN;
3544   int i, j, x, y;
3545
3546   // required here to update video display before fading (FIX THIS)
3547   DrawMaskedBorder(REDRAW_DOOR_2);
3548
3549   if (!game.restart_level)
3550     CloseDoor(DOOR_CLOSE_1);
3551
3552   SetGameStatus(GAME_MODE_PLAYING);
3553
3554   if (level_editor_test_game)
3555     FadeSkipNextFadeOut();
3556   else
3557     FadeSetEnterScreen();
3558
3559   if (CheckFadeAll())
3560     fade_mask = REDRAW_ALL;
3561
3562   FadeLevelSoundsAndMusic();
3563
3564   ExpireSoundLoops(TRUE);
3565
3566   FadeOut(fade_mask);
3567
3568   if (level_editor_test_game)
3569     FadeSkipNextFadeIn();
3570
3571   // needed if different viewport properties defined for playing
3572   ChangeViewportPropertiesIfNeeded();
3573
3574   ClearField();
3575
3576   DrawCompleteVideoDisplay();
3577
3578   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3579
3580   InitGameEngine();
3581   InitGameControlValues();
3582
3583   if (tape.recording)
3584   {
3585     // initialize tape actions from game when recording tape
3586     tape.use_key_actions   = game.use_key_actions;
3587     tape.use_mouse_actions = game.use_mouse_actions;
3588
3589     // initialize visible playfield size when recording tape (for team mode)
3590     tape.scr_fieldx = SCR_FIELDX;
3591     tape.scr_fieldy = SCR_FIELDY;
3592   }
3593
3594   // don't play tapes over network
3595   network_playing = (network.enabled && !tape.playing);
3596
3597   for (i = 0; i < MAX_PLAYERS; i++)
3598   {
3599     struct PlayerInfo *player = &stored_player[i];
3600
3601     player->index_nr = i;
3602     player->index_bit = (1 << i);
3603     player->element_nr = EL_PLAYER_1 + i;
3604
3605     player->present = FALSE;
3606     player->active = FALSE;
3607     player->mapped = FALSE;
3608
3609     player->killed = FALSE;
3610     player->reanimated = FALSE;
3611     player->buried = FALSE;
3612
3613     player->action = 0;
3614     player->effective_action = 0;
3615     player->programmed_action = 0;
3616     player->snap_action = 0;
3617
3618     player->mouse_action.lx = 0;
3619     player->mouse_action.ly = 0;
3620     player->mouse_action.button = 0;
3621     player->mouse_action.button_hint = 0;
3622
3623     player->effective_mouse_action.lx = 0;
3624     player->effective_mouse_action.ly = 0;
3625     player->effective_mouse_action.button = 0;
3626     player->effective_mouse_action.button_hint = 0;
3627
3628     for (j = 0; j < MAX_NUM_KEYS; j++)
3629       player->key[j] = FALSE;
3630
3631     player->num_white_keys = 0;
3632
3633     player->dynabomb_count = 0;
3634     player->dynabomb_size = 1;
3635     player->dynabombs_left = 0;
3636     player->dynabomb_xl = FALSE;
3637
3638     player->MovDir = initial_move_dir;
3639     player->MovPos = 0;
3640     player->GfxPos = 0;
3641     player->GfxDir = initial_move_dir;
3642     player->GfxAction = ACTION_DEFAULT;
3643     player->Frame = 0;
3644     player->StepFrame = 0;
3645
3646     player->initial_element = player->element_nr;
3647     player->artwork_element =
3648       (level.use_artwork_element[i] ? level.artwork_element[i] :
3649        player->element_nr);
3650     player->use_murphy = FALSE;
3651
3652     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3653     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3654
3655     player->gravity = level.initial_player_gravity[i];
3656
3657     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3658
3659     player->actual_frame_counter = 0;
3660
3661     player->step_counter = 0;
3662
3663     player->last_move_dir = initial_move_dir;
3664
3665     player->is_active = FALSE;
3666
3667     player->is_waiting = FALSE;
3668     player->is_moving = FALSE;
3669     player->is_auto_moving = FALSE;
3670     player->is_digging = FALSE;
3671     player->is_snapping = FALSE;
3672     player->is_collecting = FALSE;
3673     player->is_pushing = FALSE;
3674     player->is_switching = FALSE;
3675     player->is_dropping = FALSE;
3676     player->is_dropping_pressed = FALSE;
3677
3678     player->is_bored = FALSE;
3679     player->is_sleeping = FALSE;
3680
3681     player->was_waiting = TRUE;
3682     player->was_moving = FALSE;
3683     player->was_snapping = FALSE;
3684     player->was_dropping = FALSE;
3685
3686     player->force_dropping = FALSE;
3687
3688     player->frame_counter_bored = -1;
3689     player->frame_counter_sleeping = -1;
3690
3691     player->anim_delay_counter = 0;
3692     player->post_delay_counter = 0;
3693
3694     player->dir_waiting = initial_move_dir;
3695     player->action_waiting = ACTION_DEFAULT;
3696     player->last_action_waiting = ACTION_DEFAULT;
3697     player->special_action_bored = ACTION_DEFAULT;
3698     player->special_action_sleeping = ACTION_DEFAULT;
3699
3700     player->switch_x = -1;
3701     player->switch_y = -1;
3702
3703     player->drop_x = -1;
3704     player->drop_y = -1;
3705
3706     player->show_envelope = 0;
3707
3708     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3709
3710     player->push_delay       = -1;      // initialized when pushing starts
3711     player->push_delay_value = game.initial_push_delay_value;
3712
3713     player->drop_delay = 0;
3714     player->drop_pressed_delay = 0;
3715
3716     player->last_jx = -1;
3717     player->last_jy = -1;
3718     player->jx = -1;
3719     player->jy = -1;
3720
3721     player->shield_normal_time_left = 0;
3722     player->shield_deadly_time_left = 0;
3723
3724     player->last_removed_element = EL_UNDEFINED;
3725
3726     player->inventory_infinite_element = EL_UNDEFINED;
3727     player->inventory_size = 0;
3728
3729     if (level.use_initial_inventory[i])
3730     {
3731       for (j = 0; j < level.initial_inventory_size[i]; j++)
3732       {
3733         int element = level.initial_inventory_content[i][j];
3734         int collect_count = element_info[element].collect_count_initial;
3735         int k;
3736
3737         if (!IS_CUSTOM_ELEMENT(element))
3738           collect_count = 1;
3739
3740         if (collect_count == 0)
3741           player->inventory_infinite_element = element;
3742         else
3743           for (k = 0; k < collect_count; k++)
3744             if (player->inventory_size < MAX_INVENTORY_SIZE)
3745               player->inventory_element[player->inventory_size++] = element;
3746       }
3747     }
3748
3749     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3750     SnapField(player, 0, 0);
3751
3752     map_player_action[i] = i;
3753   }
3754
3755   network_player_action_received = FALSE;
3756
3757   // initial null action
3758   if (network_playing)
3759     SendToServer_MovePlayer(MV_NONE);
3760
3761   FrameCounter = 0;
3762   TimeFrames = 0;
3763   TimePlayed = 0;
3764   TimeLeft = level.time;
3765   TapeTime = 0;
3766
3767   ScreenMovDir = MV_NONE;
3768   ScreenMovPos = 0;
3769   ScreenGfxPos = 0;
3770
3771   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3772
3773   game.robot_wheel_x = -1;
3774   game.robot_wheel_y = -1;
3775
3776   game.exit_x = -1;
3777   game.exit_y = -1;
3778
3779   game.all_players_gone = FALSE;
3780
3781   game.LevelSolved = FALSE;
3782   game.GameOver = FALSE;
3783
3784   game.GamePlayed = !tape.playing;
3785
3786   game.LevelSolved_GameWon = FALSE;
3787   game.LevelSolved_GameEnd = FALSE;
3788   game.LevelSolved_SaveTape = FALSE;
3789   game.LevelSolved_SaveScore = FALSE;
3790
3791   game.LevelSolved_CountingTime = 0;
3792   game.LevelSolved_CountingScore = 0;
3793   game.LevelSolved_CountingHealth = 0;
3794
3795   game.panel.active = TRUE;
3796
3797   game.no_time_limit = (level.time == 0);
3798
3799   game.yamyam_content_nr = 0;
3800   game.robot_wheel_active = FALSE;
3801   game.magic_wall_active = FALSE;
3802   game.magic_wall_time_left = 0;
3803   game.light_time_left = 0;
3804   game.timegate_time_left = 0;
3805   game.switchgate_pos = 0;
3806   game.wind_direction = level.wind_direction_initial;
3807
3808   game.score = 0;
3809   game.score_final = 0;
3810
3811   game.health = MAX_HEALTH;
3812   game.health_final = MAX_HEALTH;
3813
3814   game.gems_still_needed = level.gems_needed;
3815   game.sokoban_fields_still_needed = 0;
3816   game.sokoban_objects_still_needed = 0;
3817   game.lights_still_needed = 0;
3818   game.players_still_needed = 0;
3819   game.friends_still_needed = 0;
3820
3821   game.lenses_time_left = 0;
3822   game.magnify_time_left = 0;
3823
3824   game.ball_active = level.ball_active_initial;
3825   game.ball_content_nr = 0;
3826
3827   game.explosions_delayed = TRUE;
3828
3829   game.envelope_active = FALSE;
3830
3831   for (i = 0; i < NUM_BELTS; i++)
3832   {
3833     game.belt_dir[i] = MV_NONE;
3834     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3835   }
3836
3837   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3838     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3839
3840 #if DEBUG_INIT_PLAYER
3841   DebugPrintPlayerStatus("Player status at level initialization");
3842 #endif
3843
3844   SCAN_PLAYFIELD(x, y)
3845   {
3846     Tile[x][y] = Last[x][y] = level.field[x][y];
3847     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3848     ChangeDelay[x][y] = 0;
3849     ChangePage[x][y] = -1;
3850     CustomValue[x][y] = 0;              // initialized in InitField()
3851     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3852     AmoebaNr[x][y] = 0;
3853     WasJustMoving[x][y] = 0;
3854     WasJustFalling[x][y] = 0;
3855     CheckCollision[x][y] = 0;
3856     CheckImpact[x][y] = 0;
3857     Stop[x][y] = FALSE;
3858     Pushed[x][y] = FALSE;
3859
3860     ChangeCount[x][y] = 0;
3861     ChangeEvent[x][y] = -1;
3862
3863     ExplodePhase[x][y] = 0;
3864     ExplodeDelay[x][y] = 0;
3865     ExplodeField[x][y] = EX_TYPE_NONE;
3866
3867     RunnerVisit[x][y] = 0;
3868     PlayerVisit[x][y] = 0;
3869
3870     GfxFrame[x][y] = 0;
3871     GfxRandom[x][y] = INIT_GFX_RANDOM();
3872     GfxElement[x][y] = EL_UNDEFINED;
3873     GfxAction[x][y] = ACTION_DEFAULT;
3874     GfxDir[x][y] = MV_NONE;
3875     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3876   }
3877
3878   SCAN_PLAYFIELD(x, y)
3879   {
3880     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3881       emulate_bd = FALSE;
3882     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3883       emulate_sb = FALSE;
3884     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3885       emulate_sp = FALSE;
3886
3887     InitField(x, y, TRUE);
3888
3889     ResetGfxAnimation(x, y);
3890   }
3891
3892   InitBeltMovement();
3893
3894   for (i = 0; i < MAX_PLAYERS; i++)
3895   {
3896     struct PlayerInfo *player = &stored_player[i];
3897
3898     // set number of special actions for bored and sleeping animation
3899     player->num_special_action_bored =
3900       get_num_special_action(player->artwork_element,
3901                              ACTION_BORING_1, ACTION_BORING_LAST);
3902     player->num_special_action_sleeping =
3903       get_num_special_action(player->artwork_element,
3904                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3905   }
3906
3907   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3908                     emulate_sb ? EMU_SOKOBAN :
3909                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3910
3911   // initialize type of slippery elements
3912   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3913   {
3914     if (!IS_CUSTOM_ELEMENT(i))
3915     {
3916       // default: elements slip down either to the left or right randomly
3917       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3918
3919       // SP style elements prefer to slip down on the left side
3920       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3921         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3922
3923       // BD style elements prefer to slip down on the left side
3924       if (game.emulation == EMU_BOULDERDASH)
3925         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3926     }
3927   }
3928
3929   // initialize explosion and ignition delay
3930   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3931   {
3932     if (!IS_CUSTOM_ELEMENT(i))
3933     {
3934       int num_phase = 8;
3935       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3936                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3937                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3938       int last_phase = (num_phase + 1) * delay;
3939       int half_phase = (num_phase / 2) * delay;
3940
3941       element_info[i].explosion_delay = last_phase - 1;
3942       element_info[i].ignition_delay = half_phase;
3943
3944       if (i == EL_BLACK_ORB)
3945         element_info[i].ignition_delay = 1;
3946     }
3947   }
3948
3949   // correct non-moving belts to start moving left
3950   for (i = 0; i < NUM_BELTS; i++)
3951     if (game.belt_dir[i] == MV_NONE)
3952       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3953
3954 #if USE_NEW_PLAYER_ASSIGNMENTS
3955   // use preferred player also in local single-player mode
3956   if (!network.enabled && !game.team_mode)
3957   {
3958     int new_index_nr = setup.network_player_nr;
3959
3960     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3961     {
3962       for (i = 0; i < MAX_PLAYERS; i++)
3963         stored_player[i].connected_locally = FALSE;
3964
3965       stored_player[new_index_nr].connected_locally = TRUE;
3966     }
3967   }
3968
3969   for (i = 0; i < MAX_PLAYERS; i++)
3970   {
3971     stored_player[i].connected = FALSE;
3972
3973     // in network game mode, the local player might not be the first player
3974     if (stored_player[i].connected_locally)
3975       local_player = &stored_player[i];
3976   }
3977
3978   if (!network.enabled)
3979     local_player->connected = TRUE;
3980
3981   if (tape.playing)
3982   {
3983     for (i = 0; i < MAX_PLAYERS; i++)
3984       stored_player[i].connected = tape.player_participates[i];
3985   }
3986   else if (network.enabled)
3987   {
3988     // add team mode players connected over the network (needed for correct
3989     // assignment of player figures from level to locally playing players)
3990
3991     for (i = 0; i < MAX_PLAYERS; i++)
3992       if (stored_player[i].connected_network)
3993         stored_player[i].connected = TRUE;
3994   }
3995   else if (game.team_mode)
3996   {
3997     // try to guess locally connected team mode players (needed for correct
3998     // assignment of player figures from level to locally playing players)
3999
4000     for (i = 0; i < MAX_PLAYERS; i++)
4001       if (setup.input[i].use_joystick ||
4002           setup.input[i].key.left != KSYM_UNDEFINED)
4003         stored_player[i].connected = TRUE;
4004   }
4005
4006 #if DEBUG_INIT_PLAYER
4007   DebugPrintPlayerStatus("Player status after level initialization");
4008 #endif
4009
4010 #if DEBUG_INIT_PLAYER
4011   Debug("game:init:player", "Reassigning players ...");
4012 #endif
4013
4014   // check if any connected player was not found in playfield
4015   for (i = 0; i < MAX_PLAYERS; i++)
4016   {
4017     struct PlayerInfo *player = &stored_player[i];
4018
4019     if (player->connected && !player->present)
4020     {
4021       struct PlayerInfo *field_player = NULL;
4022
4023 #if DEBUG_INIT_PLAYER
4024       Debug("game:init:player",
4025             "- looking for field player for player %d ...", i + 1);
4026 #endif
4027
4028       // assign first free player found that is present in the playfield
4029
4030       // first try: look for unmapped playfield player that is not connected
4031       for (j = 0; j < MAX_PLAYERS; j++)
4032         if (field_player == NULL &&
4033             stored_player[j].present &&
4034             !stored_player[j].mapped &&
4035             !stored_player[j].connected)
4036           field_player = &stored_player[j];
4037
4038       // second try: look for *any* unmapped playfield player
4039       for (j = 0; j < MAX_PLAYERS; j++)
4040         if (field_player == NULL &&
4041             stored_player[j].present &&
4042             !stored_player[j].mapped)
4043           field_player = &stored_player[j];
4044
4045       if (field_player != NULL)
4046       {
4047         int jx = field_player->jx, jy = field_player->jy;
4048
4049 #if DEBUG_INIT_PLAYER
4050         Debug("game:init:player", "- found player %d",
4051               field_player->index_nr + 1);
4052 #endif
4053
4054         player->present = FALSE;
4055         player->active = FALSE;
4056
4057         field_player->present = TRUE;
4058         field_player->active = TRUE;
4059
4060         /*
4061         player->initial_element = field_player->initial_element;
4062         player->artwork_element = field_player->artwork_element;
4063
4064         player->block_last_field       = field_player->block_last_field;
4065         player->block_delay_adjustment = field_player->block_delay_adjustment;
4066         */
4067
4068         StorePlayer[jx][jy] = field_player->element_nr;
4069
4070         field_player->jx = field_player->last_jx = jx;
4071         field_player->jy = field_player->last_jy = jy;
4072
4073         if (local_player == player)
4074           local_player = field_player;
4075
4076         map_player_action[field_player->index_nr] = i;
4077
4078         field_player->mapped = TRUE;
4079
4080 #if DEBUG_INIT_PLAYER
4081         Debug("game:init:player", "- map_player_action[%d] == %d",
4082               field_player->index_nr + 1, i + 1);
4083 #endif
4084       }
4085     }
4086
4087     if (player->connected && player->present)
4088       player->mapped = TRUE;
4089   }
4090
4091 #if DEBUG_INIT_PLAYER
4092   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4093 #endif
4094
4095 #else
4096
4097   // check if any connected player was not found in playfield
4098   for (i = 0; i < MAX_PLAYERS; i++)
4099   {
4100     struct PlayerInfo *player = &stored_player[i];
4101
4102     if (player->connected && !player->present)
4103     {
4104       for (j = 0; j < MAX_PLAYERS; j++)
4105       {
4106         struct PlayerInfo *field_player = &stored_player[j];
4107         int jx = field_player->jx, jy = field_player->jy;
4108
4109         // assign first free player found that is present in the playfield
4110         if (field_player->present && !field_player->connected)
4111         {
4112           player->present = TRUE;
4113           player->active = TRUE;
4114
4115           field_player->present = FALSE;
4116           field_player->active = FALSE;
4117
4118           player->initial_element = field_player->initial_element;
4119           player->artwork_element = field_player->artwork_element;
4120
4121           player->block_last_field       = field_player->block_last_field;
4122           player->block_delay_adjustment = field_player->block_delay_adjustment;
4123
4124           StorePlayer[jx][jy] = player->element_nr;
4125
4126           player->jx = player->last_jx = jx;
4127           player->jy = player->last_jy = jy;
4128
4129           break;
4130         }
4131       }
4132     }
4133   }
4134 #endif
4135
4136 #if 0
4137   Debug("game:init:player", "local_player->present == %d",
4138         local_player->present);
4139 #endif
4140
4141   // set focus to local player for network games, else to all players
4142   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4143   game.centered_player_nr_next = game.centered_player_nr;
4144   game.set_centered_player = FALSE;
4145   game.set_centered_player_wrap = FALSE;
4146
4147   if (network_playing && tape.recording)
4148   {
4149     // store client dependent player focus when recording network games
4150     tape.centered_player_nr_next = game.centered_player_nr_next;
4151     tape.set_centered_player = TRUE;
4152   }
4153
4154   if (tape.playing)
4155   {
4156     // when playing a tape, eliminate all players who do not participate
4157
4158 #if USE_NEW_PLAYER_ASSIGNMENTS
4159
4160     if (!game.team_mode)
4161     {
4162       for (i = 0; i < MAX_PLAYERS; i++)
4163       {
4164         if (stored_player[i].active &&
4165             !tape.player_participates[map_player_action[i]])
4166         {
4167           struct PlayerInfo *player = &stored_player[i];
4168           int jx = player->jx, jy = player->jy;
4169
4170 #if DEBUG_INIT_PLAYER
4171           Debug("game:init:player", "Removing player %d at (%d, %d)",
4172                 i + 1, jx, jy);
4173 #endif
4174
4175           player->active = FALSE;
4176           StorePlayer[jx][jy] = 0;
4177           Tile[jx][jy] = EL_EMPTY;
4178         }
4179       }
4180     }
4181
4182 #else
4183
4184     for (i = 0; i < MAX_PLAYERS; i++)
4185     {
4186       if (stored_player[i].active &&
4187           !tape.player_participates[i])
4188       {
4189         struct PlayerInfo *player = &stored_player[i];
4190         int jx = player->jx, jy = player->jy;
4191
4192         player->active = FALSE;
4193         StorePlayer[jx][jy] = 0;
4194         Tile[jx][jy] = EL_EMPTY;
4195       }
4196     }
4197 #endif
4198   }
4199   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4200   {
4201     // when in single player mode, eliminate all but the local player
4202
4203     for (i = 0; i < MAX_PLAYERS; i++)
4204     {
4205       struct PlayerInfo *player = &stored_player[i];
4206
4207       if (player->active && player != local_player)
4208       {
4209         int jx = player->jx, jy = player->jy;
4210
4211         player->active = FALSE;
4212         player->present = FALSE;
4213
4214         StorePlayer[jx][jy] = 0;
4215         Tile[jx][jy] = EL_EMPTY;
4216       }
4217     }
4218   }
4219
4220   for (i = 0; i < MAX_PLAYERS; i++)
4221     if (stored_player[i].active)
4222       game.players_still_needed++;
4223
4224   if (level.solved_by_one_player)
4225     game.players_still_needed = 1;
4226
4227   // when recording the game, store which players take part in the game
4228   if (tape.recording)
4229   {
4230 #if USE_NEW_PLAYER_ASSIGNMENTS
4231     for (i = 0; i < MAX_PLAYERS; i++)
4232       if (stored_player[i].connected)
4233         tape.player_participates[i] = TRUE;
4234 #else
4235     for (i = 0; i < MAX_PLAYERS; i++)
4236       if (stored_player[i].active)
4237         tape.player_participates[i] = TRUE;
4238 #endif
4239   }
4240
4241 #if DEBUG_INIT_PLAYER
4242   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4243 #endif
4244
4245   if (BorderElement == EL_EMPTY)
4246   {
4247     SBX_Left = 0;
4248     SBX_Right = lev_fieldx - SCR_FIELDX;
4249     SBY_Upper = 0;
4250     SBY_Lower = lev_fieldy - SCR_FIELDY;
4251   }
4252   else
4253   {
4254     SBX_Left = -1;
4255     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4256     SBY_Upper = -1;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4258   }
4259
4260   if (full_lev_fieldx <= SCR_FIELDX)
4261     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4262   if (full_lev_fieldy <= SCR_FIELDY)
4263     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4264
4265   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4266     SBX_Left--;
4267   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4268     SBY_Upper--;
4269
4270   // if local player not found, look for custom element that might create
4271   // the player (make some assumptions about the right custom element)
4272   if (!local_player->present)
4273   {
4274     int start_x = 0, start_y = 0;
4275     int found_rating = 0;
4276     int found_element = EL_UNDEFINED;
4277     int player_nr = local_player->index_nr;
4278
4279     SCAN_PLAYFIELD(x, y)
4280     {
4281       int element = Tile[x][y];
4282       int content;
4283       int xx, yy;
4284       boolean is_player;
4285
4286       if (level.use_start_element[player_nr] &&
4287           level.start_element[player_nr] == element &&
4288           found_rating < 4)
4289       {
4290         start_x = x;
4291         start_y = y;
4292
4293         found_rating = 4;
4294         found_element = element;
4295       }
4296
4297       if (!IS_CUSTOM_ELEMENT(element))
4298         continue;
4299
4300       if (CAN_CHANGE(element))
4301       {
4302         for (i = 0; i < element_info[element].num_change_pages; i++)
4303         {
4304           // check for player created from custom element as single target
4305           content = element_info[element].change_page[i].target_element;
4306           is_player = ELEM_IS_PLAYER(content);
4307
4308           if (is_player && (found_rating < 3 ||
4309                             (found_rating == 3 && element < found_element)))
4310           {
4311             start_x = x;
4312             start_y = y;
4313
4314             found_rating = 3;
4315             found_element = element;
4316           }
4317         }
4318       }
4319
4320       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4321       {
4322         // check for player created from custom element as explosion content
4323         content = element_info[element].content.e[xx][yy];
4324         is_player = ELEM_IS_PLAYER(content);
4325
4326         if (is_player && (found_rating < 2 ||
4327                           (found_rating == 2 && element < found_element)))
4328         {
4329           start_x = x + xx - 1;
4330           start_y = y + yy - 1;
4331
4332           found_rating = 2;
4333           found_element = element;
4334         }
4335
4336         if (!CAN_CHANGE(element))
4337           continue;
4338
4339         for (i = 0; i < element_info[element].num_change_pages; i++)
4340         {
4341           // check for player created from custom element as extended target
4342           content =
4343             element_info[element].change_page[i].target_content.e[xx][yy];
4344
4345           is_player = ELEM_IS_PLAYER(content);
4346
4347           if (is_player && (found_rating < 1 ||
4348                             (found_rating == 1 && element < found_element)))
4349           {
4350             start_x = x + xx - 1;
4351             start_y = y + yy - 1;
4352
4353             found_rating = 1;
4354             found_element = element;
4355           }
4356         }
4357       }
4358     }
4359
4360     scroll_x = SCROLL_POSITION_X(start_x);
4361     scroll_y = SCROLL_POSITION_Y(start_y);
4362   }
4363   else
4364   {
4365     scroll_x = SCROLL_POSITION_X(local_player->jx);
4366     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4367   }
4368
4369   // !!! FIX THIS (START) !!!
4370   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4371   {
4372     InitGameEngine_EM();
4373   }
4374   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4375   {
4376     InitGameEngine_SP();
4377   }
4378   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4379   {
4380     InitGameEngine_MM();
4381   }
4382   else
4383   {
4384     DrawLevel(REDRAW_FIELD);
4385     DrawAllPlayers();
4386
4387     // after drawing the level, correct some elements
4388     if (game.timegate_time_left == 0)
4389       CloseAllOpenTimegates();
4390   }
4391
4392   // blit playfield from scroll buffer to normal back buffer for fading in
4393   BlitScreenToBitmap(backbuffer);
4394   // !!! FIX THIS (END) !!!
4395
4396   DrawMaskedBorder(fade_mask);
4397
4398   FadeIn(fade_mask);
4399
4400 #if 1
4401   // full screen redraw is required at this point in the following cases:
4402   // - special editor door undrawn when game was started from level editor
4403   // - drawing area (playfield) was changed and has to be removed completely
4404   redraw_mask = REDRAW_ALL;
4405   BackToFront();
4406 #endif
4407
4408   if (!game.restart_level)
4409   {
4410     // copy default game door content to main double buffer
4411
4412     // !!! CHECK AGAIN !!!
4413     SetPanelBackground();
4414     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4415     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4416   }
4417
4418   SetPanelBackground();
4419   SetDrawBackgroundMask(REDRAW_DOOR_1);
4420
4421   UpdateAndDisplayGameControlValues();
4422
4423   if (!game.restart_level)
4424   {
4425     UnmapGameButtons();
4426     UnmapTapeButtons();
4427
4428     FreeGameButtons();
4429     CreateGameButtons();
4430
4431     MapGameButtons();
4432     MapTapeButtons();
4433
4434     // copy actual game door content to door double buffer for OpenDoor()
4435     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4436
4437     OpenDoor(DOOR_OPEN_ALL);
4438
4439     KeyboardAutoRepeatOffUnlessAutoplay();
4440
4441 #if DEBUG_INIT_PLAYER
4442     DebugPrintPlayerStatus("Player status (final)");
4443 #endif
4444   }
4445
4446   UnmapAllGadgets();
4447
4448   MapGameButtons();
4449   MapTapeButtons();
4450
4451   if (!game.restart_level && !tape.playing)
4452   {
4453     LevelStats_incPlayed(level_nr);
4454
4455     SaveLevelSetup_SeriesInfo();
4456   }
4457
4458   game.restart_level = FALSE;
4459   game.restart_game_message = NULL;
4460
4461   game.request_active = FALSE;
4462   game.request_active_or_moving = FALSE;
4463
4464   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4465     InitGameActions_MM();
4466
4467   SaveEngineSnapshotToListInitial();
4468
4469   if (!game.restart_level)
4470   {
4471     PlaySound(SND_GAME_STARTING);
4472
4473     if (setup.sound_music)
4474       PlayLevelMusic();
4475   }
4476 }
4477
4478 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4479                         int actual_player_x, int actual_player_y)
4480 {
4481   // this is used for non-R'n'D game engines to update certain engine values
4482
4483   // needed to determine if sounds are played within the visible screen area
4484   scroll_x = actual_scroll_x;
4485   scroll_y = actual_scroll_y;
4486
4487   // needed to get player position for "follow finger" playing input method
4488   local_player->jx = actual_player_x;
4489   local_player->jy = actual_player_y;
4490 }
4491
4492 void InitMovDir(int x, int y)
4493 {
4494   int i, element = Tile[x][y];
4495   static int xy[4][2] =
4496   {
4497     {  0, +1 },
4498     { +1,  0 },
4499     {  0, -1 },
4500     { -1,  0 }
4501   };
4502   static int direction[3][4] =
4503   {
4504     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4505     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4506     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4507   };
4508
4509   switch (element)
4510   {
4511     case EL_BUG_RIGHT:
4512     case EL_BUG_UP:
4513     case EL_BUG_LEFT:
4514     case EL_BUG_DOWN:
4515       Tile[x][y] = EL_BUG;
4516       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4517       break;
4518
4519     case EL_SPACESHIP_RIGHT:
4520     case EL_SPACESHIP_UP:
4521     case EL_SPACESHIP_LEFT:
4522     case EL_SPACESHIP_DOWN:
4523       Tile[x][y] = EL_SPACESHIP;
4524       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4525       break;
4526
4527     case EL_BD_BUTTERFLY_RIGHT:
4528     case EL_BD_BUTTERFLY_UP:
4529     case EL_BD_BUTTERFLY_LEFT:
4530     case EL_BD_BUTTERFLY_DOWN:
4531       Tile[x][y] = EL_BD_BUTTERFLY;
4532       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4533       break;
4534
4535     case EL_BD_FIREFLY_RIGHT:
4536     case EL_BD_FIREFLY_UP:
4537     case EL_BD_FIREFLY_LEFT:
4538     case EL_BD_FIREFLY_DOWN:
4539       Tile[x][y] = EL_BD_FIREFLY;
4540       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4541       break;
4542
4543     case EL_PACMAN_RIGHT:
4544     case EL_PACMAN_UP:
4545     case EL_PACMAN_LEFT:
4546     case EL_PACMAN_DOWN:
4547       Tile[x][y] = EL_PACMAN;
4548       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4549       break;
4550
4551     case EL_YAMYAM_LEFT:
4552     case EL_YAMYAM_RIGHT:
4553     case EL_YAMYAM_UP:
4554     case EL_YAMYAM_DOWN:
4555       Tile[x][y] = EL_YAMYAM;
4556       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4557       break;
4558
4559     case EL_SP_SNIKSNAK:
4560       MovDir[x][y] = MV_UP;
4561       break;
4562
4563     case EL_SP_ELECTRON:
4564       MovDir[x][y] = MV_LEFT;
4565       break;
4566
4567     case EL_MOLE_LEFT:
4568     case EL_MOLE_RIGHT:
4569     case EL_MOLE_UP:
4570     case EL_MOLE_DOWN:
4571       Tile[x][y] = EL_MOLE;
4572       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4573       break;
4574
4575     case EL_SPRING_LEFT:
4576     case EL_SPRING_RIGHT:
4577       Tile[x][y] = EL_SPRING;
4578       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4579       break;
4580
4581     default:
4582       if (IS_CUSTOM_ELEMENT(element))
4583       {
4584         struct ElementInfo *ei = &element_info[element];
4585         int move_direction_initial = ei->move_direction_initial;
4586         int move_pattern = ei->move_pattern;
4587
4588         if (move_direction_initial == MV_START_PREVIOUS)
4589         {
4590           if (MovDir[x][y] != MV_NONE)
4591             return;
4592
4593           move_direction_initial = MV_START_AUTOMATIC;
4594         }
4595
4596         if (move_direction_initial == MV_START_RANDOM)
4597           MovDir[x][y] = 1 << RND(4);
4598         else if (move_direction_initial & MV_ANY_DIRECTION)
4599           MovDir[x][y] = move_direction_initial;
4600         else if (move_pattern == MV_ALL_DIRECTIONS ||
4601                  move_pattern == MV_TURNING_LEFT ||
4602                  move_pattern == MV_TURNING_RIGHT ||
4603                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4604                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4605                  move_pattern == MV_TURNING_RANDOM)
4606           MovDir[x][y] = 1 << RND(4);
4607         else if (move_pattern == MV_HORIZONTAL)
4608           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4609         else if (move_pattern == MV_VERTICAL)
4610           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4611         else if (move_pattern & MV_ANY_DIRECTION)
4612           MovDir[x][y] = element_info[element].move_pattern;
4613         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4614                  move_pattern == MV_ALONG_RIGHT_SIDE)
4615         {
4616           // use random direction as default start direction
4617           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4618             MovDir[x][y] = 1 << RND(4);
4619
4620           for (i = 0; i < NUM_DIRECTIONS; i++)
4621           {
4622             int x1 = x + xy[i][0];
4623             int y1 = y + xy[i][1];
4624
4625             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4626             {
4627               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4628                 MovDir[x][y] = direction[0][i];
4629               else
4630                 MovDir[x][y] = direction[1][i];
4631
4632               break;
4633             }
4634           }
4635         }                
4636       }
4637       else
4638       {
4639         MovDir[x][y] = 1 << RND(4);
4640
4641         if (element != EL_BUG &&
4642             element != EL_SPACESHIP &&
4643             element != EL_BD_BUTTERFLY &&
4644             element != EL_BD_FIREFLY)
4645           break;
4646
4647         for (i = 0; i < NUM_DIRECTIONS; i++)
4648         {
4649           int x1 = x + xy[i][0];
4650           int y1 = y + xy[i][1];
4651
4652           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4653           {
4654             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4655             {
4656               MovDir[x][y] = direction[0][i];
4657               break;
4658             }
4659             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4660                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4661             {
4662               MovDir[x][y] = direction[1][i];
4663               break;
4664             }
4665           }
4666         }
4667       }
4668       break;
4669   }
4670
4671   GfxDir[x][y] = MovDir[x][y];
4672 }
4673
4674 void InitAmoebaNr(int x, int y)
4675 {
4676   int i;
4677   int group_nr = AmoebaNeighbourNr(x, y);
4678
4679   if (group_nr == 0)
4680   {
4681     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4682     {
4683       if (AmoebaCnt[i] == 0)
4684       {
4685         group_nr = i;
4686         break;
4687       }
4688     }
4689   }
4690
4691   AmoebaNr[x][y] = group_nr;
4692   AmoebaCnt[group_nr]++;
4693   AmoebaCnt2[group_nr]++;
4694 }
4695
4696 static void LevelSolved(void)
4697 {
4698   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4699       game.players_still_needed > 0)
4700     return;
4701
4702   game.LevelSolved = TRUE;
4703   game.GameOver = TRUE;
4704
4705   game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4706                       game_em.lev->score :
4707                       level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4708                       game_mm.score :
4709                       game.score);
4710   game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4711                        MM_HEALTH(game_mm.laser_overload_value) :
4712                        game.health);
4713
4714   game.LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed : TimeLeft);
4715   game.LevelSolved_CountingScore = game.score_final;
4716   game.LevelSolved_CountingHealth = game.health_final;
4717 }
4718
4719 void GameWon(void)
4720 {
4721   static int time_count_steps;
4722   static int time, time_final;
4723   static float score, score_final; // needed for time score < 10 for 10 seconds
4724   static int health, health_final;
4725   static int game_over_delay_1 = 0;
4726   static int game_over_delay_2 = 0;
4727   static int game_over_delay_3 = 0;
4728   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4729   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4730
4731   if (!game.LevelSolved_GameWon)
4732   {
4733     int i;
4734
4735     // do not start end game actions before the player stops moving (to exit)
4736     if (local_player->active && local_player->MovPos)
4737       return;
4738
4739     game.LevelSolved_GameWon = TRUE;
4740     game.LevelSolved_SaveTape = tape.recording;
4741     game.LevelSolved_SaveScore = !tape.playing;
4742
4743     if (!tape.playing)
4744     {
4745       LevelStats_incSolved(level_nr);
4746
4747       SaveLevelSetup_SeriesInfo();
4748     }
4749
4750     if (tape.auto_play)         // tape might already be stopped here
4751       tape.auto_play_level_solved = TRUE;
4752
4753     TapeStop();
4754
4755     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4756     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4757     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4758
4759     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4760     score = score_final = game.score_final;
4761     health = health_final = game.health_final;
4762
4763     if (time_score > 0)
4764     {
4765       int time_frames = 0;
4766
4767       if (TimeLeft > 0)
4768       {
4769         time_final = 0;
4770         time_frames = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4771       }
4772       else if (game.no_time_limit && TimePlayed < 999)
4773       {
4774         time_final = 999;
4775         time_frames = (999 - TimePlayed) * FRAMES_PER_SECOND - TimeFrames;
4776       }
4777
4778       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4779
4780       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4781
4782       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4783       {
4784         health_final = 0;
4785         score_final += health * time_score;
4786       }
4787
4788       game.score_final = score_final;
4789       game.health_final = health_final;
4790     }
4791
4792     if (level_editor_test_game || !setup.count_score_after_game)
4793     {
4794       time = time_final;
4795       score = score_final;
4796
4797       game.LevelSolved_CountingTime = time;
4798       game.LevelSolved_CountingScore = score;
4799
4800       game_panel_controls[GAME_PANEL_TIME].value = time;
4801       game_panel_controls[GAME_PANEL_SCORE].value = score;
4802
4803       DisplayGameControlValues();
4804     }
4805
4806     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4807     {
4808       // check if last player has left the level
4809       if (game.exit_x >= 0 &&
4810           game.exit_y >= 0)
4811       {
4812         int x = game.exit_x;
4813         int y = game.exit_y;
4814         int element = Tile[x][y];
4815
4816         // close exit door after last player
4817         if ((game.all_players_gone &&
4818              (element == EL_EXIT_OPEN ||
4819               element == EL_SP_EXIT_OPEN ||
4820               element == EL_STEEL_EXIT_OPEN)) ||
4821             element == EL_EM_EXIT_OPEN ||
4822             element == EL_EM_STEEL_EXIT_OPEN)
4823         {
4824
4825           Tile[x][y] =
4826             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4827              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4828              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4829              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4830              EL_EM_STEEL_EXIT_CLOSING);
4831
4832           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4833         }
4834
4835         // player disappears
4836         DrawLevelField(x, y);
4837       }
4838
4839       for (i = 0; i < MAX_PLAYERS; i++)
4840       {
4841         struct PlayerInfo *player = &stored_player[i];
4842
4843         if (player->present)
4844         {
4845           RemovePlayer(player);
4846
4847           // player disappears
4848           DrawLevelField(player->jx, player->jy);
4849         }
4850       }
4851     }
4852
4853     PlaySound(SND_GAME_WINNING);
4854   }
4855
4856   if (setup.count_score_after_game)
4857   {
4858     if (time != time_final)
4859     {
4860       if (game_over_delay_1 > 0)
4861       {
4862         game_over_delay_1--;
4863
4864         return;
4865       }
4866
4867       int time_to_go = ABS(time_final - time);
4868       int time_count_dir = (time < time_final ? +1 : -1);
4869
4870       if (time_to_go < time_count_steps)
4871         time_count_steps = 1;
4872
4873       time  += time_count_steps * time_count_dir;
4874       score += time_count_steps * time_score;
4875
4876       // set final score to correct rounding differences after counting score
4877       if (time == time_final)
4878         score = score_final;
4879
4880       game.LevelSolved_CountingTime = time;
4881       game.LevelSolved_CountingScore = score;
4882
4883       game_panel_controls[GAME_PANEL_TIME].value = time;
4884       game_panel_controls[GAME_PANEL_SCORE].value = score;
4885
4886       DisplayGameControlValues();
4887
4888       if (time == time_final)
4889         StopSound(SND_GAME_LEVELTIME_BONUS);
4890       else if (setup.sound_loops)
4891         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4892       else
4893         PlaySound(SND_GAME_LEVELTIME_BONUS);
4894
4895       return;
4896     }
4897
4898     if (health != health_final)
4899     {
4900       if (game_over_delay_2 > 0)
4901       {
4902         game_over_delay_2--;
4903
4904         return;
4905       }
4906
4907       int health_count_dir = (health < health_final ? +1 : -1);
4908
4909       health += health_count_dir;
4910       score  += time_score;
4911
4912       game.LevelSolved_CountingHealth = health;
4913       game.LevelSolved_CountingScore = score;
4914
4915       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4916       game_panel_controls[GAME_PANEL_SCORE].value = score;
4917
4918       DisplayGameControlValues();
4919
4920       if (health == health_final)
4921         StopSound(SND_GAME_LEVELTIME_BONUS);
4922       else if (setup.sound_loops)
4923         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4924       else
4925         PlaySound(SND_GAME_LEVELTIME_BONUS);
4926
4927       return;
4928     }
4929   }
4930
4931   game.panel.active = FALSE;
4932
4933   if (game_over_delay_3 > 0)
4934   {
4935     game_over_delay_3--;
4936
4937     return;
4938   }
4939
4940   GameEnd();
4941 }
4942
4943 void GameEnd(void)
4944 {
4945   // used instead of "level_nr" (needed for network games)
4946   int last_level_nr = levelset.level_nr;
4947   int hi_pos;
4948
4949   game.LevelSolved_GameEnd = TRUE;
4950
4951   if (game.LevelSolved_SaveTape)
4952   {
4953     // make sure that request dialog to save tape does not open door again
4954     if (!global.use_envelope_request)
4955       CloseDoor(DOOR_CLOSE_1);
4956
4957     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4958   }
4959
4960   // if no tape is to be saved, close both doors simultaneously
4961   CloseDoor(DOOR_CLOSE_ALL);
4962
4963   if (level_editor_test_game)
4964   {
4965     SetGameStatus(GAME_MODE_MAIN);
4966
4967     DrawMainMenu();
4968
4969     return;
4970   }
4971
4972   if (!game.LevelSolved_SaveScore)
4973   {
4974     SetGameStatus(GAME_MODE_MAIN);
4975
4976     DrawMainMenu();
4977
4978     return;
4979   }
4980
4981   if (level_nr == leveldir_current->handicap_level)
4982   {
4983     leveldir_current->handicap_level++;
4984
4985     SaveLevelSetup_SeriesInfo();
4986   }
4987
4988   if (setup.increment_levels &&
4989       level_nr < leveldir_current->last_level &&
4990       !network_playing)
4991   {
4992     level_nr++;         // advance to next level
4993     TapeErase();        // start with empty tape
4994
4995     if (setup.auto_play_next_level)
4996     {
4997       LoadLevel(level_nr);
4998
4999       SaveLevelSetup_SeriesInfo();
5000     }
5001   }
5002
5003   hi_pos = NewHiScore(last_level_nr);
5004
5005   if (hi_pos >= 0 && setup.show_scores_after_game)
5006   {
5007     SetGameStatus(GAME_MODE_SCORES);
5008
5009     DrawHallOfFame(last_level_nr, hi_pos);
5010   }
5011   else if (setup.auto_play_next_level && setup.increment_levels &&
5012            last_level_nr < leveldir_current->last_level &&
5013            !network_playing)
5014   {
5015     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5016   }
5017   else
5018   {
5019     SetGameStatus(GAME_MODE_MAIN);
5020
5021     DrawMainMenu();
5022   }
5023 }
5024
5025 int NewHiScore(int level_nr)
5026 {
5027   int k, l;
5028   int position = -1;
5029   boolean one_score_entry_per_name = !program.many_scores_per_name;
5030
5031   LoadScore(level_nr);
5032
5033   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5034       game.score_final < highscore[MAX_SCORE_ENTRIES - 1].Score)
5035     return -1;
5036
5037   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5038   {
5039     if (game.score_final > highscore[k].Score)
5040     {
5041       // player has made it to the hall of fame
5042
5043       if (k < MAX_SCORE_ENTRIES - 1)
5044       {
5045         int m = MAX_SCORE_ENTRIES - 1;
5046
5047         if (one_score_entry_per_name)
5048         {
5049           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5050             if (strEqual(setup.player_name, highscore[l].Name))
5051               m = l;
5052
5053           if (m == k)   // player's new highscore overwrites his old one
5054             goto put_into_list;
5055         }
5056
5057         for (l = m; l > k; l--)
5058         {
5059           strcpy(highscore[l].Name, highscore[l - 1].Name);
5060           highscore[l].Score = highscore[l - 1].Score;
5061         }
5062       }
5063
5064       put_into_list:
5065
5066       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
5067       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
5068       highscore[k].Score = game.score_final;
5069       position = k;
5070
5071       break;
5072     }
5073     else if (one_score_entry_per_name &&
5074              !strncmp(setup.player_name, highscore[k].Name,
5075                       MAX_PLAYER_NAME_LEN))
5076       break;    // player already there with a higher score
5077   }
5078
5079   if (position >= 0) 
5080     SaveScore(level_nr);
5081
5082   return position;
5083 }
5084
5085 static int getElementMoveStepsizeExt(int x, int y, int direction)
5086 {
5087   int element = Tile[x][y];
5088   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5089   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5090   int horiz_move = (dx != 0);
5091   int sign = (horiz_move ? dx : dy);
5092   int step = sign * element_info[element].move_stepsize;
5093
5094   // special values for move stepsize for spring and things on conveyor belt
5095   if (horiz_move)
5096   {
5097     if (CAN_FALL(element) &&
5098         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5099       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5100     else if (element == EL_SPRING)
5101       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5102   }
5103
5104   return step;
5105 }
5106
5107 static int getElementMoveStepsize(int x, int y)
5108 {
5109   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5110 }
5111
5112 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5113 {
5114   if (player->GfxAction != action || player->GfxDir != dir)
5115   {
5116     player->GfxAction = action;
5117     player->GfxDir = dir;
5118     player->Frame = 0;
5119     player->StepFrame = 0;
5120   }
5121 }
5122
5123 static void ResetGfxFrame(int x, int y)
5124 {
5125   // profiling showed that "autotest" spends 10~20% of its time in this function
5126   if (DrawingDeactivatedField())
5127     return;
5128
5129   int element = Tile[x][y];
5130   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5131
5132   if (graphic_info[graphic].anim_global_sync)
5133     GfxFrame[x][y] = FrameCounter;
5134   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5135     GfxFrame[x][y] = CustomValue[x][y];
5136   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5137     GfxFrame[x][y] = element_info[element].collect_score;
5138   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5139     GfxFrame[x][y] = ChangeDelay[x][y];
5140 }
5141
5142 static void ResetGfxAnimation(int x, int y)
5143 {
5144   GfxAction[x][y] = ACTION_DEFAULT;
5145   GfxDir[x][y] = MovDir[x][y];
5146   GfxFrame[x][y] = 0;
5147
5148   ResetGfxFrame(x, y);
5149 }
5150
5151 static void ResetRandomAnimationValue(int x, int y)
5152 {
5153   GfxRandom[x][y] = INIT_GFX_RANDOM();
5154 }
5155
5156 static void InitMovingField(int x, int y, int direction)
5157 {
5158   int element = Tile[x][y];
5159   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5160   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5161   int newx = x + dx;
5162   int newy = y + dy;
5163   boolean is_moving_before, is_moving_after;
5164
5165   // check if element was/is moving or being moved before/after mode change
5166   is_moving_before = (WasJustMoving[x][y] != 0);
5167   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5168
5169   // reset animation only for moving elements which change direction of moving
5170   // or which just started or stopped moving
5171   // (else CEs with property "can move" / "not moving" are reset each frame)
5172   if (is_moving_before != is_moving_after ||
5173       direction != MovDir[x][y])
5174     ResetGfxAnimation(x, y);
5175
5176   MovDir[x][y] = direction;
5177   GfxDir[x][y] = direction;
5178
5179   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5180                      direction == MV_DOWN && CAN_FALL(element) ?
5181                      ACTION_FALLING : ACTION_MOVING);
5182
5183   // this is needed for CEs with property "can move" / "not moving"
5184
5185   if (is_moving_after)
5186   {
5187     if (Tile[newx][newy] == EL_EMPTY)
5188       Tile[newx][newy] = EL_BLOCKED;
5189
5190     MovDir[newx][newy] = MovDir[x][y];
5191
5192     CustomValue[newx][newy] = CustomValue[x][y];
5193
5194     GfxFrame[newx][newy] = GfxFrame[x][y];
5195     GfxRandom[newx][newy] = GfxRandom[x][y];
5196     GfxAction[newx][newy] = GfxAction[x][y];
5197     GfxDir[newx][newy] = GfxDir[x][y];
5198   }
5199 }
5200
5201 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5202 {
5203   int direction = MovDir[x][y];
5204   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5205   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5206
5207   *goes_to_x = newx;
5208   *goes_to_y = newy;
5209 }
5210
5211 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5212 {
5213   int oldx = x, oldy = y;
5214   int direction = MovDir[x][y];
5215
5216   if (direction == MV_LEFT)
5217     oldx++;
5218   else if (direction == MV_RIGHT)
5219     oldx--;
5220   else if (direction == MV_UP)
5221     oldy++;
5222   else if (direction == MV_DOWN)
5223     oldy--;
5224
5225   *comes_from_x = oldx;
5226   *comes_from_y = oldy;
5227 }
5228
5229 static int MovingOrBlocked2Element(int x, int y)
5230 {
5231   int element = Tile[x][y];
5232
5233   if (element == EL_BLOCKED)
5234   {
5235     int oldx, oldy;
5236
5237     Blocked2Moving(x, y, &oldx, &oldy);
5238     return Tile[oldx][oldy];
5239   }
5240   else
5241     return element;
5242 }
5243
5244 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5245 {
5246   // like MovingOrBlocked2Element(), but if element is moving
5247   // and (x,y) is the field the moving element is just leaving,
5248   // return EL_BLOCKED instead of the element value
5249   int element = Tile[x][y];
5250
5251   if (IS_MOVING(x, y))
5252   {
5253     if (element == EL_BLOCKED)
5254     {
5255       int oldx, oldy;
5256
5257       Blocked2Moving(x, y, &oldx, &oldy);
5258       return Tile[oldx][oldy];
5259     }
5260     else
5261       return EL_BLOCKED;
5262   }
5263   else
5264     return element;
5265 }
5266
5267 static void RemoveField(int x, int y)
5268 {
5269   Tile[x][y] = EL_EMPTY;
5270
5271   MovPos[x][y] = 0;
5272   MovDir[x][y] = 0;
5273   MovDelay[x][y] = 0;
5274
5275   CustomValue[x][y] = 0;
5276
5277   AmoebaNr[x][y] = 0;
5278   ChangeDelay[x][y] = 0;
5279   ChangePage[x][y] = -1;
5280   Pushed[x][y] = FALSE;
5281
5282   GfxElement[x][y] = EL_UNDEFINED;
5283   GfxAction[x][y] = ACTION_DEFAULT;
5284   GfxDir[x][y] = MV_NONE;
5285 }
5286
5287 static void RemoveMovingField(int x, int y)
5288 {
5289   int oldx = x, oldy = y, newx = x, newy = y;
5290   int element = Tile[x][y];
5291   int next_element = EL_UNDEFINED;
5292
5293   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5294     return;
5295
5296   if (IS_MOVING(x, y))
5297   {
5298     Moving2Blocked(x, y, &newx, &newy);
5299
5300     if (Tile[newx][newy] != EL_BLOCKED)
5301     {
5302       // element is moving, but target field is not free (blocked), but
5303       // already occupied by something different (example: acid pool);
5304       // in this case, only remove the moving field, but not the target
5305
5306       RemoveField(oldx, oldy);
5307
5308       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5309
5310       TEST_DrawLevelField(oldx, oldy);
5311
5312       return;
5313     }
5314   }
5315   else if (element == EL_BLOCKED)
5316   {
5317     Blocked2Moving(x, y, &oldx, &oldy);
5318     if (!IS_MOVING(oldx, oldy))
5319       return;
5320   }
5321
5322   if (element == EL_BLOCKED &&
5323       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5324        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5325        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5326        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5327        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5328        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5329     next_element = get_next_element(Tile[oldx][oldy]);
5330
5331   RemoveField(oldx, oldy);
5332   RemoveField(newx, newy);
5333
5334   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5335
5336   if (next_element != EL_UNDEFINED)
5337     Tile[oldx][oldy] = next_element;
5338
5339   TEST_DrawLevelField(oldx, oldy);
5340   TEST_DrawLevelField(newx, newy);
5341 }
5342
5343 void DrawDynamite(int x, int y)
5344 {
5345   int sx = SCREENX(x), sy = SCREENY(y);
5346   int graphic = el2img(Tile[x][y]);
5347   int frame;
5348
5349   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5350     return;
5351
5352   if (IS_WALKABLE_INSIDE(Back[x][y]))
5353     return;
5354
5355   if (Back[x][y])
5356     DrawLevelElement(x, y, Back[x][y]);
5357   else if (Store[x][y])
5358     DrawLevelElement(x, y, Store[x][y]);
5359   else if (game.use_masked_elements)
5360     DrawLevelElement(x, y, EL_EMPTY);
5361
5362   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5363
5364   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5365     DrawGraphicThruMask(sx, sy, graphic, frame);
5366   else
5367     DrawGraphic(sx, sy, graphic, frame);
5368 }
5369
5370 static void CheckDynamite(int x, int y)
5371 {
5372   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5373   {
5374     MovDelay[x][y]--;
5375
5376     if (MovDelay[x][y] != 0)
5377     {
5378       DrawDynamite(x, y);
5379       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5380
5381       return;
5382     }
5383   }
5384
5385   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5386
5387   Bang(x, y);
5388 }
5389
5390 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5391 {
5392   boolean num_checked_players = 0;
5393   int i;
5394
5395   for (i = 0; i < MAX_PLAYERS; i++)
5396   {
5397     if (stored_player[i].active)
5398     {
5399       int sx = stored_player[i].jx;
5400       int sy = stored_player[i].jy;
5401
5402       if (num_checked_players == 0)
5403       {
5404         *sx1 = *sx2 = sx;
5405         *sy1 = *sy2 = sy;
5406       }
5407       else
5408       {
5409         *sx1 = MIN(*sx1, sx);
5410         *sy1 = MIN(*sy1, sy);
5411         *sx2 = MAX(*sx2, sx);
5412         *sy2 = MAX(*sy2, sy);
5413       }
5414
5415       num_checked_players++;
5416     }
5417   }
5418 }
5419
5420 static boolean checkIfAllPlayersFitToScreen_RND(void)
5421 {
5422   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5423
5424   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5425
5426   return (sx2 - sx1 < SCR_FIELDX &&
5427           sy2 - sy1 < SCR_FIELDY);
5428 }
5429
5430 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5431 {
5432   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5433
5434   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5435
5436   *sx = (sx1 + sx2) / 2;
5437   *sy = (sy1 + sy2) / 2;
5438 }
5439
5440 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5441                                boolean center_screen, boolean quick_relocation)
5442 {
5443   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5444   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5445   boolean no_delay = (tape.warp_forward);
5446   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5447   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5448   int new_scroll_x, new_scroll_y;
5449
5450   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5451   {
5452     // case 1: quick relocation inside visible screen (without scrolling)
5453
5454     RedrawPlayfield();
5455
5456     return;
5457   }
5458
5459   if (!level.shifted_relocation || center_screen)
5460   {
5461     // relocation _with_ centering of screen
5462
5463     new_scroll_x = SCROLL_POSITION_X(x);
5464     new_scroll_y = SCROLL_POSITION_Y(y);
5465   }
5466   else
5467   {
5468     // relocation _without_ centering of screen
5469
5470     int center_scroll_x = SCROLL_POSITION_X(old_x);
5471     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5472     int offset_x = x + (scroll_x - center_scroll_x);
5473     int offset_y = y + (scroll_y - center_scroll_y);
5474
5475     // for new screen position, apply previous offset to center position
5476     new_scroll_x = SCROLL_POSITION_X(offset_x);
5477     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5478   }
5479
5480   if (quick_relocation)
5481   {
5482     // case 2: quick relocation (redraw without visible scrolling)
5483
5484     scroll_x = new_scroll_x;
5485     scroll_y = new_scroll_y;
5486
5487     RedrawPlayfield();
5488
5489     return;
5490   }
5491
5492   // case 3: visible relocation (with scrolling to new position)
5493
5494   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5495
5496   SetVideoFrameDelay(wait_delay_value);
5497
5498   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5499   {
5500     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5501     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5502
5503     if (dx == 0 && dy == 0)             // no scrolling needed at all
5504       break;
5505
5506     scroll_x -= dx;
5507     scroll_y -= dy;
5508
5509     // set values for horizontal/vertical screen scrolling (half tile size)
5510     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5511     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5512     int pos_x = dx * TILEX / 2;
5513     int pos_y = dy * TILEY / 2;
5514     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5515     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5516
5517     ScrollLevel(dx, dy);
5518     DrawAllPlayers();
5519
5520     // scroll in two steps of half tile size to make things smoother
5521     BlitScreenToBitmapExt_RND(window, fx, fy);
5522
5523     // scroll second step to align at full tile size
5524     BlitScreenToBitmap(window);
5525   }
5526
5527   DrawAllPlayers();
5528   BackToFront();
5529
5530   SetVideoFrameDelay(frame_delay_value_old);
5531 }
5532
5533 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5534 {
5535   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5536   int player_nr = GET_PLAYER_NR(el_player);
5537   struct PlayerInfo *player = &stored_player[player_nr];
5538   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5539   boolean no_delay = (tape.warp_forward);
5540   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5541   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5542   int old_jx = player->jx;
5543   int old_jy = player->jy;
5544   int old_element = Tile[old_jx][old_jy];
5545   int element = Tile[jx][jy];
5546   boolean player_relocated = (old_jx != jx || old_jy != jy);
5547
5548   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5549   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5550   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5551   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5552   int leave_side_horiz = move_dir_horiz;
5553   int leave_side_vert  = move_dir_vert;
5554   int enter_side = enter_side_horiz | enter_side_vert;
5555   int leave_side = leave_side_horiz | leave_side_vert;
5556
5557   if (player->buried)           // do not reanimate dead player
5558     return;
5559
5560   if (!player_relocated)        // no need to relocate the player
5561     return;
5562
5563   if (IS_PLAYER(jx, jy))        // player already placed at new position
5564   {
5565     RemoveField(jx, jy);        // temporarily remove newly placed player
5566     DrawLevelField(jx, jy);
5567   }
5568
5569   if (player->present)
5570   {
5571     while (player->MovPos)
5572     {
5573       ScrollPlayer(player, SCROLL_GO_ON);
5574       ScrollScreen(NULL, SCROLL_GO_ON);
5575
5576       AdvanceFrameAndPlayerCounters(player->index_nr);
5577
5578       DrawPlayer(player);
5579
5580       BackToFront_WithFrameDelay(wait_delay_value);
5581     }
5582
5583     DrawPlayer(player);         // needed here only to cleanup last field
5584     DrawLevelField(player->jx, player->jy);     // remove player graphic
5585
5586     player->is_moving = FALSE;
5587   }
5588
5589   if (IS_CUSTOM_ELEMENT(old_element))
5590     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5591                                CE_LEFT_BY_PLAYER,
5592                                player->index_bit, leave_side);
5593
5594   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5595                                       CE_PLAYER_LEAVES_X,
5596                                       player->index_bit, leave_side);
5597
5598   Tile[jx][jy] = el_player;
5599   InitPlayerField(jx, jy, el_player, TRUE);
5600
5601   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5602      possible that the relocation target field did not contain a player element,
5603      but a walkable element, to which the new player was relocated -- in this
5604      case, restore that (already initialized!) element on the player field */
5605   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5606   {
5607     Tile[jx][jy] = element;     // restore previously existing element
5608   }
5609
5610   // only visually relocate centered player
5611   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5612                      FALSE, level.instant_relocation);
5613
5614   TestIfPlayerTouchesBadThing(jx, jy);
5615   TestIfPlayerTouchesCustomElement(jx, jy);
5616
5617   if (IS_CUSTOM_ELEMENT(element))
5618     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5619                                player->index_bit, enter_side);
5620
5621   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5622                                       player->index_bit, enter_side);
5623
5624   if (player->is_switching)
5625   {
5626     /* ensure that relocation while still switching an element does not cause
5627        a new element to be treated as also switched directly after relocation
5628        (this is important for teleporter switches that teleport the player to
5629        a place where another teleporter switch is in the same direction, which
5630        would then incorrectly be treated as immediately switched before the
5631        direction key that caused the switch was released) */
5632
5633     player->switch_x += jx - old_jx;
5634     player->switch_y += jy - old_jy;
5635   }
5636 }
5637
5638 static void Explode(int ex, int ey, int phase, int mode)
5639 {
5640   int x, y;
5641   int last_phase;
5642   int border_element;
5643
5644   // !!! eliminate this variable !!!
5645   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5646
5647   if (game.explosions_delayed)
5648   {
5649     ExplodeField[ex][ey] = mode;
5650     return;
5651   }
5652
5653   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5654   {
5655     int center_element = Tile[ex][ey];
5656     int artwork_element, explosion_element;     // set these values later
5657
5658     // remove things displayed in background while burning dynamite
5659     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5660       Back[ex][ey] = 0;
5661
5662     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5663     {
5664       // put moving element to center field (and let it explode there)
5665       center_element = MovingOrBlocked2Element(ex, ey);
5666       RemoveMovingField(ex, ey);
5667       Tile[ex][ey] = center_element;
5668     }
5669
5670     // now "center_element" is finally determined -- set related values now
5671     artwork_element = center_element;           // for custom player artwork
5672     explosion_element = center_element;         // for custom player artwork
5673
5674     if (IS_PLAYER(ex, ey))
5675     {
5676       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5677
5678       artwork_element = stored_player[player_nr].artwork_element;
5679
5680       if (level.use_explosion_element[player_nr])
5681       {
5682         explosion_element = level.explosion_element[player_nr];
5683         artwork_element = explosion_element;
5684       }
5685     }
5686
5687     if (mode == EX_TYPE_NORMAL ||
5688         mode == EX_TYPE_CENTER ||
5689         mode == EX_TYPE_CROSS)
5690       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5691
5692     last_phase = element_info[explosion_element].explosion_delay + 1;
5693
5694     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5695     {
5696       int xx = x - ex + 1;
5697       int yy = y - ey + 1;
5698       int element;
5699
5700       if (!IN_LEV_FIELD(x, y) ||
5701           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5702           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5703         continue;
5704
5705       element = Tile[x][y];
5706
5707       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5708       {
5709         element = MovingOrBlocked2Element(x, y);
5710
5711         if (!IS_EXPLOSION_PROOF(element))
5712           RemoveMovingField(x, y);
5713       }
5714
5715       // indestructible elements can only explode in center (but not flames)
5716       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5717                                            mode == EX_TYPE_BORDER)) ||
5718           element == EL_FLAMES)
5719         continue;
5720
5721       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5722          behaviour, for example when touching a yamyam that explodes to rocks
5723          with active deadly shield, a rock is created under the player !!! */
5724       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5725 #if 0
5726       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5727           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5728            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5729 #else
5730       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5731 #endif
5732       {
5733         if (IS_ACTIVE_BOMB(element))
5734         {
5735           // re-activate things under the bomb like gate or penguin
5736           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5737           Back[x][y] = 0;
5738         }
5739
5740         continue;
5741       }
5742
5743       // save walkable background elements while explosion on same tile
5744       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5745           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5746         Back[x][y] = element;
5747
5748       // ignite explodable elements reached by other explosion
5749       if (element == EL_EXPLOSION)
5750         element = Store2[x][y];
5751
5752       if (AmoebaNr[x][y] &&
5753           (element == EL_AMOEBA_FULL ||
5754            element == EL_BD_AMOEBA ||
5755            element == EL_AMOEBA_GROWING))
5756       {
5757         AmoebaCnt[AmoebaNr[x][y]]--;
5758         AmoebaCnt2[AmoebaNr[x][y]]--;
5759       }
5760
5761       RemoveField(x, y);
5762
5763       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5764       {
5765         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5766
5767         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5768
5769         if (PLAYERINFO(ex, ey)->use_murphy)
5770           Store[x][y] = EL_EMPTY;
5771       }
5772
5773       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5774       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5775       else if (ELEM_IS_PLAYER(center_element))
5776         Store[x][y] = EL_EMPTY;
5777       else if (center_element == EL_YAMYAM)
5778         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5779       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5780         Store[x][y] = element_info[center_element].content.e[xx][yy];
5781 #if 1
5782       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5783       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5784       // otherwise) -- FIX THIS !!!
5785       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5786         Store[x][y] = element_info[element].content.e[1][1];
5787 #else
5788       else if (!CAN_EXPLODE(element))
5789         Store[x][y] = element_info[element].content.e[1][1];
5790 #endif
5791       else
5792         Store[x][y] = EL_EMPTY;
5793
5794       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5795           center_element == EL_AMOEBA_TO_DIAMOND)
5796         Store2[x][y] = element;
5797
5798       Tile[x][y] = EL_EXPLOSION;
5799       GfxElement[x][y] = artwork_element;
5800
5801       ExplodePhase[x][y] = 1;
5802       ExplodeDelay[x][y] = last_phase;
5803
5804       Stop[x][y] = TRUE;
5805     }
5806
5807     if (center_element == EL_YAMYAM)
5808       game.yamyam_content_nr =
5809         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5810
5811     return;
5812   }
5813
5814   if (Stop[ex][ey])
5815     return;
5816
5817   x = ex;
5818   y = ey;
5819
5820   if (phase == 1)
5821     GfxFrame[x][y] = 0;         // restart explosion animation
5822
5823   last_phase = ExplodeDelay[x][y];
5824
5825   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5826
5827   // this can happen if the player leaves an explosion just in time
5828   if (GfxElement[x][y] == EL_UNDEFINED)
5829     GfxElement[x][y] = EL_EMPTY;
5830
5831   border_element = Store2[x][y];
5832   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5833     border_element = StorePlayer[x][y];
5834
5835   if (phase == element_info[border_element].ignition_delay ||
5836       phase == last_phase)
5837   {
5838     boolean border_explosion = FALSE;
5839
5840     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5841         !PLAYER_EXPLOSION_PROTECTED(x, y))
5842     {
5843       KillPlayerUnlessExplosionProtected(x, y);
5844       border_explosion = TRUE;
5845     }
5846     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5847     {
5848       Tile[x][y] = Store2[x][y];
5849       Store2[x][y] = 0;
5850       Bang(x, y);
5851       border_explosion = TRUE;
5852     }
5853     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5854     {
5855       AmoebaToDiamond(x, y);
5856       Store2[x][y] = 0;
5857       border_explosion = TRUE;
5858     }
5859
5860     // if an element just explodes due to another explosion (chain-reaction),
5861     // do not immediately end the new explosion when it was the last frame of
5862     // the explosion (as it would be done in the following "if"-statement!)
5863     if (border_explosion && phase == last_phase)
5864       return;
5865   }
5866
5867   if (phase == last_phase)
5868   {
5869     int element;
5870
5871     element = Tile[x][y] = Store[x][y];
5872     Store[x][y] = Store2[x][y] = 0;
5873     GfxElement[x][y] = EL_UNDEFINED;
5874
5875     // player can escape from explosions and might therefore be still alive
5876     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5877         element <= EL_PLAYER_IS_EXPLODING_4)
5878     {
5879       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5880       int explosion_element = EL_PLAYER_1 + player_nr;
5881       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5882       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5883
5884       if (level.use_explosion_element[player_nr])
5885         explosion_element = level.explosion_element[player_nr];
5886
5887       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5888                     element_info[explosion_element].content.e[xx][yy]);
5889     }
5890
5891     // restore probably existing indestructible background element
5892     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5893       element = Tile[x][y] = Back[x][y];
5894     Back[x][y] = 0;
5895
5896     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5897     GfxDir[x][y] = MV_NONE;
5898     ChangeDelay[x][y] = 0;
5899     ChangePage[x][y] = -1;
5900
5901     CustomValue[x][y] = 0;
5902
5903     InitField_WithBug2(x, y, FALSE);
5904
5905     TEST_DrawLevelField(x, y);
5906
5907     TestIfElementTouchesCustomElement(x, y);
5908
5909     if (GFX_CRUMBLED(element))
5910       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5911
5912     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5913       StorePlayer[x][y] = 0;
5914
5915     if (ELEM_IS_PLAYER(element))
5916       RelocatePlayer(x, y, element);
5917   }
5918   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5919   {
5920     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5921     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5922
5923     if (phase == delay)
5924       TEST_DrawLevelFieldCrumbled(x, y);
5925
5926     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5927     {
5928       DrawLevelElement(x, y, Back[x][y]);
5929       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5930     }
5931     else if (IS_WALKABLE_UNDER(Back[x][y]))
5932     {
5933       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5934       DrawLevelElementThruMask(x, y, Back[x][y]);
5935     }
5936     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5937       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5938   }
5939 }
5940
5941 static void DynaExplode(int ex, int ey)
5942 {
5943   int i, j;
5944   int dynabomb_element = Tile[ex][ey];
5945   int dynabomb_size = 1;
5946   boolean dynabomb_xl = FALSE;
5947   struct PlayerInfo *player;
5948   static int xy[4][2] =
5949   {
5950     { 0, -1 },
5951     { -1, 0 },
5952     { +1, 0 },
5953     { 0, +1 }
5954   };
5955
5956   if (IS_ACTIVE_BOMB(dynabomb_element))
5957   {
5958     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5959     dynabomb_size = player->dynabomb_size;
5960     dynabomb_xl = player->dynabomb_xl;
5961     player->dynabombs_left++;
5962   }
5963
5964   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5965
5966   for (i = 0; i < NUM_DIRECTIONS; i++)
5967   {
5968     for (j = 1; j <= dynabomb_size; j++)
5969     {
5970       int x = ex + j * xy[i][0];
5971       int y = ey + j * xy[i][1];
5972       int element;
5973
5974       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
5975         break;
5976
5977       element = Tile[x][y];
5978
5979       // do not restart explosions of fields with active bombs
5980       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5981         continue;
5982
5983       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5984
5985       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5986           !IS_DIGGABLE(element) && !dynabomb_xl)
5987         break;
5988     }
5989   }
5990 }
5991
5992 void Bang(int x, int y)
5993 {
5994   int element = MovingOrBlocked2Element(x, y);
5995   int explosion_type = EX_TYPE_NORMAL;
5996
5997   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5998   {
5999     struct PlayerInfo *player = PLAYERINFO(x, y);
6000
6001     element = Tile[x][y] = player->initial_element;
6002
6003     if (level.use_explosion_element[player->index_nr])
6004     {
6005       int explosion_element = level.explosion_element[player->index_nr];
6006
6007       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6008         explosion_type = EX_TYPE_CROSS;
6009       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6010         explosion_type = EX_TYPE_CENTER;
6011     }
6012   }
6013
6014   switch (element)
6015   {
6016     case EL_BUG:
6017     case EL_SPACESHIP:
6018     case EL_BD_BUTTERFLY:
6019     case EL_BD_FIREFLY:
6020     case EL_YAMYAM:
6021     case EL_DARK_YAMYAM:
6022     case EL_ROBOT:
6023     case EL_PACMAN:
6024     case EL_MOLE:
6025       RaiseScoreElement(element);
6026       break;
6027
6028     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6029     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6030     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6031     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6032     case EL_DYNABOMB_INCREASE_NUMBER:
6033     case EL_DYNABOMB_INCREASE_SIZE:
6034     case EL_DYNABOMB_INCREASE_POWER:
6035       explosion_type = EX_TYPE_DYNA;
6036       break;
6037
6038     case EL_DC_LANDMINE:
6039       explosion_type = EX_TYPE_CENTER;
6040       break;
6041
6042     case EL_PENGUIN:
6043     case EL_LAMP:
6044     case EL_LAMP_ACTIVE:
6045     case EL_AMOEBA_TO_DIAMOND:
6046       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6047         explosion_type = EX_TYPE_CENTER;
6048       break;
6049
6050     default:
6051       if (element_info[element].explosion_type == EXPLODES_CROSS)
6052         explosion_type = EX_TYPE_CROSS;
6053       else if (element_info[element].explosion_type == EXPLODES_1X1)
6054         explosion_type = EX_TYPE_CENTER;
6055       break;
6056   }
6057
6058   if (explosion_type == EX_TYPE_DYNA)
6059     DynaExplode(x, y);
6060   else
6061     Explode(x, y, EX_PHASE_START, explosion_type);
6062
6063   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6064 }
6065
6066 static void SplashAcid(int x, int y)
6067 {
6068   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6069       (!IN_LEV_FIELD(x - 1, y - 2) ||
6070        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6071     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6072
6073   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6074       (!IN_LEV_FIELD(x + 1, y - 2) ||
6075        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6076     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6077
6078   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6079 }
6080
6081 static void InitBeltMovement(void)
6082 {
6083   static int belt_base_element[4] =
6084   {
6085     EL_CONVEYOR_BELT_1_LEFT,
6086     EL_CONVEYOR_BELT_2_LEFT,
6087     EL_CONVEYOR_BELT_3_LEFT,
6088     EL_CONVEYOR_BELT_4_LEFT
6089   };
6090   static int belt_base_active_element[4] =
6091   {
6092     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6093     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6094     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6095     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6096   };
6097
6098   int x, y, i, j;
6099
6100   // set frame order for belt animation graphic according to belt direction
6101   for (i = 0; i < NUM_BELTS; i++)
6102   {
6103     int belt_nr = i;
6104
6105     for (j = 0; j < NUM_BELT_PARTS; j++)
6106     {
6107       int element = belt_base_active_element[belt_nr] + j;
6108       int graphic_1 = el2img(element);
6109       int graphic_2 = el2panelimg(element);
6110
6111       if (game.belt_dir[i] == MV_LEFT)
6112       {
6113         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6114         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6115       }
6116       else
6117       {
6118         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6119         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6120       }
6121     }
6122   }
6123
6124   SCAN_PLAYFIELD(x, y)
6125   {
6126     int element = Tile[x][y];
6127
6128     for (i = 0; i < NUM_BELTS; i++)
6129     {
6130       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6131       {
6132         int e_belt_nr = getBeltNrFromBeltElement(element);
6133         int belt_nr = i;
6134
6135         if (e_belt_nr == belt_nr)
6136         {
6137           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6138
6139           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6140         }
6141       }
6142     }
6143   }
6144 }
6145
6146 static void ToggleBeltSwitch(int x, int y)
6147 {
6148   static int belt_base_element[4] =
6149   {
6150     EL_CONVEYOR_BELT_1_LEFT,
6151     EL_CONVEYOR_BELT_2_LEFT,
6152     EL_CONVEYOR_BELT_3_LEFT,
6153     EL_CONVEYOR_BELT_4_LEFT
6154   };
6155   static int belt_base_active_element[4] =
6156   {
6157     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6158     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6159     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6160     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6161   };
6162   static int belt_base_switch_element[4] =
6163   {
6164     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6165     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6166     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6167     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6168   };
6169   static int belt_move_dir[4] =
6170   {
6171     MV_LEFT,
6172     MV_NONE,
6173     MV_RIGHT,
6174     MV_NONE,
6175   };
6176
6177   int element = Tile[x][y];
6178   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6179   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6180   int belt_dir = belt_move_dir[belt_dir_nr];
6181   int xx, yy, i;
6182
6183   if (!IS_BELT_SWITCH(element))
6184     return;
6185
6186   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6187   game.belt_dir[belt_nr] = belt_dir;
6188
6189   if (belt_dir_nr == 3)
6190     belt_dir_nr = 1;
6191
6192   // set frame order for belt animation graphic according to belt direction
6193   for (i = 0; i < NUM_BELT_PARTS; i++)
6194   {
6195     int element = belt_base_active_element[belt_nr] + i;
6196     int graphic_1 = el2img(element);
6197     int graphic_2 = el2panelimg(element);
6198
6199     if (belt_dir == MV_LEFT)
6200     {
6201       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6202       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6203     }
6204     else
6205     {
6206       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6207       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6208     }
6209   }
6210
6211   SCAN_PLAYFIELD(xx, yy)
6212   {
6213     int element = Tile[xx][yy];
6214
6215     if (IS_BELT_SWITCH(element))
6216     {
6217       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6218
6219       if (e_belt_nr == belt_nr)
6220       {
6221         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6222         TEST_DrawLevelField(xx, yy);
6223       }
6224     }
6225     else if (IS_BELT(element) && belt_dir != MV_NONE)
6226     {
6227       int e_belt_nr = getBeltNrFromBeltElement(element);
6228
6229       if (e_belt_nr == belt_nr)
6230       {
6231         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6232
6233         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6234         TEST_DrawLevelField(xx, yy);
6235       }
6236     }
6237     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6238     {
6239       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6240
6241       if (e_belt_nr == belt_nr)
6242       {
6243         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6244
6245         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6246         TEST_DrawLevelField(xx, yy);
6247       }
6248     }
6249   }
6250 }
6251
6252 static void ToggleSwitchgateSwitch(int x, int y)
6253 {
6254   int xx, yy;
6255
6256   game.switchgate_pos = !game.switchgate_pos;
6257
6258   SCAN_PLAYFIELD(xx, yy)
6259   {
6260     int element = Tile[xx][yy];
6261
6262     if (element == EL_SWITCHGATE_SWITCH_UP)
6263     {
6264       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6265       TEST_DrawLevelField(xx, yy);
6266     }
6267     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6268     {
6269       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6270       TEST_DrawLevelField(xx, yy);
6271     }
6272     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6273     {
6274       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6275       TEST_DrawLevelField(xx, yy);
6276     }
6277     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6278     {
6279       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6280       TEST_DrawLevelField(xx, yy);
6281     }
6282     else if (element == EL_SWITCHGATE_OPEN ||
6283              element == EL_SWITCHGATE_OPENING)
6284     {
6285       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6286
6287       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6288     }
6289     else if (element == EL_SWITCHGATE_CLOSED ||
6290              element == EL_SWITCHGATE_CLOSING)
6291     {
6292       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6293
6294       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6295     }
6296   }
6297 }
6298
6299 static int getInvisibleActiveFromInvisibleElement(int element)
6300 {
6301   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6302           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6303           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6304           element);
6305 }
6306
6307 static int getInvisibleFromInvisibleActiveElement(int element)
6308 {
6309   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6310           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6311           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6312           element);
6313 }
6314
6315 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6316 {
6317   int x, y;
6318
6319   SCAN_PLAYFIELD(x, y)
6320   {
6321     int element = Tile[x][y];
6322
6323     if (element == EL_LIGHT_SWITCH &&
6324         game.light_time_left > 0)
6325     {
6326       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6327       TEST_DrawLevelField(x, y);
6328     }
6329     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6330              game.light_time_left == 0)
6331     {
6332       Tile[x][y] = EL_LIGHT_SWITCH;
6333       TEST_DrawLevelField(x, y);
6334     }
6335     else if (element == EL_EMC_DRIPPER &&
6336              game.light_time_left > 0)
6337     {
6338       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6339       TEST_DrawLevelField(x, y);
6340     }
6341     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6342              game.light_time_left == 0)
6343     {
6344       Tile[x][y] = EL_EMC_DRIPPER;
6345       TEST_DrawLevelField(x, y);
6346     }
6347     else if (element == EL_INVISIBLE_STEELWALL ||
6348              element == EL_INVISIBLE_WALL ||
6349              element == EL_INVISIBLE_SAND)
6350     {
6351       if (game.light_time_left > 0)
6352         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6353
6354       TEST_DrawLevelField(x, y);
6355
6356       // uncrumble neighbour fields, if needed
6357       if (element == EL_INVISIBLE_SAND)
6358         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6359     }
6360     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6361              element == EL_INVISIBLE_WALL_ACTIVE ||
6362              element == EL_INVISIBLE_SAND_ACTIVE)
6363     {
6364       if (game.light_time_left == 0)
6365         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6366
6367       TEST_DrawLevelField(x, y);
6368
6369       // re-crumble neighbour fields, if needed
6370       if (element == EL_INVISIBLE_SAND)
6371         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6372     }
6373   }
6374 }
6375
6376 static void RedrawAllInvisibleElementsForLenses(void)
6377 {
6378   int x, y;
6379
6380   SCAN_PLAYFIELD(x, y)
6381   {
6382     int element = Tile[x][y];
6383
6384     if (element == EL_EMC_DRIPPER &&
6385         game.lenses_time_left > 0)
6386     {
6387       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6388       TEST_DrawLevelField(x, y);
6389     }
6390     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6391              game.lenses_time_left == 0)
6392     {
6393       Tile[x][y] = EL_EMC_DRIPPER;
6394       TEST_DrawLevelField(x, y);
6395     }
6396     else if (element == EL_INVISIBLE_STEELWALL ||
6397              element == EL_INVISIBLE_WALL ||
6398              element == EL_INVISIBLE_SAND)
6399     {
6400       if (game.lenses_time_left > 0)
6401         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6402
6403       TEST_DrawLevelField(x, y);
6404
6405       // uncrumble neighbour fields, if needed
6406       if (element == EL_INVISIBLE_SAND)
6407         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6408     }
6409     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6410              element == EL_INVISIBLE_WALL_ACTIVE ||
6411              element == EL_INVISIBLE_SAND_ACTIVE)
6412     {
6413       if (game.lenses_time_left == 0)
6414         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6415
6416       TEST_DrawLevelField(x, y);
6417
6418       // re-crumble neighbour fields, if needed
6419       if (element == EL_INVISIBLE_SAND)
6420         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6421     }
6422   }
6423 }
6424
6425 static void RedrawAllInvisibleElementsForMagnifier(void)
6426 {
6427   int x, y;
6428
6429   SCAN_PLAYFIELD(x, y)
6430   {
6431     int element = Tile[x][y];
6432
6433     if (element == EL_EMC_FAKE_GRASS &&
6434         game.magnify_time_left > 0)
6435     {
6436       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6437       TEST_DrawLevelField(x, y);
6438     }
6439     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6440              game.magnify_time_left == 0)
6441     {
6442       Tile[x][y] = EL_EMC_FAKE_GRASS;
6443       TEST_DrawLevelField(x, y);
6444     }
6445     else if (IS_GATE_GRAY(element) &&
6446              game.magnify_time_left > 0)
6447     {
6448       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6449                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6450                     IS_EM_GATE_GRAY(element) ?
6451                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6452                     IS_EMC_GATE_GRAY(element) ?
6453                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6454                     IS_DC_GATE_GRAY(element) ?
6455                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6456                     element);
6457       TEST_DrawLevelField(x, y);
6458     }
6459     else if (IS_GATE_GRAY_ACTIVE(element) &&
6460              game.magnify_time_left == 0)
6461     {
6462       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6463                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6464                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6465                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6466                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6467                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6468                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6469                     EL_DC_GATE_WHITE_GRAY :
6470                     element);
6471       TEST_DrawLevelField(x, y);
6472     }
6473   }
6474 }
6475
6476 static void ToggleLightSwitch(int x, int y)
6477 {
6478   int element = Tile[x][y];
6479
6480   game.light_time_left =
6481     (element == EL_LIGHT_SWITCH ?
6482      level.time_light * FRAMES_PER_SECOND : 0);
6483
6484   RedrawAllLightSwitchesAndInvisibleElements();
6485 }
6486
6487 static void ActivateTimegateSwitch(int x, int y)
6488 {
6489   int xx, yy;
6490
6491   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6492
6493   SCAN_PLAYFIELD(xx, yy)
6494   {
6495     int element = Tile[xx][yy];
6496
6497     if (element == EL_TIMEGATE_CLOSED ||
6498         element == EL_TIMEGATE_CLOSING)
6499     {
6500       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6501       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6502     }
6503
6504     /*
6505     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6506     {
6507       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6508       TEST_DrawLevelField(xx, yy);
6509     }
6510     */
6511
6512   }
6513
6514   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6515                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6516 }
6517
6518 static void Impact(int x, int y)
6519 {
6520   boolean last_line = (y == lev_fieldy - 1);
6521   boolean object_hit = FALSE;
6522   boolean impact = (last_line || object_hit);
6523   int element = Tile[x][y];
6524   int smashed = EL_STEELWALL;
6525
6526   if (!last_line)       // check if element below was hit
6527   {
6528     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6529       return;
6530
6531     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6532                                          MovDir[x][y + 1] != MV_DOWN ||
6533                                          MovPos[x][y + 1] <= TILEY / 2));
6534
6535     // do not smash moving elements that left the smashed field in time
6536     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6537         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6538       object_hit = FALSE;
6539
6540 #if USE_QUICKSAND_IMPACT_BUGFIX
6541     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6542     {
6543       RemoveMovingField(x, y + 1);
6544       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6545       Tile[x][y + 2] = EL_ROCK;
6546       TEST_DrawLevelField(x, y + 2);
6547
6548       object_hit = TRUE;
6549     }
6550
6551     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6552     {
6553       RemoveMovingField(x, y + 1);
6554       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6555       Tile[x][y + 2] = EL_ROCK;
6556       TEST_DrawLevelField(x, y + 2);
6557
6558       object_hit = TRUE;
6559     }
6560 #endif
6561
6562     if (object_hit)
6563       smashed = MovingOrBlocked2Element(x, y + 1);
6564
6565     impact = (last_line || object_hit);
6566   }
6567
6568   if (!last_line && smashed == EL_ACID) // element falls into acid
6569   {
6570     SplashAcid(x, y + 1);
6571     return;
6572   }
6573
6574   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6575   // only reset graphic animation if graphic really changes after impact
6576   if (impact &&
6577       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6578   {
6579     ResetGfxAnimation(x, y);
6580     TEST_DrawLevelField(x, y);
6581   }
6582
6583   if (impact && CAN_EXPLODE_IMPACT(element))
6584   {
6585     Bang(x, y);
6586     return;
6587   }
6588   else if (impact && element == EL_PEARL &&
6589            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6590   {
6591     ResetGfxAnimation(x, y);
6592
6593     Tile[x][y] = EL_PEARL_BREAKING;
6594     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6595     return;
6596   }
6597   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6598   {
6599     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6600
6601     return;
6602   }
6603
6604   if (impact && element == EL_AMOEBA_DROP)
6605   {
6606     if (object_hit && IS_PLAYER(x, y + 1))
6607       KillPlayerUnlessEnemyProtected(x, y + 1);
6608     else if (object_hit && smashed == EL_PENGUIN)
6609       Bang(x, y + 1);
6610     else
6611     {
6612       Tile[x][y] = EL_AMOEBA_GROWING;
6613       Store[x][y] = EL_AMOEBA_WET;
6614
6615       ResetRandomAnimationValue(x, y);
6616     }
6617     return;
6618   }
6619
6620   if (object_hit)               // check which object was hit
6621   {
6622     if ((CAN_PASS_MAGIC_WALL(element) && 
6623          (smashed == EL_MAGIC_WALL ||
6624           smashed == EL_BD_MAGIC_WALL)) ||
6625         (CAN_PASS_DC_MAGIC_WALL(element) &&
6626          smashed == EL_DC_MAGIC_WALL))
6627     {
6628       int xx, yy;
6629       int activated_magic_wall =
6630         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6631          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6632          EL_DC_MAGIC_WALL_ACTIVE);
6633
6634       // activate magic wall / mill
6635       SCAN_PLAYFIELD(xx, yy)
6636       {
6637         if (Tile[xx][yy] == smashed)
6638           Tile[xx][yy] = activated_magic_wall;
6639       }
6640
6641       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6642       game.magic_wall_active = TRUE;
6643
6644       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6645                             SND_MAGIC_WALL_ACTIVATING :
6646                             smashed == EL_BD_MAGIC_WALL ?
6647                             SND_BD_MAGIC_WALL_ACTIVATING :
6648                             SND_DC_MAGIC_WALL_ACTIVATING));
6649     }
6650
6651     if (IS_PLAYER(x, y + 1))
6652     {
6653       if (CAN_SMASH_PLAYER(element))
6654       {
6655         KillPlayerUnlessEnemyProtected(x, y + 1);
6656         return;
6657       }
6658     }
6659     else if (smashed == EL_PENGUIN)
6660     {
6661       if (CAN_SMASH_PLAYER(element))
6662       {
6663         Bang(x, y + 1);
6664         return;
6665       }
6666     }
6667     else if (element == EL_BD_DIAMOND)
6668     {
6669       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6670       {
6671         Bang(x, y + 1);
6672         return;
6673       }
6674     }
6675     else if (((element == EL_SP_INFOTRON ||
6676                element == EL_SP_ZONK) &&
6677               (smashed == EL_SP_SNIKSNAK ||
6678                smashed == EL_SP_ELECTRON ||
6679                smashed == EL_SP_DISK_ORANGE)) ||
6680              (element == EL_SP_INFOTRON &&
6681               smashed == EL_SP_DISK_YELLOW))
6682     {
6683       Bang(x, y + 1);
6684       return;
6685     }
6686     else if (CAN_SMASH_EVERYTHING(element))
6687     {
6688       if (IS_CLASSIC_ENEMY(smashed) ||
6689           CAN_EXPLODE_SMASHED(smashed))
6690       {
6691         Bang(x, y + 1);
6692         return;
6693       }
6694       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6695       {
6696         if (smashed == EL_LAMP ||
6697             smashed == EL_LAMP_ACTIVE)
6698         {
6699           Bang(x, y + 1);
6700           return;
6701         }
6702         else if (smashed == EL_NUT)
6703         {
6704           Tile[x][y + 1] = EL_NUT_BREAKING;
6705           PlayLevelSound(x, y, SND_NUT_BREAKING);
6706           RaiseScoreElement(EL_NUT);
6707           return;
6708         }
6709         else if (smashed == EL_PEARL)
6710         {
6711           ResetGfxAnimation(x, y);
6712
6713           Tile[x][y + 1] = EL_PEARL_BREAKING;
6714           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6715           return;
6716         }
6717         else if (smashed == EL_DIAMOND)
6718         {
6719           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6720           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6721           return;
6722         }
6723         else if (IS_BELT_SWITCH(smashed))
6724         {
6725           ToggleBeltSwitch(x, y + 1);
6726         }
6727         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6728                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6729                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6730                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6731         {
6732           ToggleSwitchgateSwitch(x, y + 1);
6733         }
6734         else if (smashed == EL_LIGHT_SWITCH ||
6735                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6736         {
6737           ToggleLightSwitch(x, y + 1);
6738         }
6739         else
6740         {
6741           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6742
6743           CheckElementChangeBySide(x, y + 1, smashed, element,
6744                                    CE_SWITCHED, CH_SIDE_TOP);
6745           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6746                                             CH_SIDE_TOP);
6747         }
6748       }
6749       else
6750       {
6751         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6752       }
6753     }
6754   }
6755
6756   // play sound of magic wall / mill
6757   if (!last_line &&
6758       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6759        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6760        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6761   {
6762     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6763       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6764     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6765       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6766     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6767       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6768
6769     return;
6770   }
6771
6772   // play sound of object that hits the ground
6773   if (last_line || object_hit)
6774     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6775 }
6776
6777 static void TurnRoundExt(int x, int y)
6778 {
6779   static struct
6780   {
6781     int dx, dy;
6782   } move_xy[] =
6783   {
6784     {  0,  0 },
6785     { -1,  0 },
6786     { +1,  0 },
6787     {  0,  0 },
6788     {  0, -1 },
6789     {  0,  0 }, { 0, 0 }, { 0, 0 },
6790     {  0, +1 }
6791   };
6792   static struct
6793   {
6794     int left, right, back;
6795   } turn[] =
6796   {
6797     { 0,        0,              0        },
6798     { MV_DOWN,  MV_UP,          MV_RIGHT },
6799     { MV_UP,    MV_DOWN,        MV_LEFT  },
6800     { 0,        0,              0        },
6801     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6802     { 0,        0,              0        },
6803     { 0,        0,              0        },
6804     { 0,        0,              0        },
6805     { MV_RIGHT, MV_LEFT,        MV_UP    }
6806   };
6807
6808   int element = Tile[x][y];
6809   int move_pattern = element_info[element].move_pattern;
6810
6811   int old_move_dir = MovDir[x][y];
6812   int left_dir  = turn[old_move_dir].left;
6813   int right_dir = turn[old_move_dir].right;
6814   int back_dir  = turn[old_move_dir].back;
6815
6816   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6817   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6818   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6819   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6820
6821   int left_x  = x + left_dx,  left_y  = y + left_dy;
6822   int right_x = x + right_dx, right_y = y + right_dy;
6823   int move_x  = x + move_dx,  move_y  = y + move_dy;
6824
6825   int xx, yy;
6826
6827   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6828   {
6829     TestIfBadThingTouchesOtherBadThing(x, y);
6830
6831     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6832       MovDir[x][y] = right_dir;
6833     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6834       MovDir[x][y] = left_dir;
6835
6836     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6837       MovDelay[x][y] = 9;
6838     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6839       MovDelay[x][y] = 1;
6840   }
6841   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6842   {
6843     TestIfBadThingTouchesOtherBadThing(x, y);
6844
6845     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6846       MovDir[x][y] = left_dir;
6847     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6848       MovDir[x][y] = right_dir;
6849
6850     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6851       MovDelay[x][y] = 9;
6852     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6853       MovDelay[x][y] = 1;
6854   }
6855   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6856   {
6857     TestIfBadThingTouchesOtherBadThing(x, y);
6858
6859     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6860       MovDir[x][y] = left_dir;
6861     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6862       MovDir[x][y] = right_dir;
6863
6864     if (MovDir[x][y] != old_move_dir)
6865       MovDelay[x][y] = 9;
6866   }
6867   else if (element == EL_YAMYAM)
6868   {
6869     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6870     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6871
6872     if (can_turn_left && can_turn_right)
6873       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6874     else if (can_turn_left)
6875       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6876     else if (can_turn_right)
6877       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6878     else
6879       MovDir[x][y] = back_dir;
6880
6881     MovDelay[x][y] = 16 + 16 * RND(3);
6882   }
6883   else if (element == EL_DARK_YAMYAM)
6884   {
6885     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6886                                                          left_x, left_y);
6887     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6888                                                          right_x, right_y);
6889
6890     if (can_turn_left && can_turn_right)
6891       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6892     else if (can_turn_left)
6893       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6894     else if (can_turn_right)
6895       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6896     else
6897       MovDir[x][y] = back_dir;
6898
6899     MovDelay[x][y] = 16 + 16 * RND(3);
6900   }
6901   else if (element == EL_PACMAN)
6902   {
6903     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6904     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6905
6906     if (can_turn_left && can_turn_right)
6907       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6908     else if (can_turn_left)
6909       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6910     else if (can_turn_right)
6911       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6912     else
6913       MovDir[x][y] = back_dir;
6914
6915     MovDelay[x][y] = 6 + RND(40);
6916   }
6917   else if (element == EL_PIG)
6918   {
6919     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6920     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6921     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6922     boolean should_turn_left, should_turn_right, should_move_on;
6923     int rnd_value = 24;
6924     int rnd = RND(rnd_value);
6925
6926     should_turn_left = (can_turn_left &&
6927                         (!can_move_on ||
6928                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6929                                                    y + back_dy + left_dy)));
6930     should_turn_right = (can_turn_right &&
6931                          (!can_move_on ||
6932                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6933                                                     y + back_dy + right_dy)));
6934     should_move_on = (can_move_on &&
6935                       (!can_turn_left ||
6936                        !can_turn_right ||
6937                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6938                                                  y + move_dy + left_dy) ||
6939                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6940                                                  y + move_dy + right_dy)));
6941
6942     if (should_turn_left || should_turn_right || should_move_on)
6943     {
6944       if (should_turn_left && should_turn_right && should_move_on)
6945         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6946                         rnd < 2 * rnd_value / 3 ? right_dir :
6947                         old_move_dir);
6948       else if (should_turn_left && should_turn_right)
6949         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6950       else if (should_turn_left && should_move_on)
6951         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6952       else if (should_turn_right && should_move_on)
6953         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6954       else if (should_turn_left)
6955         MovDir[x][y] = left_dir;
6956       else if (should_turn_right)
6957         MovDir[x][y] = right_dir;
6958       else if (should_move_on)
6959         MovDir[x][y] = old_move_dir;
6960     }
6961     else if (can_move_on && rnd > rnd_value / 8)
6962       MovDir[x][y] = old_move_dir;
6963     else if (can_turn_left && can_turn_right)
6964       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6965     else if (can_turn_left && rnd > rnd_value / 8)
6966       MovDir[x][y] = left_dir;
6967     else if (can_turn_right && rnd > rnd_value/8)
6968       MovDir[x][y] = right_dir;
6969     else
6970       MovDir[x][y] = back_dir;
6971
6972     xx = x + move_xy[MovDir[x][y]].dx;
6973     yy = y + move_xy[MovDir[x][y]].dy;
6974
6975     if (!IN_LEV_FIELD(xx, yy) ||
6976         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
6977       MovDir[x][y] = old_move_dir;
6978
6979     MovDelay[x][y] = 0;
6980   }
6981   else if (element == EL_DRAGON)
6982   {
6983     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6984     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6985     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6986     int rnd_value = 24;
6987     int rnd = RND(rnd_value);
6988
6989     if (can_move_on && rnd > rnd_value / 8)
6990       MovDir[x][y] = old_move_dir;
6991     else if (can_turn_left && can_turn_right)
6992       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6993     else if (can_turn_left && rnd > rnd_value / 8)
6994       MovDir[x][y] = left_dir;
6995     else if (can_turn_right && rnd > rnd_value / 8)
6996       MovDir[x][y] = right_dir;
6997     else
6998       MovDir[x][y] = back_dir;
6999
7000     xx = x + move_xy[MovDir[x][y]].dx;
7001     yy = y + move_xy[MovDir[x][y]].dy;
7002
7003     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7004       MovDir[x][y] = old_move_dir;
7005
7006     MovDelay[x][y] = 0;
7007   }
7008   else if (element == EL_MOLE)
7009   {
7010     boolean can_move_on =
7011       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7012                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7013                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7014     if (!can_move_on)
7015     {
7016       boolean can_turn_left =
7017         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7018                               IS_AMOEBOID(Tile[left_x][left_y])));
7019
7020       boolean can_turn_right =
7021         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7022                               IS_AMOEBOID(Tile[right_x][right_y])));
7023
7024       if (can_turn_left && can_turn_right)
7025         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7026       else if (can_turn_left)
7027         MovDir[x][y] = left_dir;
7028       else
7029         MovDir[x][y] = right_dir;
7030     }
7031
7032     if (MovDir[x][y] != old_move_dir)
7033       MovDelay[x][y] = 9;
7034   }
7035   else if (element == EL_BALLOON)
7036   {
7037     MovDir[x][y] = game.wind_direction;
7038     MovDelay[x][y] = 0;
7039   }
7040   else if (element == EL_SPRING)
7041   {
7042     if (MovDir[x][y] & MV_HORIZONTAL)
7043     {
7044       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7045           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7046       {
7047         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7048         ResetGfxAnimation(move_x, move_y);
7049         TEST_DrawLevelField(move_x, move_y);
7050
7051         MovDir[x][y] = back_dir;
7052       }
7053       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7054                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7055         MovDir[x][y] = MV_NONE;
7056     }
7057
7058     MovDelay[x][y] = 0;
7059   }
7060   else if (element == EL_ROBOT ||
7061            element == EL_SATELLITE ||
7062            element == EL_PENGUIN ||
7063            element == EL_EMC_ANDROID)
7064   {
7065     int attr_x = -1, attr_y = -1;
7066
7067     if (game.all_players_gone)
7068     {
7069       attr_x = game.exit_x;
7070       attr_y = game.exit_y;
7071     }
7072     else
7073     {
7074       int i;
7075
7076       for (i = 0; i < MAX_PLAYERS; i++)
7077       {
7078         struct PlayerInfo *player = &stored_player[i];
7079         int jx = player->jx, jy = player->jy;
7080
7081         if (!player->active)
7082           continue;
7083
7084         if (attr_x == -1 ||
7085             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7086         {
7087           attr_x = jx;
7088           attr_y = jy;
7089         }
7090       }
7091     }
7092
7093     if (element == EL_ROBOT &&
7094         game.robot_wheel_x >= 0 &&
7095         game.robot_wheel_y >= 0 &&
7096         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7097          game.engine_version < VERSION_IDENT(3,1,0,0)))
7098     {
7099       attr_x = game.robot_wheel_x;
7100       attr_y = game.robot_wheel_y;
7101     }
7102
7103     if (element == EL_PENGUIN)
7104     {
7105       int i;
7106       static int xy[4][2] =
7107       {
7108         { 0, -1 },
7109         { -1, 0 },
7110         { +1, 0 },
7111         { 0, +1 }
7112       };
7113
7114       for (i = 0; i < NUM_DIRECTIONS; i++)
7115       {
7116         int ex = x + xy[i][0];
7117         int ey = y + xy[i][1];
7118
7119         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7120                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7121                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7122                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7123         {
7124           attr_x = ex;
7125           attr_y = ey;
7126           break;
7127         }
7128       }
7129     }
7130
7131     MovDir[x][y] = MV_NONE;
7132     if (attr_x < x)
7133       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7134     else if (attr_x > x)
7135       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7136     if (attr_y < y)
7137       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7138     else if (attr_y > y)
7139       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7140
7141     if (element == EL_ROBOT)
7142     {
7143       int newx, newy;
7144
7145       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7146         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7147       Moving2Blocked(x, y, &newx, &newy);
7148
7149       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7150         MovDelay[x][y] = 8 + 8 * !RND(3);
7151       else
7152         MovDelay[x][y] = 16;
7153     }
7154     else if (element == EL_PENGUIN)
7155     {
7156       int newx, newy;
7157
7158       MovDelay[x][y] = 1;
7159
7160       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7161       {
7162         boolean first_horiz = RND(2);
7163         int new_move_dir = MovDir[x][y];
7164
7165         MovDir[x][y] =
7166           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7167         Moving2Blocked(x, y, &newx, &newy);
7168
7169         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7170           return;
7171
7172         MovDir[x][y] =
7173           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7174         Moving2Blocked(x, y, &newx, &newy);
7175
7176         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7177           return;
7178
7179         MovDir[x][y] = old_move_dir;
7180         return;
7181       }
7182     }
7183     else if (element == EL_SATELLITE)
7184     {
7185       int newx, newy;
7186
7187       MovDelay[x][y] = 1;
7188
7189       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7190       {
7191         boolean first_horiz = RND(2);
7192         int new_move_dir = MovDir[x][y];
7193
7194         MovDir[x][y] =
7195           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7196         Moving2Blocked(x, y, &newx, &newy);
7197
7198         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7199           return;
7200
7201         MovDir[x][y] =
7202           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7203         Moving2Blocked(x, y, &newx, &newy);
7204
7205         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7206           return;
7207
7208         MovDir[x][y] = old_move_dir;
7209         return;
7210       }
7211     }
7212     else if (element == EL_EMC_ANDROID)
7213     {
7214       static int check_pos[16] =
7215       {
7216         -1,             //  0 => (invalid)
7217         7,              //  1 => MV_LEFT
7218         3,              //  2 => MV_RIGHT
7219         -1,             //  3 => (invalid)
7220         1,              //  4 =>            MV_UP
7221         0,              //  5 => MV_LEFT  | MV_UP
7222         2,              //  6 => MV_RIGHT | MV_UP
7223         -1,             //  7 => (invalid)
7224         5,              //  8 =>            MV_DOWN
7225         6,              //  9 => MV_LEFT  | MV_DOWN
7226         4,              // 10 => MV_RIGHT | MV_DOWN
7227         -1,             // 11 => (invalid)
7228         -1,             // 12 => (invalid)
7229         -1,             // 13 => (invalid)
7230         -1,             // 14 => (invalid)
7231         -1,             // 15 => (invalid)
7232       };
7233       static struct
7234       {
7235         int dx, dy;
7236         int dir;
7237       } check_xy[8] =
7238       {
7239         { -1, -1,       MV_LEFT  | MV_UP   },
7240         {  0, -1,                  MV_UP   },
7241         { +1, -1,       MV_RIGHT | MV_UP   },
7242         { +1,  0,       MV_RIGHT           },
7243         { +1, +1,       MV_RIGHT | MV_DOWN },
7244         {  0, +1,                  MV_DOWN },
7245         { -1, +1,       MV_LEFT  | MV_DOWN },
7246         { -1,  0,       MV_LEFT            },
7247       };
7248       int start_pos, check_order;
7249       boolean can_clone = FALSE;
7250       int i;
7251
7252       // check if there is any free field around current position
7253       for (i = 0; i < 8; i++)
7254       {
7255         int newx = x + check_xy[i].dx;
7256         int newy = y + check_xy[i].dy;
7257
7258         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7259         {
7260           can_clone = TRUE;
7261
7262           break;
7263         }
7264       }
7265
7266       if (can_clone)            // randomly find an element to clone
7267       {
7268         can_clone = FALSE;
7269
7270         start_pos = check_pos[RND(8)];
7271         check_order = (RND(2) ? -1 : +1);
7272
7273         for (i = 0; i < 8; i++)
7274         {
7275           int pos_raw = start_pos + i * check_order;
7276           int pos = (pos_raw + 8) % 8;
7277           int newx = x + check_xy[pos].dx;
7278           int newy = y + check_xy[pos].dy;
7279
7280           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7281           {
7282             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7283             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7284
7285             Store[x][y] = Tile[newx][newy];
7286
7287             can_clone = TRUE;
7288
7289             break;
7290           }
7291         }
7292       }
7293
7294       if (can_clone)            // randomly find a direction to move
7295       {
7296         can_clone = FALSE;
7297
7298         start_pos = check_pos[RND(8)];
7299         check_order = (RND(2) ? -1 : +1);
7300
7301         for (i = 0; i < 8; i++)
7302         {
7303           int pos_raw = start_pos + i * check_order;
7304           int pos = (pos_raw + 8) % 8;
7305           int newx = x + check_xy[pos].dx;
7306           int newy = y + check_xy[pos].dy;
7307           int new_move_dir = check_xy[pos].dir;
7308
7309           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7310           {
7311             MovDir[x][y] = new_move_dir;
7312             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7313
7314             can_clone = TRUE;
7315
7316             break;
7317           }
7318         }
7319       }
7320
7321       if (can_clone)            // cloning and moving successful
7322         return;
7323
7324       // cannot clone -- try to move towards player
7325
7326       start_pos = check_pos[MovDir[x][y] & 0x0f];
7327       check_order = (RND(2) ? -1 : +1);
7328
7329       for (i = 0; i < 3; i++)
7330       {
7331         // first check start_pos, then previous/next or (next/previous) pos
7332         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7333         int pos = (pos_raw + 8) % 8;
7334         int newx = x + check_xy[pos].dx;
7335         int newy = y + check_xy[pos].dy;
7336         int new_move_dir = check_xy[pos].dir;
7337
7338         if (IS_PLAYER(newx, newy))
7339           break;
7340
7341         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7342         {
7343           MovDir[x][y] = new_move_dir;
7344           MovDelay[x][y] = level.android_move_time * 8 + 1;
7345
7346           break;
7347         }
7348       }
7349     }
7350   }
7351   else if (move_pattern == MV_TURNING_LEFT ||
7352            move_pattern == MV_TURNING_RIGHT ||
7353            move_pattern == MV_TURNING_LEFT_RIGHT ||
7354            move_pattern == MV_TURNING_RIGHT_LEFT ||
7355            move_pattern == MV_TURNING_RANDOM ||
7356            move_pattern == MV_ALL_DIRECTIONS)
7357   {
7358     boolean can_turn_left =
7359       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7360     boolean can_turn_right =
7361       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7362
7363     if (element_info[element].move_stepsize == 0)       // "not moving"
7364       return;
7365
7366     if (move_pattern == MV_TURNING_LEFT)
7367       MovDir[x][y] = left_dir;
7368     else if (move_pattern == MV_TURNING_RIGHT)
7369       MovDir[x][y] = right_dir;
7370     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7371       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7372     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7373       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7374     else if (move_pattern == MV_TURNING_RANDOM)
7375       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7376                       can_turn_right && !can_turn_left ? right_dir :
7377                       RND(2) ? left_dir : right_dir);
7378     else if (can_turn_left && can_turn_right)
7379       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7380     else if (can_turn_left)
7381       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7382     else if (can_turn_right)
7383       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7384     else
7385       MovDir[x][y] = back_dir;
7386
7387     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7388   }
7389   else if (move_pattern == MV_HORIZONTAL ||
7390            move_pattern == MV_VERTICAL)
7391   {
7392     if (move_pattern & old_move_dir)
7393       MovDir[x][y] = back_dir;
7394     else if (move_pattern == MV_HORIZONTAL)
7395       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7396     else if (move_pattern == MV_VERTICAL)
7397       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7398
7399     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7400   }
7401   else if (move_pattern & MV_ANY_DIRECTION)
7402   {
7403     MovDir[x][y] = move_pattern;
7404     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7405   }
7406   else if (move_pattern & MV_WIND_DIRECTION)
7407   {
7408     MovDir[x][y] = game.wind_direction;
7409     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7410   }
7411   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7412   {
7413     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7414       MovDir[x][y] = left_dir;
7415     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7416       MovDir[x][y] = right_dir;
7417
7418     if (MovDir[x][y] != old_move_dir)
7419       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7420   }
7421   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7422   {
7423     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7424       MovDir[x][y] = right_dir;
7425     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7426       MovDir[x][y] = left_dir;
7427
7428     if (MovDir[x][y] != old_move_dir)
7429       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7430   }
7431   else if (move_pattern == MV_TOWARDS_PLAYER ||
7432            move_pattern == MV_AWAY_FROM_PLAYER)
7433   {
7434     int attr_x = -1, attr_y = -1;
7435     int newx, newy;
7436     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7437
7438     if (game.all_players_gone)
7439     {
7440       attr_x = game.exit_x;
7441       attr_y = game.exit_y;
7442     }
7443     else
7444     {
7445       int i;
7446
7447       for (i = 0; i < MAX_PLAYERS; i++)
7448       {
7449         struct PlayerInfo *player = &stored_player[i];
7450         int jx = player->jx, jy = player->jy;
7451
7452         if (!player->active)
7453           continue;
7454
7455         if (attr_x == -1 ||
7456             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7457         {
7458           attr_x = jx;
7459           attr_y = jy;
7460         }
7461       }
7462     }
7463
7464     MovDir[x][y] = MV_NONE;
7465     if (attr_x < x)
7466       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7467     else if (attr_x > x)
7468       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7469     if (attr_y < y)
7470       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7471     else if (attr_y > y)
7472       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7473
7474     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7475
7476     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7477     {
7478       boolean first_horiz = RND(2);
7479       int new_move_dir = MovDir[x][y];
7480
7481       if (element_info[element].move_stepsize == 0)     // "not moving"
7482       {
7483         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7484         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7485
7486         return;
7487       }
7488
7489       MovDir[x][y] =
7490         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7491       Moving2Blocked(x, y, &newx, &newy);
7492
7493       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7494         return;
7495
7496       MovDir[x][y] =
7497         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7498       Moving2Blocked(x, y, &newx, &newy);
7499
7500       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7501         return;
7502
7503       MovDir[x][y] = old_move_dir;
7504     }
7505   }
7506   else if (move_pattern == MV_WHEN_PUSHED ||
7507            move_pattern == MV_WHEN_DROPPED)
7508   {
7509     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7510       MovDir[x][y] = MV_NONE;
7511
7512     MovDelay[x][y] = 0;
7513   }
7514   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7515   {
7516     static int test_xy[7][2] =
7517     {
7518       { 0, -1 },
7519       { -1, 0 },
7520       { +1, 0 },
7521       { 0, +1 },
7522       { 0, -1 },
7523       { -1, 0 },
7524       { +1, 0 },
7525     };
7526     static int test_dir[7] =
7527     {
7528       MV_UP,
7529       MV_LEFT,
7530       MV_RIGHT,
7531       MV_DOWN,
7532       MV_UP,
7533       MV_LEFT,
7534       MV_RIGHT,
7535     };
7536     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7537     int move_preference = -1000000;     // start with very low preference
7538     int new_move_dir = MV_NONE;
7539     int start_test = RND(4);
7540     int i;
7541
7542     for (i = 0; i < NUM_DIRECTIONS; i++)
7543     {
7544       int move_dir = test_dir[start_test + i];
7545       int move_dir_preference;
7546
7547       xx = x + test_xy[start_test + i][0];
7548       yy = y + test_xy[start_test + i][1];
7549
7550       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7551           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7552       {
7553         new_move_dir = move_dir;
7554
7555         break;
7556       }
7557
7558       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7559         continue;
7560
7561       move_dir_preference = -1 * RunnerVisit[xx][yy];
7562       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7563         move_dir_preference = PlayerVisit[xx][yy];
7564
7565       if (move_dir_preference > move_preference)
7566       {
7567         // prefer field that has not been visited for the longest time
7568         move_preference = move_dir_preference;
7569         new_move_dir = move_dir;
7570       }
7571       else if (move_dir_preference == move_preference &&
7572                move_dir == old_move_dir)
7573       {
7574         // prefer last direction when all directions are preferred equally
7575         move_preference = move_dir_preference;
7576         new_move_dir = move_dir;
7577       }
7578     }
7579
7580     MovDir[x][y] = new_move_dir;
7581     if (old_move_dir != new_move_dir)
7582       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7583   }
7584 }
7585
7586 static void TurnRound(int x, int y)
7587 {
7588   int direction = MovDir[x][y];
7589
7590   TurnRoundExt(x, y);
7591
7592   GfxDir[x][y] = MovDir[x][y];
7593
7594   if (direction != MovDir[x][y])
7595     GfxFrame[x][y] = 0;
7596
7597   if (MovDelay[x][y])
7598     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7599
7600   ResetGfxFrame(x, y);
7601 }
7602
7603 static boolean JustBeingPushed(int x, int y)
7604 {
7605   int i;
7606
7607   for (i = 0; i < MAX_PLAYERS; i++)
7608   {
7609     struct PlayerInfo *player = &stored_player[i];
7610
7611     if (player->active && player->is_pushing && player->MovPos)
7612     {
7613       int next_jx = player->jx + (player->jx - player->last_jx);
7614       int next_jy = player->jy + (player->jy - player->last_jy);
7615
7616       if (x == next_jx && y == next_jy)
7617         return TRUE;
7618     }
7619   }
7620
7621   return FALSE;
7622 }
7623
7624 static void StartMoving(int x, int y)
7625 {
7626   boolean started_moving = FALSE;       // some elements can fall _and_ move
7627   int element = Tile[x][y];
7628
7629   if (Stop[x][y])
7630     return;
7631
7632   if (MovDelay[x][y] == 0)
7633     GfxAction[x][y] = ACTION_DEFAULT;
7634
7635   if (CAN_FALL(element) && y < lev_fieldy - 1)
7636   {
7637     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7638         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7639       if (JustBeingPushed(x, y))
7640         return;
7641
7642     if (element == EL_QUICKSAND_FULL)
7643     {
7644       if (IS_FREE(x, y + 1))
7645       {
7646         InitMovingField(x, y, MV_DOWN);
7647         started_moving = TRUE;
7648
7649         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7650 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7651         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7652           Store[x][y] = EL_ROCK;
7653 #else
7654         Store[x][y] = EL_ROCK;
7655 #endif
7656
7657         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7658       }
7659       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7660       {
7661         if (!MovDelay[x][y])
7662         {
7663           MovDelay[x][y] = TILEY + 1;
7664
7665           ResetGfxAnimation(x, y);
7666           ResetGfxAnimation(x, y + 1);
7667         }
7668
7669         if (MovDelay[x][y])
7670         {
7671           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7672           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7673
7674           MovDelay[x][y]--;
7675           if (MovDelay[x][y])
7676             return;
7677         }
7678
7679         Tile[x][y] = EL_QUICKSAND_EMPTY;
7680         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7681         Store[x][y + 1] = Store[x][y];
7682         Store[x][y] = 0;
7683
7684         PlayLevelSoundAction(x, y, ACTION_FILLING);
7685       }
7686       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7687       {
7688         if (!MovDelay[x][y])
7689         {
7690           MovDelay[x][y] = TILEY + 1;
7691
7692           ResetGfxAnimation(x, y);
7693           ResetGfxAnimation(x, y + 1);
7694         }
7695
7696         if (MovDelay[x][y])
7697         {
7698           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7699           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7700
7701           MovDelay[x][y]--;
7702           if (MovDelay[x][y])
7703             return;
7704         }
7705
7706         Tile[x][y] = EL_QUICKSAND_EMPTY;
7707         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7708         Store[x][y + 1] = Store[x][y];
7709         Store[x][y] = 0;
7710
7711         PlayLevelSoundAction(x, y, ACTION_FILLING);
7712       }
7713     }
7714     else if (element == EL_QUICKSAND_FAST_FULL)
7715     {
7716       if (IS_FREE(x, y + 1))
7717       {
7718         InitMovingField(x, y, MV_DOWN);
7719         started_moving = TRUE;
7720
7721         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7722 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7723         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7724           Store[x][y] = EL_ROCK;
7725 #else
7726         Store[x][y] = EL_ROCK;
7727 #endif
7728
7729         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7730       }
7731       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7732       {
7733         if (!MovDelay[x][y])
7734         {
7735           MovDelay[x][y] = TILEY + 1;
7736
7737           ResetGfxAnimation(x, y);
7738           ResetGfxAnimation(x, y + 1);
7739         }
7740
7741         if (MovDelay[x][y])
7742         {
7743           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7744           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7745
7746           MovDelay[x][y]--;
7747           if (MovDelay[x][y])
7748             return;
7749         }
7750
7751         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7752         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7753         Store[x][y + 1] = Store[x][y];
7754         Store[x][y] = 0;
7755
7756         PlayLevelSoundAction(x, y, ACTION_FILLING);
7757       }
7758       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7759       {
7760         if (!MovDelay[x][y])
7761         {
7762           MovDelay[x][y] = TILEY + 1;
7763
7764           ResetGfxAnimation(x, y);
7765           ResetGfxAnimation(x, y + 1);
7766         }
7767
7768         if (MovDelay[x][y])
7769         {
7770           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7771           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7772
7773           MovDelay[x][y]--;
7774           if (MovDelay[x][y])
7775             return;
7776         }
7777
7778         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7779         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7780         Store[x][y + 1] = Store[x][y];
7781         Store[x][y] = 0;
7782
7783         PlayLevelSoundAction(x, y, ACTION_FILLING);
7784       }
7785     }
7786     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7787              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7788     {
7789       InitMovingField(x, y, MV_DOWN);
7790       started_moving = TRUE;
7791
7792       Tile[x][y] = EL_QUICKSAND_FILLING;
7793       Store[x][y] = element;
7794
7795       PlayLevelSoundAction(x, y, ACTION_FILLING);
7796     }
7797     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7798              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7799     {
7800       InitMovingField(x, y, MV_DOWN);
7801       started_moving = TRUE;
7802
7803       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7804       Store[x][y] = element;
7805
7806       PlayLevelSoundAction(x, y, ACTION_FILLING);
7807     }
7808     else if (element == EL_MAGIC_WALL_FULL)
7809     {
7810       if (IS_FREE(x, y + 1))
7811       {
7812         InitMovingField(x, y, MV_DOWN);
7813         started_moving = TRUE;
7814
7815         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7816         Store[x][y] = EL_CHANGED(Store[x][y]);
7817       }
7818       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7819       {
7820         if (!MovDelay[x][y])
7821           MovDelay[x][y] = TILEY / 4 + 1;
7822
7823         if (MovDelay[x][y])
7824         {
7825           MovDelay[x][y]--;
7826           if (MovDelay[x][y])
7827             return;
7828         }
7829
7830         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7831         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7832         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7833         Store[x][y] = 0;
7834       }
7835     }
7836     else if (element == EL_BD_MAGIC_WALL_FULL)
7837     {
7838       if (IS_FREE(x, y + 1))
7839       {
7840         InitMovingField(x, y, MV_DOWN);
7841         started_moving = TRUE;
7842
7843         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7844         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7845       }
7846       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7847       {
7848         if (!MovDelay[x][y])
7849           MovDelay[x][y] = TILEY / 4 + 1;
7850
7851         if (MovDelay[x][y])
7852         {
7853           MovDelay[x][y]--;
7854           if (MovDelay[x][y])
7855             return;
7856         }
7857
7858         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7859         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7860         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7861         Store[x][y] = 0;
7862       }
7863     }
7864     else if (element == EL_DC_MAGIC_WALL_FULL)
7865     {
7866       if (IS_FREE(x, y + 1))
7867       {
7868         InitMovingField(x, y, MV_DOWN);
7869         started_moving = TRUE;
7870
7871         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7872         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7873       }
7874       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7875       {
7876         if (!MovDelay[x][y])
7877           MovDelay[x][y] = TILEY / 4 + 1;
7878
7879         if (MovDelay[x][y])
7880         {
7881           MovDelay[x][y]--;
7882           if (MovDelay[x][y])
7883             return;
7884         }
7885
7886         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7887         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7888         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7889         Store[x][y] = 0;
7890       }
7891     }
7892     else if ((CAN_PASS_MAGIC_WALL(element) &&
7893               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7894                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7895              (CAN_PASS_DC_MAGIC_WALL(element) &&
7896               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7897
7898     {
7899       InitMovingField(x, y, MV_DOWN);
7900       started_moving = TRUE;
7901
7902       Tile[x][y] =
7903         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7904          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7905          EL_DC_MAGIC_WALL_FILLING);
7906       Store[x][y] = element;
7907     }
7908     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7909     {
7910       SplashAcid(x, y + 1);
7911
7912       InitMovingField(x, y, MV_DOWN);
7913       started_moving = TRUE;
7914
7915       Store[x][y] = EL_ACID;
7916     }
7917     else if (
7918              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7919               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7920              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7921               CAN_FALL(element) && WasJustFalling[x][y] &&
7922               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7923
7924              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7925               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7926               (Tile[x][y + 1] == EL_BLOCKED)))
7927     {
7928       /* this is needed for a special case not covered by calling "Impact()"
7929          from "ContinueMoving()": if an element moves to a tile directly below
7930          another element which was just falling on that tile (which was empty
7931          in the previous frame), the falling element above would just stop
7932          instead of smashing the element below (in previous version, the above
7933          element was just checked for "moving" instead of "falling", resulting
7934          in incorrect smashes caused by horizontal movement of the above
7935          element; also, the case of the player being the element to smash was
7936          simply not covered here... :-/ ) */
7937
7938       CheckCollision[x][y] = 0;
7939       CheckImpact[x][y] = 0;
7940
7941       Impact(x, y);
7942     }
7943     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7944     {
7945       if (MovDir[x][y] == MV_NONE)
7946       {
7947         InitMovingField(x, y, MV_DOWN);
7948         started_moving = TRUE;
7949       }
7950     }
7951     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7952     {
7953       if (WasJustFalling[x][y]) // prevent animation from being restarted
7954         MovDir[x][y] = MV_DOWN;
7955
7956       InitMovingField(x, y, MV_DOWN);
7957       started_moving = TRUE;
7958     }
7959     else if (element == EL_AMOEBA_DROP)
7960     {
7961       Tile[x][y] = EL_AMOEBA_GROWING;
7962       Store[x][y] = EL_AMOEBA_WET;
7963     }
7964     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7965               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7966              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7967              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7968     {
7969       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7970                                 (IS_FREE(x - 1, y + 1) ||
7971                                  Tile[x - 1][y + 1] == EL_ACID));
7972       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7973                                 (IS_FREE(x + 1, y + 1) ||
7974                                  Tile[x + 1][y + 1] == EL_ACID));
7975       boolean can_fall_any  = (can_fall_left || can_fall_right);
7976       boolean can_fall_both = (can_fall_left && can_fall_right);
7977       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
7978
7979       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7980       {
7981         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7982           can_fall_right = FALSE;
7983         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7984           can_fall_left = FALSE;
7985         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7986           can_fall_right = FALSE;
7987         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7988           can_fall_left = FALSE;
7989
7990         can_fall_any  = (can_fall_left || can_fall_right);
7991         can_fall_both = FALSE;
7992       }
7993
7994       if (can_fall_both)
7995       {
7996         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7997           can_fall_right = FALSE;       // slip down on left side
7998         else
7999           can_fall_left = !(can_fall_right = RND(2));
8000
8001         can_fall_both = FALSE;
8002       }
8003
8004       if (can_fall_any)
8005       {
8006         // if not determined otherwise, prefer left side for slipping down
8007         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8008         started_moving = TRUE;
8009       }
8010     }
8011     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8012     {
8013       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8014       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8015       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8016       int belt_dir = game.belt_dir[belt_nr];
8017
8018       if ((belt_dir == MV_LEFT  && left_is_free) ||
8019           (belt_dir == MV_RIGHT && right_is_free))
8020       {
8021         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8022
8023         InitMovingField(x, y, belt_dir);
8024         started_moving = TRUE;
8025
8026         Pushed[x][y] = TRUE;
8027         Pushed[nextx][y] = TRUE;
8028
8029         GfxAction[x][y] = ACTION_DEFAULT;
8030       }
8031       else
8032       {
8033         MovDir[x][y] = 0;       // if element was moving, stop it
8034       }
8035     }
8036   }
8037
8038   // not "else if" because of elements that can fall and move (EL_SPRING)
8039   if (CAN_MOVE(element) && !started_moving)
8040   {
8041     int move_pattern = element_info[element].move_pattern;
8042     int newx, newy;
8043
8044     Moving2Blocked(x, y, &newx, &newy);
8045
8046     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8047       return;
8048
8049     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8050         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8051     {
8052       WasJustMoving[x][y] = 0;
8053       CheckCollision[x][y] = 0;
8054
8055       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8056
8057       if (Tile[x][y] != element)        // element has changed
8058         return;
8059     }
8060
8061     if (!MovDelay[x][y])        // start new movement phase
8062     {
8063       // all objects that can change their move direction after each step
8064       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8065
8066       if (element != EL_YAMYAM &&
8067           element != EL_DARK_YAMYAM &&
8068           element != EL_PACMAN &&
8069           !(move_pattern & MV_ANY_DIRECTION) &&
8070           move_pattern != MV_TURNING_LEFT &&
8071           move_pattern != MV_TURNING_RIGHT &&
8072           move_pattern != MV_TURNING_LEFT_RIGHT &&
8073           move_pattern != MV_TURNING_RIGHT_LEFT &&
8074           move_pattern != MV_TURNING_RANDOM)
8075       {
8076         TurnRound(x, y);
8077
8078         if (MovDelay[x][y] && (element == EL_BUG ||
8079                                element == EL_SPACESHIP ||
8080                                element == EL_SP_SNIKSNAK ||
8081                                element == EL_SP_ELECTRON ||
8082                                element == EL_MOLE))
8083           TEST_DrawLevelField(x, y);
8084       }
8085     }
8086
8087     if (MovDelay[x][y])         // wait some time before next movement
8088     {
8089       MovDelay[x][y]--;
8090
8091       if (element == EL_ROBOT ||
8092           element == EL_YAMYAM ||
8093           element == EL_DARK_YAMYAM)
8094       {
8095         DrawLevelElementAnimationIfNeeded(x, y, element);
8096         PlayLevelSoundAction(x, y, ACTION_WAITING);
8097       }
8098       else if (element == EL_SP_ELECTRON)
8099         DrawLevelElementAnimationIfNeeded(x, y, element);
8100       else if (element == EL_DRAGON)
8101       {
8102         int i;
8103         int dir = MovDir[x][y];
8104         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8105         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8106         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8107                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8108                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8109                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8110         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8111
8112         GfxAction[x][y] = ACTION_ATTACKING;
8113
8114         if (IS_PLAYER(x, y))
8115           DrawPlayerField(x, y);
8116         else
8117           TEST_DrawLevelField(x, y);
8118
8119         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8120
8121         for (i = 1; i <= 3; i++)
8122         {
8123           int xx = x + i * dx;
8124           int yy = y + i * dy;
8125           int sx = SCREENX(xx);
8126           int sy = SCREENY(yy);
8127           int flame_graphic = graphic + (i - 1);
8128
8129           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8130             break;
8131
8132           if (MovDelay[x][y])
8133           {
8134             int flamed = MovingOrBlocked2Element(xx, yy);
8135
8136             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8137               Bang(xx, yy);
8138             else
8139               RemoveMovingField(xx, yy);
8140
8141             ChangeDelay[xx][yy] = 0;
8142
8143             Tile[xx][yy] = EL_FLAMES;
8144
8145             if (IN_SCR_FIELD(sx, sy))
8146             {
8147               TEST_DrawLevelFieldCrumbled(xx, yy);
8148               DrawGraphic(sx, sy, flame_graphic, frame);
8149             }
8150           }
8151           else
8152           {
8153             if (Tile[xx][yy] == EL_FLAMES)
8154               Tile[xx][yy] = EL_EMPTY;
8155             TEST_DrawLevelField(xx, yy);
8156           }
8157         }
8158       }
8159
8160       if (MovDelay[x][y])       // element still has to wait some time
8161       {
8162         PlayLevelSoundAction(x, y, ACTION_WAITING);
8163
8164         return;
8165       }
8166     }
8167
8168     // now make next step
8169
8170     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8171
8172     if (DONT_COLLIDE_WITH(element) &&
8173         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8174         !PLAYER_ENEMY_PROTECTED(newx, newy))
8175     {
8176       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8177
8178       return;
8179     }
8180
8181     else if (CAN_MOVE_INTO_ACID(element) &&
8182              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8183              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8184              (MovDir[x][y] == MV_DOWN ||
8185               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8186     {
8187       SplashAcid(newx, newy);
8188       Store[x][y] = EL_ACID;
8189     }
8190     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8191     {
8192       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8193           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8194           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8195           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8196       {
8197         RemoveField(x, y);
8198         TEST_DrawLevelField(x, y);
8199
8200         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8201         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8202           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8203
8204         game.friends_still_needed--;
8205         if (!game.friends_still_needed &&
8206             !game.GameOver &&
8207             game.all_players_gone)
8208           LevelSolved();
8209
8210         return;
8211       }
8212       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8213       {
8214         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8215           TEST_DrawLevelField(newx, newy);
8216         else
8217           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8218       }
8219       else if (!IS_FREE(newx, newy))
8220       {
8221         GfxAction[x][y] = ACTION_WAITING;
8222
8223         if (IS_PLAYER(x, y))
8224           DrawPlayerField(x, y);
8225         else
8226           TEST_DrawLevelField(x, y);
8227
8228         return;
8229       }
8230     }
8231     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8232     {
8233       if (IS_FOOD_PIG(Tile[newx][newy]))
8234       {
8235         if (IS_MOVING(newx, newy))
8236           RemoveMovingField(newx, newy);
8237         else
8238         {
8239           Tile[newx][newy] = EL_EMPTY;
8240           TEST_DrawLevelField(newx, newy);
8241         }
8242
8243         PlayLevelSound(x, y, SND_PIG_DIGGING);
8244       }
8245       else if (!IS_FREE(newx, newy))
8246       {
8247         if (IS_PLAYER(x, y))
8248           DrawPlayerField(x, y);
8249         else
8250           TEST_DrawLevelField(x, y);
8251
8252         return;
8253       }
8254     }
8255     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8256     {
8257       if (Store[x][y] != EL_EMPTY)
8258       {
8259         boolean can_clone = FALSE;
8260         int xx, yy;
8261
8262         // check if element to clone is still there
8263         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8264         {
8265           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8266           {
8267             can_clone = TRUE;
8268
8269             break;
8270           }
8271         }
8272
8273         // cannot clone or target field not free anymore -- do not clone
8274         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8275           Store[x][y] = EL_EMPTY;
8276       }
8277
8278       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8279       {
8280         if (IS_MV_DIAGONAL(MovDir[x][y]))
8281         {
8282           int diagonal_move_dir = MovDir[x][y];
8283           int stored = Store[x][y];
8284           int change_delay = 8;
8285           int graphic;
8286
8287           // android is moving diagonally
8288
8289           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8290
8291           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8292           GfxElement[x][y] = EL_EMC_ANDROID;
8293           GfxAction[x][y] = ACTION_SHRINKING;
8294           GfxDir[x][y] = diagonal_move_dir;
8295           ChangeDelay[x][y] = change_delay;
8296
8297           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8298                                    GfxDir[x][y]);
8299
8300           DrawLevelGraphicAnimation(x, y, graphic);
8301           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8302
8303           if (Tile[newx][newy] == EL_ACID)
8304           {
8305             SplashAcid(newx, newy);
8306
8307             return;
8308           }
8309
8310           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8311
8312           Store[newx][newy] = EL_EMC_ANDROID;
8313           GfxElement[newx][newy] = EL_EMC_ANDROID;
8314           GfxAction[newx][newy] = ACTION_GROWING;
8315           GfxDir[newx][newy] = diagonal_move_dir;
8316           ChangeDelay[newx][newy] = change_delay;
8317
8318           graphic = el_act_dir2img(GfxElement[newx][newy],
8319                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8320
8321           DrawLevelGraphicAnimation(newx, newy, graphic);
8322           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8323
8324           return;
8325         }
8326         else
8327         {
8328           Tile[newx][newy] = EL_EMPTY;
8329           TEST_DrawLevelField(newx, newy);
8330
8331           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8332         }
8333       }
8334       else if (!IS_FREE(newx, newy))
8335       {
8336         return;
8337       }
8338     }
8339     else if (IS_CUSTOM_ELEMENT(element) &&
8340              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8341     {
8342       if (!DigFieldByCE(newx, newy, element))
8343         return;
8344
8345       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8346       {
8347         RunnerVisit[x][y] = FrameCounter;
8348         PlayerVisit[x][y] /= 8;         // expire player visit path
8349       }
8350     }
8351     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8352     {
8353       if (!IS_FREE(newx, newy))
8354       {
8355         if (IS_PLAYER(x, y))
8356           DrawPlayerField(x, y);
8357         else
8358           TEST_DrawLevelField(x, y);
8359
8360         return;
8361       }
8362       else
8363       {
8364         boolean wanna_flame = !RND(10);
8365         int dx = newx - x, dy = newy - y;
8366         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8367         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8368         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8369                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8370         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8371                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8372
8373         if ((wanna_flame ||
8374              IS_CLASSIC_ENEMY(element1) ||
8375              IS_CLASSIC_ENEMY(element2)) &&
8376             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8377             element1 != EL_FLAMES && element2 != EL_FLAMES)
8378         {
8379           ResetGfxAnimation(x, y);
8380           GfxAction[x][y] = ACTION_ATTACKING;
8381
8382           if (IS_PLAYER(x, y))
8383             DrawPlayerField(x, y);
8384           else
8385             TEST_DrawLevelField(x, y);
8386
8387           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8388
8389           MovDelay[x][y] = 50;
8390
8391           Tile[newx][newy] = EL_FLAMES;
8392           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8393             Tile[newx1][newy1] = EL_FLAMES;
8394           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8395             Tile[newx2][newy2] = EL_FLAMES;
8396
8397           return;
8398         }
8399       }
8400     }
8401     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8402              Tile[newx][newy] == EL_DIAMOND)
8403     {
8404       if (IS_MOVING(newx, newy))
8405         RemoveMovingField(newx, newy);
8406       else
8407       {
8408         Tile[newx][newy] = EL_EMPTY;
8409         TEST_DrawLevelField(newx, newy);
8410       }
8411
8412       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8413     }
8414     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8415              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8416     {
8417       if (AmoebaNr[newx][newy])
8418       {
8419         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8420         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8421             Tile[newx][newy] == EL_BD_AMOEBA)
8422           AmoebaCnt[AmoebaNr[newx][newy]]--;
8423       }
8424
8425       if (IS_MOVING(newx, newy))
8426       {
8427         RemoveMovingField(newx, newy);
8428       }
8429       else
8430       {
8431         Tile[newx][newy] = EL_EMPTY;
8432         TEST_DrawLevelField(newx, newy);
8433       }
8434
8435       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8436     }
8437     else if ((element == EL_PACMAN || element == EL_MOLE)
8438              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8439     {
8440       if (AmoebaNr[newx][newy])
8441       {
8442         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8443         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8444             Tile[newx][newy] == EL_BD_AMOEBA)
8445           AmoebaCnt[AmoebaNr[newx][newy]]--;
8446       }
8447
8448       if (element == EL_MOLE)
8449       {
8450         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8451         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8452
8453         ResetGfxAnimation(x, y);
8454         GfxAction[x][y] = ACTION_DIGGING;
8455         TEST_DrawLevelField(x, y);
8456
8457         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8458
8459         return;                         // wait for shrinking amoeba
8460       }
8461       else      // element == EL_PACMAN
8462       {
8463         Tile[newx][newy] = EL_EMPTY;
8464         TEST_DrawLevelField(newx, newy);
8465         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8466       }
8467     }
8468     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8469              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8470               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8471     {
8472       // wait for shrinking amoeba to completely disappear
8473       return;
8474     }
8475     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8476     {
8477       // object was running against a wall
8478
8479       TurnRound(x, y);
8480
8481       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8482         DrawLevelElementAnimation(x, y, element);
8483
8484       if (DONT_TOUCH(element))
8485         TestIfBadThingTouchesPlayer(x, y);
8486
8487       return;
8488     }
8489
8490     InitMovingField(x, y, MovDir[x][y]);
8491
8492     PlayLevelSoundAction(x, y, ACTION_MOVING);
8493   }
8494
8495   if (MovDir[x][y])
8496     ContinueMoving(x, y);
8497 }
8498
8499 void ContinueMoving(int x, int y)
8500 {
8501   int element = Tile[x][y];
8502   struct ElementInfo *ei = &element_info[element];
8503   int direction = MovDir[x][y];
8504   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8505   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8506   int newx = x + dx, newy = y + dy;
8507   int stored = Store[x][y];
8508   int stored_new = Store[newx][newy];
8509   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8510   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8511   boolean last_line = (newy == lev_fieldy - 1);
8512   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8513
8514   if (pushed_by_player)         // special case: moving object pushed by player
8515   {
8516     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8517   }
8518   else if (use_step_delay)      // special case: moving object has step delay
8519   {
8520     if (!MovDelay[x][y])
8521       MovPos[x][y] += getElementMoveStepsize(x, y);
8522
8523     if (MovDelay[x][y])
8524       MovDelay[x][y]--;
8525     else
8526       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8527
8528     if (MovDelay[x][y])
8529     {
8530       TEST_DrawLevelField(x, y);
8531
8532       return;   // element is still waiting
8533     }
8534   }
8535   else                          // normal case: generically moving object
8536   {
8537     MovPos[x][y] += getElementMoveStepsize(x, y);
8538   }
8539
8540   if (ABS(MovPos[x][y]) < TILEX)
8541   {
8542     TEST_DrawLevelField(x, y);
8543
8544     return;     // element is still moving
8545   }
8546
8547   // element reached destination field
8548
8549   Tile[x][y] = EL_EMPTY;
8550   Tile[newx][newy] = element;
8551   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8552
8553   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8554   {
8555     element = Tile[newx][newy] = EL_ACID;
8556   }
8557   else if (element == EL_MOLE)
8558   {
8559     Tile[x][y] = EL_SAND;
8560
8561     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8562   }
8563   else if (element == EL_QUICKSAND_FILLING)
8564   {
8565     element = Tile[newx][newy] = get_next_element(element);
8566     Store[newx][newy] = Store[x][y];
8567   }
8568   else if (element == EL_QUICKSAND_EMPTYING)
8569   {
8570     Tile[x][y] = get_next_element(element);
8571     element = Tile[newx][newy] = Store[x][y];
8572   }
8573   else if (element == EL_QUICKSAND_FAST_FILLING)
8574   {
8575     element = Tile[newx][newy] = get_next_element(element);
8576     Store[newx][newy] = Store[x][y];
8577   }
8578   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8579   {
8580     Tile[x][y] = get_next_element(element);
8581     element = Tile[newx][newy] = Store[x][y];
8582   }
8583   else if (element == EL_MAGIC_WALL_FILLING)
8584   {
8585     element = Tile[newx][newy] = get_next_element(element);
8586     if (!game.magic_wall_active)
8587       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8588     Store[newx][newy] = Store[x][y];
8589   }
8590   else if (element == EL_MAGIC_WALL_EMPTYING)
8591   {
8592     Tile[x][y] = get_next_element(element);
8593     if (!game.magic_wall_active)
8594       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8595     element = Tile[newx][newy] = Store[x][y];
8596
8597     InitField(newx, newy, FALSE);
8598   }
8599   else if (element == EL_BD_MAGIC_WALL_FILLING)
8600   {
8601     element = Tile[newx][newy] = get_next_element(element);
8602     if (!game.magic_wall_active)
8603       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8604     Store[newx][newy] = Store[x][y];
8605   }
8606   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8607   {
8608     Tile[x][y] = get_next_element(element);
8609     if (!game.magic_wall_active)
8610       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8611     element = Tile[newx][newy] = Store[x][y];
8612
8613     InitField(newx, newy, FALSE);
8614   }
8615   else if (element == EL_DC_MAGIC_WALL_FILLING)
8616   {
8617     element = Tile[newx][newy] = get_next_element(element);
8618     if (!game.magic_wall_active)
8619       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8620     Store[newx][newy] = Store[x][y];
8621   }
8622   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8623   {
8624     Tile[x][y] = get_next_element(element);
8625     if (!game.magic_wall_active)
8626       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8627     element = Tile[newx][newy] = Store[x][y];
8628
8629     InitField(newx, newy, FALSE);
8630   }
8631   else if (element == EL_AMOEBA_DROPPING)
8632   {
8633     Tile[x][y] = get_next_element(element);
8634     element = Tile[newx][newy] = Store[x][y];
8635   }
8636   else if (element == EL_SOKOBAN_OBJECT)
8637   {
8638     if (Back[x][y])
8639       Tile[x][y] = Back[x][y];
8640
8641     if (Back[newx][newy])
8642       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8643
8644     Back[x][y] = Back[newx][newy] = 0;
8645   }
8646
8647   Store[x][y] = EL_EMPTY;
8648   MovPos[x][y] = 0;
8649   MovDir[x][y] = 0;
8650   MovDelay[x][y] = 0;
8651
8652   MovDelay[newx][newy] = 0;
8653
8654   if (CAN_CHANGE_OR_HAS_ACTION(element))
8655   {
8656     // copy element change control values to new field
8657     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8658     ChangePage[newx][newy]  = ChangePage[x][y];
8659     ChangeCount[newx][newy] = ChangeCount[x][y];
8660     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8661   }
8662
8663   CustomValue[newx][newy] = CustomValue[x][y];
8664
8665   ChangeDelay[x][y] = 0;
8666   ChangePage[x][y] = -1;
8667   ChangeCount[x][y] = 0;
8668   ChangeEvent[x][y] = -1;
8669
8670   CustomValue[x][y] = 0;
8671
8672   // copy animation control values to new field
8673   GfxFrame[newx][newy]  = GfxFrame[x][y];
8674   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8675   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8676   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8677
8678   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8679
8680   // some elements can leave other elements behind after moving
8681   if (ei->move_leave_element != EL_EMPTY &&
8682       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8683       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8684   {
8685     int move_leave_element = ei->move_leave_element;
8686
8687     // this makes it possible to leave the removed element again
8688     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8689       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8690
8691     Tile[x][y] = move_leave_element;
8692
8693     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8694       MovDir[x][y] = direction;
8695
8696     InitField(x, y, FALSE);
8697
8698     if (GFX_CRUMBLED(Tile[x][y]))
8699       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8700
8701     if (ELEM_IS_PLAYER(move_leave_element))
8702       RelocatePlayer(x, y, move_leave_element);
8703   }
8704
8705   // do this after checking for left-behind element
8706   ResetGfxAnimation(x, y);      // reset animation values for old field
8707
8708   if (!CAN_MOVE(element) ||
8709       (CAN_FALL(element) && direction == MV_DOWN &&
8710        (element == EL_SPRING ||
8711         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8712         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8713     GfxDir[x][y] = MovDir[newx][newy] = 0;
8714
8715   TEST_DrawLevelField(x, y);
8716   TEST_DrawLevelField(newx, newy);
8717
8718   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8719
8720   // prevent pushed element from moving on in pushed direction
8721   if (pushed_by_player && CAN_MOVE(element) &&
8722       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8723       !(element_info[element].move_pattern & direction))
8724     TurnRound(newx, newy);
8725
8726   // prevent elements on conveyor belt from moving on in last direction
8727   if (pushed_by_conveyor && CAN_FALL(element) &&
8728       direction & MV_HORIZONTAL)
8729     MovDir[newx][newy] = 0;
8730
8731   if (!pushed_by_player)
8732   {
8733     int nextx = newx + dx, nexty = newy + dy;
8734     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8735
8736     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8737
8738     if (CAN_FALL(element) && direction == MV_DOWN)
8739       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8740
8741     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8742       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8743
8744     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8745       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8746   }
8747
8748   if (DONT_TOUCH(element))      // object may be nasty to player or others
8749   {
8750     TestIfBadThingTouchesPlayer(newx, newy);
8751     TestIfBadThingTouchesFriend(newx, newy);
8752
8753     if (!IS_CUSTOM_ELEMENT(element))
8754       TestIfBadThingTouchesOtherBadThing(newx, newy);
8755   }
8756   else if (element == EL_PENGUIN)
8757     TestIfFriendTouchesBadThing(newx, newy);
8758
8759   if (DONT_GET_HIT_BY(element))
8760   {
8761     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8762   }
8763
8764   // give the player one last chance (one more frame) to move away
8765   if (CAN_FALL(element) && direction == MV_DOWN &&
8766       (last_line || (!IS_FREE(x, newy + 1) &&
8767                      (!IS_PLAYER(x, newy + 1) ||
8768                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8769     Impact(x, newy);
8770
8771   if (pushed_by_player && !game.use_change_when_pushing_bug)
8772   {
8773     int push_side = MV_DIR_OPPOSITE(direction);
8774     struct PlayerInfo *player = PLAYERINFO(x, y);
8775
8776     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8777                                player->index_bit, push_side);
8778     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8779                                         player->index_bit, push_side);
8780   }
8781
8782   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8783     MovDelay[newx][newy] = 1;
8784
8785   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8786
8787   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8788   TestIfElementHitsCustomElement(newx, newy, direction);
8789   TestIfPlayerTouchesCustomElement(newx, newy);
8790   TestIfElementTouchesCustomElement(newx, newy);
8791
8792   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8793       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8794     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8795                              MV_DIR_OPPOSITE(direction));
8796 }
8797
8798 int AmoebaNeighbourNr(int ax, int ay)
8799 {
8800   int i;
8801   int element = Tile[ax][ay];
8802   int group_nr = 0;
8803   static int xy[4][2] =
8804   {
8805     { 0, -1 },
8806     { -1, 0 },
8807     { +1, 0 },
8808     { 0, +1 }
8809   };
8810
8811   for (i = 0; i < NUM_DIRECTIONS; i++)
8812   {
8813     int x = ax + xy[i][0];
8814     int y = ay + xy[i][1];
8815
8816     if (!IN_LEV_FIELD(x, y))
8817       continue;
8818
8819     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8820       group_nr = AmoebaNr[x][y];
8821   }
8822
8823   return group_nr;
8824 }
8825
8826 static void AmoebaMerge(int ax, int ay)
8827 {
8828   int i, x, y, xx, yy;
8829   int new_group_nr = AmoebaNr[ax][ay];
8830   static int xy[4][2] =
8831   {
8832     { 0, -1 },
8833     { -1, 0 },
8834     { +1, 0 },
8835     { 0, +1 }
8836   };
8837
8838   if (new_group_nr == 0)
8839     return;
8840
8841   for (i = 0; i < NUM_DIRECTIONS; i++)
8842   {
8843     x = ax + xy[i][0];
8844     y = ay + xy[i][1];
8845
8846     if (!IN_LEV_FIELD(x, y))
8847       continue;
8848
8849     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8850          Tile[x][y] == EL_BD_AMOEBA ||
8851          Tile[x][y] == EL_AMOEBA_DEAD) &&
8852         AmoebaNr[x][y] != new_group_nr)
8853     {
8854       int old_group_nr = AmoebaNr[x][y];
8855
8856       if (old_group_nr == 0)
8857         return;
8858
8859       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8860       AmoebaCnt[old_group_nr] = 0;
8861       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8862       AmoebaCnt2[old_group_nr] = 0;
8863
8864       SCAN_PLAYFIELD(xx, yy)
8865       {
8866         if (AmoebaNr[xx][yy] == old_group_nr)
8867           AmoebaNr[xx][yy] = new_group_nr;
8868       }
8869     }
8870   }
8871 }
8872
8873 void AmoebaToDiamond(int ax, int ay)
8874 {
8875   int i, x, y;
8876
8877   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8878   {
8879     int group_nr = AmoebaNr[ax][ay];
8880
8881 #ifdef DEBUG
8882     if (group_nr == 0)
8883     {
8884       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8885       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8886
8887       return;
8888     }
8889 #endif
8890
8891     SCAN_PLAYFIELD(x, y)
8892     {
8893       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8894       {
8895         AmoebaNr[x][y] = 0;
8896         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8897       }
8898     }
8899
8900     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8901                             SND_AMOEBA_TURNING_TO_GEM :
8902                             SND_AMOEBA_TURNING_TO_ROCK));
8903     Bang(ax, ay);
8904   }
8905   else
8906   {
8907     static int xy[4][2] =
8908     {
8909       { 0, -1 },
8910       { -1, 0 },
8911       { +1, 0 },
8912       { 0, +1 }
8913     };
8914
8915     for (i = 0; i < NUM_DIRECTIONS; i++)
8916     {
8917       x = ax + xy[i][0];
8918       y = ay + xy[i][1];
8919
8920       if (!IN_LEV_FIELD(x, y))
8921         continue;
8922
8923       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8924       {
8925         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8926                               SND_AMOEBA_TURNING_TO_GEM :
8927                               SND_AMOEBA_TURNING_TO_ROCK));
8928         Bang(x, y);
8929       }
8930     }
8931   }
8932 }
8933
8934 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8935 {
8936   int x, y;
8937   int group_nr = AmoebaNr[ax][ay];
8938   boolean done = FALSE;
8939
8940 #ifdef DEBUG
8941   if (group_nr == 0)
8942   {
8943     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8944     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8945
8946     return;
8947   }
8948 #endif
8949
8950   SCAN_PLAYFIELD(x, y)
8951   {
8952     if (AmoebaNr[x][y] == group_nr &&
8953         (Tile[x][y] == EL_AMOEBA_DEAD ||
8954          Tile[x][y] == EL_BD_AMOEBA ||
8955          Tile[x][y] == EL_AMOEBA_GROWING))
8956     {
8957       AmoebaNr[x][y] = 0;
8958       Tile[x][y] = new_element;
8959       InitField(x, y, FALSE);
8960       TEST_DrawLevelField(x, y);
8961       done = TRUE;
8962     }
8963   }
8964
8965   if (done)
8966     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8967                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8968                             SND_BD_AMOEBA_TURNING_TO_GEM));
8969 }
8970
8971 static void AmoebaGrowing(int x, int y)
8972 {
8973   static unsigned int sound_delay = 0;
8974   static unsigned int sound_delay_value = 0;
8975
8976   if (!MovDelay[x][y])          // start new growing cycle
8977   {
8978     MovDelay[x][y] = 7;
8979
8980     if (DelayReached(&sound_delay, sound_delay_value))
8981     {
8982       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8983       sound_delay_value = 30;
8984     }
8985   }
8986
8987   if (MovDelay[x][y])           // wait some time before growing bigger
8988   {
8989     MovDelay[x][y]--;
8990     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8991     {
8992       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8993                                            6 - MovDelay[x][y]);
8994
8995       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8996     }
8997
8998     if (!MovDelay[x][y])
8999     {
9000       Tile[x][y] = Store[x][y];
9001       Store[x][y] = 0;
9002       TEST_DrawLevelField(x, y);
9003     }
9004   }
9005 }
9006
9007 static void AmoebaShrinking(int x, int y)
9008 {
9009   static unsigned int sound_delay = 0;
9010   static unsigned int sound_delay_value = 0;
9011
9012   if (!MovDelay[x][y])          // start new shrinking cycle
9013   {
9014     MovDelay[x][y] = 7;
9015
9016     if (DelayReached(&sound_delay, sound_delay_value))
9017       sound_delay_value = 30;
9018   }
9019
9020   if (MovDelay[x][y])           // wait some time before shrinking
9021   {
9022     MovDelay[x][y]--;
9023     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9024     {
9025       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9026                                            6 - MovDelay[x][y]);
9027
9028       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9029     }
9030
9031     if (!MovDelay[x][y])
9032     {
9033       Tile[x][y] = EL_EMPTY;
9034       TEST_DrawLevelField(x, y);
9035
9036       // don't let mole enter this field in this cycle;
9037       // (give priority to objects falling to this field from above)
9038       Stop[x][y] = TRUE;
9039     }
9040   }
9041 }
9042
9043 static void AmoebaReproduce(int ax, int ay)
9044 {
9045   int i;
9046   int element = Tile[ax][ay];
9047   int graphic = el2img(element);
9048   int newax = ax, neway = ay;
9049   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9050   static int xy[4][2] =
9051   {
9052     { 0, -1 },
9053     { -1, 0 },
9054     { +1, 0 },
9055     { 0, +1 }
9056   };
9057
9058   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9059   {
9060     Tile[ax][ay] = EL_AMOEBA_DEAD;
9061     TEST_DrawLevelField(ax, ay);
9062     return;
9063   }
9064
9065   if (IS_ANIMATED(graphic))
9066     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9067
9068   if (!MovDelay[ax][ay])        // start making new amoeba field
9069     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9070
9071   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9072   {
9073     MovDelay[ax][ay]--;
9074     if (MovDelay[ax][ay])
9075       return;
9076   }
9077
9078   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9079   {
9080     int start = RND(4);
9081     int x = ax + xy[start][0];
9082     int y = ay + xy[start][1];
9083
9084     if (!IN_LEV_FIELD(x, y))
9085       return;
9086
9087     if (IS_FREE(x, y) ||
9088         CAN_GROW_INTO(Tile[x][y]) ||
9089         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9090         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9091     {
9092       newax = x;
9093       neway = y;
9094     }
9095
9096     if (newax == ax && neway == ay)
9097       return;
9098   }
9099   else                          // normal or "filled" (BD style) amoeba
9100   {
9101     int start = RND(4);
9102     boolean waiting_for_player = FALSE;
9103
9104     for (i = 0; i < NUM_DIRECTIONS; i++)
9105     {
9106       int j = (start + i) % 4;
9107       int x = ax + xy[j][0];
9108       int y = ay + xy[j][1];
9109
9110       if (!IN_LEV_FIELD(x, y))
9111         continue;
9112
9113       if (IS_FREE(x, y) ||
9114           CAN_GROW_INTO(Tile[x][y]) ||
9115           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9116           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9117       {
9118         newax = x;
9119         neway = y;
9120         break;
9121       }
9122       else if (IS_PLAYER(x, y))
9123         waiting_for_player = TRUE;
9124     }
9125
9126     if (newax == ax && neway == ay)             // amoeba cannot grow
9127     {
9128       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9129       {
9130         Tile[ax][ay] = EL_AMOEBA_DEAD;
9131         TEST_DrawLevelField(ax, ay);
9132         AmoebaCnt[AmoebaNr[ax][ay]]--;
9133
9134         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9135         {
9136           if (element == EL_AMOEBA_FULL)
9137             AmoebaToDiamond(ax, ay);
9138           else if (element == EL_BD_AMOEBA)
9139             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9140         }
9141       }
9142       return;
9143     }
9144     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9145     {
9146       // amoeba gets larger by growing in some direction
9147
9148       int new_group_nr = AmoebaNr[ax][ay];
9149
9150 #ifdef DEBUG
9151   if (new_group_nr == 0)
9152   {
9153     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9154           newax, neway);
9155     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9156
9157     return;
9158   }
9159 #endif
9160
9161       AmoebaNr[newax][neway] = new_group_nr;
9162       AmoebaCnt[new_group_nr]++;
9163       AmoebaCnt2[new_group_nr]++;
9164
9165       // if amoeba touches other amoeba(s) after growing, unify them
9166       AmoebaMerge(newax, neway);
9167
9168       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9169       {
9170         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9171         return;
9172       }
9173     }
9174   }
9175
9176   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9177       (neway == lev_fieldy - 1 && newax != ax))
9178   {
9179     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9180     Store[newax][neway] = element;
9181   }
9182   else if (neway == ay || element == EL_EMC_DRIPPER)
9183   {
9184     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9185
9186     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9187   }
9188   else
9189   {
9190     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9191     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9192     Store[ax][ay] = EL_AMOEBA_DROP;
9193     ContinueMoving(ax, ay);
9194     return;
9195   }
9196
9197   TEST_DrawLevelField(newax, neway);
9198 }
9199
9200 static void Life(int ax, int ay)
9201 {
9202   int x1, y1, x2, y2;
9203   int life_time = 40;
9204   int element = Tile[ax][ay];
9205   int graphic = el2img(element);
9206   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9207                          level.biomaze);
9208   boolean changed = FALSE;
9209
9210   if (IS_ANIMATED(graphic))
9211     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9212
9213   if (Stop[ax][ay])
9214     return;
9215
9216   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9217     MovDelay[ax][ay] = life_time;
9218
9219   if (MovDelay[ax][ay])         // wait some time before next cycle
9220   {
9221     MovDelay[ax][ay]--;
9222     if (MovDelay[ax][ay])
9223       return;
9224   }
9225
9226   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9227   {
9228     int xx = ax+x1, yy = ay+y1;
9229     int old_element = Tile[xx][yy];
9230     int num_neighbours = 0;
9231
9232     if (!IN_LEV_FIELD(xx, yy))
9233       continue;
9234
9235     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9236     {
9237       int x = xx+x2, y = yy+y2;
9238
9239       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9240         continue;
9241
9242       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9243       boolean is_neighbour = FALSE;
9244
9245       if (level.use_life_bugs)
9246         is_neighbour =
9247           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9248            (IS_FREE(x, y)                             &&  Stop[x][y]));
9249       else
9250         is_neighbour =
9251           (Last[x][y] == element || is_player_cell);
9252
9253       if (is_neighbour)
9254         num_neighbours++;
9255     }
9256
9257     boolean is_free = FALSE;
9258
9259     if (level.use_life_bugs)
9260       is_free = (IS_FREE(xx, yy));
9261     else
9262       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9263
9264     if (xx == ax && yy == ay)           // field in the middle
9265     {
9266       if (num_neighbours < life_parameter[0] ||
9267           num_neighbours > life_parameter[1])
9268       {
9269         Tile[xx][yy] = EL_EMPTY;
9270         if (Tile[xx][yy] != old_element)
9271           TEST_DrawLevelField(xx, yy);
9272         Stop[xx][yy] = TRUE;
9273         changed = TRUE;
9274       }
9275     }
9276     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9277     {                                   // free border field
9278       if (num_neighbours >= life_parameter[2] &&
9279           num_neighbours <= life_parameter[3])
9280       {
9281         Tile[xx][yy] = element;
9282         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9283         if (Tile[xx][yy] != old_element)
9284           TEST_DrawLevelField(xx, yy);
9285         Stop[xx][yy] = TRUE;
9286         changed = TRUE;
9287       }
9288     }
9289   }
9290
9291   if (changed)
9292     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9293                    SND_GAME_OF_LIFE_GROWING);
9294 }
9295
9296 static void InitRobotWheel(int x, int y)
9297 {
9298   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9299 }
9300
9301 static void RunRobotWheel(int x, int y)
9302 {
9303   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9304 }
9305
9306 static void StopRobotWheel(int x, int y)
9307 {
9308   if (game.robot_wheel_x == x &&
9309       game.robot_wheel_y == y)
9310   {
9311     game.robot_wheel_x = -1;
9312     game.robot_wheel_y = -1;
9313     game.robot_wheel_active = FALSE;
9314   }
9315 }
9316
9317 static void InitTimegateWheel(int x, int y)
9318 {
9319   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9320 }
9321
9322 static void RunTimegateWheel(int x, int y)
9323 {
9324   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9325 }
9326
9327 static void InitMagicBallDelay(int x, int y)
9328 {
9329   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9330 }
9331
9332 static void ActivateMagicBall(int bx, int by)
9333 {
9334   int x, y;
9335
9336   if (level.ball_random)
9337   {
9338     int pos_border = RND(8);    // select one of the eight border elements
9339     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9340     int xx = pos_content % 3;
9341     int yy = pos_content / 3;
9342
9343     x = bx - 1 + xx;
9344     y = by - 1 + yy;
9345
9346     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9347       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9348   }
9349   else
9350   {
9351     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9352     {
9353       int xx = x - bx + 1;
9354       int yy = y - by + 1;
9355
9356       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9357         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9358     }
9359   }
9360
9361   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9362 }
9363
9364 static void CheckExit(int x, int y)
9365 {
9366   if (game.gems_still_needed > 0 ||
9367       game.sokoban_fields_still_needed > 0 ||
9368       game.sokoban_objects_still_needed > 0 ||
9369       game.lights_still_needed > 0)
9370   {
9371     int element = Tile[x][y];
9372     int graphic = el2img(element);
9373
9374     if (IS_ANIMATED(graphic))
9375       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9376
9377     return;
9378   }
9379
9380   // do not re-open exit door closed after last player
9381   if (game.all_players_gone)
9382     return;
9383
9384   Tile[x][y] = EL_EXIT_OPENING;
9385
9386   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9387 }
9388
9389 static void CheckExitEM(int x, int y)
9390 {
9391   if (game.gems_still_needed > 0 ||
9392       game.sokoban_fields_still_needed > 0 ||
9393       game.sokoban_objects_still_needed > 0 ||
9394       game.lights_still_needed > 0)
9395   {
9396     int element = Tile[x][y];
9397     int graphic = el2img(element);
9398
9399     if (IS_ANIMATED(graphic))
9400       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9401
9402     return;
9403   }
9404
9405   // do not re-open exit door closed after last player
9406   if (game.all_players_gone)
9407     return;
9408
9409   Tile[x][y] = EL_EM_EXIT_OPENING;
9410
9411   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9412 }
9413
9414 static void CheckExitSteel(int x, int y)
9415 {
9416   if (game.gems_still_needed > 0 ||
9417       game.sokoban_fields_still_needed > 0 ||
9418       game.sokoban_objects_still_needed > 0 ||
9419       game.lights_still_needed > 0)
9420   {
9421     int element = Tile[x][y];
9422     int graphic = el2img(element);
9423
9424     if (IS_ANIMATED(graphic))
9425       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9426
9427     return;
9428   }
9429
9430   // do not re-open exit door closed after last player
9431   if (game.all_players_gone)
9432     return;
9433
9434   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9435
9436   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9437 }
9438
9439 static void CheckExitSteelEM(int x, int y)
9440 {
9441   if (game.gems_still_needed > 0 ||
9442       game.sokoban_fields_still_needed > 0 ||
9443       game.sokoban_objects_still_needed > 0 ||
9444       game.lights_still_needed > 0)
9445   {
9446     int element = Tile[x][y];
9447     int graphic = el2img(element);
9448
9449     if (IS_ANIMATED(graphic))
9450       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9451
9452     return;
9453   }
9454
9455   // do not re-open exit door closed after last player
9456   if (game.all_players_gone)
9457     return;
9458
9459   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9460
9461   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9462 }
9463
9464 static void CheckExitSP(int x, int y)
9465 {
9466   if (game.gems_still_needed > 0)
9467   {
9468     int element = Tile[x][y];
9469     int graphic = el2img(element);
9470
9471     if (IS_ANIMATED(graphic))
9472       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9473
9474     return;
9475   }
9476
9477   // do not re-open exit door closed after last player
9478   if (game.all_players_gone)
9479     return;
9480
9481   Tile[x][y] = EL_SP_EXIT_OPENING;
9482
9483   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9484 }
9485
9486 static void CloseAllOpenTimegates(void)
9487 {
9488   int x, y;
9489
9490   SCAN_PLAYFIELD(x, y)
9491   {
9492     int element = Tile[x][y];
9493
9494     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9495     {
9496       Tile[x][y] = EL_TIMEGATE_CLOSING;
9497
9498       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9499     }
9500   }
9501 }
9502
9503 static void DrawTwinkleOnField(int x, int y)
9504 {
9505   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9506     return;
9507
9508   if (Tile[x][y] == EL_BD_DIAMOND)
9509     return;
9510
9511   if (MovDelay[x][y] == 0)      // next animation frame
9512     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9513
9514   if (MovDelay[x][y] != 0)      // wait some time before next frame
9515   {
9516     MovDelay[x][y]--;
9517
9518     DrawLevelElementAnimation(x, y, Tile[x][y]);
9519
9520     if (MovDelay[x][y] != 0)
9521     {
9522       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9523                                            10 - MovDelay[x][y]);
9524
9525       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9526     }
9527   }
9528 }
9529
9530 static void MauerWaechst(int x, int y)
9531 {
9532   int delay = 6;
9533
9534   if (!MovDelay[x][y])          // next animation frame
9535     MovDelay[x][y] = 3 * delay;
9536
9537   if (MovDelay[x][y])           // wait some time before next frame
9538   {
9539     MovDelay[x][y]--;
9540
9541     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9542     {
9543       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9544       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9545
9546       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9547     }
9548
9549     if (!MovDelay[x][y])
9550     {
9551       if (MovDir[x][y] == MV_LEFT)
9552       {
9553         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9554           TEST_DrawLevelField(x - 1, y);
9555       }
9556       else if (MovDir[x][y] == MV_RIGHT)
9557       {
9558         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9559           TEST_DrawLevelField(x + 1, y);
9560       }
9561       else if (MovDir[x][y] == MV_UP)
9562       {
9563         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9564           TEST_DrawLevelField(x, y - 1);
9565       }
9566       else
9567       {
9568         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9569           TEST_DrawLevelField(x, y + 1);
9570       }
9571
9572       Tile[x][y] = Store[x][y];
9573       Store[x][y] = 0;
9574       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9575       TEST_DrawLevelField(x, y);
9576     }
9577   }
9578 }
9579
9580 static void MauerAbleger(int ax, int ay)
9581 {
9582   int element = Tile[ax][ay];
9583   int graphic = el2img(element);
9584   boolean oben_frei = FALSE, unten_frei = FALSE;
9585   boolean links_frei = FALSE, rechts_frei = FALSE;
9586   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9587   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9588   boolean new_wall = FALSE;
9589
9590   if (IS_ANIMATED(graphic))
9591     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9592
9593   if (!MovDelay[ax][ay])        // start building new wall
9594     MovDelay[ax][ay] = 6;
9595
9596   if (MovDelay[ax][ay])         // wait some time before building new wall
9597   {
9598     MovDelay[ax][ay]--;
9599     if (MovDelay[ax][ay])
9600       return;
9601   }
9602
9603   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9604     oben_frei = TRUE;
9605   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9606     unten_frei = TRUE;
9607   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9608     links_frei = TRUE;
9609   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9610     rechts_frei = TRUE;
9611
9612   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9613       element == EL_EXPANDABLE_WALL_ANY)
9614   {
9615     if (oben_frei)
9616     {
9617       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9618       Store[ax][ay-1] = element;
9619       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9620       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9621         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9622                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9623       new_wall = TRUE;
9624     }
9625     if (unten_frei)
9626     {
9627       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9628       Store[ax][ay+1] = element;
9629       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9630       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9631         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9632                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9633       new_wall = TRUE;
9634     }
9635   }
9636
9637   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9638       element == EL_EXPANDABLE_WALL_ANY ||
9639       element == EL_EXPANDABLE_WALL ||
9640       element == EL_BD_EXPANDABLE_WALL)
9641   {
9642     if (links_frei)
9643     {
9644       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9645       Store[ax-1][ay] = element;
9646       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9647       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9648         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9649                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9650       new_wall = TRUE;
9651     }
9652
9653     if (rechts_frei)
9654     {
9655       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9656       Store[ax+1][ay] = element;
9657       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9658       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9659         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9660                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9661       new_wall = TRUE;
9662     }
9663   }
9664
9665   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9666     TEST_DrawLevelField(ax, ay);
9667
9668   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9669     oben_massiv = TRUE;
9670   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9671     unten_massiv = TRUE;
9672   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9673     links_massiv = TRUE;
9674   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9675     rechts_massiv = TRUE;
9676
9677   if (((oben_massiv && unten_massiv) ||
9678        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9679        element == EL_EXPANDABLE_WALL) &&
9680       ((links_massiv && rechts_massiv) ||
9681        element == EL_EXPANDABLE_WALL_VERTICAL))
9682     Tile[ax][ay] = EL_WALL;
9683
9684   if (new_wall)
9685     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9686 }
9687
9688 static void MauerAblegerStahl(int ax, int ay)
9689 {
9690   int element = Tile[ax][ay];
9691   int graphic = el2img(element);
9692   boolean oben_frei = FALSE, unten_frei = FALSE;
9693   boolean links_frei = FALSE, rechts_frei = FALSE;
9694   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9695   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9696   boolean new_wall = FALSE;
9697
9698   if (IS_ANIMATED(graphic))
9699     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9700
9701   if (!MovDelay[ax][ay])        // start building new wall
9702     MovDelay[ax][ay] = 6;
9703
9704   if (MovDelay[ax][ay])         // wait some time before building new wall
9705   {
9706     MovDelay[ax][ay]--;
9707     if (MovDelay[ax][ay])
9708       return;
9709   }
9710
9711   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9712     oben_frei = TRUE;
9713   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9714     unten_frei = TRUE;
9715   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9716     links_frei = TRUE;
9717   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9718     rechts_frei = TRUE;
9719
9720   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9721       element == EL_EXPANDABLE_STEELWALL_ANY)
9722   {
9723     if (oben_frei)
9724     {
9725       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9726       Store[ax][ay-1] = element;
9727       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9728       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9729         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9730                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9731       new_wall = TRUE;
9732     }
9733     if (unten_frei)
9734     {
9735       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9736       Store[ax][ay+1] = element;
9737       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9738       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9739         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9740                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9741       new_wall = TRUE;
9742     }
9743   }
9744
9745   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9746       element == EL_EXPANDABLE_STEELWALL_ANY)
9747   {
9748     if (links_frei)
9749     {
9750       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9751       Store[ax-1][ay] = element;
9752       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9753       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9754         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9755                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9756       new_wall = TRUE;
9757     }
9758
9759     if (rechts_frei)
9760     {
9761       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9762       Store[ax+1][ay] = element;
9763       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9764       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9765         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9766                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9767       new_wall = TRUE;
9768     }
9769   }
9770
9771   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9772     oben_massiv = TRUE;
9773   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9774     unten_massiv = TRUE;
9775   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9776     links_massiv = TRUE;
9777   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9778     rechts_massiv = TRUE;
9779
9780   if (((oben_massiv && unten_massiv) ||
9781        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9782       ((links_massiv && rechts_massiv) ||
9783        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9784     Tile[ax][ay] = EL_STEELWALL;
9785
9786   if (new_wall)
9787     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9788 }
9789
9790 static void CheckForDragon(int x, int y)
9791 {
9792   int i, j;
9793   boolean dragon_found = FALSE;
9794   static int xy[4][2] =
9795   {
9796     { 0, -1 },
9797     { -1, 0 },
9798     { +1, 0 },
9799     { 0, +1 }
9800   };
9801
9802   for (i = 0; i < NUM_DIRECTIONS; i++)
9803   {
9804     for (j = 0; j < 4; j++)
9805     {
9806       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9807
9808       if (IN_LEV_FIELD(xx, yy) &&
9809           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9810       {
9811         if (Tile[xx][yy] == EL_DRAGON)
9812           dragon_found = TRUE;
9813       }
9814       else
9815         break;
9816     }
9817   }
9818
9819   if (!dragon_found)
9820   {
9821     for (i = 0; i < NUM_DIRECTIONS; i++)
9822     {
9823       for (j = 0; j < 3; j++)
9824       {
9825         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9826   
9827         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9828         {
9829           Tile[xx][yy] = EL_EMPTY;
9830           TEST_DrawLevelField(xx, yy);
9831         }
9832         else
9833           break;
9834       }
9835     }
9836   }
9837 }
9838
9839 static void InitBuggyBase(int x, int y)
9840 {
9841   int element = Tile[x][y];
9842   int activating_delay = FRAMES_PER_SECOND / 4;
9843
9844   ChangeDelay[x][y] =
9845     (element == EL_SP_BUGGY_BASE ?
9846      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9847      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9848      activating_delay :
9849      element == EL_SP_BUGGY_BASE_ACTIVE ?
9850      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9851 }
9852
9853 static void WarnBuggyBase(int x, int y)
9854 {
9855   int i;
9856   static int xy[4][2] =
9857   {
9858     { 0, -1 },
9859     { -1, 0 },
9860     { +1, 0 },
9861     { 0, +1 }
9862   };
9863
9864   for (i = 0; i < NUM_DIRECTIONS; i++)
9865   {
9866     int xx = x + xy[i][0];
9867     int yy = y + xy[i][1];
9868
9869     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9870     {
9871       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9872
9873       break;
9874     }
9875   }
9876 }
9877
9878 static void InitTrap(int x, int y)
9879 {
9880   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9881 }
9882
9883 static void ActivateTrap(int x, int y)
9884 {
9885   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9886 }
9887
9888 static void ChangeActiveTrap(int x, int y)
9889 {
9890   int graphic = IMG_TRAP_ACTIVE;
9891
9892   // if new animation frame was drawn, correct crumbled sand border
9893   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9894     TEST_DrawLevelFieldCrumbled(x, y);
9895 }
9896
9897 static int getSpecialActionElement(int element, int number, int base_element)
9898 {
9899   return (element != EL_EMPTY ? element :
9900           number != -1 ? base_element + number - 1 :
9901           EL_EMPTY);
9902 }
9903
9904 static int getModifiedActionNumber(int value_old, int operator, int operand,
9905                                    int value_min, int value_max)
9906 {
9907   int value_new = (operator == CA_MODE_SET      ? operand :
9908                    operator == CA_MODE_ADD      ? value_old + operand :
9909                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9910                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9911                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9912                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9913                    value_old);
9914
9915   return (value_new < value_min ? value_min :
9916           value_new > value_max ? value_max :
9917           value_new);
9918 }
9919
9920 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9921 {
9922   struct ElementInfo *ei = &element_info[element];
9923   struct ElementChangeInfo *change = &ei->change_page[page];
9924   int target_element = change->target_element;
9925   int action_type = change->action_type;
9926   int action_mode = change->action_mode;
9927   int action_arg = change->action_arg;
9928   int action_element = change->action_element;
9929   int i;
9930
9931   if (!change->has_action)
9932     return;
9933
9934   // ---------- determine action paramater values -----------------------------
9935
9936   int level_time_value =
9937     (level.time > 0 ? TimeLeft :
9938      TimePlayed);
9939
9940   int action_arg_element_raw =
9941     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9942      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9943      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9944      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9945      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9946      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9947      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9948      EL_EMPTY);
9949   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9950
9951   int action_arg_direction =
9952     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9953      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9954      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9955      change->actual_trigger_side :
9956      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9957      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9958      MV_NONE);
9959
9960   int action_arg_number_min =
9961     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9962      CA_ARG_MIN);
9963
9964   int action_arg_number_max =
9965     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9966      action_type == CA_SET_LEVEL_GEMS ? 999 :
9967      action_type == CA_SET_LEVEL_TIME ? 9999 :
9968      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9969      action_type == CA_SET_CE_VALUE ? 9999 :
9970      action_type == CA_SET_CE_SCORE ? 9999 :
9971      CA_ARG_MAX);
9972
9973   int action_arg_number_reset =
9974     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9975      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9976      action_type == CA_SET_LEVEL_TIME ? level.time :
9977      action_type == CA_SET_LEVEL_SCORE ? 0 :
9978      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9979      action_type == CA_SET_CE_SCORE ? 0 :
9980      0);
9981
9982   int action_arg_number =
9983     (action_arg <= CA_ARG_MAX ? action_arg :
9984      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9985      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9986      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9987      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9988      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9989      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9990      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9991      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9992      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9993      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9994      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
9995      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
9996      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9997      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9998      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9999      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10000      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10001      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10002      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10003      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10004      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10005      -1);
10006
10007   int action_arg_number_old =
10008     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10009      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10010      action_type == CA_SET_LEVEL_SCORE ? game.score :
10011      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10012      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10013      0);
10014
10015   int action_arg_number_new =
10016     getModifiedActionNumber(action_arg_number_old,
10017                             action_mode, action_arg_number,
10018                             action_arg_number_min, action_arg_number_max);
10019
10020   int trigger_player_bits =
10021     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10022      change->actual_trigger_player_bits : change->trigger_player);
10023
10024   int action_arg_player_bits =
10025     (action_arg >= CA_ARG_PLAYER_1 &&
10026      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10027      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10028      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10029      PLAYER_BITS_ANY);
10030
10031   // ---------- execute action  -----------------------------------------------
10032
10033   switch (action_type)
10034   {
10035     case CA_NO_ACTION:
10036     {
10037       return;
10038     }
10039
10040     // ---------- level actions  ----------------------------------------------
10041
10042     case CA_RESTART_LEVEL:
10043     {
10044       game.restart_level = TRUE;
10045
10046       break;
10047     }
10048
10049     case CA_SHOW_ENVELOPE:
10050     {
10051       int element = getSpecialActionElement(action_arg_element,
10052                                             action_arg_number, EL_ENVELOPE_1);
10053
10054       if (IS_ENVELOPE(element))
10055         local_player->show_envelope = element;
10056
10057       break;
10058     }
10059
10060     case CA_SET_LEVEL_TIME:
10061     {
10062       if (level.time > 0)       // only modify limited time value
10063       {
10064         TimeLeft = action_arg_number_new;
10065
10066         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10067
10068         DisplayGameControlValues();
10069
10070         if (!TimeLeft && setup.time_limit)
10071           for (i = 0; i < MAX_PLAYERS; i++)
10072             KillPlayer(&stored_player[i]);
10073       }
10074
10075       break;
10076     }
10077
10078     case CA_SET_LEVEL_SCORE:
10079     {
10080       game.score = action_arg_number_new;
10081
10082       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10083
10084       DisplayGameControlValues();
10085
10086       break;
10087     }
10088
10089     case CA_SET_LEVEL_GEMS:
10090     {
10091       game.gems_still_needed = action_arg_number_new;
10092
10093       game.snapshot.collected_item = TRUE;
10094
10095       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10096
10097       DisplayGameControlValues();
10098
10099       break;
10100     }
10101
10102     case CA_SET_LEVEL_WIND:
10103     {
10104       game.wind_direction = action_arg_direction;
10105
10106       break;
10107     }
10108
10109     case CA_SET_LEVEL_RANDOM_SEED:
10110     {
10111       // ensure that setting a new random seed while playing is predictable
10112       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10113
10114       break;
10115     }
10116
10117     // ---------- player actions  ---------------------------------------------
10118
10119     case CA_MOVE_PLAYER:
10120     case CA_MOVE_PLAYER_NEW:
10121     {
10122       // automatically move to the next field in specified direction
10123       for (i = 0; i < MAX_PLAYERS; i++)
10124         if (trigger_player_bits & (1 << i))
10125           if (action_type == CA_MOVE_PLAYER ||
10126               stored_player[i].MovPos == 0)
10127             stored_player[i].programmed_action = action_arg_direction;
10128
10129       break;
10130     }
10131
10132     case CA_EXIT_PLAYER:
10133     {
10134       for (i = 0; i < MAX_PLAYERS; i++)
10135         if (action_arg_player_bits & (1 << i))
10136           ExitPlayer(&stored_player[i]);
10137
10138       if (game.players_still_needed == 0)
10139         LevelSolved();
10140
10141       break;
10142     }
10143
10144     case CA_KILL_PLAYER:
10145     {
10146       for (i = 0; i < MAX_PLAYERS; i++)
10147         if (action_arg_player_bits & (1 << i))
10148           KillPlayer(&stored_player[i]);
10149
10150       break;
10151     }
10152
10153     case CA_SET_PLAYER_KEYS:
10154     {
10155       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10156       int element = getSpecialActionElement(action_arg_element,
10157                                             action_arg_number, EL_KEY_1);
10158
10159       if (IS_KEY(element))
10160       {
10161         for (i = 0; i < MAX_PLAYERS; i++)
10162         {
10163           if (trigger_player_bits & (1 << i))
10164           {
10165             stored_player[i].key[KEY_NR(element)] = key_state;
10166
10167             DrawGameDoorValues();
10168           }
10169         }
10170       }
10171
10172       break;
10173     }
10174
10175     case CA_SET_PLAYER_SPEED:
10176     {
10177       for (i = 0; i < MAX_PLAYERS; i++)
10178       {
10179         if (trigger_player_bits & (1 << i))
10180         {
10181           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10182
10183           if (action_arg == CA_ARG_SPEED_FASTER &&
10184               stored_player[i].cannot_move)
10185           {
10186             action_arg_number = STEPSIZE_VERY_SLOW;
10187           }
10188           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10189                    action_arg == CA_ARG_SPEED_FASTER)
10190           {
10191             action_arg_number = 2;
10192             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10193                            CA_MODE_MULTIPLY);
10194           }
10195           else if (action_arg == CA_ARG_NUMBER_RESET)
10196           {
10197             action_arg_number = level.initial_player_stepsize[i];
10198           }
10199
10200           move_stepsize =
10201             getModifiedActionNumber(move_stepsize,
10202                                     action_mode,
10203                                     action_arg_number,
10204                                     action_arg_number_min,
10205                                     action_arg_number_max);
10206
10207           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10208         }
10209       }
10210
10211       break;
10212     }
10213
10214     case CA_SET_PLAYER_SHIELD:
10215     {
10216       for (i = 0; i < MAX_PLAYERS; i++)
10217       {
10218         if (trigger_player_bits & (1 << i))
10219         {
10220           if (action_arg == CA_ARG_SHIELD_OFF)
10221           {
10222             stored_player[i].shield_normal_time_left = 0;
10223             stored_player[i].shield_deadly_time_left = 0;
10224           }
10225           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10226           {
10227             stored_player[i].shield_normal_time_left = 999999;
10228           }
10229           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10230           {
10231             stored_player[i].shield_normal_time_left = 999999;
10232             stored_player[i].shield_deadly_time_left = 999999;
10233           }
10234         }
10235       }
10236
10237       break;
10238     }
10239
10240     case CA_SET_PLAYER_GRAVITY:
10241     {
10242       for (i = 0; i < MAX_PLAYERS; i++)
10243       {
10244         if (trigger_player_bits & (1 << i))
10245         {
10246           stored_player[i].gravity =
10247             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10248              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10249              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10250              stored_player[i].gravity);
10251         }
10252       }
10253
10254       break;
10255     }
10256
10257     case CA_SET_PLAYER_ARTWORK:
10258     {
10259       for (i = 0; i < MAX_PLAYERS; i++)
10260       {
10261         if (trigger_player_bits & (1 << i))
10262         {
10263           int artwork_element = action_arg_element;
10264
10265           if (action_arg == CA_ARG_ELEMENT_RESET)
10266             artwork_element =
10267               (level.use_artwork_element[i] ? level.artwork_element[i] :
10268                stored_player[i].element_nr);
10269
10270           if (stored_player[i].artwork_element != artwork_element)
10271             stored_player[i].Frame = 0;
10272
10273           stored_player[i].artwork_element = artwork_element;
10274
10275           SetPlayerWaiting(&stored_player[i], FALSE);
10276
10277           // set number of special actions for bored and sleeping animation
10278           stored_player[i].num_special_action_bored =
10279             get_num_special_action(artwork_element,
10280                                    ACTION_BORING_1, ACTION_BORING_LAST);
10281           stored_player[i].num_special_action_sleeping =
10282             get_num_special_action(artwork_element,
10283                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10284         }
10285       }
10286
10287       break;
10288     }
10289
10290     case CA_SET_PLAYER_INVENTORY:
10291     {
10292       for (i = 0; i < MAX_PLAYERS; i++)
10293       {
10294         struct PlayerInfo *player = &stored_player[i];
10295         int j, k;
10296
10297         if (trigger_player_bits & (1 << i))
10298         {
10299           int inventory_element = action_arg_element;
10300
10301           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10302               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10303               action_arg == CA_ARG_ELEMENT_ACTION)
10304           {
10305             int element = inventory_element;
10306             int collect_count = element_info[element].collect_count_initial;
10307
10308             if (!IS_CUSTOM_ELEMENT(element))
10309               collect_count = 1;
10310
10311             if (collect_count == 0)
10312               player->inventory_infinite_element = element;
10313             else
10314               for (k = 0; k < collect_count; k++)
10315                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10316                   player->inventory_element[player->inventory_size++] =
10317                     element;
10318           }
10319           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10320                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10321                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10322           {
10323             if (player->inventory_infinite_element != EL_UNDEFINED &&
10324                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10325                                      action_arg_element_raw))
10326               player->inventory_infinite_element = EL_UNDEFINED;
10327
10328             for (k = 0, j = 0; j < player->inventory_size; j++)
10329             {
10330               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10331                                         action_arg_element_raw))
10332                 player->inventory_element[k++] = player->inventory_element[j];
10333             }
10334
10335             player->inventory_size = k;
10336           }
10337           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10338           {
10339             if (player->inventory_size > 0)
10340             {
10341               for (j = 0; j < player->inventory_size - 1; j++)
10342                 player->inventory_element[j] = player->inventory_element[j + 1];
10343
10344               player->inventory_size--;
10345             }
10346           }
10347           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10348           {
10349             if (player->inventory_size > 0)
10350               player->inventory_size--;
10351           }
10352           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10353           {
10354             player->inventory_infinite_element = EL_UNDEFINED;
10355             player->inventory_size = 0;
10356           }
10357           else if (action_arg == CA_ARG_INVENTORY_RESET)
10358           {
10359             player->inventory_infinite_element = EL_UNDEFINED;
10360             player->inventory_size = 0;
10361
10362             if (level.use_initial_inventory[i])
10363             {
10364               for (j = 0; j < level.initial_inventory_size[i]; j++)
10365               {
10366                 int element = level.initial_inventory_content[i][j];
10367                 int collect_count = element_info[element].collect_count_initial;
10368
10369                 if (!IS_CUSTOM_ELEMENT(element))
10370                   collect_count = 1;
10371
10372                 if (collect_count == 0)
10373                   player->inventory_infinite_element = element;
10374                 else
10375                   for (k = 0; k < collect_count; k++)
10376                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10377                       player->inventory_element[player->inventory_size++] =
10378                         element;
10379               }
10380             }
10381           }
10382         }
10383       }
10384
10385       break;
10386     }
10387
10388     // ---------- CE actions  -------------------------------------------------
10389
10390     case CA_SET_CE_VALUE:
10391     {
10392       int last_ce_value = CustomValue[x][y];
10393
10394       CustomValue[x][y] = action_arg_number_new;
10395
10396       if (CustomValue[x][y] != last_ce_value)
10397       {
10398         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10399         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10400
10401         if (CustomValue[x][y] == 0)
10402         {
10403           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10404           ChangeCount[x][y] = 0;        // allow at least one more change
10405
10406           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10407           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10408         }
10409       }
10410
10411       break;
10412     }
10413
10414     case CA_SET_CE_SCORE:
10415     {
10416       int last_ce_score = ei->collect_score;
10417
10418       ei->collect_score = action_arg_number_new;
10419
10420       if (ei->collect_score != last_ce_score)
10421       {
10422         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10423         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10424
10425         if (ei->collect_score == 0)
10426         {
10427           int xx, yy;
10428
10429           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10430           ChangeCount[x][y] = 0;        // allow at least one more change
10431
10432           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10433           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10434
10435           /*
10436             This is a very special case that seems to be a mixture between
10437             CheckElementChange() and CheckTriggeredElementChange(): while
10438             the first one only affects single elements that are triggered
10439             directly, the second one affects multiple elements in the playfield
10440             that are triggered indirectly by another element. This is a third
10441             case: Changing the CE score always affects multiple identical CEs,
10442             so every affected CE must be checked, not only the single CE for
10443             which the CE score was changed in the first place (as every instance
10444             of that CE shares the same CE score, and therefore also can change)!
10445           */
10446           SCAN_PLAYFIELD(xx, yy)
10447           {
10448             if (Tile[xx][yy] == element)
10449               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10450                                  CE_SCORE_GETS_ZERO);
10451           }
10452         }
10453       }
10454
10455       break;
10456     }
10457
10458     case CA_SET_CE_ARTWORK:
10459     {
10460       int artwork_element = action_arg_element;
10461       boolean reset_frame = FALSE;
10462       int xx, yy;
10463
10464       if (action_arg == CA_ARG_ELEMENT_RESET)
10465         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10466                            element);
10467
10468       if (ei->gfx_element != artwork_element)
10469         reset_frame = TRUE;
10470
10471       ei->gfx_element = artwork_element;
10472
10473       SCAN_PLAYFIELD(xx, yy)
10474       {
10475         if (Tile[xx][yy] == element)
10476         {
10477           if (reset_frame)
10478           {
10479             ResetGfxAnimation(xx, yy);
10480             ResetRandomAnimationValue(xx, yy);
10481           }
10482
10483           TEST_DrawLevelField(xx, yy);
10484         }
10485       }
10486
10487       break;
10488     }
10489
10490     // ---------- engine actions  ---------------------------------------------
10491
10492     case CA_SET_ENGINE_SCAN_MODE:
10493     {
10494       InitPlayfieldScanMode(action_arg);
10495
10496       break;
10497     }
10498
10499     default:
10500       break;
10501   }
10502 }
10503
10504 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10505 {
10506   int old_element = Tile[x][y];
10507   int new_element = GetElementFromGroupElement(element);
10508   int previous_move_direction = MovDir[x][y];
10509   int last_ce_value = CustomValue[x][y];
10510   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10511   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10512   boolean add_player_onto_element = (new_element_is_player &&
10513                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10514                                      IS_WALKABLE(old_element));
10515
10516   if (!add_player_onto_element)
10517   {
10518     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10519       RemoveMovingField(x, y);
10520     else
10521       RemoveField(x, y);
10522
10523     Tile[x][y] = new_element;
10524
10525     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10526       MovDir[x][y] = previous_move_direction;
10527
10528     if (element_info[new_element].use_last_ce_value)
10529       CustomValue[x][y] = last_ce_value;
10530
10531     InitField_WithBug1(x, y, FALSE);
10532
10533     new_element = Tile[x][y];   // element may have changed
10534
10535     ResetGfxAnimation(x, y);
10536     ResetRandomAnimationValue(x, y);
10537
10538     TEST_DrawLevelField(x, y);
10539
10540     if (GFX_CRUMBLED(new_element))
10541       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10542   }
10543
10544   // check if element under the player changes from accessible to unaccessible
10545   // (needed for special case of dropping element which then changes)
10546   // (must be checked after creating new element for walkable group elements)
10547   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10548       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10549   {
10550     Bang(x, y);
10551
10552     return;
10553   }
10554
10555   // "ChangeCount" not set yet to allow "entered by player" change one time
10556   if (new_element_is_player)
10557     RelocatePlayer(x, y, new_element);
10558
10559   if (is_change)
10560     ChangeCount[x][y]++;        // count number of changes in the same frame
10561
10562   TestIfBadThingTouchesPlayer(x, y);
10563   TestIfPlayerTouchesCustomElement(x, y);
10564   TestIfElementTouchesCustomElement(x, y);
10565 }
10566
10567 static void CreateField(int x, int y, int element)
10568 {
10569   CreateFieldExt(x, y, element, FALSE);
10570 }
10571
10572 static void CreateElementFromChange(int x, int y, int element)
10573 {
10574   element = GET_VALID_RUNTIME_ELEMENT(element);
10575
10576   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10577   {
10578     int old_element = Tile[x][y];
10579
10580     // prevent changed element from moving in same engine frame
10581     // unless both old and new element can either fall or move
10582     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10583         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10584       Stop[x][y] = TRUE;
10585   }
10586
10587   CreateFieldExt(x, y, element, TRUE);
10588 }
10589
10590 static boolean ChangeElement(int x, int y, int element, int page)
10591 {
10592   struct ElementInfo *ei = &element_info[element];
10593   struct ElementChangeInfo *change = &ei->change_page[page];
10594   int ce_value = CustomValue[x][y];
10595   int ce_score = ei->collect_score;
10596   int target_element;
10597   int old_element = Tile[x][y];
10598
10599   // always use default change event to prevent running into a loop
10600   if (ChangeEvent[x][y] == -1)
10601     ChangeEvent[x][y] = CE_DELAY;
10602
10603   if (ChangeEvent[x][y] == CE_DELAY)
10604   {
10605     // reset actual trigger element, trigger player and action element
10606     change->actual_trigger_element = EL_EMPTY;
10607     change->actual_trigger_player = EL_EMPTY;
10608     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10609     change->actual_trigger_side = CH_SIDE_NONE;
10610     change->actual_trigger_ce_value = 0;
10611     change->actual_trigger_ce_score = 0;
10612   }
10613
10614   // do not change elements more than a specified maximum number of changes
10615   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10616     return FALSE;
10617
10618   ChangeCount[x][y]++;          // count number of changes in the same frame
10619
10620   if (change->explode)
10621   {
10622     Bang(x, y);
10623
10624     return TRUE;
10625   }
10626
10627   if (change->use_target_content)
10628   {
10629     boolean complete_replace = TRUE;
10630     boolean can_replace[3][3];
10631     int xx, yy;
10632
10633     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10634     {
10635       boolean is_empty;
10636       boolean is_walkable;
10637       boolean is_diggable;
10638       boolean is_collectible;
10639       boolean is_removable;
10640       boolean is_destructible;
10641       int ex = x + xx - 1;
10642       int ey = y + yy - 1;
10643       int content_element = change->target_content.e[xx][yy];
10644       int e;
10645
10646       can_replace[xx][yy] = TRUE;
10647
10648       if (ex == x && ey == y)   // do not check changing element itself
10649         continue;
10650
10651       if (content_element == EL_EMPTY_SPACE)
10652       {
10653         can_replace[xx][yy] = FALSE;    // do not replace border with space
10654
10655         continue;
10656       }
10657
10658       if (!IN_LEV_FIELD(ex, ey))
10659       {
10660         can_replace[xx][yy] = FALSE;
10661         complete_replace = FALSE;
10662
10663         continue;
10664       }
10665
10666       e = Tile[ex][ey];
10667
10668       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10669         e = MovingOrBlocked2Element(ex, ey);
10670
10671       is_empty = (IS_FREE(ex, ey) ||
10672                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10673
10674       is_walkable     = (is_empty || IS_WALKABLE(e));
10675       is_diggable     = (is_empty || IS_DIGGABLE(e));
10676       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10677       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10678       is_removable    = (is_diggable || is_collectible);
10679
10680       can_replace[xx][yy] =
10681         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10682           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10683           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10684           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10685           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10686           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10687          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10688
10689       if (!can_replace[xx][yy])
10690         complete_replace = FALSE;
10691     }
10692
10693     if (!change->only_if_complete || complete_replace)
10694     {
10695       boolean something_has_changed = FALSE;
10696
10697       if (change->only_if_complete && change->use_random_replace &&
10698           RND(100) < change->random_percentage)
10699         return FALSE;
10700
10701       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10702       {
10703         int ex = x + xx - 1;
10704         int ey = y + yy - 1;
10705         int content_element;
10706
10707         if (can_replace[xx][yy] && (!change->use_random_replace ||
10708                                     RND(100) < change->random_percentage))
10709         {
10710           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10711             RemoveMovingField(ex, ey);
10712
10713           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10714
10715           content_element = change->target_content.e[xx][yy];
10716           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10717                                               ce_value, ce_score);
10718
10719           CreateElementFromChange(ex, ey, target_element);
10720
10721           something_has_changed = TRUE;
10722
10723           // for symmetry reasons, freeze newly created border elements
10724           if (ex != x || ey != y)
10725             Stop[ex][ey] = TRUE;        // no more moving in this frame
10726         }
10727       }
10728
10729       if (something_has_changed)
10730       {
10731         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10732         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10733       }
10734     }
10735   }
10736   else
10737   {
10738     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10739                                         ce_value, ce_score);
10740
10741     if (element == EL_DIAGONAL_GROWING ||
10742         element == EL_DIAGONAL_SHRINKING)
10743     {
10744       target_element = Store[x][y];
10745
10746       Store[x][y] = EL_EMPTY;
10747     }
10748
10749     CreateElementFromChange(x, y, target_element);
10750
10751     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10752     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10753   }
10754
10755   // this uses direct change before indirect change
10756   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10757
10758   return TRUE;
10759 }
10760
10761 static void HandleElementChange(int x, int y, int page)
10762 {
10763   int element = MovingOrBlocked2Element(x, y);
10764   struct ElementInfo *ei = &element_info[element];
10765   struct ElementChangeInfo *change = &ei->change_page[page];
10766   boolean handle_action_before_change = FALSE;
10767
10768 #ifdef DEBUG
10769   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10770       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10771   {
10772     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10773           x, y, element, element_info[element].token_name);
10774     Debug("game:playing:HandleElementChange", "This should never happen!");
10775   }
10776 #endif
10777
10778   // this can happen with classic bombs on walkable, changing elements
10779   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10780   {
10781     return;
10782   }
10783
10784   if (ChangeDelay[x][y] == 0)           // initialize element change
10785   {
10786     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10787
10788     if (change->can_change)
10789     {
10790       // !!! not clear why graphic animation should be reset at all here !!!
10791       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10792       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10793
10794       /*
10795         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10796
10797         When using an animation frame delay of 1 (this only happens with
10798         "sp_zonk.moving.left/right" in the classic graphics), the default
10799         (non-moving) animation shows wrong animation frames (while the
10800         moving animation, like "sp_zonk.moving.left/right", is correct,
10801         so this graphical bug never shows up with the classic graphics).
10802         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10803         be drawn instead of the correct frames 0,1,2,3. This is caused by
10804         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10805         an element change: First when the change delay ("ChangeDelay[][]")
10806         counter has reached zero after decrementing, then a second time in
10807         the next frame (after "GfxFrame[][]" was already incremented) when
10808         "ChangeDelay[][]" is reset to the initial delay value again.
10809
10810         This causes frame 0 to be drawn twice, while the last frame won't
10811         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10812
10813         As some animations may already be cleverly designed around this bug
10814         (at least the "Snake Bite" snake tail animation does this), it cannot
10815         simply be fixed here without breaking such existing animations.
10816         Unfortunately, it cannot easily be detected if a graphics set was
10817         designed "before" or "after" the bug was fixed. As a workaround,
10818         a new graphics set option "game.graphics_engine_version" was added
10819         to be able to specify the game's major release version for which the
10820         graphics set was designed, which can then be used to decide if the
10821         bugfix should be used (version 4 and above) or not (version 3 or
10822         below, or if no version was specified at all, as with old sets).
10823
10824         (The wrong/fixed animation frames can be tested with the test level set
10825         "test_gfxframe" and level "000", which contains a specially prepared
10826         custom element at level position (x/y) == (11/9) which uses the zonk
10827         animation mentioned above. Using "game.graphics_engine_version: 4"
10828         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10829         This can also be seen from the debug output for this test element.)
10830       */
10831
10832       // when a custom element is about to change (for example by change delay),
10833       // do not reset graphic animation when the custom element is moving
10834       if (game.graphics_engine_version < 4 &&
10835           !IS_MOVING(x, y))
10836       {
10837         ResetGfxAnimation(x, y);
10838         ResetRandomAnimationValue(x, y);
10839       }
10840
10841       if (change->pre_change_function)
10842         change->pre_change_function(x, y);
10843     }
10844   }
10845
10846   ChangeDelay[x][y]--;
10847
10848   if (ChangeDelay[x][y] != 0)           // continue element change
10849   {
10850     if (change->can_change)
10851     {
10852       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10853
10854       if (IS_ANIMATED(graphic))
10855         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10856
10857       if (change->change_function)
10858         change->change_function(x, y);
10859     }
10860   }
10861   else                                  // finish element change
10862   {
10863     if (ChangePage[x][y] != -1)         // remember page from delayed change
10864     {
10865       page = ChangePage[x][y];
10866       ChangePage[x][y] = -1;
10867
10868       change = &ei->change_page[page];
10869     }
10870
10871     if (IS_MOVING(x, y))                // never change a running system ;-)
10872     {
10873       ChangeDelay[x][y] = 1;            // try change after next move step
10874       ChangePage[x][y] = page;          // remember page to use for change
10875
10876       return;
10877     }
10878
10879     // special case: set new level random seed before changing element
10880     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10881       handle_action_before_change = TRUE;
10882
10883     if (change->has_action && handle_action_before_change)
10884       ExecuteCustomElementAction(x, y, element, page);
10885
10886     if (change->can_change)
10887     {
10888       if (ChangeElement(x, y, element, page))
10889       {
10890         if (change->post_change_function)
10891           change->post_change_function(x, y);
10892       }
10893     }
10894
10895     if (change->has_action && !handle_action_before_change)
10896       ExecuteCustomElementAction(x, y, element, page);
10897   }
10898 }
10899
10900 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10901                                               int trigger_element,
10902                                               int trigger_event,
10903                                               int trigger_player,
10904                                               int trigger_side,
10905                                               int trigger_page)
10906 {
10907   boolean change_done_any = FALSE;
10908   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10909   int i;
10910
10911   if (!(trigger_events[trigger_element][trigger_event]))
10912     return FALSE;
10913
10914   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10915
10916   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10917   {
10918     int element = EL_CUSTOM_START + i;
10919     boolean change_done = FALSE;
10920     int p;
10921
10922     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10923         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10924       continue;
10925
10926     for (p = 0; p < element_info[element].num_change_pages; p++)
10927     {
10928       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10929
10930       if (change->can_change_or_has_action &&
10931           change->has_event[trigger_event] &&
10932           change->trigger_side & trigger_side &&
10933           change->trigger_player & trigger_player &&
10934           change->trigger_page & trigger_page_bits &&
10935           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10936       {
10937         change->actual_trigger_element = trigger_element;
10938         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10939         change->actual_trigger_player_bits = trigger_player;
10940         change->actual_trigger_side = trigger_side;
10941         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10942         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10943
10944         if ((change->can_change && !change_done) || change->has_action)
10945         {
10946           int x, y;
10947
10948           SCAN_PLAYFIELD(x, y)
10949           {
10950             if (Tile[x][y] == element)
10951             {
10952               if (change->can_change && !change_done)
10953               {
10954                 // if element already changed in this frame, not only prevent
10955                 // another element change (checked in ChangeElement()), but
10956                 // also prevent additional element actions for this element
10957
10958                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10959                     !level.use_action_after_change_bug)
10960                   continue;
10961
10962                 ChangeDelay[x][y] = 1;
10963                 ChangeEvent[x][y] = trigger_event;
10964
10965                 HandleElementChange(x, y, p);
10966               }
10967               else if (change->has_action)
10968               {
10969                 // if element already changed in this frame, not only prevent
10970                 // another element change (checked in ChangeElement()), but
10971                 // also prevent additional element actions for this element
10972
10973                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10974                     !level.use_action_after_change_bug)
10975                   continue;
10976
10977                 ExecuteCustomElementAction(x, y, element, p);
10978                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10979               }
10980             }
10981           }
10982
10983           if (change->can_change)
10984           {
10985             change_done = TRUE;
10986             change_done_any = TRUE;
10987           }
10988         }
10989       }
10990     }
10991   }
10992
10993   RECURSION_LOOP_DETECTION_END();
10994
10995   return change_done_any;
10996 }
10997
10998 static boolean CheckElementChangeExt(int x, int y,
10999                                      int element,
11000                                      int trigger_element,
11001                                      int trigger_event,
11002                                      int trigger_player,
11003                                      int trigger_side)
11004 {
11005   boolean change_done = FALSE;
11006   int p;
11007
11008   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11009       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11010     return FALSE;
11011
11012   if (Tile[x][y] == EL_BLOCKED)
11013   {
11014     Blocked2Moving(x, y, &x, &y);
11015     element = Tile[x][y];
11016   }
11017
11018   // check if element has already changed or is about to change after moving
11019   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11020        Tile[x][y] != element) ||
11021
11022       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11023        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11024         ChangePage[x][y] != -1)))
11025     return FALSE;
11026
11027   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11028
11029   for (p = 0; p < element_info[element].num_change_pages; p++)
11030   {
11031     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11032
11033     /* check trigger element for all events where the element that is checked
11034        for changing interacts with a directly adjacent element -- this is
11035        different to element changes that affect other elements to change on the
11036        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11037     boolean check_trigger_element =
11038       (trigger_event == CE_TOUCHING_X ||
11039        trigger_event == CE_HITTING_X ||
11040        trigger_event == CE_HIT_BY_X ||
11041        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11042
11043     if (change->can_change_or_has_action &&
11044         change->has_event[trigger_event] &&
11045         change->trigger_side & trigger_side &&
11046         change->trigger_player & trigger_player &&
11047         (!check_trigger_element ||
11048          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11049     {
11050       change->actual_trigger_element = trigger_element;
11051       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11052       change->actual_trigger_player_bits = trigger_player;
11053       change->actual_trigger_side = trigger_side;
11054       change->actual_trigger_ce_value = CustomValue[x][y];
11055       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11056
11057       // special case: trigger element not at (x,y) position for some events
11058       if (check_trigger_element)
11059       {
11060         static struct
11061         {
11062           int dx, dy;
11063         } move_xy[] =
11064           {
11065             {  0,  0 },
11066             { -1,  0 },
11067             { +1,  0 },
11068             {  0,  0 },
11069             {  0, -1 },
11070             {  0,  0 }, { 0, 0 }, { 0, 0 },
11071             {  0, +1 }
11072           };
11073
11074         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11075         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11076
11077         change->actual_trigger_ce_value = CustomValue[xx][yy];
11078         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11079       }
11080
11081       if (change->can_change && !change_done)
11082       {
11083         ChangeDelay[x][y] = 1;
11084         ChangeEvent[x][y] = trigger_event;
11085
11086         HandleElementChange(x, y, p);
11087
11088         change_done = TRUE;
11089       }
11090       else if (change->has_action)
11091       {
11092         ExecuteCustomElementAction(x, y, element, p);
11093         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11094       }
11095     }
11096   }
11097
11098   RECURSION_LOOP_DETECTION_END();
11099
11100   return change_done;
11101 }
11102
11103 static void PlayPlayerSound(struct PlayerInfo *player)
11104 {
11105   int jx = player->jx, jy = player->jy;
11106   int sound_element = player->artwork_element;
11107   int last_action = player->last_action_waiting;
11108   int action = player->action_waiting;
11109
11110   if (player->is_waiting)
11111   {
11112     if (action != last_action)
11113       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11114     else
11115       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11116   }
11117   else
11118   {
11119     if (action != last_action)
11120       StopSound(element_info[sound_element].sound[last_action]);
11121
11122     if (last_action == ACTION_SLEEPING)
11123       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11124   }
11125 }
11126
11127 static void PlayAllPlayersSound(void)
11128 {
11129   int i;
11130
11131   for (i = 0; i < MAX_PLAYERS; i++)
11132     if (stored_player[i].active)
11133       PlayPlayerSound(&stored_player[i]);
11134 }
11135
11136 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11137 {
11138   boolean last_waiting = player->is_waiting;
11139   int move_dir = player->MovDir;
11140
11141   player->dir_waiting = move_dir;
11142   player->last_action_waiting = player->action_waiting;
11143
11144   if (is_waiting)
11145   {
11146     if (!last_waiting)          // not waiting -> waiting
11147     {
11148       player->is_waiting = TRUE;
11149
11150       player->frame_counter_bored =
11151         FrameCounter +
11152         game.player_boring_delay_fixed +
11153         GetSimpleRandom(game.player_boring_delay_random);
11154       player->frame_counter_sleeping =
11155         FrameCounter +
11156         game.player_sleeping_delay_fixed +
11157         GetSimpleRandom(game.player_sleeping_delay_random);
11158
11159       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11160     }
11161
11162     if (game.player_sleeping_delay_fixed +
11163         game.player_sleeping_delay_random > 0 &&
11164         player->anim_delay_counter == 0 &&
11165         player->post_delay_counter == 0 &&
11166         FrameCounter >= player->frame_counter_sleeping)
11167       player->is_sleeping = TRUE;
11168     else if (game.player_boring_delay_fixed +
11169              game.player_boring_delay_random > 0 &&
11170              FrameCounter >= player->frame_counter_bored)
11171       player->is_bored = TRUE;
11172
11173     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11174                               player->is_bored ? ACTION_BORING :
11175                               ACTION_WAITING);
11176
11177     if (player->is_sleeping && player->use_murphy)
11178     {
11179       // special case for sleeping Murphy when leaning against non-free tile
11180
11181       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11182           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11183            !IS_MOVING(player->jx - 1, player->jy)))
11184         move_dir = MV_LEFT;
11185       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11186                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11187                 !IS_MOVING(player->jx + 1, player->jy)))
11188         move_dir = MV_RIGHT;
11189       else
11190         player->is_sleeping = FALSE;
11191
11192       player->dir_waiting = move_dir;
11193     }
11194
11195     if (player->is_sleeping)
11196     {
11197       if (player->num_special_action_sleeping > 0)
11198       {
11199         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11200         {
11201           int last_special_action = player->special_action_sleeping;
11202           int num_special_action = player->num_special_action_sleeping;
11203           int special_action =
11204             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11205              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11206              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11207              last_special_action + 1 : ACTION_SLEEPING);
11208           int special_graphic =
11209             el_act_dir2img(player->artwork_element, special_action, move_dir);
11210
11211           player->anim_delay_counter =
11212             graphic_info[special_graphic].anim_delay_fixed +
11213             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11214           player->post_delay_counter =
11215             graphic_info[special_graphic].post_delay_fixed +
11216             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11217
11218           player->special_action_sleeping = special_action;
11219         }
11220
11221         if (player->anim_delay_counter > 0)
11222         {
11223           player->action_waiting = player->special_action_sleeping;
11224           player->anim_delay_counter--;
11225         }
11226         else if (player->post_delay_counter > 0)
11227         {
11228           player->post_delay_counter--;
11229         }
11230       }
11231     }
11232     else if (player->is_bored)
11233     {
11234       if (player->num_special_action_bored > 0)
11235       {
11236         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11237         {
11238           int special_action =
11239             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11240           int special_graphic =
11241             el_act_dir2img(player->artwork_element, special_action, move_dir);
11242
11243           player->anim_delay_counter =
11244             graphic_info[special_graphic].anim_delay_fixed +
11245             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11246           player->post_delay_counter =
11247             graphic_info[special_graphic].post_delay_fixed +
11248             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11249
11250           player->special_action_bored = special_action;
11251         }
11252
11253         if (player->anim_delay_counter > 0)
11254         {
11255           player->action_waiting = player->special_action_bored;
11256           player->anim_delay_counter--;
11257         }
11258         else if (player->post_delay_counter > 0)
11259         {
11260           player->post_delay_counter--;
11261         }
11262       }
11263     }
11264   }
11265   else if (last_waiting)        // waiting -> not waiting
11266   {
11267     player->is_waiting = FALSE;
11268     player->is_bored = FALSE;
11269     player->is_sleeping = FALSE;
11270
11271     player->frame_counter_bored = -1;
11272     player->frame_counter_sleeping = -1;
11273
11274     player->anim_delay_counter = 0;
11275     player->post_delay_counter = 0;
11276
11277     player->dir_waiting = player->MovDir;
11278     player->action_waiting = ACTION_DEFAULT;
11279
11280     player->special_action_bored = ACTION_DEFAULT;
11281     player->special_action_sleeping = ACTION_DEFAULT;
11282   }
11283 }
11284
11285 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11286 {
11287   if ((!player->is_moving  && player->was_moving) ||
11288       (player->MovPos == 0 && player->was_moving) ||
11289       (player->is_snapping && !player->was_snapping) ||
11290       (player->is_dropping && !player->was_dropping))
11291   {
11292     if (!CheckSaveEngineSnapshotToList())
11293       return;
11294
11295     player->was_moving = FALSE;
11296     player->was_snapping = TRUE;
11297     player->was_dropping = TRUE;
11298   }
11299   else
11300   {
11301     if (player->is_moving)
11302       player->was_moving = TRUE;
11303
11304     if (!player->is_snapping)
11305       player->was_snapping = FALSE;
11306
11307     if (!player->is_dropping)
11308       player->was_dropping = FALSE;
11309   }
11310
11311   static struct MouseActionInfo mouse_action_last = { 0 };
11312   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11313   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11314
11315   if (new_released)
11316     CheckSaveEngineSnapshotToList();
11317
11318   mouse_action_last = mouse_action;
11319 }
11320
11321 static void CheckSingleStepMode(struct PlayerInfo *player)
11322 {
11323   if (tape.single_step && tape.recording && !tape.pausing)
11324   {
11325     // as it is called "single step mode", just return to pause mode when the
11326     // player stopped moving after one tile (or never starts moving at all)
11327     // (reverse logic needed here in case single step mode used in team mode)
11328     if (player->is_moving ||
11329         player->is_pushing ||
11330         player->is_dropping_pressed ||
11331         player->effective_mouse_action.button)
11332       game.enter_single_step_mode = FALSE;
11333   }
11334
11335   CheckSaveEngineSnapshot(player);
11336 }
11337
11338 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11339 {
11340   int left      = player_action & JOY_LEFT;
11341   int right     = player_action & JOY_RIGHT;
11342   int up        = player_action & JOY_UP;
11343   int down      = player_action & JOY_DOWN;
11344   int button1   = player_action & JOY_BUTTON_1;
11345   int button2   = player_action & JOY_BUTTON_2;
11346   int dx        = (left ? -1 : right ? 1 : 0);
11347   int dy        = (up   ? -1 : down  ? 1 : 0);
11348
11349   if (!player->active || tape.pausing)
11350     return 0;
11351
11352   if (player_action)
11353   {
11354     if (button1)
11355       SnapField(player, dx, dy);
11356     else
11357     {
11358       if (button2)
11359         DropElement(player);
11360
11361       MovePlayer(player, dx, dy);
11362     }
11363
11364     CheckSingleStepMode(player);
11365
11366     SetPlayerWaiting(player, FALSE);
11367
11368     return player_action;
11369   }
11370   else
11371   {
11372     // no actions for this player (no input at player's configured device)
11373
11374     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11375     SnapField(player, 0, 0);
11376     CheckGravityMovementWhenNotMoving(player);
11377
11378     if (player->MovPos == 0)
11379       SetPlayerWaiting(player, TRUE);
11380
11381     if (player->MovPos == 0)    // needed for tape.playing
11382       player->is_moving = FALSE;
11383
11384     player->is_dropping = FALSE;
11385     player->is_dropping_pressed = FALSE;
11386     player->drop_pressed_delay = 0;
11387
11388     CheckSingleStepMode(player);
11389
11390     return 0;
11391   }
11392 }
11393
11394 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11395                                          byte *tape_action)
11396 {
11397   if (!tape.use_mouse_actions)
11398     return;
11399
11400   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11401   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11402   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11403 }
11404
11405 static void SetTapeActionFromMouseAction(byte *tape_action,
11406                                          struct MouseActionInfo *mouse_action)
11407 {
11408   if (!tape.use_mouse_actions)
11409     return;
11410
11411   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11412   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11413   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11414 }
11415
11416 static void CheckLevelSolved(void)
11417 {
11418   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11419   {
11420     if (game_em.level_solved &&
11421         !game_em.game_over)                             // game won
11422     {
11423       LevelSolved();
11424
11425       game_em.game_over = TRUE;
11426
11427       game.all_players_gone = TRUE;
11428     }
11429
11430     if (game_em.game_over)                              // game lost
11431       game.all_players_gone = TRUE;
11432   }
11433   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11434   {
11435     if (game_sp.level_solved &&
11436         !game_sp.game_over)                             // game won
11437     {
11438       LevelSolved();
11439
11440       game_sp.game_over = TRUE;
11441
11442       game.all_players_gone = TRUE;
11443     }
11444
11445     if (game_sp.game_over)                              // game lost
11446       game.all_players_gone = TRUE;
11447   }
11448   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11449   {
11450     if (game_mm.level_solved &&
11451         !game_mm.game_over)                             // game won
11452     {
11453       LevelSolved();
11454
11455       game_mm.game_over = TRUE;
11456
11457       game.all_players_gone = TRUE;
11458     }
11459
11460     if (game_mm.game_over)                              // game lost
11461       game.all_players_gone = TRUE;
11462   }
11463 }
11464
11465 static void CheckLevelTime(void)
11466 {
11467   int i;
11468
11469   if (TimeFrames >= FRAMES_PER_SECOND)
11470   {
11471     TimeFrames = 0;
11472     TapeTime++;
11473
11474     for (i = 0; i < MAX_PLAYERS; i++)
11475     {
11476       struct PlayerInfo *player = &stored_player[i];
11477
11478       if (SHIELD_ON(player))
11479       {
11480         player->shield_normal_time_left--;
11481
11482         if (player->shield_deadly_time_left > 0)
11483           player->shield_deadly_time_left--;
11484       }
11485     }
11486
11487     if (!game.LevelSolved && !level.use_step_counter)
11488     {
11489       TimePlayed++;
11490
11491       if (TimeLeft > 0)
11492       {
11493         TimeLeft--;
11494
11495         if (TimeLeft <= 10 && setup.time_limit)
11496           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11497
11498         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11499            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11500
11501         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11502
11503         if (!TimeLeft && setup.time_limit)
11504         {
11505           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11506             game_em.lev->killed_out_of_time = TRUE;
11507           else
11508             for (i = 0; i < MAX_PLAYERS; i++)
11509               KillPlayer(&stored_player[i]);
11510         }
11511       }
11512       else if (game.no_time_limit && !game.all_players_gone)
11513       {
11514         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11515       }
11516
11517       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11518     }
11519
11520     if (tape.recording || tape.playing)
11521       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11522   }
11523
11524   if (tape.recording || tape.playing)
11525     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11526
11527   UpdateAndDisplayGameControlValues();
11528 }
11529
11530 void AdvanceFrameAndPlayerCounters(int player_nr)
11531 {
11532   int i;
11533
11534   // advance frame counters (global frame counter and time frame counter)
11535   FrameCounter++;
11536   TimeFrames++;
11537
11538   // advance player counters (counters for move delay, move animation etc.)
11539   for (i = 0; i < MAX_PLAYERS; i++)
11540   {
11541     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11542     int move_delay_value = stored_player[i].move_delay_value;
11543     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11544
11545     if (!advance_player_counters)       // not all players may be affected
11546       continue;
11547
11548     if (move_frames == 0)       // less than one move per game frame
11549     {
11550       int stepsize = TILEX / move_delay_value;
11551       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11552       int count = (stored_player[i].is_moving ?
11553                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11554
11555       if (count % delay == 0)
11556         move_frames = 1;
11557     }
11558
11559     stored_player[i].Frame += move_frames;
11560
11561     if (stored_player[i].MovPos != 0)
11562       stored_player[i].StepFrame += move_frames;
11563
11564     if (stored_player[i].move_delay > 0)
11565       stored_player[i].move_delay--;
11566
11567     // due to bugs in previous versions, counter must count up, not down
11568     if (stored_player[i].push_delay != -1)
11569       stored_player[i].push_delay++;
11570
11571     if (stored_player[i].drop_delay > 0)
11572       stored_player[i].drop_delay--;
11573
11574     if (stored_player[i].is_dropping_pressed)
11575       stored_player[i].drop_pressed_delay++;
11576   }
11577 }
11578
11579 void StartGameActions(boolean init_network_game, boolean record_tape,
11580                       int random_seed)
11581 {
11582   unsigned int new_random_seed = InitRND(random_seed);
11583
11584   if (record_tape)
11585     TapeStartRecording(new_random_seed);
11586
11587   if (init_network_game)
11588   {
11589     SendToServer_LevelFile();
11590     SendToServer_StartPlaying();
11591
11592     return;
11593   }
11594
11595   InitGame();
11596 }
11597
11598 static void GameActionsExt(void)
11599 {
11600 #if 0
11601   static unsigned int game_frame_delay = 0;
11602 #endif
11603   unsigned int game_frame_delay_value;
11604   byte *recorded_player_action;
11605   byte summarized_player_action = 0;
11606   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11607   int i;
11608
11609   // detect endless loops, caused by custom element programming
11610   if (recursion_loop_detected && recursion_loop_depth == 0)
11611   {
11612     char *message = getStringCat3("Internal Error! Element ",
11613                                   EL_NAME(recursion_loop_element),
11614                                   " caused endless loop! Quit the game?");
11615
11616     Warn("element '%s' caused endless loop in game engine",
11617          EL_NAME(recursion_loop_element));
11618
11619     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11620
11621     recursion_loop_detected = FALSE;    // if game should be continued
11622
11623     free(message);
11624
11625     return;
11626   }
11627
11628   if (game.restart_level)
11629     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11630
11631   CheckLevelSolved();
11632
11633   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11634     GameWon();
11635
11636   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11637     TapeStop();
11638
11639   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11640     return;
11641
11642   game_frame_delay_value =
11643     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11644
11645   if (tape.playing && tape.warp_forward && !tape.pausing)
11646     game_frame_delay_value = 0;
11647
11648   SetVideoFrameDelay(game_frame_delay_value);
11649
11650   // (de)activate virtual buttons depending on current game status
11651   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11652   {
11653     if (game.all_players_gone)  // if no players there to be controlled anymore
11654       SetOverlayActive(FALSE);
11655     else if (!tape.playing)     // if game continues after tape stopped playing
11656       SetOverlayActive(TRUE);
11657   }
11658
11659 #if 0
11660 #if 0
11661   // ---------- main game synchronization point ----------
11662
11663   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11664
11665   Debug("game:playing:skip", "skip == %d", skip);
11666
11667 #else
11668   // ---------- main game synchronization point ----------
11669
11670   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11671 #endif
11672 #endif
11673
11674   if (network_playing && !network_player_action_received)
11675   {
11676     // try to get network player actions in time
11677
11678     // last chance to get network player actions without main loop delay
11679     HandleNetworking();
11680
11681     // game was quit by network peer
11682     if (game_status != GAME_MODE_PLAYING)
11683       return;
11684
11685     // check if network player actions still missing and game still running
11686     if (!network_player_action_received && !checkGameEnded())
11687       return;           // failed to get network player actions in time
11688
11689     // do not yet reset "network_player_action_received" (for tape.pausing)
11690   }
11691
11692   if (tape.pausing)
11693     return;
11694
11695   // at this point we know that we really continue executing the game
11696
11697   network_player_action_received = FALSE;
11698
11699   // when playing tape, read previously recorded player input from tape data
11700   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11701
11702   local_player->effective_mouse_action = local_player->mouse_action;
11703
11704   if (recorded_player_action != NULL)
11705     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11706                                  recorded_player_action);
11707
11708   // TapePlayAction() may return NULL when toggling to "pause before death"
11709   if (tape.pausing)
11710     return;
11711
11712   if (tape.set_centered_player)
11713   {
11714     game.centered_player_nr_next = tape.centered_player_nr_next;
11715     game.set_centered_player = TRUE;
11716   }
11717
11718   for (i = 0; i < MAX_PLAYERS; i++)
11719   {
11720     summarized_player_action |= stored_player[i].action;
11721
11722     if (!network_playing && (game.team_mode || tape.playing))
11723       stored_player[i].effective_action = stored_player[i].action;
11724   }
11725
11726   if (network_playing && !checkGameEnded())
11727     SendToServer_MovePlayer(summarized_player_action);
11728
11729   // summarize all actions at local players mapped input device position
11730   // (this allows using different input devices in single player mode)
11731   if (!network.enabled && !game.team_mode)
11732     stored_player[map_player_action[local_player->index_nr]].effective_action =
11733       summarized_player_action;
11734
11735   // summarize all actions at centered player in local team mode
11736   if (tape.recording &&
11737       setup.team_mode && !network.enabled &&
11738       setup.input_on_focus &&
11739       game.centered_player_nr != -1)
11740   {
11741     for (i = 0; i < MAX_PLAYERS; i++)
11742       stored_player[map_player_action[i]].effective_action =
11743         (i == game.centered_player_nr ? summarized_player_action : 0);
11744   }
11745
11746   if (recorded_player_action != NULL)
11747     for (i = 0; i < MAX_PLAYERS; i++)
11748       stored_player[i].effective_action = recorded_player_action[i];
11749
11750   for (i = 0; i < MAX_PLAYERS; i++)
11751   {
11752     tape_action[i] = stored_player[i].effective_action;
11753
11754     /* (this may happen in the RND game engine if a player was not present on
11755        the playfield on level start, but appeared later from a custom element */
11756     if (setup.team_mode &&
11757         tape.recording &&
11758         tape_action[i] &&
11759         !tape.player_participates[i])
11760       tape.player_participates[i] = TRUE;
11761   }
11762
11763   SetTapeActionFromMouseAction(tape_action,
11764                                &local_player->effective_mouse_action);
11765
11766   // only record actions from input devices, but not programmed actions
11767   if (tape.recording)
11768     TapeRecordAction(tape_action);
11769
11770   // remember if game was played (especially after tape stopped playing)
11771   if (!tape.playing && summarized_player_action)
11772     game.GamePlayed = TRUE;
11773
11774 #if USE_NEW_PLAYER_ASSIGNMENTS
11775   // !!! also map player actions in single player mode !!!
11776   // if (game.team_mode)
11777   if (1)
11778   {
11779     byte mapped_action[MAX_PLAYERS];
11780
11781 #if DEBUG_PLAYER_ACTIONS
11782     for (i = 0; i < MAX_PLAYERS; i++)
11783       DebugContinued("", "%d, ", stored_player[i].effective_action);
11784 #endif
11785
11786     for (i = 0; i < MAX_PLAYERS; i++)
11787       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11788
11789     for (i = 0; i < MAX_PLAYERS; i++)
11790       stored_player[i].effective_action = mapped_action[i];
11791
11792 #if DEBUG_PLAYER_ACTIONS
11793     DebugContinued("", "=> ");
11794     for (i = 0; i < MAX_PLAYERS; i++)
11795       DebugContinued("", "%d, ", stored_player[i].effective_action);
11796     DebugContinued("game:playing:player", "\n");
11797 #endif
11798   }
11799 #if DEBUG_PLAYER_ACTIONS
11800   else
11801   {
11802     for (i = 0; i < MAX_PLAYERS; i++)
11803       DebugContinued("", "%d, ", stored_player[i].effective_action);
11804     DebugContinued("game:playing:player", "\n");
11805   }
11806 #endif
11807 #endif
11808
11809   for (i = 0; i < MAX_PLAYERS; i++)
11810   {
11811     // allow engine snapshot in case of changed movement attempt
11812     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11813         (stored_player[i].effective_action & KEY_MOTION))
11814       game.snapshot.changed_action = TRUE;
11815
11816     // allow engine snapshot in case of snapping/dropping attempt
11817     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11818         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11819       game.snapshot.changed_action = TRUE;
11820
11821     game.snapshot.last_action[i] = stored_player[i].effective_action;
11822   }
11823
11824   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11825   {
11826     GameActions_EM_Main();
11827   }
11828   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11829   {
11830     GameActions_SP_Main();
11831   }
11832   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11833   {
11834     GameActions_MM_Main();
11835   }
11836   else
11837   {
11838     GameActions_RND_Main();
11839   }
11840
11841   BlitScreenToBitmap(backbuffer);
11842
11843   CheckLevelSolved();
11844   CheckLevelTime();
11845
11846   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11847
11848   if (global.show_frames_per_second)
11849   {
11850     static unsigned int fps_counter = 0;
11851     static int fps_frames = 0;
11852     unsigned int fps_delay_ms = Counter() - fps_counter;
11853
11854     fps_frames++;
11855
11856     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11857     {
11858       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11859
11860       fps_frames = 0;
11861       fps_counter = Counter();
11862
11863       // always draw FPS to screen after FPS value was updated
11864       redraw_mask |= REDRAW_FPS;
11865     }
11866
11867     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11868     if (GetDrawDeactivationMask() == REDRAW_NONE)
11869       redraw_mask |= REDRAW_FPS;
11870   }
11871 }
11872
11873 static void GameActions_CheckSaveEngineSnapshot(void)
11874 {
11875   if (!game.snapshot.save_snapshot)
11876     return;
11877
11878   // clear flag for saving snapshot _before_ saving snapshot
11879   game.snapshot.save_snapshot = FALSE;
11880
11881   SaveEngineSnapshotToList();
11882 }
11883
11884 void GameActions(void)
11885 {
11886   GameActionsExt();
11887
11888   GameActions_CheckSaveEngineSnapshot();
11889 }
11890
11891 void GameActions_EM_Main(void)
11892 {
11893   byte effective_action[MAX_PLAYERS];
11894   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11895   int i;
11896
11897   for (i = 0; i < MAX_PLAYERS; i++)
11898     effective_action[i] = stored_player[i].effective_action;
11899
11900   GameActions_EM(effective_action, warp_mode);
11901 }
11902
11903 void GameActions_SP_Main(void)
11904 {
11905   byte effective_action[MAX_PLAYERS];
11906   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11907   int i;
11908
11909   for (i = 0; i < MAX_PLAYERS; i++)
11910     effective_action[i] = stored_player[i].effective_action;
11911
11912   GameActions_SP(effective_action, warp_mode);
11913
11914   for (i = 0; i < MAX_PLAYERS; i++)
11915   {
11916     if (stored_player[i].force_dropping)
11917       stored_player[i].action |= KEY_BUTTON_DROP;
11918
11919     stored_player[i].force_dropping = FALSE;
11920   }
11921 }
11922
11923 void GameActions_MM_Main(void)
11924 {
11925   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11926
11927   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11928 }
11929
11930 void GameActions_RND_Main(void)
11931 {
11932   GameActions_RND();
11933 }
11934
11935 void GameActions_RND(void)
11936 {
11937   static struct MouseActionInfo mouse_action_last = { 0 };
11938   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11939   int magic_wall_x = 0, magic_wall_y = 0;
11940   int i, x, y, element, graphic, last_gfx_frame;
11941
11942   InitPlayfieldScanModeVars();
11943
11944   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11945   {
11946     SCAN_PLAYFIELD(x, y)
11947     {
11948       ChangeCount[x][y] = 0;
11949       ChangeEvent[x][y] = -1;
11950     }
11951   }
11952
11953   if (game.set_centered_player)
11954   {
11955     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11956
11957     // switching to "all players" only possible if all players fit to screen
11958     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11959     {
11960       game.centered_player_nr_next = game.centered_player_nr;
11961       game.set_centered_player = FALSE;
11962     }
11963
11964     // do not switch focus to non-existing (or non-active) player
11965     if (game.centered_player_nr_next >= 0 &&
11966         !stored_player[game.centered_player_nr_next].active)
11967     {
11968       game.centered_player_nr_next = game.centered_player_nr;
11969       game.set_centered_player = FALSE;
11970     }
11971   }
11972
11973   if (game.set_centered_player &&
11974       ScreenMovPos == 0)        // screen currently aligned at tile position
11975   {
11976     int sx, sy;
11977
11978     if (game.centered_player_nr_next == -1)
11979     {
11980       setScreenCenteredToAllPlayers(&sx, &sy);
11981     }
11982     else
11983     {
11984       sx = stored_player[game.centered_player_nr_next].jx;
11985       sy = stored_player[game.centered_player_nr_next].jy;
11986     }
11987
11988     game.centered_player_nr = game.centered_player_nr_next;
11989     game.set_centered_player = FALSE;
11990
11991     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11992     DrawGameDoorValues();
11993   }
11994
11995   // check single step mode (set flag and clear again if any player is active)
11996   game.enter_single_step_mode =
11997     (tape.single_step && tape.recording && !tape.pausing);
11998
11999   for (i = 0; i < MAX_PLAYERS; i++)
12000   {
12001     int actual_player_action = stored_player[i].effective_action;
12002
12003 #if 1
12004     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12005        - rnd_equinox_tetrachloride 048
12006        - rnd_equinox_tetrachloride_ii 096
12007        - rnd_emanuel_schmieg 002
12008        - doctor_sloan_ww 001, 020
12009     */
12010     if (stored_player[i].MovPos == 0)
12011       CheckGravityMovement(&stored_player[i]);
12012 #endif
12013
12014     // overwrite programmed action with tape action
12015     if (stored_player[i].programmed_action)
12016       actual_player_action = stored_player[i].programmed_action;
12017
12018     PlayerActions(&stored_player[i], actual_player_action);
12019
12020     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12021   }
12022
12023   // single step pause mode may already have been toggled by "ScrollPlayer()"
12024   if (game.enter_single_step_mode && !tape.pausing)
12025     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12026
12027   ScrollScreen(NULL, SCROLL_GO_ON);
12028
12029   /* for backwards compatibility, the following code emulates a fixed bug that
12030      occured when pushing elements (causing elements that just made their last
12031      pushing step to already (if possible) make their first falling step in the
12032      same game frame, which is bad); this code is also needed to use the famous
12033      "spring push bug" which is used in older levels and might be wanted to be
12034      used also in newer levels, but in this case the buggy pushing code is only
12035      affecting the "spring" element and no other elements */
12036
12037   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12038   {
12039     for (i = 0; i < MAX_PLAYERS; i++)
12040     {
12041       struct PlayerInfo *player = &stored_player[i];
12042       int x = player->jx;
12043       int y = player->jy;
12044
12045       if (player->active && player->is_pushing && player->is_moving &&
12046           IS_MOVING(x, y) &&
12047           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12048            Tile[x][y] == EL_SPRING))
12049       {
12050         ContinueMoving(x, y);
12051
12052         // continue moving after pushing (this is actually a bug)
12053         if (!IS_MOVING(x, y))
12054           Stop[x][y] = FALSE;
12055       }
12056     }
12057   }
12058
12059   SCAN_PLAYFIELD(x, y)
12060   {
12061     Last[x][y] = Tile[x][y];
12062
12063     ChangeCount[x][y] = 0;
12064     ChangeEvent[x][y] = -1;
12065
12066     // this must be handled before main playfield loop
12067     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12068     {
12069       MovDelay[x][y]--;
12070       if (MovDelay[x][y] <= 0)
12071         RemoveField(x, y);
12072     }
12073
12074     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12075     {
12076       MovDelay[x][y]--;
12077       if (MovDelay[x][y] <= 0)
12078       {
12079         int element = Store[x][y];
12080         int move_direction = MovDir[x][y];
12081         int player_index_bit = Store2[x][y];
12082
12083         Store[x][y] = 0;
12084         Store2[x][y] = 0;
12085
12086         RemoveField(x, y);
12087         TEST_DrawLevelField(x, y);
12088
12089         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12090       }
12091     }
12092
12093 #if DEBUG
12094     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12095     {
12096       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12097             x, y);
12098       Debug("game:playing:GameActions_RND", "This should never happen!");
12099
12100       ChangePage[x][y] = -1;
12101     }
12102 #endif
12103
12104     Stop[x][y] = FALSE;
12105     if (WasJustMoving[x][y] > 0)
12106       WasJustMoving[x][y]--;
12107     if (WasJustFalling[x][y] > 0)
12108       WasJustFalling[x][y]--;
12109     if (CheckCollision[x][y] > 0)
12110       CheckCollision[x][y]--;
12111     if (CheckImpact[x][y] > 0)
12112       CheckImpact[x][y]--;
12113
12114     GfxFrame[x][y]++;
12115
12116     /* reset finished pushing action (not done in ContinueMoving() to allow
12117        continuous pushing animation for elements with zero push delay) */
12118     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12119     {
12120       ResetGfxAnimation(x, y);
12121       TEST_DrawLevelField(x, y);
12122     }
12123
12124 #if DEBUG
12125     if (IS_BLOCKED(x, y))
12126     {
12127       int oldx, oldy;
12128
12129       Blocked2Moving(x, y, &oldx, &oldy);
12130       if (!IS_MOVING(oldx, oldy))
12131       {
12132         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12133         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12134         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12135         Debug("game:playing:GameActions_RND", "This should never happen!");
12136       }
12137     }
12138 #endif
12139   }
12140
12141   if (mouse_action.button)
12142   {
12143     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12144
12145     x = mouse_action.lx;
12146     y = mouse_action.ly;
12147     element = Tile[x][y];
12148
12149     if (new_button)
12150     {
12151       CheckElementChange(x, y, element, EL_UNDEFINED, CE_CLICKED_BY_MOUSE);
12152       CheckTriggeredElementChange(x, y, element, CE_MOUSE_CLICKED_ON_X);
12153     }
12154
12155     CheckElementChange(x, y, element, EL_UNDEFINED, CE_PRESSED_BY_MOUSE);
12156     CheckTriggeredElementChange(x, y, element, CE_MOUSE_PRESSED_ON_X);
12157   }
12158
12159   SCAN_PLAYFIELD(x, y)
12160   {
12161     element = Tile[x][y];
12162     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12163     last_gfx_frame = GfxFrame[x][y];
12164
12165     ResetGfxFrame(x, y);
12166
12167     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12168       DrawLevelGraphicAnimation(x, y, graphic);
12169
12170     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12171         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12172       ResetRandomAnimationValue(x, y);
12173
12174     SetRandomAnimationValue(x, y);
12175
12176     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12177
12178     if (IS_INACTIVE(element))
12179     {
12180       if (IS_ANIMATED(graphic))
12181         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12182
12183       continue;
12184     }
12185
12186     // this may take place after moving, so 'element' may have changed
12187     if (IS_CHANGING(x, y) &&
12188         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12189     {
12190       int page = element_info[element].event_page_nr[CE_DELAY];
12191
12192       HandleElementChange(x, y, page);
12193
12194       element = Tile[x][y];
12195       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12196     }
12197
12198     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12199     {
12200       StartMoving(x, y);
12201
12202       element = Tile[x][y];
12203       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12204
12205       if (IS_ANIMATED(graphic) &&
12206           !IS_MOVING(x, y) &&
12207           !Stop[x][y])
12208         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12209
12210       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12211         TEST_DrawTwinkleOnField(x, y);
12212     }
12213     else if (element == EL_ACID)
12214     {
12215       if (!Stop[x][y])
12216         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12217     }
12218     else if ((element == EL_EXIT_OPEN ||
12219               element == EL_EM_EXIT_OPEN ||
12220               element == EL_SP_EXIT_OPEN ||
12221               element == EL_STEEL_EXIT_OPEN ||
12222               element == EL_EM_STEEL_EXIT_OPEN ||
12223               element == EL_SP_TERMINAL ||
12224               element == EL_SP_TERMINAL_ACTIVE ||
12225               element == EL_EXTRA_TIME ||
12226               element == EL_SHIELD_NORMAL ||
12227               element == EL_SHIELD_DEADLY) &&
12228              IS_ANIMATED(graphic))
12229       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12230     else if (IS_MOVING(x, y))
12231       ContinueMoving(x, y);
12232     else if (IS_ACTIVE_BOMB(element))
12233       CheckDynamite(x, y);
12234     else if (element == EL_AMOEBA_GROWING)
12235       AmoebaGrowing(x, y);
12236     else if (element == EL_AMOEBA_SHRINKING)
12237       AmoebaShrinking(x, y);
12238
12239 #if !USE_NEW_AMOEBA_CODE
12240     else if (IS_AMOEBALIVE(element))
12241       AmoebaReproduce(x, y);
12242 #endif
12243
12244     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12245       Life(x, y);
12246     else if (element == EL_EXIT_CLOSED)
12247       CheckExit(x, y);
12248     else if (element == EL_EM_EXIT_CLOSED)
12249       CheckExitEM(x, y);
12250     else if (element == EL_STEEL_EXIT_CLOSED)
12251       CheckExitSteel(x, y);
12252     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12253       CheckExitSteelEM(x, y);
12254     else if (element == EL_SP_EXIT_CLOSED)
12255       CheckExitSP(x, y);
12256     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12257              element == EL_EXPANDABLE_STEELWALL_GROWING)
12258       MauerWaechst(x, y);
12259     else if (element == EL_EXPANDABLE_WALL ||
12260              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12261              element == EL_EXPANDABLE_WALL_VERTICAL ||
12262              element == EL_EXPANDABLE_WALL_ANY ||
12263              element == EL_BD_EXPANDABLE_WALL)
12264       MauerAbleger(x, y);
12265     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12266              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12267              element == EL_EXPANDABLE_STEELWALL_ANY)
12268       MauerAblegerStahl(x, y);
12269     else if (element == EL_FLAMES)
12270       CheckForDragon(x, y);
12271     else if (element == EL_EXPLOSION)
12272       ; // drawing of correct explosion animation is handled separately
12273     else if (element == EL_ELEMENT_SNAPPING ||
12274              element == EL_DIAGONAL_SHRINKING ||
12275              element == EL_DIAGONAL_GROWING)
12276     {
12277       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12278
12279       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12280     }
12281     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12282       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12283
12284     if (IS_BELT_ACTIVE(element))
12285       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12286
12287     if (game.magic_wall_active)
12288     {
12289       int jx = local_player->jx, jy = local_player->jy;
12290
12291       // play the element sound at the position nearest to the player
12292       if ((element == EL_MAGIC_WALL_FULL ||
12293            element == EL_MAGIC_WALL_ACTIVE ||
12294            element == EL_MAGIC_WALL_EMPTYING ||
12295            element == EL_BD_MAGIC_WALL_FULL ||
12296            element == EL_BD_MAGIC_WALL_ACTIVE ||
12297            element == EL_BD_MAGIC_WALL_EMPTYING ||
12298            element == EL_DC_MAGIC_WALL_FULL ||
12299            element == EL_DC_MAGIC_WALL_ACTIVE ||
12300            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12301           ABS(x - jx) + ABS(y - jy) <
12302           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12303       {
12304         magic_wall_x = x;
12305         magic_wall_y = y;
12306       }
12307     }
12308   }
12309
12310 #if USE_NEW_AMOEBA_CODE
12311   // new experimental amoeba growth stuff
12312   if (!(FrameCounter % 8))
12313   {
12314     static unsigned int random = 1684108901;
12315
12316     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12317     {
12318       x = RND(lev_fieldx);
12319       y = RND(lev_fieldy);
12320       element = Tile[x][y];
12321
12322       if (!IS_PLAYER(x,y) &&
12323           (element == EL_EMPTY ||
12324            CAN_GROW_INTO(element) ||
12325            element == EL_QUICKSAND_EMPTY ||
12326            element == EL_QUICKSAND_FAST_EMPTY ||
12327            element == EL_ACID_SPLASH_LEFT ||
12328            element == EL_ACID_SPLASH_RIGHT))
12329       {
12330         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12331             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12332             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12333             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12334           Tile[x][y] = EL_AMOEBA_DROP;
12335       }
12336
12337       random = random * 129 + 1;
12338     }
12339   }
12340 #endif
12341
12342   game.explosions_delayed = FALSE;
12343
12344   SCAN_PLAYFIELD(x, y)
12345   {
12346     element = Tile[x][y];
12347
12348     if (ExplodeField[x][y])
12349       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12350     else if (element == EL_EXPLOSION)
12351       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12352
12353     ExplodeField[x][y] = EX_TYPE_NONE;
12354   }
12355
12356   game.explosions_delayed = TRUE;
12357
12358   if (game.magic_wall_active)
12359   {
12360     if (!(game.magic_wall_time_left % 4))
12361     {
12362       int element = Tile[magic_wall_x][magic_wall_y];
12363
12364       if (element == EL_BD_MAGIC_WALL_FULL ||
12365           element == EL_BD_MAGIC_WALL_ACTIVE ||
12366           element == EL_BD_MAGIC_WALL_EMPTYING)
12367         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12368       else if (element == EL_DC_MAGIC_WALL_FULL ||
12369                element == EL_DC_MAGIC_WALL_ACTIVE ||
12370                element == EL_DC_MAGIC_WALL_EMPTYING)
12371         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12372       else
12373         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12374     }
12375
12376     if (game.magic_wall_time_left > 0)
12377     {
12378       game.magic_wall_time_left--;
12379
12380       if (!game.magic_wall_time_left)
12381       {
12382         SCAN_PLAYFIELD(x, y)
12383         {
12384           element = Tile[x][y];
12385
12386           if (element == EL_MAGIC_WALL_ACTIVE ||
12387               element == EL_MAGIC_WALL_FULL)
12388           {
12389             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12390             TEST_DrawLevelField(x, y);
12391           }
12392           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12393                    element == EL_BD_MAGIC_WALL_FULL)
12394           {
12395             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12396             TEST_DrawLevelField(x, y);
12397           }
12398           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12399                    element == EL_DC_MAGIC_WALL_FULL)
12400           {
12401             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12402             TEST_DrawLevelField(x, y);
12403           }
12404         }
12405
12406         game.magic_wall_active = FALSE;
12407       }
12408     }
12409   }
12410
12411   if (game.light_time_left > 0)
12412   {
12413     game.light_time_left--;
12414
12415     if (game.light_time_left == 0)
12416       RedrawAllLightSwitchesAndInvisibleElements();
12417   }
12418
12419   if (game.timegate_time_left > 0)
12420   {
12421     game.timegate_time_left--;
12422
12423     if (game.timegate_time_left == 0)
12424       CloseAllOpenTimegates();
12425   }
12426
12427   if (game.lenses_time_left > 0)
12428   {
12429     game.lenses_time_left--;
12430
12431     if (game.lenses_time_left == 0)
12432       RedrawAllInvisibleElementsForLenses();
12433   }
12434
12435   if (game.magnify_time_left > 0)
12436   {
12437     game.magnify_time_left--;
12438
12439     if (game.magnify_time_left == 0)
12440       RedrawAllInvisibleElementsForMagnifier();
12441   }
12442
12443   for (i = 0; i < MAX_PLAYERS; i++)
12444   {
12445     struct PlayerInfo *player = &stored_player[i];
12446
12447     if (SHIELD_ON(player))
12448     {
12449       if (player->shield_deadly_time_left)
12450         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12451       else if (player->shield_normal_time_left)
12452         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12453     }
12454   }
12455
12456 #if USE_DELAYED_GFX_REDRAW
12457   SCAN_PLAYFIELD(x, y)
12458   {
12459     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12460     {
12461       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12462          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12463
12464       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12465         DrawLevelField(x, y);
12466
12467       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12468         DrawLevelFieldCrumbled(x, y);
12469
12470       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12471         DrawLevelFieldCrumbledNeighbours(x, y);
12472
12473       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12474         DrawTwinkleOnField(x, y);
12475     }
12476
12477     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12478   }
12479 #endif
12480
12481   DrawAllPlayers();
12482   PlayAllPlayersSound();
12483
12484   for (i = 0; i < MAX_PLAYERS; i++)
12485   {
12486     struct PlayerInfo *player = &stored_player[i];
12487
12488     if (player->show_envelope != 0 && (!player->active ||
12489                                        player->MovPos == 0))
12490     {
12491       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12492
12493       player->show_envelope = 0;
12494     }
12495   }
12496
12497   // use random number generator in every frame to make it less predictable
12498   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12499     RND(1);
12500
12501   mouse_action_last = mouse_action;
12502 }
12503
12504 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12505 {
12506   int min_x = x, min_y = y, max_x = x, max_y = y;
12507   int scr_fieldx = getScreenFieldSizeX();
12508   int scr_fieldy = getScreenFieldSizeY();
12509   int i;
12510
12511   for (i = 0; i < MAX_PLAYERS; i++)
12512   {
12513     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12514
12515     if (!stored_player[i].active || &stored_player[i] == player)
12516       continue;
12517
12518     min_x = MIN(min_x, jx);
12519     min_y = MIN(min_y, jy);
12520     max_x = MAX(max_x, jx);
12521     max_y = MAX(max_y, jy);
12522   }
12523
12524   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12525 }
12526
12527 static boolean AllPlayersInVisibleScreen(void)
12528 {
12529   int i;
12530
12531   for (i = 0; i < MAX_PLAYERS; i++)
12532   {
12533     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12534
12535     if (!stored_player[i].active)
12536       continue;
12537
12538     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12539       return FALSE;
12540   }
12541
12542   return TRUE;
12543 }
12544
12545 void ScrollLevel(int dx, int dy)
12546 {
12547   int scroll_offset = 2 * TILEX_VAR;
12548   int x, y;
12549
12550   BlitBitmap(drawto_field, drawto_field,
12551              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12552              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12553              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12554              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12555              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12556              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12557
12558   if (dx != 0)
12559   {
12560     x = (dx == 1 ? BX1 : BX2);
12561     for (y = BY1; y <= BY2; y++)
12562       DrawScreenField(x, y);
12563   }
12564
12565   if (dy != 0)
12566   {
12567     y = (dy == 1 ? BY1 : BY2);
12568     for (x = BX1; x <= BX2; x++)
12569       DrawScreenField(x, y);
12570   }
12571
12572   redraw_mask |= REDRAW_FIELD;
12573 }
12574
12575 static boolean canFallDown(struct PlayerInfo *player)
12576 {
12577   int jx = player->jx, jy = player->jy;
12578
12579   return (IN_LEV_FIELD(jx, jy + 1) &&
12580           (IS_FREE(jx, jy + 1) ||
12581            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12582           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12583           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12584 }
12585
12586 static boolean canPassField(int x, int y, int move_dir)
12587 {
12588   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12589   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12590   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12591   int nextx = x + dx;
12592   int nexty = y + dy;
12593   int element = Tile[x][y];
12594
12595   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12596           !CAN_MOVE(element) &&
12597           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12598           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12599           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12600 }
12601
12602 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12603 {
12604   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12605   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12606   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12607   int newx = x + dx;
12608   int newy = y + dy;
12609
12610   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12611           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12612           (IS_DIGGABLE(Tile[newx][newy]) ||
12613            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12614            canPassField(newx, newy, move_dir)));
12615 }
12616
12617 static void CheckGravityMovement(struct PlayerInfo *player)
12618 {
12619   if (player->gravity && !player->programmed_action)
12620   {
12621     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12622     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12623     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12624     int jx = player->jx, jy = player->jy;
12625     boolean player_is_moving_to_valid_field =
12626       (!player_is_snapping &&
12627        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12628         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12629     boolean player_can_fall_down = canFallDown(player);
12630
12631     if (player_can_fall_down &&
12632         !player_is_moving_to_valid_field)
12633       player->programmed_action = MV_DOWN;
12634   }
12635 }
12636
12637 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12638 {
12639   return CheckGravityMovement(player);
12640
12641   if (player->gravity && !player->programmed_action)
12642   {
12643     int jx = player->jx, jy = player->jy;
12644     boolean field_under_player_is_free =
12645       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12646     boolean player_is_standing_on_valid_field =
12647       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12648        (IS_WALKABLE(Tile[jx][jy]) &&
12649         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12650
12651     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12652       player->programmed_action = MV_DOWN;
12653   }
12654 }
12655
12656 /*
12657   MovePlayerOneStep()
12658   -----------------------------------------------------------------------------
12659   dx, dy:               direction (non-diagonal) to try to move the player to
12660   real_dx, real_dy:     direction as read from input device (can be diagonal)
12661 */
12662
12663 boolean MovePlayerOneStep(struct PlayerInfo *player,
12664                           int dx, int dy, int real_dx, int real_dy)
12665 {
12666   int jx = player->jx, jy = player->jy;
12667   int new_jx = jx + dx, new_jy = jy + dy;
12668   int can_move;
12669   boolean player_can_move = !player->cannot_move;
12670
12671   if (!player->active || (!dx && !dy))
12672     return MP_NO_ACTION;
12673
12674   player->MovDir = (dx < 0 ? MV_LEFT :
12675                     dx > 0 ? MV_RIGHT :
12676                     dy < 0 ? MV_UP :
12677                     dy > 0 ? MV_DOWN :  MV_NONE);
12678
12679   if (!IN_LEV_FIELD(new_jx, new_jy))
12680     return MP_NO_ACTION;
12681
12682   if (!player_can_move)
12683   {
12684     if (player->MovPos == 0)
12685     {
12686       player->is_moving = FALSE;
12687       player->is_digging = FALSE;
12688       player->is_collecting = FALSE;
12689       player->is_snapping = FALSE;
12690       player->is_pushing = FALSE;
12691     }
12692   }
12693
12694   if (!network.enabled && game.centered_player_nr == -1 &&
12695       !AllPlayersInSight(player, new_jx, new_jy))
12696     return MP_NO_ACTION;
12697
12698   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12699   if (can_move != MP_MOVING)
12700     return can_move;
12701
12702   // check if DigField() has caused relocation of the player
12703   if (player->jx != jx || player->jy != jy)
12704     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12705
12706   StorePlayer[jx][jy] = 0;
12707   player->last_jx = jx;
12708   player->last_jy = jy;
12709   player->jx = new_jx;
12710   player->jy = new_jy;
12711   StorePlayer[new_jx][new_jy] = player->element_nr;
12712
12713   if (player->move_delay_value_next != -1)
12714   {
12715     player->move_delay_value = player->move_delay_value_next;
12716     player->move_delay_value_next = -1;
12717   }
12718
12719   player->MovPos =
12720     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12721
12722   player->step_counter++;
12723
12724   PlayerVisit[jx][jy] = FrameCounter;
12725
12726   player->is_moving = TRUE;
12727
12728 #if 1
12729   // should better be called in MovePlayer(), but this breaks some tapes
12730   ScrollPlayer(player, SCROLL_INIT);
12731 #endif
12732
12733   return MP_MOVING;
12734 }
12735
12736 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12737 {
12738   int jx = player->jx, jy = player->jy;
12739   int old_jx = jx, old_jy = jy;
12740   int moved = MP_NO_ACTION;
12741
12742   if (!player->active)
12743     return FALSE;
12744
12745   if (!dx && !dy)
12746   {
12747     if (player->MovPos == 0)
12748     {
12749       player->is_moving = FALSE;
12750       player->is_digging = FALSE;
12751       player->is_collecting = FALSE;
12752       player->is_snapping = FALSE;
12753       player->is_pushing = FALSE;
12754     }
12755
12756     return FALSE;
12757   }
12758
12759   if (player->move_delay > 0)
12760     return FALSE;
12761
12762   player->move_delay = -1;              // set to "uninitialized" value
12763
12764   // store if player is automatically moved to next field
12765   player->is_auto_moving = (player->programmed_action != MV_NONE);
12766
12767   // remove the last programmed player action
12768   player->programmed_action = 0;
12769
12770   if (player->MovPos)
12771   {
12772     // should only happen if pre-1.2 tape recordings are played
12773     // this is only for backward compatibility
12774
12775     int original_move_delay_value = player->move_delay_value;
12776
12777 #if DEBUG
12778     Debug("game:playing:MovePlayer",
12779           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12780           tape.counter);
12781 #endif
12782
12783     // scroll remaining steps with finest movement resolution
12784     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12785
12786     while (player->MovPos)
12787     {
12788       ScrollPlayer(player, SCROLL_GO_ON);
12789       ScrollScreen(NULL, SCROLL_GO_ON);
12790
12791       AdvanceFrameAndPlayerCounters(player->index_nr);
12792
12793       DrawAllPlayers();
12794       BackToFront_WithFrameDelay(0);
12795     }
12796
12797     player->move_delay_value = original_move_delay_value;
12798   }
12799
12800   player->is_active = FALSE;
12801
12802   if (player->last_move_dir & MV_HORIZONTAL)
12803   {
12804     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12805       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12806   }
12807   else
12808   {
12809     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12810       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12811   }
12812
12813   if (!moved && !player->is_active)
12814   {
12815     player->is_moving = FALSE;
12816     player->is_digging = FALSE;
12817     player->is_collecting = FALSE;
12818     player->is_snapping = FALSE;
12819     player->is_pushing = FALSE;
12820   }
12821
12822   jx = player->jx;
12823   jy = player->jy;
12824
12825   if (moved & MP_MOVING && !ScreenMovPos &&
12826       (player->index_nr == game.centered_player_nr ||
12827        game.centered_player_nr == -1))
12828   {
12829     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12830
12831     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12832     {
12833       // actual player has left the screen -- scroll in that direction
12834       if (jx != old_jx)         // player has moved horizontally
12835         scroll_x += (jx - old_jx);
12836       else                      // player has moved vertically
12837         scroll_y += (jy - old_jy);
12838     }
12839     else
12840     {
12841       int offset_raw = game.scroll_delay_value;
12842
12843       if (jx != old_jx)         // player has moved horizontally
12844       {
12845         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12846         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12847         int new_scroll_x = jx - MIDPOSX + offset_x;
12848
12849         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12850             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12851           scroll_x = new_scroll_x;
12852
12853         // don't scroll over playfield boundaries
12854         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12855
12856         // don't scroll more than one field at a time
12857         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12858
12859         // don't scroll against the player's moving direction
12860         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12861             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12862           scroll_x = old_scroll_x;
12863       }
12864       else                      // player has moved vertically
12865       {
12866         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12867         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12868         int new_scroll_y = jy - MIDPOSY + offset_y;
12869
12870         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12871             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12872           scroll_y = new_scroll_y;
12873
12874         // don't scroll over playfield boundaries
12875         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12876
12877         // don't scroll more than one field at a time
12878         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12879
12880         // don't scroll against the player's moving direction
12881         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12882             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12883           scroll_y = old_scroll_y;
12884       }
12885     }
12886
12887     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12888     {
12889       if (!network.enabled && game.centered_player_nr == -1 &&
12890           !AllPlayersInVisibleScreen())
12891       {
12892         scroll_x = old_scroll_x;
12893         scroll_y = old_scroll_y;
12894       }
12895       else
12896       {
12897         ScrollScreen(player, SCROLL_INIT);
12898         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12899       }
12900     }
12901   }
12902
12903   player->StepFrame = 0;
12904
12905   if (moved & MP_MOVING)
12906   {
12907     if (old_jx != jx && old_jy == jy)
12908       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12909     else if (old_jx == jx && old_jy != jy)
12910       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12911
12912     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12913
12914     player->last_move_dir = player->MovDir;
12915     player->is_moving = TRUE;
12916     player->is_snapping = FALSE;
12917     player->is_switching = FALSE;
12918     player->is_dropping = FALSE;
12919     player->is_dropping_pressed = FALSE;
12920     player->drop_pressed_delay = 0;
12921
12922 #if 0
12923     // should better be called here than above, but this breaks some tapes
12924     ScrollPlayer(player, SCROLL_INIT);
12925 #endif
12926   }
12927   else
12928   {
12929     CheckGravityMovementWhenNotMoving(player);
12930
12931     player->is_moving = FALSE;
12932
12933     /* at this point, the player is allowed to move, but cannot move right now
12934        (e.g. because of something blocking the way) -- ensure that the player
12935        is also allowed to move in the next frame (in old versions before 3.1.1,
12936        the player was forced to wait again for eight frames before next try) */
12937
12938     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12939       player->move_delay = 0;   // allow direct movement in the next frame
12940   }
12941
12942   if (player->move_delay == -1)         // not yet initialized by DigField()
12943     player->move_delay = player->move_delay_value;
12944
12945   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12946   {
12947     TestIfPlayerTouchesBadThing(jx, jy);
12948     TestIfPlayerTouchesCustomElement(jx, jy);
12949   }
12950
12951   if (!player->active)
12952     RemovePlayer(player);
12953
12954   return moved;
12955 }
12956
12957 void ScrollPlayer(struct PlayerInfo *player, int mode)
12958 {
12959   int jx = player->jx, jy = player->jy;
12960   int last_jx = player->last_jx, last_jy = player->last_jy;
12961   int move_stepsize = TILEX / player->move_delay_value;
12962
12963   if (!player->active)
12964     return;
12965
12966   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
12967     return;
12968
12969   if (mode == SCROLL_INIT)
12970   {
12971     player->actual_frame_counter = FrameCounter;
12972     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12973
12974     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12975         Tile[last_jx][last_jy] == EL_EMPTY)
12976     {
12977       int last_field_block_delay = 0;   // start with no blocking at all
12978       int block_delay_adjustment = player->block_delay_adjustment;
12979
12980       // if player blocks last field, add delay for exactly one move
12981       if (player->block_last_field)
12982       {
12983         last_field_block_delay += player->move_delay_value;
12984
12985         // when blocking enabled, prevent moving up despite gravity
12986         if (player->gravity && player->MovDir == MV_UP)
12987           block_delay_adjustment = -1;
12988       }
12989
12990       // add block delay adjustment (also possible when not blocking)
12991       last_field_block_delay += block_delay_adjustment;
12992
12993       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12994       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12995     }
12996
12997     if (player->MovPos != 0)    // player has not yet reached destination
12998       return;
12999   }
13000   else if (!FrameReached(&player->actual_frame_counter, 1))
13001     return;
13002
13003   if (player->MovPos != 0)
13004   {
13005     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13006     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13007
13008     // before DrawPlayer() to draw correct player graphic for this case
13009     if (player->MovPos == 0)
13010       CheckGravityMovement(player);
13011   }
13012
13013   if (player->MovPos == 0)      // player reached destination field
13014   {
13015     if (player->move_delay_reset_counter > 0)
13016     {
13017       player->move_delay_reset_counter--;
13018
13019       if (player->move_delay_reset_counter == 0)
13020       {
13021         // continue with normal speed after quickly moving through gate
13022         HALVE_PLAYER_SPEED(player);
13023
13024         // be able to make the next move without delay
13025         player->move_delay = 0;
13026       }
13027     }
13028
13029     player->last_jx = jx;
13030     player->last_jy = jy;
13031
13032     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13033         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13034         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13035         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13036         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13037         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13038         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13039         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13040     {
13041       ExitPlayer(player);
13042
13043       if (game.players_still_needed == 0 &&
13044           (game.friends_still_needed == 0 ||
13045            IS_SP_ELEMENT(Tile[jx][jy])))
13046         LevelSolved();
13047     }
13048
13049     // this breaks one level: "machine", level 000
13050     {
13051       int move_direction = player->MovDir;
13052       int enter_side = MV_DIR_OPPOSITE(move_direction);
13053       int leave_side = move_direction;
13054       int old_jx = last_jx;
13055       int old_jy = last_jy;
13056       int old_element = Tile[old_jx][old_jy];
13057       int new_element = Tile[jx][jy];
13058
13059       if (IS_CUSTOM_ELEMENT(old_element))
13060         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13061                                    CE_LEFT_BY_PLAYER,
13062                                    player->index_bit, leave_side);
13063
13064       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13065                                           CE_PLAYER_LEAVES_X,
13066                                           player->index_bit, leave_side);
13067
13068       if (IS_CUSTOM_ELEMENT(new_element))
13069         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13070                                    player->index_bit, enter_side);
13071
13072       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13073                                           CE_PLAYER_ENTERS_X,
13074                                           player->index_bit, enter_side);
13075
13076       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13077                                         CE_MOVE_OF_X, move_direction);
13078     }
13079
13080     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13081     {
13082       TestIfPlayerTouchesBadThing(jx, jy);
13083       TestIfPlayerTouchesCustomElement(jx, jy);
13084
13085       /* needed because pushed element has not yet reached its destination,
13086          so it would trigger a change event at its previous field location */
13087       if (!player->is_pushing)
13088         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13089
13090       if (level.finish_dig_collect &&
13091           (player->is_digging || player->is_collecting))
13092       {
13093         int last_element = player->last_removed_element;
13094         int move_direction = player->MovDir;
13095         int enter_side = MV_DIR_OPPOSITE(move_direction);
13096         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13097                             CE_PLAYER_COLLECTS_X);
13098
13099         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13100                                             player->index_bit, enter_side);
13101
13102         player->last_removed_element = EL_UNDEFINED;
13103       }
13104
13105       if (!player->active)
13106         RemovePlayer(player);
13107     }
13108
13109     if (!game.LevelSolved && level.use_step_counter)
13110     {
13111       int i;
13112
13113       TimePlayed++;
13114
13115       if (TimeLeft > 0)
13116       {
13117         TimeLeft--;
13118
13119         if (TimeLeft <= 10 && setup.time_limit)
13120           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13121
13122         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13123
13124         DisplayGameControlValues();
13125
13126         if (!TimeLeft && setup.time_limit)
13127           for (i = 0; i < MAX_PLAYERS; i++)
13128             KillPlayer(&stored_player[i]);
13129       }
13130       else if (game.no_time_limit && !game.all_players_gone)
13131       {
13132         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13133
13134         DisplayGameControlValues();
13135       }
13136     }
13137
13138     if (tape.single_step && tape.recording && !tape.pausing &&
13139         !player->programmed_action)
13140       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13141
13142     if (!player->programmed_action)
13143       CheckSaveEngineSnapshot(player);
13144   }
13145 }
13146
13147 void ScrollScreen(struct PlayerInfo *player, int mode)
13148 {
13149   static unsigned int screen_frame_counter = 0;
13150
13151   if (mode == SCROLL_INIT)
13152   {
13153     // set scrolling step size according to actual player's moving speed
13154     ScrollStepSize = TILEX / player->move_delay_value;
13155
13156     screen_frame_counter = FrameCounter;
13157     ScreenMovDir = player->MovDir;
13158     ScreenMovPos = player->MovPos;
13159     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13160     return;
13161   }
13162   else if (!FrameReached(&screen_frame_counter, 1))
13163     return;
13164
13165   if (ScreenMovPos)
13166   {
13167     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13168     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13169     redraw_mask |= REDRAW_FIELD;
13170   }
13171   else
13172     ScreenMovDir = MV_NONE;
13173 }
13174
13175 void TestIfPlayerTouchesCustomElement(int x, int y)
13176 {
13177   static int xy[4][2] =
13178   {
13179     { 0, -1 },
13180     { -1, 0 },
13181     { +1, 0 },
13182     { 0, +1 }
13183   };
13184   static int trigger_sides[4][2] =
13185   {
13186     // center side       border side
13187     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13188     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13189     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13190     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13191   };
13192   static int touch_dir[4] =
13193   {
13194     MV_LEFT | MV_RIGHT,
13195     MV_UP   | MV_DOWN,
13196     MV_UP   | MV_DOWN,
13197     MV_LEFT | MV_RIGHT
13198   };
13199   int center_element = Tile[x][y];      // should always be non-moving!
13200   int i;
13201
13202   for (i = 0; i < NUM_DIRECTIONS; i++)
13203   {
13204     int xx = x + xy[i][0];
13205     int yy = y + xy[i][1];
13206     int center_side = trigger_sides[i][0];
13207     int border_side = trigger_sides[i][1];
13208     int border_element;
13209
13210     if (!IN_LEV_FIELD(xx, yy))
13211       continue;
13212
13213     if (IS_PLAYER(x, y))                // player found at center element
13214     {
13215       struct PlayerInfo *player = PLAYERINFO(x, y);
13216
13217       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13218         border_element = Tile[xx][yy];          // may be moving!
13219       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13220         border_element = Tile[xx][yy];
13221       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13222         border_element = MovingOrBlocked2Element(xx, yy);
13223       else
13224         continue;               // center and border element do not touch
13225
13226       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13227                                  player->index_bit, border_side);
13228       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13229                                           CE_PLAYER_TOUCHES_X,
13230                                           player->index_bit, border_side);
13231
13232       {
13233         /* use player element that is initially defined in the level playfield,
13234            not the player element that corresponds to the runtime player number
13235            (example: a level that contains EL_PLAYER_3 as the only player would
13236            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13237         int player_element = PLAYERINFO(x, y)->initial_element;
13238
13239         CheckElementChangeBySide(xx, yy, border_element, player_element,
13240                                  CE_TOUCHING_X, border_side);
13241       }
13242     }
13243     else if (IS_PLAYER(xx, yy))         // player found at border element
13244     {
13245       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13246
13247       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13248       {
13249         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13250           continue;             // center and border element do not touch
13251       }
13252
13253       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13254                                  player->index_bit, center_side);
13255       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13256                                           CE_PLAYER_TOUCHES_X,
13257                                           player->index_bit, center_side);
13258
13259       {
13260         /* use player element that is initially defined in the level playfield,
13261            not the player element that corresponds to the runtime player number
13262            (example: a level that contains EL_PLAYER_3 as the only player would
13263            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13264         int player_element = PLAYERINFO(xx, yy)->initial_element;
13265
13266         CheckElementChangeBySide(x, y, center_element, player_element,
13267                                  CE_TOUCHING_X, center_side);
13268       }
13269
13270       break;
13271     }
13272   }
13273 }
13274
13275 void TestIfElementTouchesCustomElement(int x, int y)
13276 {
13277   static int xy[4][2] =
13278   {
13279     { 0, -1 },
13280     { -1, 0 },
13281     { +1, 0 },
13282     { 0, +1 }
13283   };
13284   static int trigger_sides[4][2] =
13285   {
13286     // center side      border side
13287     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13288     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13289     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13290     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13291   };
13292   static int touch_dir[4] =
13293   {
13294     MV_LEFT | MV_RIGHT,
13295     MV_UP   | MV_DOWN,
13296     MV_UP   | MV_DOWN,
13297     MV_LEFT | MV_RIGHT
13298   };
13299   boolean change_center_element = FALSE;
13300   int center_element = Tile[x][y];      // should always be non-moving!
13301   int border_element_old[NUM_DIRECTIONS];
13302   int i;
13303
13304   for (i = 0; i < NUM_DIRECTIONS; i++)
13305   {
13306     int xx = x + xy[i][0];
13307     int yy = y + xy[i][1];
13308     int border_element;
13309
13310     border_element_old[i] = -1;
13311
13312     if (!IN_LEV_FIELD(xx, yy))
13313       continue;
13314
13315     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13316       border_element = Tile[xx][yy];    // may be moving!
13317     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13318       border_element = Tile[xx][yy];
13319     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13320       border_element = MovingOrBlocked2Element(xx, yy);
13321     else
13322       continue;                 // center and border element do not touch
13323
13324     border_element_old[i] = border_element;
13325   }
13326
13327   for (i = 0; i < NUM_DIRECTIONS; i++)
13328   {
13329     int xx = x + xy[i][0];
13330     int yy = y + xy[i][1];
13331     int center_side = trigger_sides[i][0];
13332     int border_element = border_element_old[i];
13333
13334     if (border_element == -1)
13335       continue;
13336
13337     // check for change of border element
13338     CheckElementChangeBySide(xx, yy, border_element, center_element,
13339                              CE_TOUCHING_X, center_side);
13340
13341     // (center element cannot be player, so we dont have to check this here)
13342   }
13343
13344   for (i = 0; i < NUM_DIRECTIONS; i++)
13345   {
13346     int xx = x + xy[i][0];
13347     int yy = y + xy[i][1];
13348     int border_side = trigger_sides[i][1];
13349     int border_element = border_element_old[i];
13350
13351     if (border_element == -1)
13352       continue;
13353
13354     // check for change of center element (but change it only once)
13355     if (!change_center_element)
13356       change_center_element =
13357         CheckElementChangeBySide(x, y, center_element, border_element,
13358                                  CE_TOUCHING_X, border_side);
13359
13360     if (IS_PLAYER(xx, yy))
13361     {
13362       /* use player element that is initially defined in the level playfield,
13363          not the player element that corresponds to the runtime player number
13364          (example: a level that contains EL_PLAYER_3 as the only player would
13365          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13366       int player_element = PLAYERINFO(xx, yy)->initial_element;
13367
13368       CheckElementChangeBySide(x, y, center_element, player_element,
13369                                CE_TOUCHING_X, border_side);
13370     }
13371   }
13372 }
13373
13374 void TestIfElementHitsCustomElement(int x, int y, int direction)
13375 {
13376   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13377   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13378   int hitx = x + dx, hity = y + dy;
13379   int hitting_element = Tile[x][y];
13380   int touched_element;
13381
13382   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13383     return;
13384
13385   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13386                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13387
13388   if (IN_LEV_FIELD(hitx, hity))
13389   {
13390     int opposite_direction = MV_DIR_OPPOSITE(direction);
13391     int hitting_side = direction;
13392     int touched_side = opposite_direction;
13393     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13394                           MovDir[hitx][hity] != direction ||
13395                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13396
13397     object_hit = TRUE;
13398
13399     if (object_hit)
13400     {
13401       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13402                                CE_HITTING_X, touched_side);
13403
13404       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13405                                CE_HIT_BY_X, hitting_side);
13406
13407       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13408                                CE_HIT_BY_SOMETHING, opposite_direction);
13409
13410       if (IS_PLAYER(hitx, hity))
13411       {
13412         /* use player element that is initially defined in the level playfield,
13413            not the player element that corresponds to the runtime player number
13414            (example: a level that contains EL_PLAYER_3 as the only player would
13415            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13416         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13417
13418         CheckElementChangeBySide(x, y, hitting_element, player_element,
13419                                  CE_HITTING_X, touched_side);
13420       }
13421     }
13422   }
13423
13424   // "hitting something" is also true when hitting the playfield border
13425   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13426                            CE_HITTING_SOMETHING, direction);
13427 }
13428
13429 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13430 {
13431   int i, kill_x = -1, kill_y = -1;
13432
13433   int bad_element = -1;
13434   static int test_xy[4][2] =
13435   {
13436     { 0, -1 },
13437     { -1, 0 },
13438     { +1, 0 },
13439     { 0, +1 }
13440   };
13441   static int test_dir[4] =
13442   {
13443     MV_UP,
13444     MV_LEFT,
13445     MV_RIGHT,
13446     MV_DOWN
13447   };
13448
13449   for (i = 0; i < NUM_DIRECTIONS; i++)
13450   {
13451     int test_x, test_y, test_move_dir, test_element;
13452
13453     test_x = good_x + test_xy[i][0];
13454     test_y = good_y + test_xy[i][1];
13455
13456     if (!IN_LEV_FIELD(test_x, test_y))
13457       continue;
13458
13459     test_move_dir =
13460       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13461
13462     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13463
13464     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13465        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13466     */
13467     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13468         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13469     {
13470       kill_x = test_x;
13471       kill_y = test_y;
13472       bad_element = test_element;
13473
13474       break;
13475     }
13476   }
13477
13478   if (kill_x != -1 || kill_y != -1)
13479   {
13480     if (IS_PLAYER(good_x, good_y))
13481     {
13482       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13483
13484       if (player->shield_deadly_time_left > 0 &&
13485           !IS_INDESTRUCTIBLE(bad_element))
13486         Bang(kill_x, kill_y);
13487       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13488         KillPlayer(player);
13489     }
13490     else
13491       Bang(good_x, good_y);
13492   }
13493 }
13494
13495 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13496 {
13497   int i, kill_x = -1, kill_y = -1;
13498   int bad_element = Tile[bad_x][bad_y];
13499   static int test_xy[4][2] =
13500   {
13501     { 0, -1 },
13502     { -1, 0 },
13503     { +1, 0 },
13504     { 0, +1 }
13505   };
13506   static int touch_dir[4] =
13507   {
13508     MV_LEFT | MV_RIGHT,
13509     MV_UP   | MV_DOWN,
13510     MV_UP   | MV_DOWN,
13511     MV_LEFT | MV_RIGHT
13512   };
13513   static int test_dir[4] =
13514   {
13515     MV_UP,
13516     MV_LEFT,
13517     MV_RIGHT,
13518     MV_DOWN
13519   };
13520
13521   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13522     return;
13523
13524   for (i = 0; i < NUM_DIRECTIONS; i++)
13525   {
13526     int test_x, test_y, test_move_dir, test_element;
13527
13528     test_x = bad_x + test_xy[i][0];
13529     test_y = bad_y + test_xy[i][1];
13530
13531     if (!IN_LEV_FIELD(test_x, test_y))
13532       continue;
13533
13534     test_move_dir =
13535       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13536
13537     test_element = Tile[test_x][test_y];
13538
13539     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13540        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13541     */
13542     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13543         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13544     {
13545       // good thing is player or penguin that does not move away
13546       if (IS_PLAYER(test_x, test_y))
13547       {
13548         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13549
13550         if (bad_element == EL_ROBOT && player->is_moving)
13551           continue;     // robot does not kill player if he is moving
13552
13553         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13554         {
13555           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13556             continue;           // center and border element do not touch
13557         }
13558
13559         kill_x = test_x;
13560         kill_y = test_y;
13561
13562         break;
13563       }
13564       else if (test_element == EL_PENGUIN)
13565       {
13566         kill_x = test_x;
13567         kill_y = test_y;
13568
13569         break;
13570       }
13571     }
13572   }
13573
13574   if (kill_x != -1 || kill_y != -1)
13575   {
13576     if (IS_PLAYER(kill_x, kill_y))
13577     {
13578       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13579
13580       if (player->shield_deadly_time_left > 0 &&
13581           !IS_INDESTRUCTIBLE(bad_element))
13582         Bang(bad_x, bad_y);
13583       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13584         KillPlayer(player);
13585     }
13586     else
13587       Bang(kill_x, kill_y);
13588   }
13589 }
13590
13591 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13592 {
13593   int bad_element = Tile[bad_x][bad_y];
13594   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13595   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13596   int test_x = bad_x + dx, test_y = bad_y + dy;
13597   int test_move_dir, test_element;
13598   int kill_x = -1, kill_y = -1;
13599
13600   if (!IN_LEV_FIELD(test_x, test_y))
13601     return;
13602
13603   test_move_dir =
13604     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13605
13606   test_element = Tile[test_x][test_y];
13607
13608   if (test_move_dir != bad_move_dir)
13609   {
13610     // good thing can be player or penguin that does not move away
13611     if (IS_PLAYER(test_x, test_y))
13612     {
13613       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13614
13615       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13616          player as being hit when he is moving towards the bad thing, because
13617          the "get hit by" condition would be lost after the player stops) */
13618       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13619         return;         // player moves away from bad thing
13620
13621       kill_x = test_x;
13622       kill_y = test_y;
13623     }
13624     else if (test_element == EL_PENGUIN)
13625     {
13626       kill_x = test_x;
13627       kill_y = test_y;
13628     }
13629   }
13630
13631   if (kill_x != -1 || kill_y != -1)
13632   {
13633     if (IS_PLAYER(kill_x, kill_y))
13634     {
13635       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13636
13637       if (player->shield_deadly_time_left > 0 &&
13638           !IS_INDESTRUCTIBLE(bad_element))
13639         Bang(bad_x, bad_y);
13640       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13641         KillPlayer(player);
13642     }
13643     else
13644       Bang(kill_x, kill_y);
13645   }
13646 }
13647
13648 void TestIfPlayerTouchesBadThing(int x, int y)
13649 {
13650   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13651 }
13652
13653 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13654 {
13655   TestIfGoodThingHitsBadThing(x, y, move_dir);
13656 }
13657
13658 void TestIfBadThingTouchesPlayer(int x, int y)
13659 {
13660   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13661 }
13662
13663 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13664 {
13665   TestIfBadThingHitsGoodThing(x, y, move_dir);
13666 }
13667
13668 void TestIfFriendTouchesBadThing(int x, int y)
13669 {
13670   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13671 }
13672
13673 void TestIfBadThingTouchesFriend(int x, int y)
13674 {
13675   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13676 }
13677
13678 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13679 {
13680   int i, kill_x = bad_x, kill_y = bad_y;
13681   static int xy[4][2] =
13682   {
13683     { 0, -1 },
13684     { -1, 0 },
13685     { +1, 0 },
13686     { 0, +1 }
13687   };
13688
13689   for (i = 0; i < NUM_DIRECTIONS; i++)
13690   {
13691     int x, y, element;
13692
13693     x = bad_x + xy[i][0];
13694     y = bad_y + xy[i][1];
13695     if (!IN_LEV_FIELD(x, y))
13696       continue;
13697
13698     element = Tile[x][y];
13699     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13700         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13701     {
13702       kill_x = x;
13703       kill_y = y;
13704       break;
13705     }
13706   }
13707
13708   if (kill_x != bad_x || kill_y != bad_y)
13709     Bang(bad_x, bad_y);
13710 }
13711
13712 void KillPlayer(struct PlayerInfo *player)
13713 {
13714   int jx = player->jx, jy = player->jy;
13715
13716   if (!player->active)
13717     return;
13718
13719 #if 0
13720   Debug("game:playing:KillPlayer",
13721         "0: killed == %d, active == %d, reanimated == %d",
13722         player->killed, player->active, player->reanimated);
13723 #endif
13724
13725   /* the following code was introduced to prevent an infinite loop when calling
13726      -> Bang()
13727      -> CheckTriggeredElementChangeExt()
13728      -> ExecuteCustomElementAction()
13729      -> KillPlayer()
13730      -> (infinitely repeating the above sequence of function calls)
13731      which occurs when killing the player while having a CE with the setting
13732      "kill player X when explosion of <player X>"; the solution using a new
13733      field "player->killed" was chosen for backwards compatibility, although
13734      clever use of the fields "player->active" etc. would probably also work */
13735 #if 1
13736   if (player->killed)
13737     return;
13738 #endif
13739
13740   player->killed = TRUE;
13741
13742   // remove accessible field at the player's position
13743   Tile[jx][jy] = EL_EMPTY;
13744
13745   // deactivate shield (else Bang()/Explode() would not work right)
13746   player->shield_normal_time_left = 0;
13747   player->shield_deadly_time_left = 0;
13748
13749 #if 0
13750   Debug("game:playing:KillPlayer",
13751         "1: killed == %d, active == %d, reanimated == %d",
13752         player->killed, player->active, player->reanimated);
13753 #endif
13754
13755   Bang(jx, jy);
13756
13757 #if 0
13758   Debug("game:playing:KillPlayer",
13759         "2: killed == %d, active == %d, reanimated == %d",
13760         player->killed, player->active, player->reanimated);
13761 #endif
13762
13763   if (player->reanimated)       // killed player may have been reanimated
13764     player->killed = player->reanimated = FALSE;
13765   else
13766     BuryPlayer(player);
13767 }
13768
13769 static void KillPlayerUnlessEnemyProtected(int x, int y)
13770 {
13771   if (!PLAYER_ENEMY_PROTECTED(x, y))
13772     KillPlayer(PLAYERINFO(x, y));
13773 }
13774
13775 static void KillPlayerUnlessExplosionProtected(int x, int y)
13776 {
13777   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13778     KillPlayer(PLAYERINFO(x, y));
13779 }
13780
13781 void BuryPlayer(struct PlayerInfo *player)
13782 {
13783   int jx = player->jx, jy = player->jy;
13784
13785   if (!player->active)
13786     return;
13787
13788   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13789   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13790
13791   RemovePlayer(player);
13792
13793   player->buried = TRUE;
13794
13795   if (game.all_players_gone)
13796     game.GameOver = TRUE;
13797 }
13798
13799 void RemovePlayer(struct PlayerInfo *player)
13800 {
13801   int jx = player->jx, jy = player->jy;
13802   int i, found = FALSE;
13803
13804   player->present = FALSE;
13805   player->active = FALSE;
13806
13807   // required for some CE actions (even if the player is not active anymore)
13808   player->MovPos = 0;
13809
13810   if (!ExplodeField[jx][jy])
13811     StorePlayer[jx][jy] = 0;
13812
13813   if (player->is_moving)
13814     TEST_DrawLevelField(player->last_jx, player->last_jy);
13815
13816   for (i = 0; i < MAX_PLAYERS; i++)
13817     if (stored_player[i].active)
13818       found = TRUE;
13819
13820   if (!found)
13821   {
13822     game.all_players_gone = TRUE;
13823     game.GameOver = TRUE;
13824   }
13825
13826   game.exit_x = game.robot_wheel_x = jx;
13827   game.exit_y = game.robot_wheel_y = jy;
13828 }
13829
13830 void ExitPlayer(struct PlayerInfo *player)
13831 {
13832   DrawPlayer(player);   // needed here only to cleanup last field
13833   RemovePlayer(player);
13834
13835   if (game.players_still_needed > 0)
13836     game.players_still_needed--;
13837 }
13838
13839 static void SetFieldForSnapping(int x, int y, int element, int direction,
13840                                 int player_index_bit)
13841 {
13842   struct ElementInfo *ei = &element_info[element];
13843   int direction_bit = MV_DIR_TO_BIT(direction);
13844   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13845   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13846                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13847
13848   Tile[x][y] = EL_ELEMENT_SNAPPING;
13849   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13850   MovDir[x][y] = direction;
13851   Store[x][y] = element;
13852   Store2[x][y] = player_index_bit;
13853
13854   ResetGfxAnimation(x, y);
13855
13856   GfxElement[x][y] = element;
13857   GfxAction[x][y] = action;
13858   GfxDir[x][y] = direction;
13859   GfxFrame[x][y] = -1;
13860 }
13861
13862 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13863                                    int player_index_bit)
13864 {
13865   TestIfElementTouchesCustomElement(x, y);      // for empty space
13866
13867   if (level.finish_dig_collect)
13868   {
13869     int dig_side = MV_DIR_OPPOSITE(direction);
13870
13871     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13872                                         player_index_bit, dig_side);
13873   }
13874 }
13875
13876 /*
13877   =============================================================================
13878   checkDiagonalPushing()
13879   -----------------------------------------------------------------------------
13880   check if diagonal input device direction results in pushing of object
13881   (by checking if the alternative direction is walkable, diggable, ...)
13882   =============================================================================
13883 */
13884
13885 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13886                                     int x, int y, int real_dx, int real_dy)
13887 {
13888   int jx, jy, dx, dy, xx, yy;
13889
13890   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13891     return TRUE;
13892
13893   // diagonal direction: check alternative direction
13894   jx = player->jx;
13895   jy = player->jy;
13896   dx = x - jx;
13897   dy = y - jy;
13898   xx = jx + (dx == 0 ? real_dx : 0);
13899   yy = jy + (dy == 0 ? real_dy : 0);
13900
13901   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13902 }
13903
13904 /*
13905   =============================================================================
13906   DigField()
13907   -----------------------------------------------------------------------------
13908   x, y:                 field next to player (non-diagonal) to try to dig to
13909   real_dx, real_dy:     direction as read from input device (can be diagonal)
13910   =============================================================================
13911 */
13912
13913 static int DigField(struct PlayerInfo *player,
13914                     int oldx, int oldy, int x, int y,
13915                     int real_dx, int real_dy, int mode)
13916 {
13917   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13918   boolean player_was_pushing = player->is_pushing;
13919   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13920   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13921   int jx = oldx, jy = oldy;
13922   int dx = x - jx, dy = y - jy;
13923   int nextx = x + dx, nexty = y + dy;
13924   int move_direction = (dx == -1 ? MV_LEFT  :
13925                         dx == +1 ? MV_RIGHT :
13926                         dy == -1 ? MV_UP    :
13927                         dy == +1 ? MV_DOWN  : MV_NONE);
13928   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13929   int dig_side = MV_DIR_OPPOSITE(move_direction);
13930   int old_element = Tile[jx][jy];
13931   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13932   int collect_count;
13933
13934   if (is_player)                // function can also be called by EL_PENGUIN
13935   {
13936     if (player->MovPos == 0)
13937     {
13938       player->is_digging = FALSE;
13939       player->is_collecting = FALSE;
13940     }
13941
13942     if (player->MovPos == 0)    // last pushing move finished
13943       player->is_pushing = FALSE;
13944
13945     if (mode == DF_NO_PUSH)     // player just stopped pushing
13946     {
13947       player->is_switching = FALSE;
13948       player->push_delay = -1;
13949
13950       return MP_NO_ACTION;
13951     }
13952   }
13953
13954   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13955     old_element = Back[jx][jy];
13956
13957   // in case of element dropped at player position, check background
13958   else if (Back[jx][jy] != EL_EMPTY &&
13959            game.engine_version >= VERSION_IDENT(2,2,0,0))
13960     old_element = Back[jx][jy];
13961
13962   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13963     return MP_NO_ACTION;        // field has no opening in this direction
13964
13965   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13966     return MP_NO_ACTION;        // field has no opening in this direction
13967
13968   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13969   {
13970     SplashAcid(x, y);
13971
13972     Tile[jx][jy] = player->artwork_element;
13973     InitMovingField(jx, jy, MV_DOWN);
13974     Store[jx][jy] = EL_ACID;
13975     ContinueMoving(jx, jy);
13976     BuryPlayer(player);
13977
13978     return MP_DONT_RUN_INTO;
13979   }
13980
13981   if (player_can_move && DONT_RUN_INTO(element))
13982   {
13983     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13984
13985     return MP_DONT_RUN_INTO;
13986   }
13987
13988   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13989     return MP_NO_ACTION;
13990
13991   collect_count = element_info[element].collect_count_initial;
13992
13993   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
13994     return MP_NO_ACTION;
13995
13996   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13997     player_can_move = player_can_move_or_snap;
13998
13999   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14000       game.engine_version >= VERSION_IDENT(2,2,0,0))
14001   {
14002     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14003                                player->index_bit, dig_side);
14004     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14005                                         player->index_bit, dig_side);
14006
14007     if (element == EL_DC_LANDMINE)
14008       Bang(x, y);
14009
14010     if (Tile[x][y] != element)          // field changed by snapping
14011       return MP_ACTION;
14012
14013     return MP_NO_ACTION;
14014   }
14015
14016   if (player->gravity && is_player && !player->is_auto_moving &&
14017       canFallDown(player) && move_direction != MV_DOWN &&
14018       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14019     return MP_NO_ACTION;        // player cannot walk here due to gravity
14020
14021   if (player_can_move &&
14022       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14023   {
14024     int sound_element = SND_ELEMENT(element);
14025     int sound_action = ACTION_WALKING;
14026
14027     if (IS_RND_GATE(element))
14028     {
14029       if (!player->key[RND_GATE_NR(element)])
14030         return MP_NO_ACTION;
14031     }
14032     else if (IS_RND_GATE_GRAY(element))
14033     {
14034       if (!player->key[RND_GATE_GRAY_NR(element)])
14035         return MP_NO_ACTION;
14036     }
14037     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14038     {
14039       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14040         return MP_NO_ACTION;
14041     }
14042     else if (element == EL_EXIT_OPEN ||
14043              element == EL_EM_EXIT_OPEN ||
14044              element == EL_EM_EXIT_OPENING ||
14045              element == EL_STEEL_EXIT_OPEN ||
14046              element == EL_EM_STEEL_EXIT_OPEN ||
14047              element == EL_EM_STEEL_EXIT_OPENING ||
14048              element == EL_SP_EXIT_OPEN ||
14049              element == EL_SP_EXIT_OPENING)
14050     {
14051       sound_action = ACTION_PASSING;    // player is passing exit
14052     }
14053     else if (element == EL_EMPTY)
14054     {
14055       sound_action = ACTION_MOVING;             // nothing to walk on
14056     }
14057
14058     // play sound from background or player, whatever is available
14059     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14060       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14061     else
14062       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14063   }
14064   else if (player_can_move &&
14065            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14066   {
14067     if (!ACCESS_FROM(element, opposite_direction))
14068       return MP_NO_ACTION;      // field not accessible from this direction
14069
14070     if (CAN_MOVE(element))      // only fixed elements can be passed!
14071       return MP_NO_ACTION;
14072
14073     if (IS_EM_GATE(element))
14074     {
14075       if (!player->key[EM_GATE_NR(element)])
14076         return MP_NO_ACTION;
14077     }
14078     else if (IS_EM_GATE_GRAY(element))
14079     {
14080       if (!player->key[EM_GATE_GRAY_NR(element)])
14081         return MP_NO_ACTION;
14082     }
14083     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14084     {
14085       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14086         return MP_NO_ACTION;
14087     }
14088     else if (IS_EMC_GATE(element))
14089     {
14090       if (!player->key[EMC_GATE_NR(element)])
14091         return MP_NO_ACTION;
14092     }
14093     else if (IS_EMC_GATE_GRAY(element))
14094     {
14095       if (!player->key[EMC_GATE_GRAY_NR(element)])
14096         return MP_NO_ACTION;
14097     }
14098     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14099     {
14100       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14101         return MP_NO_ACTION;
14102     }
14103     else if (element == EL_DC_GATE_WHITE ||
14104              element == EL_DC_GATE_WHITE_GRAY ||
14105              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14106     {
14107       if (player->num_white_keys == 0)
14108         return MP_NO_ACTION;
14109
14110       player->num_white_keys--;
14111     }
14112     else if (IS_SP_PORT(element))
14113     {
14114       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14115           element == EL_SP_GRAVITY_PORT_RIGHT ||
14116           element == EL_SP_GRAVITY_PORT_UP ||
14117           element == EL_SP_GRAVITY_PORT_DOWN)
14118         player->gravity = !player->gravity;
14119       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14120                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14121                element == EL_SP_GRAVITY_ON_PORT_UP ||
14122                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14123         player->gravity = TRUE;
14124       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14125                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14126                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14127                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14128         player->gravity = FALSE;
14129     }
14130
14131     // automatically move to the next field with double speed
14132     player->programmed_action = move_direction;
14133
14134     if (player->move_delay_reset_counter == 0)
14135     {
14136       player->move_delay_reset_counter = 2;     // two double speed steps
14137
14138       DOUBLE_PLAYER_SPEED(player);
14139     }
14140
14141     PlayLevelSoundAction(x, y, ACTION_PASSING);
14142   }
14143   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14144   {
14145     RemoveField(x, y);
14146
14147     if (mode != DF_SNAP)
14148     {
14149       GfxElement[x][y] = GFX_ELEMENT(element);
14150       player->is_digging = TRUE;
14151     }
14152
14153     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14154
14155     // use old behaviour for old levels (digging)
14156     if (!level.finish_dig_collect)
14157     {
14158       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14159                                           player->index_bit, dig_side);
14160
14161       // if digging triggered player relocation, finish digging tile
14162       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14163         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14164     }
14165
14166     if (mode == DF_SNAP)
14167     {
14168       if (level.block_snap_field)
14169         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14170       else
14171         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14172
14173       // use old behaviour for old levels (snapping)
14174       if (!level.finish_dig_collect)
14175         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14176                                             player->index_bit, dig_side);
14177     }
14178   }
14179   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14180   {
14181     RemoveField(x, y);
14182
14183     if (is_player && mode != DF_SNAP)
14184     {
14185       GfxElement[x][y] = element;
14186       player->is_collecting = TRUE;
14187     }
14188
14189     if (element == EL_SPEED_PILL)
14190     {
14191       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14192     }
14193     else if (element == EL_EXTRA_TIME && level.time > 0)
14194     {
14195       TimeLeft += level.extra_time;
14196
14197       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14198
14199       DisplayGameControlValues();
14200     }
14201     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14202     {
14203       player->shield_normal_time_left += level.shield_normal_time;
14204       if (element == EL_SHIELD_DEADLY)
14205         player->shield_deadly_time_left += level.shield_deadly_time;
14206     }
14207     else if (element == EL_DYNAMITE ||
14208              element == EL_EM_DYNAMITE ||
14209              element == EL_SP_DISK_RED)
14210     {
14211       if (player->inventory_size < MAX_INVENTORY_SIZE)
14212         player->inventory_element[player->inventory_size++] = element;
14213
14214       DrawGameDoorValues();
14215     }
14216     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14217     {
14218       player->dynabomb_count++;
14219       player->dynabombs_left++;
14220     }
14221     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14222     {
14223       player->dynabomb_size++;
14224     }
14225     else if (element == EL_DYNABOMB_INCREASE_POWER)
14226     {
14227       player->dynabomb_xl = TRUE;
14228     }
14229     else if (IS_KEY(element))
14230     {
14231       player->key[KEY_NR(element)] = TRUE;
14232
14233       DrawGameDoorValues();
14234     }
14235     else if (element == EL_DC_KEY_WHITE)
14236     {
14237       player->num_white_keys++;
14238
14239       // display white keys?
14240       // DrawGameDoorValues();
14241     }
14242     else if (IS_ENVELOPE(element))
14243     {
14244       player->show_envelope = element;
14245     }
14246     else if (element == EL_EMC_LENSES)
14247     {
14248       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14249
14250       RedrawAllInvisibleElementsForLenses();
14251     }
14252     else if (element == EL_EMC_MAGNIFIER)
14253     {
14254       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14255
14256       RedrawAllInvisibleElementsForMagnifier();
14257     }
14258     else if (IS_DROPPABLE(element) ||
14259              IS_THROWABLE(element))     // can be collected and dropped
14260     {
14261       int i;
14262
14263       if (collect_count == 0)
14264         player->inventory_infinite_element = element;
14265       else
14266         for (i = 0; i < collect_count; i++)
14267           if (player->inventory_size < MAX_INVENTORY_SIZE)
14268             player->inventory_element[player->inventory_size++] = element;
14269
14270       DrawGameDoorValues();
14271     }
14272     else if (collect_count > 0)
14273     {
14274       game.gems_still_needed -= collect_count;
14275       if (game.gems_still_needed < 0)
14276         game.gems_still_needed = 0;
14277
14278       game.snapshot.collected_item = TRUE;
14279
14280       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14281
14282       DisplayGameControlValues();
14283     }
14284
14285     RaiseScoreElement(element);
14286     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14287
14288     // use old behaviour for old levels (collecting)
14289     if (!level.finish_dig_collect && is_player)
14290     {
14291       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14292                                           player->index_bit, dig_side);
14293
14294       // if collecting triggered player relocation, finish collecting tile
14295       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14296         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14297     }
14298
14299     if (mode == DF_SNAP)
14300     {
14301       if (level.block_snap_field)
14302         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14303       else
14304         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14305
14306       // use old behaviour for old levels (snapping)
14307       if (!level.finish_dig_collect)
14308         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14309                                             player->index_bit, dig_side);
14310     }
14311   }
14312   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14313   {
14314     if (mode == DF_SNAP && element != EL_BD_ROCK)
14315       return MP_NO_ACTION;
14316
14317     if (CAN_FALL(element) && dy)
14318       return MP_NO_ACTION;
14319
14320     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14321         !(element == EL_SPRING && level.use_spring_bug))
14322       return MP_NO_ACTION;
14323
14324     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14325         ((move_direction & MV_VERTICAL &&
14326           ((element_info[element].move_pattern & MV_LEFT &&
14327             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14328            (element_info[element].move_pattern & MV_RIGHT &&
14329             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14330          (move_direction & MV_HORIZONTAL &&
14331           ((element_info[element].move_pattern & MV_UP &&
14332             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14333            (element_info[element].move_pattern & MV_DOWN &&
14334             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14335       return MP_NO_ACTION;
14336
14337     // do not push elements already moving away faster than player
14338     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14339         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14340       return MP_NO_ACTION;
14341
14342     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14343     {
14344       if (player->push_delay_value == -1 || !player_was_pushing)
14345         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14346     }
14347     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14348     {
14349       if (player->push_delay_value == -1)
14350         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14351     }
14352     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14353     {
14354       if (!player->is_pushing)
14355         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14356     }
14357
14358     player->is_pushing = TRUE;
14359     player->is_active = TRUE;
14360
14361     if (!(IN_LEV_FIELD(nextx, nexty) &&
14362           (IS_FREE(nextx, nexty) ||
14363            (IS_SB_ELEMENT(element) &&
14364             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14365            (IS_CUSTOM_ELEMENT(element) &&
14366             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14367       return MP_NO_ACTION;
14368
14369     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14370       return MP_NO_ACTION;
14371
14372     if (player->push_delay == -1)       // new pushing; restart delay
14373       player->push_delay = 0;
14374
14375     if (player->push_delay < player->push_delay_value &&
14376         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14377         element != EL_SPRING && element != EL_BALLOON)
14378     {
14379       // make sure that there is no move delay before next try to push
14380       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14381         player->move_delay = 0;
14382
14383       return MP_NO_ACTION;
14384     }
14385
14386     if (IS_CUSTOM_ELEMENT(element) &&
14387         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14388     {
14389       if (!DigFieldByCE(nextx, nexty, element))
14390         return MP_NO_ACTION;
14391     }
14392
14393     if (IS_SB_ELEMENT(element))
14394     {
14395       boolean sokoban_task_solved = FALSE;
14396
14397       if (element == EL_SOKOBAN_FIELD_FULL)
14398       {
14399         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14400
14401         IncrementSokobanFieldsNeeded();
14402         IncrementSokobanObjectsNeeded();
14403       }
14404
14405       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14406       {
14407         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14408
14409         DecrementSokobanFieldsNeeded();
14410         DecrementSokobanObjectsNeeded();
14411
14412         // sokoban object was pushed from empty field to sokoban field
14413         if (Back[x][y] == EL_EMPTY)
14414           sokoban_task_solved = TRUE;
14415       }
14416
14417       Tile[x][y] = EL_SOKOBAN_OBJECT;
14418
14419       if (Back[x][y] == Back[nextx][nexty])
14420         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14421       else if (Back[x][y] != 0)
14422         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14423                                     ACTION_EMPTYING);
14424       else
14425         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14426                                     ACTION_FILLING);
14427
14428       if (sokoban_task_solved &&
14429           game.sokoban_fields_still_needed == 0 &&
14430           game.sokoban_objects_still_needed == 0 &&
14431           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14432       {
14433         game.players_still_needed = 0;
14434
14435         LevelSolved();
14436
14437         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14438       }
14439     }
14440     else
14441       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14442
14443     InitMovingField(x, y, move_direction);
14444     GfxAction[x][y] = ACTION_PUSHING;
14445
14446     if (mode == DF_SNAP)
14447       ContinueMoving(x, y);
14448     else
14449       MovPos[x][y] = (dx != 0 ? dx : dy);
14450
14451     Pushed[x][y] = TRUE;
14452     Pushed[nextx][nexty] = TRUE;
14453
14454     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14455       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14456     else
14457       player->push_delay_value = -1;    // get new value later
14458
14459     // check for element change _after_ element has been pushed
14460     if (game.use_change_when_pushing_bug)
14461     {
14462       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14463                                  player->index_bit, dig_side);
14464       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14465                                           player->index_bit, dig_side);
14466     }
14467   }
14468   else if (IS_SWITCHABLE(element))
14469   {
14470     if (PLAYER_SWITCHING(player, x, y))
14471     {
14472       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14473                                           player->index_bit, dig_side);
14474
14475       return MP_ACTION;
14476     }
14477
14478     player->is_switching = TRUE;
14479     player->switch_x = x;
14480     player->switch_y = y;
14481
14482     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14483
14484     if (element == EL_ROBOT_WHEEL)
14485     {
14486       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14487
14488       game.robot_wheel_x = x;
14489       game.robot_wheel_y = y;
14490       game.robot_wheel_active = TRUE;
14491
14492       TEST_DrawLevelField(x, y);
14493     }
14494     else if (element == EL_SP_TERMINAL)
14495     {
14496       int xx, yy;
14497
14498       SCAN_PLAYFIELD(xx, yy)
14499       {
14500         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14501         {
14502           Bang(xx, yy);
14503         }
14504         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14505         {
14506           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14507
14508           ResetGfxAnimation(xx, yy);
14509           TEST_DrawLevelField(xx, yy);
14510         }
14511       }
14512     }
14513     else if (IS_BELT_SWITCH(element))
14514     {
14515       ToggleBeltSwitch(x, y);
14516     }
14517     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14518              element == EL_SWITCHGATE_SWITCH_DOWN ||
14519              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14520              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14521     {
14522       ToggleSwitchgateSwitch(x, y);
14523     }
14524     else if (element == EL_LIGHT_SWITCH ||
14525              element == EL_LIGHT_SWITCH_ACTIVE)
14526     {
14527       ToggleLightSwitch(x, y);
14528     }
14529     else if (element == EL_TIMEGATE_SWITCH ||
14530              element == EL_DC_TIMEGATE_SWITCH)
14531     {
14532       ActivateTimegateSwitch(x, y);
14533     }
14534     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14535              element == EL_BALLOON_SWITCH_RIGHT ||
14536              element == EL_BALLOON_SWITCH_UP    ||
14537              element == EL_BALLOON_SWITCH_DOWN  ||
14538              element == EL_BALLOON_SWITCH_NONE  ||
14539              element == EL_BALLOON_SWITCH_ANY)
14540     {
14541       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14542                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14543                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14544                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14545                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14546                              move_direction);
14547     }
14548     else if (element == EL_LAMP)
14549     {
14550       Tile[x][y] = EL_LAMP_ACTIVE;
14551       game.lights_still_needed--;
14552
14553       ResetGfxAnimation(x, y);
14554       TEST_DrawLevelField(x, y);
14555     }
14556     else if (element == EL_TIME_ORB_FULL)
14557     {
14558       Tile[x][y] = EL_TIME_ORB_EMPTY;
14559
14560       if (level.time > 0 || level.use_time_orb_bug)
14561       {
14562         TimeLeft += level.time_orb_time;
14563         game.no_time_limit = FALSE;
14564
14565         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14566
14567         DisplayGameControlValues();
14568       }
14569
14570       ResetGfxAnimation(x, y);
14571       TEST_DrawLevelField(x, y);
14572     }
14573     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14574              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14575     {
14576       int xx, yy;
14577
14578       game.ball_active = !game.ball_active;
14579
14580       SCAN_PLAYFIELD(xx, yy)
14581       {
14582         int e = Tile[xx][yy];
14583
14584         if (game.ball_active)
14585         {
14586           if (e == EL_EMC_MAGIC_BALL)
14587             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14588           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14589             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14590         }
14591         else
14592         {
14593           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14594             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14595           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14596             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14597         }
14598       }
14599     }
14600
14601     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14602                                         player->index_bit, dig_side);
14603
14604     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14605                                         player->index_bit, dig_side);
14606
14607     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14608                                         player->index_bit, dig_side);
14609
14610     return MP_ACTION;
14611   }
14612   else
14613   {
14614     if (!PLAYER_SWITCHING(player, x, y))
14615     {
14616       player->is_switching = TRUE;
14617       player->switch_x = x;
14618       player->switch_y = y;
14619
14620       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14621                                  player->index_bit, dig_side);
14622       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14623                                           player->index_bit, dig_side);
14624
14625       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14626                                  player->index_bit, dig_side);
14627       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14628                                           player->index_bit, dig_side);
14629     }
14630
14631     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14632                                player->index_bit, dig_side);
14633     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14634                                         player->index_bit, dig_side);
14635
14636     return MP_NO_ACTION;
14637   }
14638
14639   player->push_delay = -1;
14640
14641   if (is_player)                // function can also be called by EL_PENGUIN
14642   {
14643     if (Tile[x][y] != element)          // really digged/collected something
14644     {
14645       player->is_collecting = !player->is_digging;
14646       player->is_active = TRUE;
14647
14648       player->last_removed_element = element;
14649     }
14650   }
14651
14652   return MP_MOVING;
14653 }
14654
14655 static boolean DigFieldByCE(int x, int y, int digging_element)
14656 {
14657   int element = Tile[x][y];
14658
14659   if (!IS_FREE(x, y))
14660   {
14661     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14662                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14663                   ACTION_BREAKING);
14664
14665     // no element can dig solid indestructible elements
14666     if (IS_INDESTRUCTIBLE(element) &&
14667         !IS_DIGGABLE(element) &&
14668         !IS_COLLECTIBLE(element))
14669       return FALSE;
14670
14671     if (AmoebaNr[x][y] &&
14672         (element == EL_AMOEBA_FULL ||
14673          element == EL_BD_AMOEBA ||
14674          element == EL_AMOEBA_GROWING))
14675     {
14676       AmoebaCnt[AmoebaNr[x][y]]--;
14677       AmoebaCnt2[AmoebaNr[x][y]]--;
14678     }
14679
14680     if (IS_MOVING(x, y))
14681       RemoveMovingField(x, y);
14682     else
14683     {
14684       RemoveField(x, y);
14685       TEST_DrawLevelField(x, y);
14686     }
14687
14688     // if digged element was about to explode, prevent the explosion
14689     ExplodeField[x][y] = EX_TYPE_NONE;
14690
14691     PlayLevelSoundAction(x, y, action);
14692   }
14693
14694   Store[x][y] = EL_EMPTY;
14695
14696   // this makes it possible to leave the removed element again
14697   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14698     Store[x][y] = element;
14699
14700   return TRUE;
14701 }
14702
14703 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14704 {
14705   int jx = player->jx, jy = player->jy;
14706   int x = jx + dx, y = jy + dy;
14707   int snap_direction = (dx == -1 ? MV_LEFT  :
14708                         dx == +1 ? MV_RIGHT :
14709                         dy == -1 ? MV_UP    :
14710                         dy == +1 ? MV_DOWN  : MV_NONE);
14711   boolean can_continue_snapping = (level.continuous_snapping &&
14712                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14713
14714   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14715     return FALSE;
14716
14717   if (!player->active || !IN_LEV_FIELD(x, y))
14718     return FALSE;
14719
14720   if (dx && dy)
14721     return FALSE;
14722
14723   if (!dx && !dy)
14724   {
14725     if (player->MovPos == 0)
14726       player->is_pushing = FALSE;
14727
14728     player->is_snapping = FALSE;
14729
14730     if (player->MovPos == 0)
14731     {
14732       player->is_moving = FALSE;
14733       player->is_digging = FALSE;
14734       player->is_collecting = FALSE;
14735     }
14736
14737     return FALSE;
14738   }
14739
14740   // prevent snapping with already pressed snap key when not allowed
14741   if (player->is_snapping && !can_continue_snapping)
14742     return FALSE;
14743
14744   player->MovDir = snap_direction;
14745
14746   if (player->MovPos == 0)
14747   {
14748     player->is_moving = FALSE;
14749     player->is_digging = FALSE;
14750     player->is_collecting = FALSE;
14751   }
14752
14753   player->is_dropping = FALSE;
14754   player->is_dropping_pressed = FALSE;
14755   player->drop_pressed_delay = 0;
14756
14757   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14758     return FALSE;
14759
14760   player->is_snapping = TRUE;
14761   player->is_active = TRUE;
14762
14763   if (player->MovPos == 0)
14764   {
14765     player->is_moving = FALSE;
14766     player->is_digging = FALSE;
14767     player->is_collecting = FALSE;
14768   }
14769
14770   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14771     TEST_DrawLevelField(player->last_jx, player->last_jy);
14772
14773   TEST_DrawLevelField(x, y);
14774
14775   return TRUE;
14776 }
14777
14778 static boolean DropElement(struct PlayerInfo *player)
14779 {
14780   int old_element, new_element;
14781   int dropx = player->jx, dropy = player->jy;
14782   int drop_direction = player->MovDir;
14783   int drop_side = drop_direction;
14784   int drop_element = get_next_dropped_element(player);
14785
14786   /* do not drop an element on top of another element; when holding drop key
14787      pressed without moving, dropped element must move away before the next
14788      element can be dropped (this is especially important if the next element
14789      is dynamite, which can be placed on background for historical reasons) */
14790   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14791     return MP_ACTION;
14792
14793   if (IS_THROWABLE(drop_element))
14794   {
14795     dropx += GET_DX_FROM_DIR(drop_direction);
14796     dropy += GET_DY_FROM_DIR(drop_direction);
14797
14798     if (!IN_LEV_FIELD(dropx, dropy))
14799       return FALSE;
14800   }
14801
14802   old_element = Tile[dropx][dropy];     // old element at dropping position
14803   new_element = drop_element;           // default: no change when dropping
14804
14805   // check if player is active, not moving and ready to drop
14806   if (!player->active || player->MovPos || player->drop_delay > 0)
14807     return FALSE;
14808
14809   // check if player has anything that can be dropped
14810   if (new_element == EL_UNDEFINED)
14811     return FALSE;
14812
14813   // only set if player has anything that can be dropped
14814   player->is_dropping_pressed = TRUE;
14815
14816   // check if drop key was pressed long enough for EM style dynamite
14817   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14818     return FALSE;
14819
14820   // check if anything can be dropped at the current position
14821   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14822     return FALSE;
14823
14824   // collected custom elements can only be dropped on empty fields
14825   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14826     return FALSE;
14827
14828   if (old_element != EL_EMPTY)
14829     Back[dropx][dropy] = old_element;   // store old element on this field
14830
14831   ResetGfxAnimation(dropx, dropy);
14832   ResetRandomAnimationValue(dropx, dropy);
14833
14834   if (player->inventory_size > 0 ||
14835       player->inventory_infinite_element != EL_UNDEFINED)
14836   {
14837     if (player->inventory_size > 0)
14838     {
14839       player->inventory_size--;
14840
14841       DrawGameDoorValues();
14842
14843       if (new_element == EL_DYNAMITE)
14844         new_element = EL_DYNAMITE_ACTIVE;
14845       else if (new_element == EL_EM_DYNAMITE)
14846         new_element = EL_EM_DYNAMITE_ACTIVE;
14847       else if (new_element == EL_SP_DISK_RED)
14848         new_element = EL_SP_DISK_RED_ACTIVE;
14849     }
14850
14851     Tile[dropx][dropy] = new_element;
14852
14853     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14854       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14855                           el2img(Tile[dropx][dropy]), 0);
14856
14857     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14858
14859     // needed if previous element just changed to "empty" in the last frame
14860     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14861
14862     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14863                                player->index_bit, drop_side);
14864     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14865                                         CE_PLAYER_DROPS_X,
14866                                         player->index_bit, drop_side);
14867
14868     TestIfElementTouchesCustomElement(dropx, dropy);
14869   }
14870   else          // player is dropping a dyna bomb
14871   {
14872     player->dynabombs_left--;
14873
14874     Tile[dropx][dropy] = new_element;
14875
14876     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14877       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14878                           el2img(Tile[dropx][dropy]), 0);
14879
14880     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14881   }
14882
14883   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14884     InitField_WithBug1(dropx, dropy, FALSE);
14885
14886   new_element = Tile[dropx][dropy];     // element might have changed
14887
14888   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14889       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14890   {
14891     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14892       MovDir[dropx][dropy] = drop_direction;
14893
14894     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14895
14896     // do not cause impact style collision by dropping elements that can fall
14897     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14898   }
14899
14900   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14901   player->is_dropping = TRUE;
14902
14903   player->drop_pressed_delay = 0;
14904   player->is_dropping_pressed = FALSE;
14905
14906   player->drop_x = dropx;
14907   player->drop_y = dropy;
14908
14909   return TRUE;
14910 }
14911
14912 // ----------------------------------------------------------------------------
14913 // game sound playing functions
14914 // ----------------------------------------------------------------------------
14915
14916 static int *loop_sound_frame = NULL;
14917 static int *loop_sound_volume = NULL;
14918
14919 void InitPlayLevelSound(void)
14920 {
14921   int num_sounds = getSoundListSize();
14922
14923   checked_free(loop_sound_frame);
14924   checked_free(loop_sound_volume);
14925
14926   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14927   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14928 }
14929
14930 static void PlayLevelSound(int x, int y, int nr)
14931 {
14932   int sx = SCREENX(x), sy = SCREENY(y);
14933   int volume, stereo_position;
14934   int max_distance = 8;
14935   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14936
14937   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14938       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14939     return;
14940
14941   if (!IN_LEV_FIELD(x, y) ||
14942       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14943       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14944     return;
14945
14946   volume = SOUND_MAX_VOLUME;
14947
14948   if (!IN_SCR_FIELD(sx, sy))
14949   {
14950     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14951     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14952
14953     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14954   }
14955
14956   stereo_position = (SOUND_MAX_LEFT +
14957                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14958                      (SCR_FIELDX + 2 * max_distance));
14959
14960   if (IS_LOOP_SOUND(nr))
14961   {
14962     /* This assures that quieter loop sounds do not overwrite louder ones,
14963        while restarting sound volume comparison with each new game frame. */
14964
14965     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14966       return;
14967
14968     loop_sound_volume[nr] = volume;
14969     loop_sound_frame[nr] = FrameCounter;
14970   }
14971
14972   PlaySoundExt(nr, volume, stereo_position, type);
14973 }
14974
14975 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14976 {
14977   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14978                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14979                  y < LEVELY(BY1) ? LEVELY(BY1) :
14980                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14981                  sound_action);
14982 }
14983
14984 static void PlayLevelSoundAction(int x, int y, int action)
14985 {
14986   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
14987 }
14988
14989 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14990 {
14991   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14992
14993   if (sound_effect != SND_UNDEFINED)
14994     PlayLevelSound(x, y, sound_effect);
14995 }
14996
14997 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14998                                               int action)
14999 {
15000   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15001
15002   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15003     PlayLevelSound(x, y, sound_effect);
15004 }
15005
15006 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15007 {
15008   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15009
15010   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15011     PlayLevelSound(x, y, sound_effect);
15012 }
15013
15014 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15015 {
15016   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15017
15018   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15019     StopSound(sound_effect);
15020 }
15021
15022 static int getLevelMusicNr(void)
15023 {
15024   if (levelset.music[level_nr] != MUS_UNDEFINED)
15025     return levelset.music[level_nr];            // from config file
15026   else
15027     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15028 }
15029
15030 static void FadeLevelSounds(void)
15031 {
15032   FadeSounds();
15033 }
15034
15035 static void FadeLevelMusic(void)
15036 {
15037   int music_nr = getLevelMusicNr();
15038   char *curr_music = getCurrentlyPlayingMusicFilename();
15039   char *next_music = getMusicInfoEntryFilename(music_nr);
15040
15041   if (!strEqual(curr_music, next_music))
15042     FadeMusic();
15043 }
15044
15045 void FadeLevelSoundsAndMusic(void)
15046 {
15047   FadeLevelSounds();
15048   FadeLevelMusic();
15049 }
15050
15051 static void PlayLevelMusic(void)
15052 {
15053   int music_nr = getLevelMusicNr();
15054   char *curr_music = getCurrentlyPlayingMusicFilename();
15055   char *next_music = getMusicInfoEntryFilename(music_nr);
15056
15057   if (!strEqual(curr_music, next_music))
15058     PlayMusicLoop(music_nr);
15059 }
15060
15061 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15062 {
15063   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15064   int offset = 0;
15065   int x = xx - offset;
15066   int y = yy - offset;
15067
15068   switch (sample)
15069   {
15070     case SOUND_blank:
15071       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15072       break;
15073
15074     case SOUND_roll:
15075       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15076       break;
15077
15078     case SOUND_stone:
15079       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15080       break;
15081
15082     case SOUND_nut:
15083       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15084       break;
15085
15086     case SOUND_crack:
15087       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15088       break;
15089
15090     case SOUND_bug:
15091       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15092       break;
15093
15094     case SOUND_tank:
15095       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15096       break;
15097
15098     case SOUND_android_clone:
15099       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15100       break;
15101
15102     case SOUND_android_move:
15103       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15104       break;
15105
15106     case SOUND_spring:
15107       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15108       break;
15109
15110     case SOUND_slurp:
15111       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15112       break;
15113
15114     case SOUND_eater:
15115       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15116       break;
15117
15118     case SOUND_eater_eat:
15119       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15120       break;
15121
15122     case SOUND_alien:
15123       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15124       break;
15125
15126     case SOUND_collect:
15127       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15128       break;
15129
15130     case SOUND_diamond:
15131       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15132       break;
15133
15134     case SOUND_squash:
15135       // !!! CHECK THIS !!!
15136 #if 1
15137       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15138 #else
15139       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15140 #endif
15141       break;
15142
15143     case SOUND_wonderfall:
15144       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15145       break;
15146
15147     case SOUND_drip:
15148       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15149       break;
15150
15151     case SOUND_push:
15152       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15153       break;
15154
15155     case SOUND_dirt:
15156       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15157       break;
15158
15159     case SOUND_acid:
15160       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15161       break;
15162
15163     case SOUND_ball:
15164       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15165       break;
15166
15167     case SOUND_slide:
15168       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15169       break;
15170
15171     case SOUND_wonder:
15172       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15173       break;
15174
15175     case SOUND_door:
15176       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15177       break;
15178
15179     case SOUND_exit_open:
15180       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15181       break;
15182
15183     case SOUND_exit_leave:
15184       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15185       break;
15186
15187     case SOUND_dynamite:
15188       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15189       break;
15190
15191     case SOUND_tick:
15192       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15193       break;
15194
15195     case SOUND_press:
15196       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15197       break;
15198
15199     case SOUND_wheel:
15200       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15201       break;
15202
15203     case SOUND_boom:
15204       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15205       break;
15206
15207     case SOUND_die:
15208       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15209       break;
15210
15211     case SOUND_time:
15212       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15213       break;
15214
15215     default:
15216       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15217       break;
15218   }
15219 }
15220
15221 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15222 {
15223   int element = map_element_SP_to_RND(element_sp);
15224   int action = map_action_SP_to_RND(action_sp);
15225   int offset = (setup.sp_show_border_elements ? 0 : 1);
15226   int x = xx - offset;
15227   int y = yy - offset;
15228
15229   PlayLevelSoundElementAction(x, y, element, action);
15230 }
15231
15232 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15233 {
15234   int element = map_element_MM_to_RND(element_mm);
15235   int action = map_action_MM_to_RND(action_mm);
15236   int offset = 0;
15237   int x = xx - offset;
15238   int y = yy - offset;
15239
15240   if (!IS_MM_ELEMENT(element))
15241     element = EL_MM_DEFAULT;
15242
15243   PlayLevelSoundElementAction(x, y, element, action);
15244 }
15245
15246 void PlaySound_MM(int sound_mm)
15247 {
15248   int sound = map_sound_MM_to_RND(sound_mm);
15249
15250   if (sound == SND_UNDEFINED)
15251     return;
15252
15253   PlaySound(sound);
15254 }
15255
15256 void PlaySoundLoop_MM(int sound_mm)
15257 {
15258   int sound = map_sound_MM_to_RND(sound_mm);
15259
15260   if (sound == SND_UNDEFINED)
15261     return;
15262
15263   PlaySoundLoop(sound);
15264 }
15265
15266 void StopSound_MM(int sound_mm)
15267 {
15268   int sound = map_sound_MM_to_RND(sound_mm);
15269
15270   if (sound == SND_UNDEFINED)
15271     return;
15272
15273   StopSound(sound);
15274 }
15275
15276 void RaiseScore(int value)
15277 {
15278   game.score += value;
15279
15280   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15281
15282   DisplayGameControlValues();
15283 }
15284
15285 void RaiseScoreElement(int element)
15286 {
15287   switch (element)
15288   {
15289     case EL_EMERALD:
15290     case EL_BD_DIAMOND:
15291     case EL_EMERALD_YELLOW:
15292     case EL_EMERALD_RED:
15293     case EL_EMERALD_PURPLE:
15294     case EL_SP_INFOTRON:
15295       RaiseScore(level.score[SC_EMERALD]);
15296       break;
15297     case EL_DIAMOND:
15298       RaiseScore(level.score[SC_DIAMOND]);
15299       break;
15300     case EL_CRYSTAL:
15301       RaiseScore(level.score[SC_CRYSTAL]);
15302       break;
15303     case EL_PEARL:
15304       RaiseScore(level.score[SC_PEARL]);
15305       break;
15306     case EL_BUG:
15307     case EL_BD_BUTTERFLY:
15308     case EL_SP_ELECTRON:
15309       RaiseScore(level.score[SC_BUG]);
15310       break;
15311     case EL_SPACESHIP:
15312     case EL_BD_FIREFLY:
15313     case EL_SP_SNIKSNAK:
15314       RaiseScore(level.score[SC_SPACESHIP]);
15315       break;
15316     case EL_YAMYAM:
15317     case EL_DARK_YAMYAM:
15318       RaiseScore(level.score[SC_YAMYAM]);
15319       break;
15320     case EL_ROBOT:
15321       RaiseScore(level.score[SC_ROBOT]);
15322       break;
15323     case EL_PACMAN:
15324       RaiseScore(level.score[SC_PACMAN]);
15325       break;
15326     case EL_NUT:
15327       RaiseScore(level.score[SC_NUT]);
15328       break;
15329     case EL_DYNAMITE:
15330     case EL_EM_DYNAMITE:
15331     case EL_SP_DISK_RED:
15332     case EL_DYNABOMB_INCREASE_NUMBER:
15333     case EL_DYNABOMB_INCREASE_SIZE:
15334     case EL_DYNABOMB_INCREASE_POWER:
15335       RaiseScore(level.score[SC_DYNAMITE]);
15336       break;
15337     case EL_SHIELD_NORMAL:
15338     case EL_SHIELD_DEADLY:
15339       RaiseScore(level.score[SC_SHIELD]);
15340       break;
15341     case EL_EXTRA_TIME:
15342       RaiseScore(level.extra_time_score);
15343       break;
15344     case EL_KEY_1:
15345     case EL_KEY_2:
15346     case EL_KEY_3:
15347     case EL_KEY_4:
15348     case EL_EM_KEY_1:
15349     case EL_EM_KEY_2:
15350     case EL_EM_KEY_3:
15351     case EL_EM_KEY_4:
15352     case EL_EMC_KEY_5:
15353     case EL_EMC_KEY_6:
15354     case EL_EMC_KEY_7:
15355     case EL_EMC_KEY_8:
15356     case EL_DC_KEY_WHITE:
15357       RaiseScore(level.score[SC_KEY]);
15358       break;
15359     default:
15360       RaiseScore(element_info[element].collect_score);
15361       break;
15362   }
15363 }
15364
15365 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15366 {
15367   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15368   {
15369     // closing door required in case of envelope style request dialogs
15370     if (!skip_request)
15371     {
15372       // prevent short reactivation of overlay buttons while closing door
15373       SetOverlayActive(FALSE);
15374
15375       CloseDoor(DOOR_CLOSE_1);
15376     }
15377
15378     if (network.enabled)
15379       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15380     else
15381     {
15382       if (quick_quit)
15383         FadeSkipNextFadeIn();
15384
15385       SetGameStatus(GAME_MODE_MAIN);
15386
15387       DrawMainMenu();
15388     }
15389   }
15390   else          // continue playing the game
15391   {
15392     if (tape.playing && tape.deactivate_display)
15393       TapeDeactivateDisplayOff(TRUE);
15394
15395     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15396
15397     if (tape.playing && tape.deactivate_display)
15398       TapeDeactivateDisplayOn();
15399   }
15400 }
15401
15402 void RequestQuitGame(boolean ask_if_really_quit)
15403 {
15404   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
15405   boolean skip_request = game.all_players_gone || quick_quit;
15406
15407   RequestQuitGameExt(skip_request, quick_quit,
15408                      "Do you really want to quit the game?");
15409 }
15410
15411 void RequestRestartGame(char *message)
15412 {
15413   game.restart_game_message = NULL;
15414
15415   boolean has_started_game = hasStartedNetworkGame();
15416   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15417
15418   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15419   {
15420     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15421   }
15422   else
15423   {
15424     // needed in case of envelope request to close game panel
15425     CloseDoor(DOOR_CLOSE_1);
15426
15427     SetGameStatus(GAME_MODE_MAIN);
15428
15429     DrawMainMenu();
15430   }
15431 }
15432
15433 void CheckGameOver(void)
15434 {
15435   static boolean last_game_over = FALSE;
15436   static int game_over_delay = 0;
15437   int game_over_delay_value = 50;
15438   boolean game_over = checkGameFailed();
15439
15440   // do not handle game over if request dialog is already active
15441   if (game.request_active)
15442     return;
15443
15444   // do not ask to play again if game was never actually played
15445   if (!game.GamePlayed)
15446     return;
15447
15448   if (!game_over)
15449   {
15450     last_game_over = FALSE;
15451     game_over_delay = game_over_delay_value;
15452
15453     return;
15454   }
15455
15456   if (game_over_delay > 0)
15457   {
15458     game_over_delay--;
15459
15460     return;
15461   }
15462
15463   if (last_game_over != game_over)
15464     game.restart_game_message = (hasStartedNetworkGame() ?
15465                                  "Game over! Play it again?" :
15466                                  "Game over!");
15467
15468   last_game_over = game_over;
15469 }
15470
15471 boolean checkGameSolved(void)
15472 {
15473   // set for all game engines if level was solved
15474   return game.LevelSolved_GameEnd;
15475 }
15476
15477 boolean checkGameFailed(void)
15478 {
15479   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15480     return (game_em.game_over && !game_em.level_solved);
15481   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15482     return (game_sp.game_over && !game_sp.level_solved);
15483   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15484     return (game_mm.game_over && !game_mm.level_solved);
15485   else                          // GAME_ENGINE_TYPE_RND
15486     return (game.GameOver && !game.LevelSolved);
15487 }
15488
15489 boolean checkGameEnded(void)
15490 {
15491   return (checkGameSolved() || checkGameFailed());
15492 }
15493
15494
15495 // ----------------------------------------------------------------------------
15496 // random generator functions
15497 // ----------------------------------------------------------------------------
15498
15499 unsigned int InitEngineRandom_RND(int seed)
15500 {
15501   game.num_random_calls = 0;
15502
15503   return InitEngineRandom(seed);
15504 }
15505
15506 unsigned int RND(int max)
15507 {
15508   if (max > 0)
15509   {
15510     game.num_random_calls++;
15511
15512     return GetEngineRandom(max);
15513   }
15514
15515   return 0;
15516 }
15517
15518
15519 // ----------------------------------------------------------------------------
15520 // game engine snapshot handling functions
15521 // ----------------------------------------------------------------------------
15522
15523 struct EngineSnapshotInfo
15524 {
15525   // runtime values for custom element collect score
15526   int collect_score[NUM_CUSTOM_ELEMENTS];
15527
15528   // runtime values for group element choice position
15529   int choice_pos[NUM_GROUP_ELEMENTS];
15530
15531   // runtime values for belt position animations
15532   int belt_graphic[4][NUM_BELT_PARTS];
15533   int belt_anim_mode[4][NUM_BELT_PARTS];
15534 };
15535
15536 static struct EngineSnapshotInfo engine_snapshot_rnd;
15537 static char *snapshot_level_identifier = NULL;
15538 static int snapshot_level_nr = -1;
15539
15540 static void SaveEngineSnapshotValues_RND(void)
15541 {
15542   static int belt_base_active_element[4] =
15543   {
15544     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15545     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15546     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15547     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15548   };
15549   int i, j;
15550
15551   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15552   {
15553     int element = EL_CUSTOM_START + i;
15554
15555     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15556   }
15557
15558   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15559   {
15560     int element = EL_GROUP_START + i;
15561
15562     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15563   }
15564
15565   for (i = 0; i < 4; i++)
15566   {
15567     for (j = 0; j < NUM_BELT_PARTS; j++)
15568     {
15569       int element = belt_base_active_element[i] + j;
15570       int graphic = el2img(element);
15571       int anim_mode = graphic_info[graphic].anim_mode;
15572
15573       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15574       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15575     }
15576   }
15577 }
15578
15579 static void LoadEngineSnapshotValues_RND(void)
15580 {
15581   unsigned int num_random_calls = game.num_random_calls;
15582   int i, j;
15583
15584   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15585   {
15586     int element = EL_CUSTOM_START + i;
15587
15588     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15589   }
15590
15591   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15592   {
15593     int element = EL_GROUP_START + i;
15594
15595     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15596   }
15597
15598   for (i = 0; i < 4; i++)
15599   {
15600     for (j = 0; j < NUM_BELT_PARTS; j++)
15601     {
15602       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15603       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15604
15605       graphic_info[graphic].anim_mode = anim_mode;
15606     }
15607   }
15608
15609   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15610   {
15611     InitRND(tape.random_seed);
15612     for (i = 0; i < num_random_calls; i++)
15613       RND(1);
15614   }
15615
15616   if (game.num_random_calls != num_random_calls)
15617   {
15618     Error("number of random calls out of sync");
15619     Error("number of random calls should be %d", num_random_calls);
15620     Error("number of random calls is %d", game.num_random_calls);
15621
15622     Fail("this should not happen -- please debug");
15623   }
15624 }
15625
15626 void FreeEngineSnapshotSingle(void)
15627 {
15628   FreeSnapshotSingle();
15629
15630   setString(&snapshot_level_identifier, NULL);
15631   snapshot_level_nr = -1;
15632 }
15633
15634 void FreeEngineSnapshotList(void)
15635 {
15636   FreeSnapshotList();
15637 }
15638
15639 static ListNode *SaveEngineSnapshotBuffers(void)
15640 {
15641   ListNode *buffers = NULL;
15642
15643   // copy some special values to a structure better suited for the snapshot
15644
15645   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15646     SaveEngineSnapshotValues_RND();
15647   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15648     SaveEngineSnapshotValues_EM();
15649   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15650     SaveEngineSnapshotValues_SP(&buffers);
15651   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15652     SaveEngineSnapshotValues_MM(&buffers);
15653
15654   // save values stored in special snapshot structure
15655
15656   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15657     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15658   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15659     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15660   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15661     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15662   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15663     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15664
15665   // save further RND engine values
15666
15667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15670
15671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15676
15677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15680
15681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15682
15683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15685
15686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15704
15705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15707
15708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15711
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15714
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15720
15721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15723
15724 #if 0
15725   ListNode *node = engine_snapshot_list_rnd;
15726   int num_bytes = 0;
15727
15728   while (node != NULL)
15729   {
15730     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15731
15732     node = node->next;
15733   }
15734
15735   Debug("game:playing:SaveEngineSnapshotBuffers",
15736         "size of engine snapshot: %d bytes", num_bytes);
15737 #endif
15738
15739   return buffers;
15740 }
15741
15742 void SaveEngineSnapshotSingle(void)
15743 {
15744   ListNode *buffers = SaveEngineSnapshotBuffers();
15745
15746   // finally save all snapshot buffers to single snapshot
15747   SaveSnapshotSingle(buffers);
15748
15749   // save level identification information
15750   setString(&snapshot_level_identifier, leveldir_current->identifier);
15751   snapshot_level_nr = level_nr;
15752 }
15753
15754 boolean CheckSaveEngineSnapshotToList(void)
15755 {
15756   boolean save_snapshot =
15757     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15758      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15759       game.snapshot.changed_action) ||
15760      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15761       game.snapshot.collected_item));
15762
15763   game.snapshot.changed_action = FALSE;
15764   game.snapshot.collected_item = FALSE;
15765   game.snapshot.save_snapshot = save_snapshot;
15766
15767   return save_snapshot;
15768 }
15769
15770 void SaveEngineSnapshotToList(void)
15771 {
15772   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15773       tape.quick_resume)
15774     return;
15775
15776   ListNode *buffers = SaveEngineSnapshotBuffers();
15777
15778   // finally save all snapshot buffers to snapshot list
15779   SaveSnapshotToList(buffers);
15780 }
15781
15782 void SaveEngineSnapshotToListInitial(void)
15783 {
15784   FreeEngineSnapshotList();
15785
15786   SaveEngineSnapshotToList();
15787 }
15788
15789 static void LoadEngineSnapshotValues(void)
15790 {
15791   // restore special values from snapshot structure
15792
15793   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15794     LoadEngineSnapshotValues_RND();
15795   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15796     LoadEngineSnapshotValues_EM();
15797   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15798     LoadEngineSnapshotValues_SP();
15799   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15800     LoadEngineSnapshotValues_MM();
15801 }
15802
15803 void LoadEngineSnapshotSingle(void)
15804 {
15805   LoadSnapshotSingle();
15806
15807   LoadEngineSnapshotValues();
15808 }
15809
15810 static void LoadEngineSnapshot_Undo(int steps)
15811 {
15812   LoadSnapshotFromList_Older(steps);
15813
15814   LoadEngineSnapshotValues();
15815 }
15816
15817 static void LoadEngineSnapshot_Redo(int steps)
15818 {
15819   LoadSnapshotFromList_Newer(steps);
15820
15821   LoadEngineSnapshotValues();
15822 }
15823
15824 boolean CheckEngineSnapshotSingle(void)
15825 {
15826   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15827           snapshot_level_nr == level_nr);
15828 }
15829
15830 boolean CheckEngineSnapshotList(void)
15831 {
15832   return CheckSnapshotList();
15833 }
15834
15835
15836 // ---------- new game button stuff -------------------------------------------
15837
15838 static struct
15839 {
15840   int graphic;
15841   struct XY *pos;
15842   int gadget_id;
15843   boolean *setup_value;
15844   boolean allowed_on_tape;
15845   boolean is_touch_button;
15846   char *infotext;
15847 } gamebutton_info[NUM_GAME_BUTTONS] =
15848 {
15849   {
15850     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15851     GAME_CTRL_ID_STOP,                          NULL,
15852     TRUE, FALSE,                                "stop game"
15853   },
15854   {
15855     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15856     GAME_CTRL_ID_PAUSE,                         NULL,
15857     TRUE, FALSE,                                "pause game"
15858   },
15859   {
15860     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15861     GAME_CTRL_ID_PLAY,                          NULL,
15862     TRUE, FALSE,                                "play game"
15863   },
15864   {
15865     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15866     GAME_CTRL_ID_UNDO,                          NULL,
15867     TRUE, FALSE,                                "undo step"
15868   },
15869   {
15870     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15871     GAME_CTRL_ID_REDO,                          NULL,
15872     TRUE, FALSE,                                "redo step"
15873   },
15874   {
15875     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15876     GAME_CTRL_ID_SAVE,                          NULL,
15877     TRUE, FALSE,                                "save game"
15878   },
15879   {
15880     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15881     GAME_CTRL_ID_PAUSE2,                        NULL,
15882     TRUE, FALSE,                                "pause game"
15883   },
15884   {
15885     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15886     GAME_CTRL_ID_LOAD,                          NULL,
15887     TRUE, FALSE,                                "load game"
15888   },
15889   {
15890     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15891     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15892     FALSE, FALSE,                               "stop game"
15893   },
15894   {
15895     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15896     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15897     FALSE, FALSE,                               "pause game"
15898   },
15899   {
15900     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15901     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15902     FALSE, FALSE,                               "play game"
15903   },
15904   {
15905     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15906     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15907     FALSE, TRUE,                                "stop game"
15908   },
15909   {
15910     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15911     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15912     FALSE, TRUE,                                "pause game"
15913   },
15914   {
15915     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15916     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15917     TRUE, FALSE,                                "background music on/off"
15918   },
15919   {
15920     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15921     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15922     TRUE, FALSE,                                "sound loops on/off"
15923   },
15924   {
15925     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15926     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15927     TRUE, FALSE,                                "normal sounds on/off"
15928   },
15929   {
15930     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15931     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15932     FALSE, FALSE,                               "background music on/off"
15933   },
15934   {
15935     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15936     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15937     FALSE, FALSE,                               "sound loops on/off"
15938   },
15939   {
15940     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15941     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15942     FALSE, FALSE,                               "normal sounds on/off"
15943   }
15944 };
15945
15946 void CreateGameButtons(void)
15947 {
15948   int i;
15949
15950   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15951   {
15952     int graphic = gamebutton_info[i].graphic;
15953     struct GraphicInfo *gfx = &graphic_info[graphic];
15954     struct XY *pos = gamebutton_info[i].pos;
15955     struct GadgetInfo *gi;
15956     int button_type;
15957     boolean checked;
15958     unsigned int event_mask;
15959     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15960     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15961     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15962     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
15963     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
15964     int gd_x   = gfx->src_x;
15965     int gd_y   = gfx->src_y;
15966     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
15967     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
15968     int gd_xa  = gfx->src_x + gfx->active_xoffset;
15969     int gd_ya  = gfx->src_y + gfx->active_yoffset;
15970     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
15971     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
15972     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
15973     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
15974     int id = i;
15975
15976     if (gfx->bitmap == NULL)
15977     {
15978       game_gadget[id] = NULL;
15979
15980       continue;
15981     }
15982
15983     if (id == GAME_CTRL_ID_STOP ||
15984         id == GAME_CTRL_ID_PANEL_STOP ||
15985         id == GAME_CTRL_ID_TOUCH_STOP ||
15986         id == GAME_CTRL_ID_PLAY ||
15987         id == GAME_CTRL_ID_PANEL_PLAY ||
15988         id == GAME_CTRL_ID_SAVE ||
15989         id == GAME_CTRL_ID_LOAD)
15990     {
15991       button_type = GD_TYPE_NORMAL_BUTTON;
15992       checked = FALSE;
15993       event_mask = GD_EVENT_RELEASED;
15994     }
15995     else if (id == GAME_CTRL_ID_UNDO ||
15996              id == GAME_CTRL_ID_REDO)
15997     {
15998       button_type = GD_TYPE_NORMAL_BUTTON;
15999       checked = FALSE;
16000       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16001     }
16002     else
16003     {
16004       button_type = GD_TYPE_CHECK_BUTTON;
16005       checked = (gamebutton_info[i].setup_value != NULL ?
16006                  *gamebutton_info[i].setup_value : FALSE);
16007       event_mask = GD_EVENT_PRESSED;
16008     }
16009
16010     gi = CreateGadget(GDI_CUSTOM_ID, id,
16011                       GDI_IMAGE_ID, graphic,
16012                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16013                       GDI_X, base_x + x,
16014                       GDI_Y, base_y + y,
16015                       GDI_WIDTH, gfx->width,
16016                       GDI_HEIGHT, gfx->height,
16017                       GDI_TYPE, button_type,
16018                       GDI_STATE, GD_BUTTON_UNPRESSED,
16019                       GDI_CHECKED, checked,
16020                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16021                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16022                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16023                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16024                       GDI_DIRECT_DRAW, FALSE,
16025                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16026                       GDI_EVENT_MASK, event_mask,
16027                       GDI_CALLBACK_ACTION, HandleGameButtons,
16028                       GDI_END);
16029
16030     if (gi == NULL)
16031       Fail("cannot create gadget");
16032
16033     game_gadget[id] = gi;
16034   }
16035 }
16036
16037 void FreeGameButtons(void)
16038 {
16039   int i;
16040
16041   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16042     FreeGadget(game_gadget[i]);
16043 }
16044
16045 static void UnmapGameButtonsAtSamePosition(int id)
16046 {
16047   int i;
16048
16049   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16050     if (i != id &&
16051         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16052         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16053       UnmapGadget(game_gadget[i]);
16054 }
16055
16056 static void UnmapGameButtonsAtSamePosition_All(void)
16057 {
16058   if (setup.show_snapshot_buttons)
16059   {
16060     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16061     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16062     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16063   }
16064   else
16065   {
16066     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16067     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16068     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16069
16070     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16071     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16072     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16073   }
16074 }
16075
16076 static void MapGameButtonsAtSamePosition(int id)
16077 {
16078   int i;
16079
16080   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16081     if (i != id &&
16082         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16083         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16084       MapGadget(game_gadget[i]);
16085
16086   UnmapGameButtonsAtSamePosition_All();
16087 }
16088
16089 void MapUndoRedoButtons(void)
16090 {
16091   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16092   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16093
16094   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16095   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16096 }
16097
16098 void UnmapUndoRedoButtons(void)
16099 {
16100   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16101   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16102
16103   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16104   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16105 }
16106
16107 void ModifyPauseButtons(void)
16108 {
16109   static int ids[] =
16110   {
16111     GAME_CTRL_ID_PAUSE,
16112     GAME_CTRL_ID_PAUSE2,
16113     GAME_CTRL_ID_PANEL_PAUSE,
16114     GAME_CTRL_ID_TOUCH_PAUSE,
16115     -1
16116   };
16117   int i;
16118
16119   for (i = 0; ids[i] > -1; i++)
16120     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16121 }
16122
16123 static void MapGameButtonsExt(boolean on_tape)
16124 {
16125   int i;
16126
16127   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16128     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16129         i != GAME_CTRL_ID_UNDO &&
16130         i != GAME_CTRL_ID_REDO)
16131       MapGadget(game_gadget[i]);
16132
16133   UnmapGameButtonsAtSamePosition_All();
16134
16135   RedrawGameButtons();
16136 }
16137
16138 static void UnmapGameButtonsExt(boolean on_tape)
16139 {
16140   int i;
16141
16142   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16143     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16144       UnmapGadget(game_gadget[i]);
16145 }
16146
16147 static void RedrawGameButtonsExt(boolean on_tape)
16148 {
16149   int i;
16150
16151   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16152     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16153       RedrawGadget(game_gadget[i]);
16154 }
16155
16156 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16157 {
16158   if (gi == NULL)
16159     return;
16160
16161   gi->checked = state;
16162 }
16163
16164 static void RedrawSoundButtonGadget(int id)
16165 {
16166   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16167              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16168              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16169              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16170              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16171              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16172              id);
16173
16174   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16175   RedrawGadget(game_gadget[id2]);
16176 }
16177
16178 void MapGameButtons(void)
16179 {
16180   MapGameButtonsExt(FALSE);
16181 }
16182
16183 void UnmapGameButtons(void)
16184 {
16185   UnmapGameButtonsExt(FALSE);
16186 }
16187
16188 void RedrawGameButtons(void)
16189 {
16190   RedrawGameButtonsExt(FALSE);
16191 }
16192
16193 void MapGameButtonsOnTape(void)
16194 {
16195   MapGameButtonsExt(TRUE);
16196 }
16197
16198 void UnmapGameButtonsOnTape(void)
16199 {
16200   UnmapGameButtonsExt(TRUE);
16201 }
16202
16203 void RedrawGameButtonsOnTape(void)
16204 {
16205   RedrawGameButtonsExt(TRUE);
16206 }
16207
16208 static void GameUndoRedoExt(void)
16209 {
16210   ClearPlayerAction();
16211
16212   tape.pausing = TRUE;
16213
16214   RedrawPlayfield();
16215   UpdateAndDisplayGameControlValues();
16216
16217   DrawCompleteVideoDisplay();
16218   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16219   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16220   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16221
16222   BackToFront();
16223 }
16224
16225 static void GameUndo(int steps)
16226 {
16227   if (!CheckEngineSnapshotList())
16228     return;
16229
16230   LoadEngineSnapshot_Undo(steps);
16231
16232   GameUndoRedoExt();
16233 }
16234
16235 static void GameRedo(int steps)
16236 {
16237   if (!CheckEngineSnapshotList())
16238     return;
16239
16240   LoadEngineSnapshot_Redo(steps);
16241
16242   GameUndoRedoExt();
16243 }
16244
16245 static void HandleGameButtonsExt(int id, int button)
16246 {
16247   static boolean game_undo_executed = FALSE;
16248   int steps = BUTTON_STEPSIZE(button);
16249   boolean handle_game_buttons =
16250     (game_status == GAME_MODE_PLAYING ||
16251      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16252
16253   if (!handle_game_buttons)
16254     return;
16255
16256   switch (id)
16257   {
16258     case GAME_CTRL_ID_STOP:
16259     case GAME_CTRL_ID_PANEL_STOP:
16260     case GAME_CTRL_ID_TOUCH_STOP:
16261       if (game_status == GAME_MODE_MAIN)
16262         break;
16263
16264       if (tape.playing)
16265         TapeStop();
16266       else
16267         RequestQuitGame(TRUE);
16268
16269       break;
16270
16271     case GAME_CTRL_ID_PAUSE:
16272     case GAME_CTRL_ID_PAUSE2:
16273     case GAME_CTRL_ID_PANEL_PAUSE:
16274     case GAME_CTRL_ID_TOUCH_PAUSE:
16275       if (network.enabled && game_status == GAME_MODE_PLAYING)
16276       {
16277         if (tape.pausing)
16278           SendToServer_ContinuePlaying();
16279         else
16280           SendToServer_PausePlaying();
16281       }
16282       else
16283         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16284
16285       game_undo_executed = FALSE;
16286
16287       break;
16288
16289     case GAME_CTRL_ID_PLAY:
16290     case GAME_CTRL_ID_PANEL_PLAY:
16291       if (game_status == GAME_MODE_MAIN)
16292       {
16293         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16294       }
16295       else if (tape.pausing)
16296       {
16297         if (network.enabled)
16298           SendToServer_ContinuePlaying();
16299         else
16300           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16301       }
16302       break;
16303
16304     case GAME_CTRL_ID_UNDO:
16305       // Important: When using "save snapshot when collecting an item" mode,
16306       // load last (current) snapshot for first "undo" after pressing "pause"
16307       // (else the last-but-one snapshot would be loaded, because the snapshot
16308       // pointer already points to the last snapshot when pressing "pause",
16309       // which is fine for "every step/move" mode, but not for "every collect")
16310       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16311           !game_undo_executed)
16312         steps--;
16313
16314       game_undo_executed = TRUE;
16315
16316       GameUndo(steps);
16317       break;
16318
16319     case GAME_CTRL_ID_REDO:
16320       GameRedo(steps);
16321       break;
16322
16323     case GAME_CTRL_ID_SAVE:
16324       TapeQuickSave();
16325       break;
16326
16327     case GAME_CTRL_ID_LOAD:
16328       TapeQuickLoad();
16329       break;
16330
16331     case SOUND_CTRL_ID_MUSIC:
16332     case SOUND_CTRL_ID_PANEL_MUSIC:
16333       if (setup.sound_music)
16334       { 
16335         setup.sound_music = FALSE;
16336
16337         FadeMusic();
16338       }
16339       else if (audio.music_available)
16340       { 
16341         setup.sound = setup.sound_music = TRUE;
16342
16343         SetAudioMode(setup.sound);
16344
16345         if (game_status == GAME_MODE_PLAYING)
16346           PlayLevelMusic();
16347       }
16348
16349       RedrawSoundButtonGadget(id);
16350
16351       break;
16352
16353     case SOUND_CTRL_ID_LOOPS:
16354     case SOUND_CTRL_ID_PANEL_LOOPS:
16355       if (setup.sound_loops)
16356         setup.sound_loops = FALSE;
16357       else if (audio.loops_available)
16358       {
16359         setup.sound = setup.sound_loops = TRUE;
16360
16361         SetAudioMode(setup.sound);
16362       }
16363
16364       RedrawSoundButtonGadget(id);
16365
16366       break;
16367
16368     case SOUND_CTRL_ID_SIMPLE:
16369     case SOUND_CTRL_ID_PANEL_SIMPLE:
16370       if (setup.sound_simple)
16371         setup.sound_simple = FALSE;
16372       else if (audio.sound_available)
16373       {
16374         setup.sound = setup.sound_simple = TRUE;
16375
16376         SetAudioMode(setup.sound);
16377       }
16378
16379       RedrawSoundButtonGadget(id);
16380
16381       break;
16382
16383     default:
16384       break;
16385   }
16386 }
16387
16388 static void HandleGameButtons(struct GadgetInfo *gi)
16389 {
16390   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16391 }
16392
16393 void HandleSoundButtonKeys(Key key)
16394 {
16395   if (key == setup.shortcut.sound_simple)
16396     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16397   else if (key == setup.shortcut.sound_loops)
16398     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16399   else if (key == setup.shortcut.sound_music)
16400     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16401 }