changed calculating final game values when game is really over
[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 #define CheckTriggeredElementChangeByMouse(x, y, e, ev, s)              \
1079         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1080
1081 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1082 #define CheckElementChange(x, y, e, te, ev)                             \
1083         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1084 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1085         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1086 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1087         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1088 #define CheckElementChangeByMouse(x, y, e, ev, s)                       \
1089         CheckElementChangeExt(x, y, e, EL_UNDEFINED, ev, CH_PLAYER_ANY, s)
1090
1091 static void PlayLevelSound(int, int, int);
1092 static void PlayLevelSoundNearest(int, int, int);
1093 static void PlayLevelSoundAction(int, int, int);
1094 static void PlayLevelSoundElementAction(int, int, int, int);
1095 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1096 static void PlayLevelSoundActionIfLoop(int, int, int);
1097 static void StopLevelSoundActionIfLoop(int, int, int);
1098 static void PlayLevelMusic(void);
1099 static void FadeLevelSoundsAndMusic(void);
1100
1101 static void HandleGameButtons(struct GadgetInfo *);
1102
1103 int AmoebaNeighbourNr(int, int);
1104 void AmoebaToDiamond(int, int);
1105 void ContinueMoving(int, int);
1106 void Bang(int, int);
1107 void InitMovDir(int, int);
1108 void InitAmoebaNr(int, int);
1109 int NewHiScore(int);
1110
1111 void TestIfGoodThingHitsBadThing(int, int, int);
1112 void TestIfBadThingHitsGoodThing(int, int, int);
1113 void TestIfPlayerTouchesBadThing(int, int);
1114 void TestIfPlayerRunsIntoBadThing(int, int, int);
1115 void TestIfBadThingTouchesPlayer(int, int);
1116 void TestIfBadThingRunsIntoPlayer(int, int, int);
1117 void TestIfFriendTouchesBadThing(int, int);
1118 void TestIfBadThingTouchesFriend(int, int);
1119 void TestIfBadThingTouchesOtherBadThing(int, int);
1120 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1121
1122 void KillPlayer(struct PlayerInfo *);
1123 void BuryPlayer(struct PlayerInfo *);
1124 void RemovePlayer(struct PlayerInfo *);
1125 void ExitPlayer(struct PlayerInfo *);
1126
1127 static int getInvisibleActiveFromInvisibleElement(int);
1128 static int getInvisibleFromInvisibleActiveElement(int);
1129
1130 static void TestFieldAfterSnapping(int, int, int, int, int);
1131
1132 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1133
1134 // for detection of endless loops, caused by custom element programming
1135 // (using maximal playfield width x 10 is just a rough approximation)
1136 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1137
1138 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1139 {                                                                       \
1140   if (recursion_loop_detected)                                          \
1141     return (rc);                                                        \
1142                                                                         \
1143   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1144   {                                                                     \
1145     recursion_loop_detected = TRUE;                                     \
1146     recursion_loop_element = (e);                                       \
1147   }                                                                     \
1148                                                                         \
1149   recursion_loop_depth++;                                               \
1150 }
1151
1152 #define RECURSION_LOOP_DETECTION_END()                                  \
1153 {                                                                       \
1154   recursion_loop_depth--;                                               \
1155 }
1156
1157 static int recursion_loop_depth;
1158 static boolean recursion_loop_detected;
1159 static boolean recursion_loop_element;
1160
1161 static int map_player_action[MAX_PLAYERS];
1162
1163
1164 // ----------------------------------------------------------------------------
1165 // definition of elements that automatically change to other elements after
1166 // a specified time, eventually calling a function when changing
1167 // ----------------------------------------------------------------------------
1168
1169 // forward declaration for changer functions
1170 static void InitBuggyBase(int, int);
1171 static void WarnBuggyBase(int, int);
1172
1173 static void InitTrap(int, int);
1174 static void ActivateTrap(int, int);
1175 static void ChangeActiveTrap(int, int);
1176
1177 static void InitRobotWheel(int, int);
1178 static void RunRobotWheel(int, int);
1179 static void StopRobotWheel(int, int);
1180
1181 static void InitTimegateWheel(int, int);
1182 static void RunTimegateWheel(int, int);
1183
1184 static void InitMagicBallDelay(int, int);
1185 static void ActivateMagicBall(int, int);
1186
1187 struct ChangingElementInfo
1188 {
1189   int element;
1190   int target_element;
1191   int change_delay;
1192   void (*pre_change_function)(int x, int y);
1193   void (*change_function)(int x, int y);
1194   void (*post_change_function)(int x, int y);
1195 };
1196
1197 static struct ChangingElementInfo change_delay_list[] =
1198 {
1199   {
1200     EL_NUT_BREAKING,
1201     EL_EMERALD,
1202     6,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_PEARL_BREAKING,
1209     EL_EMPTY,
1210     8,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EXIT_OPENING,
1217     EL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EXIT_CLOSING,
1225     EL_EXIT_CLOSED,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_STEEL_EXIT_OPENING,
1233     EL_STEEL_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_STEEL_EXIT_CLOSING,
1241     EL_STEEL_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_EM_EXIT_OPENING,
1249     EL_EM_EXIT_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_EM_EXIT_CLOSING,
1257     EL_EMPTY,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_EM_STEEL_EXIT_OPENING,
1265     EL_EM_STEEL_EXIT_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_EM_STEEL_EXIT_CLOSING,
1273     EL_STEELWALL,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_SP_EXIT_OPENING,
1281     EL_SP_EXIT_OPEN,
1282     29,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_EXIT_CLOSING,
1289     EL_SP_EXIT_CLOSED,
1290     29,
1291     NULL,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SWITCHGATE_OPENING,
1297     EL_SWITCHGATE_OPEN,
1298     29,
1299     NULL,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SWITCHGATE_CLOSING,
1305     EL_SWITCHGATE_CLOSED,
1306     29,
1307     NULL,
1308     NULL,
1309     NULL
1310   },
1311   {
1312     EL_TIMEGATE_OPENING,
1313     EL_TIMEGATE_OPEN,
1314     29,
1315     NULL,
1316     NULL,
1317     NULL
1318   },
1319   {
1320     EL_TIMEGATE_CLOSING,
1321     EL_TIMEGATE_CLOSED,
1322     29,
1323     NULL,
1324     NULL,
1325     NULL
1326   },
1327
1328   {
1329     EL_ACID_SPLASH_LEFT,
1330     EL_EMPTY,
1331     8,
1332     NULL,
1333     NULL,
1334     NULL
1335   },
1336   {
1337     EL_ACID_SPLASH_RIGHT,
1338     EL_EMPTY,
1339     8,
1340     NULL,
1341     NULL,
1342     NULL
1343   },
1344   {
1345     EL_SP_BUGGY_BASE,
1346     EL_SP_BUGGY_BASE_ACTIVATING,
1347     0,
1348     InitBuggyBase,
1349     NULL,
1350     NULL
1351   },
1352   {
1353     EL_SP_BUGGY_BASE_ACTIVATING,
1354     EL_SP_BUGGY_BASE_ACTIVE,
1355     0,
1356     InitBuggyBase,
1357     NULL,
1358     NULL
1359   },
1360   {
1361     EL_SP_BUGGY_BASE_ACTIVE,
1362     EL_SP_BUGGY_BASE,
1363     0,
1364     InitBuggyBase,
1365     WarnBuggyBase,
1366     NULL
1367   },
1368   {
1369     EL_TRAP,
1370     EL_TRAP_ACTIVE,
1371     0,
1372     InitTrap,
1373     NULL,
1374     ActivateTrap
1375   },
1376   {
1377     EL_TRAP_ACTIVE,
1378     EL_TRAP,
1379     31,
1380     NULL,
1381     ChangeActiveTrap,
1382     NULL
1383   },
1384   {
1385     EL_ROBOT_WHEEL_ACTIVE,
1386     EL_ROBOT_WHEEL,
1387     0,
1388     InitRobotWheel,
1389     RunRobotWheel,
1390     StopRobotWheel
1391   },
1392   {
1393     EL_TIMEGATE_SWITCH_ACTIVE,
1394     EL_TIMEGATE_SWITCH,
1395     0,
1396     InitTimegateWheel,
1397     RunTimegateWheel,
1398     NULL
1399   },
1400   {
1401     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1402     EL_DC_TIMEGATE_SWITCH,
1403     0,
1404     InitTimegateWheel,
1405     RunTimegateWheel,
1406     NULL
1407   },
1408   {
1409     EL_EMC_MAGIC_BALL_ACTIVE,
1410     EL_EMC_MAGIC_BALL_ACTIVE,
1411     0,
1412     InitMagicBallDelay,
1413     NULL,
1414     ActivateMagicBall
1415   },
1416   {
1417     EL_EMC_SPRING_BUMPER_ACTIVE,
1418     EL_EMC_SPRING_BUMPER,
1419     8,
1420     NULL,
1421     NULL,
1422     NULL
1423   },
1424   {
1425     EL_DIAGONAL_SHRINKING,
1426     EL_UNDEFINED,
1427     0,
1428     NULL,
1429     NULL,
1430     NULL
1431   },
1432   {
1433     EL_DIAGONAL_GROWING,
1434     EL_UNDEFINED,
1435     0,
1436     NULL,
1437     NULL,
1438     NULL,
1439   },
1440
1441   {
1442     EL_UNDEFINED,
1443     EL_UNDEFINED,
1444     -1,
1445     NULL,
1446     NULL,
1447     NULL
1448   }
1449 };
1450
1451 struct
1452 {
1453   int element;
1454   int push_delay_fixed, push_delay_random;
1455 }
1456 push_delay_list[] =
1457 {
1458   { EL_SPRING,                  0, 0 },
1459   { EL_BALLOON,                 0, 0 },
1460
1461   { EL_SOKOBAN_OBJECT,          2, 0 },
1462   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1463   { EL_SATELLITE,               2, 0 },
1464   { EL_SP_DISK_YELLOW,          2, 0 },
1465
1466   { EL_UNDEFINED,               0, 0 },
1467 };
1468
1469 struct
1470 {
1471   int element;
1472   int move_stepsize;
1473 }
1474 move_stepsize_list[] =
1475 {
1476   { EL_AMOEBA_DROP,             2 },
1477   { EL_AMOEBA_DROPPING,         2 },
1478   { EL_QUICKSAND_FILLING,       1 },
1479   { EL_QUICKSAND_EMPTYING,      1 },
1480   { EL_QUICKSAND_FAST_FILLING,  2 },
1481   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1482   { EL_MAGIC_WALL_FILLING,      2 },
1483   { EL_MAGIC_WALL_EMPTYING,     2 },
1484   { EL_BD_MAGIC_WALL_FILLING,   2 },
1485   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1486   { EL_DC_MAGIC_WALL_FILLING,   2 },
1487   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1488
1489   { EL_UNDEFINED,               0 },
1490 };
1491
1492 struct
1493 {
1494   int element;
1495   int count;
1496 }
1497 collect_count_list[] =
1498 {
1499   { EL_EMERALD,                 1 },
1500   { EL_BD_DIAMOND,              1 },
1501   { EL_EMERALD_YELLOW,          1 },
1502   { EL_EMERALD_RED,             1 },
1503   { EL_EMERALD_PURPLE,          1 },
1504   { EL_DIAMOND,                 3 },
1505   { EL_SP_INFOTRON,             1 },
1506   { EL_PEARL,                   5 },
1507   { EL_CRYSTAL,                 8 },
1508
1509   { EL_UNDEFINED,               0 },
1510 };
1511
1512 struct
1513 {
1514   int element;
1515   int direction;
1516 }
1517 access_direction_list[] =
1518 {
1519   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1520   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1521   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1522   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1523   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1524   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1525   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1526   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1527   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1528   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1529   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1530
1531   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1532   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1533   { EL_SP_PORT_UP,                                                   MV_DOWN },
1534   { EL_SP_PORT_DOWN,                                         MV_UP           },
1535   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1536   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1537   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1538   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1539   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1540   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1541   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1542   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1543   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1544   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1545   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1546   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1547   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1548   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1549   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1550
1551   { EL_UNDEFINED,                       MV_NONE                              }
1552 };
1553
1554 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1555
1556 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1557 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1558 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Tile[x][y]) || \
1559                                  IS_JUST_CHANGING(x, y))
1560
1561 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1562
1563 // static variables for playfield scan mode (scanning forward or backward)
1564 static int playfield_scan_start_x = 0;
1565 static int playfield_scan_start_y = 0;
1566 static int playfield_scan_delta_x = 1;
1567 static int playfield_scan_delta_y = 1;
1568
1569 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1570                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1571                                      (y) += playfield_scan_delta_y)     \
1572                                 for ((x) = playfield_scan_start_x;      \
1573                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1574                                      (x) += playfield_scan_delta_x)
1575
1576 #ifdef DEBUG
1577 void DEBUG_SetMaximumDynamite(void)
1578 {
1579   int i;
1580
1581   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1582     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1583       local_player->inventory_element[local_player->inventory_size++] =
1584         EL_DYNAMITE;
1585 }
1586 #endif
1587
1588 static void InitPlayfieldScanModeVars(void)
1589 {
1590   if (game.use_reverse_scan_direction)
1591   {
1592     playfield_scan_start_x = lev_fieldx - 1;
1593     playfield_scan_start_y = lev_fieldy - 1;
1594
1595     playfield_scan_delta_x = -1;
1596     playfield_scan_delta_y = -1;
1597   }
1598   else
1599   {
1600     playfield_scan_start_x = 0;
1601     playfield_scan_start_y = 0;
1602
1603     playfield_scan_delta_x = 1;
1604     playfield_scan_delta_y = 1;
1605   }
1606 }
1607
1608 static void InitPlayfieldScanMode(int mode)
1609 {
1610   game.use_reverse_scan_direction =
1611     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1612
1613   InitPlayfieldScanModeVars();
1614 }
1615
1616 static int get_move_delay_from_stepsize(int move_stepsize)
1617 {
1618   move_stepsize =
1619     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1620
1621   // make sure that stepsize value is always a power of 2
1622   move_stepsize = (1 << log_2(move_stepsize));
1623
1624   return TILEX / move_stepsize;
1625 }
1626
1627 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1628                                boolean init_game)
1629 {
1630   int player_nr = player->index_nr;
1631   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1632   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1633
1634   // do no immediately change move delay -- the player might just be moving
1635   player->move_delay_value_next = move_delay;
1636
1637   // information if player can move must be set separately
1638   player->cannot_move = cannot_move;
1639
1640   if (init_game)
1641   {
1642     player->move_delay       = game.initial_move_delay[player_nr];
1643     player->move_delay_value = game.initial_move_delay_value[player_nr];
1644
1645     player->move_delay_value_next = -1;
1646
1647     player->move_delay_reset_counter = 0;
1648   }
1649 }
1650
1651 void GetPlayerConfig(void)
1652 {
1653   GameFrameDelay = setup.game_frame_delay;
1654
1655   if (!audio.sound_available)
1656     setup.sound_simple = FALSE;
1657
1658   if (!audio.loops_available)
1659     setup.sound_loops = FALSE;
1660
1661   if (!audio.music_available)
1662     setup.sound_music = FALSE;
1663
1664   if (!video.fullscreen_available)
1665     setup.fullscreen = FALSE;
1666
1667   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1668
1669   SetAudioMode(setup.sound);
1670 }
1671
1672 int GetElementFromGroupElement(int element)
1673 {
1674   if (IS_GROUP_ELEMENT(element))
1675   {
1676     struct ElementGroupInfo *group = element_info[element].group;
1677     int last_anim_random_frame = gfx.anim_random_frame;
1678     int element_pos;
1679
1680     if (group->choice_mode == ANIM_RANDOM)
1681       gfx.anim_random_frame = RND(group->num_elements_resolved);
1682
1683     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1684                                     group->choice_mode, 0,
1685                                     group->choice_pos);
1686
1687     if (group->choice_mode == ANIM_RANDOM)
1688       gfx.anim_random_frame = last_anim_random_frame;
1689
1690     group->choice_pos++;
1691
1692     element = group->element_resolved[element_pos];
1693   }
1694
1695   return element;
1696 }
1697
1698 static void IncrementSokobanFieldsNeeded(void)
1699 {
1700   if (level.sb_fields_needed)
1701     game.sokoban_fields_still_needed++;
1702 }
1703
1704 static void IncrementSokobanObjectsNeeded(void)
1705 {
1706   if (level.sb_objects_needed)
1707     game.sokoban_objects_still_needed++;
1708 }
1709
1710 static void DecrementSokobanFieldsNeeded(void)
1711 {
1712   if (game.sokoban_fields_still_needed > 0)
1713     game.sokoban_fields_still_needed--;
1714 }
1715
1716 static void DecrementSokobanObjectsNeeded(void)
1717 {
1718   if (game.sokoban_objects_still_needed > 0)
1719     game.sokoban_objects_still_needed--;
1720 }
1721
1722 static void InitPlayerField(int x, int y, int element, boolean init_game)
1723 {
1724   if (element == EL_SP_MURPHY)
1725   {
1726     if (init_game)
1727     {
1728       if (stored_player[0].present)
1729       {
1730         Tile[x][y] = EL_SP_MURPHY_CLONE;
1731
1732         return;
1733       }
1734       else
1735       {
1736         stored_player[0].initial_element = element;
1737         stored_player[0].use_murphy = TRUE;
1738
1739         if (!level.use_artwork_element[0])
1740           stored_player[0].artwork_element = EL_SP_MURPHY;
1741       }
1742
1743       Tile[x][y] = EL_PLAYER_1;
1744     }
1745   }
1746
1747   if (init_game)
1748   {
1749     struct PlayerInfo *player = &stored_player[Tile[x][y] - EL_PLAYER_1];
1750     int jx = player->jx, jy = player->jy;
1751
1752     player->present = TRUE;
1753
1754     player->block_last_field = (element == EL_SP_MURPHY ?
1755                                 level.sp_block_last_field :
1756                                 level.block_last_field);
1757
1758     // ---------- initialize player's last field block delay ------------------
1759
1760     // always start with reliable default value (no adjustment needed)
1761     player->block_delay_adjustment = 0;
1762
1763     // special case 1: in Supaplex, Murphy blocks last field one more frame
1764     if (player->block_last_field && element == EL_SP_MURPHY)
1765       player->block_delay_adjustment = 1;
1766
1767     // special case 2: in game engines before 3.1.1, blocking was different
1768     if (game.use_block_last_field_bug)
1769       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1770
1771     if (!network.enabled || player->connected_network)
1772     {
1773       player->active = TRUE;
1774
1775       // remove potentially duplicate players
1776       if (StorePlayer[jx][jy] == Tile[x][y])
1777         StorePlayer[jx][jy] = 0;
1778
1779       StorePlayer[x][y] = Tile[x][y];
1780
1781 #if DEBUG_INIT_PLAYER
1782       Debug("game:init:player", "- player element %d activated",
1783             player->element_nr);
1784       Debug("game:init:player", "  (local player is %d and currently %s)",
1785             local_player->element_nr,
1786             local_player->active ? "active" : "not active");
1787     }
1788 #endif
1789
1790     Tile[x][y] = EL_EMPTY;
1791
1792     player->jx = player->last_jx = x;
1793     player->jy = player->last_jy = y;
1794   }
1795
1796   // always check if player was just killed and should be reanimated
1797   {
1798     int player_nr = GET_PLAYER_NR(element);
1799     struct PlayerInfo *player = &stored_player[player_nr];
1800
1801     if (player->active && player->killed)
1802       player->reanimated = TRUE; // if player was just killed, reanimate him
1803   }
1804 }
1805
1806 static void InitField(int x, int y, boolean init_game)
1807 {
1808   int element = Tile[x][y];
1809
1810   switch (element)
1811   {
1812     case EL_SP_MURPHY:
1813     case EL_PLAYER_1:
1814     case EL_PLAYER_2:
1815     case EL_PLAYER_3:
1816     case EL_PLAYER_4:
1817       InitPlayerField(x, y, element, init_game);
1818       break;
1819
1820     case EL_SOKOBAN_FIELD_PLAYER:
1821       element = Tile[x][y] = EL_PLAYER_1;
1822       InitField(x, y, init_game);
1823
1824       element = Tile[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1825       InitField(x, y, init_game);
1826       break;
1827
1828     case EL_SOKOBAN_FIELD_EMPTY:
1829       IncrementSokobanFieldsNeeded();
1830       break;
1831
1832     case EL_SOKOBAN_OBJECT:
1833       IncrementSokobanObjectsNeeded();
1834       break;
1835
1836     case EL_STONEBLOCK:
1837       if (x < lev_fieldx-1 && Tile[x+1][y] == EL_ACID)
1838         Tile[x][y] = EL_ACID_POOL_TOPLEFT;
1839       else if (x > 0 && Tile[x-1][y] == EL_ACID)
1840         Tile[x][y] = EL_ACID_POOL_TOPRIGHT;
1841       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPLEFT)
1842         Tile[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1843       else if (y > 0 && Tile[x][y-1] == EL_ACID)
1844         Tile[x][y] = EL_ACID_POOL_BOTTOM;
1845       else if (y > 0 && Tile[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1846         Tile[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1847       break;
1848
1849     case EL_BUG:
1850     case EL_BUG_RIGHT:
1851     case EL_BUG_UP:
1852     case EL_BUG_LEFT:
1853     case EL_BUG_DOWN:
1854     case EL_SPACESHIP:
1855     case EL_SPACESHIP_RIGHT:
1856     case EL_SPACESHIP_UP:
1857     case EL_SPACESHIP_LEFT:
1858     case EL_SPACESHIP_DOWN:
1859     case EL_BD_BUTTERFLY:
1860     case EL_BD_BUTTERFLY_RIGHT:
1861     case EL_BD_BUTTERFLY_UP:
1862     case EL_BD_BUTTERFLY_LEFT:
1863     case EL_BD_BUTTERFLY_DOWN:
1864     case EL_BD_FIREFLY:
1865     case EL_BD_FIREFLY_RIGHT:
1866     case EL_BD_FIREFLY_UP:
1867     case EL_BD_FIREFLY_LEFT:
1868     case EL_BD_FIREFLY_DOWN:
1869     case EL_PACMAN_RIGHT:
1870     case EL_PACMAN_UP:
1871     case EL_PACMAN_LEFT:
1872     case EL_PACMAN_DOWN:
1873     case EL_YAMYAM:
1874     case EL_YAMYAM_LEFT:
1875     case EL_YAMYAM_RIGHT:
1876     case EL_YAMYAM_UP:
1877     case EL_YAMYAM_DOWN:
1878     case EL_DARK_YAMYAM:
1879     case EL_ROBOT:
1880     case EL_PACMAN:
1881     case EL_SP_SNIKSNAK:
1882     case EL_SP_ELECTRON:
1883     case EL_MOLE:
1884     case EL_MOLE_LEFT:
1885     case EL_MOLE_RIGHT:
1886     case EL_MOLE_UP:
1887     case EL_MOLE_DOWN:
1888     case EL_SPRING_LEFT:
1889     case EL_SPRING_RIGHT:
1890       InitMovDir(x, y);
1891       break;
1892
1893     case EL_AMOEBA_FULL:
1894     case EL_BD_AMOEBA:
1895       InitAmoebaNr(x, y);
1896       break;
1897
1898     case EL_AMOEBA_DROP:
1899       if (y == lev_fieldy - 1)
1900       {
1901         Tile[x][y] = EL_AMOEBA_GROWING;
1902         Store[x][y] = EL_AMOEBA_WET;
1903       }
1904       break;
1905
1906     case EL_DYNAMITE_ACTIVE:
1907     case EL_SP_DISK_RED_ACTIVE:
1908     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1909     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1910     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1911     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1912       MovDelay[x][y] = 96;
1913       break;
1914
1915     case EL_EM_DYNAMITE_ACTIVE:
1916       MovDelay[x][y] = 32;
1917       break;
1918
1919     case EL_LAMP:
1920       game.lights_still_needed++;
1921       break;
1922
1923     case EL_PENGUIN:
1924       game.friends_still_needed++;
1925       break;
1926
1927     case EL_PIG:
1928     case EL_DRAGON:
1929       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1930       break;
1931
1932     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1933     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1934     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1935     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1936     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1937     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1938     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1939     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1940     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1941     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1942     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1943     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1944       if (init_game)
1945       {
1946         int belt_nr = getBeltNrFromBeltSwitchElement(Tile[x][y]);
1947         int belt_dir = getBeltDirFromBeltSwitchElement(Tile[x][y]);
1948         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Tile[x][y]);
1949
1950         if (game.belt_dir_nr[belt_nr] == 3)     // initial value
1951         {
1952           game.belt_dir[belt_nr] = belt_dir;
1953           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1954         }
1955         else    // more than one switch -- set it like the first switch
1956         {
1957           Tile[x][y] = Tile[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1958         }
1959       }
1960       break;
1961
1962     case EL_LIGHT_SWITCH_ACTIVE:
1963       if (init_game)
1964         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1965       break;
1966
1967     case EL_INVISIBLE_STEELWALL:
1968     case EL_INVISIBLE_WALL:
1969     case EL_INVISIBLE_SAND:
1970       if (game.light_time_left > 0 ||
1971           game.lenses_time_left > 0)
1972         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
1973       break;
1974
1975     case EL_EMC_MAGIC_BALL:
1976       if (game.ball_active)
1977         Tile[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1978       break;
1979
1980     case EL_EMC_MAGIC_BALL_SWITCH:
1981       if (game.ball_active)
1982         Tile[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1983       break;
1984
1985     case EL_TRIGGER_PLAYER:
1986     case EL_TRIGGER_ELEMENT:
1987     case EL_TRIGGER_CE_VALUE:
1988     case EL_TRIGGER_CE_SCORE:
1989     case EL_SELF:
1990     case EL_ANY_ELEMENT:
1991     case EL_CURRENT_CE_VALUE:
1992     case EL_CURRENT_CE_SCORE:
1993     case EL_PREV_CE_1:
1994     case EL_PREV_CE_2:
1995     case EL_PREV_CE_3:
1996     case EL_PREV_CE_4:
1997     case EL_PREV_CE_5:
1998     case EL_PREV_CE_6:
1999     case EL_PREV_CE_7:
2000     case EL_PREV_CE_8:
2001     case EL_NEXT_CE_1:
2002     case EL_NEXT_CE_2:
2003     case EL_NEXT_CE_3:
2004     case EL_NEXT_CE_4:
2005     case EL_NEXT_CE_5:
2006     case EL_NEXT_CE_6:
2007     case EL_NEXT_CE_7:
2008     case EL_NEXT_CE_8:
2009       // reference elements should not be used on the playfield
2010       Tile[x][y] = EL_EMPTY;
2011       break;
2012
2013     default:
2014       if (IS_CUSTOM_ELEMENT(element))
2015       {
2016         if (CAN_MOVE(element))
2017           InitMovDir(x, y);
2018
2019         if (!element_info[element].use_last_ce_value || init_game)
2020           CustomValue[x][y] = GET_NEW_CE_VALUE(Tile[x][y]);
2021       }
2022       else if (IS_GROUP_ELEMENT(element))
2023       {
2024         Tile[x][y] = GetElementFromGroupElement(element);
2025
2026         InitField(x, y, init_game);
2027       }
2028
2029       break;
2030   }
2031
2032   if (!init_game)
2033     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
2034 }
2035
2036 static void InitField_WithBug1(int x, int y, boolean init_game)
2037 {
2038   InitField(x, y, init_game);
2039
2040   // not needed to call InitMovDir() -- already done by InitField()!
2041   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2042       CAN_MOVE(Tile[x][y]))
2043     InitMovDir(x, y);
2044 }
2045
2046 static void InitField_WithBug2(int x, int y, boolean init_game)
2047 {
2048   int old_element = Tile[x][y];
2049
2050   InitField(x, y, init_game);
2051
2052   // not needed to call InitMovDir() -- already done by InitField()!
2053   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2054       CAN_MOVE(old_element) &&
2055       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
2056     InitMovDir(x, y);
2057
2058   /* this case is in fact a combination of not less than three bugs:
2059      first, it calls InitMovDir() for elements that can move, although this is
2060      already done by InitField(); then, it checks the element that was at this
2061      field _before_ the call to InitField() (which can change it); lastly, it
2062      was not called for "mole with direction" elements, which were treated as
2063      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
2064   */
2065 }
2066
2067 static int get_key_element_from_nr(int key_nr)
2068 {
2069   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
2070                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2071                           EL_EM_KEY_1 : EL_KEY_1);
2072
2073   return key_base_element + key_nr;
2074 }
2075
2076 static int get_next_dropped_element(struct PlayerInfo *player)
2077 {
2078   return (player->inventory_size > 0 ?
2079           player->inventory_element[player->inventory_size - 1] :
2080           player->inventory_infinite_element != EL_UNDEFINED ?
2081           player->inventory_infinite_element :
2082           player->dynabombs_left > 0 ?
2083           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2084           EL_UNDEFINED);
2085 }
2086
2087 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2088 {
2089   // pos >= 0: get element from bottom of the stack;
2090   // pos <  0: get element from top of the stack
2091
2092   if (pos < 0)
2093   {
2094     int min_inventory_size = -pos;
2095     int inventory_pos = player->inventory_size - min_inventory_size;
2096     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2097
2098     return (player->inventory_size >= min_inventory_size ?
2099             player->inventory_element[inventory_pos] :
2100             player->inventory_infinite_element != EL_UNDEFINED ?
2101             player->inventory_infinite_element :
2102             player->dynabombs_left >= min_dynabombs_left ?
2103             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2104             EL_UNDEFINED);
2105   }
2106   else
2107   {
2108     int min_dynabombs_left = pos + 1;
2109     int min_inventory_size = pos + 1 - player->dynabombs_left;
2110     int inventory_pos = pos - player->dynabombs_left;
2111
2112     return (player->inventory_infinite_element != EL_UNDEFINED ?
2113             player->inventory_infinite_element :
2114             player->dynabombs_left >= min_dynabombs_left ?
2115             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2116             player->inventory_size >= min_inventory_size ?
2117             player->inventory_element[inventory_pos] :
2118             EL_UNDEFINED);
2119   }
2120 }
2121
2122 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2123 {
2124   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2125   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2126   int compare_result;
2127
2128   if (gpo1->sort_priority != gpo2->sort_priority)
2129     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2130   else
2131     compare_result = gpo1->nr - gpo2->nr;
2132
2133   return compare_result;
2134 }
2135
2136 int getPlayerInventorySize(int player_nr)
2137 {
2138   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2139     return game_em.ply[player_nr]->dynamite;
2140   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2141     return game_sp.red_disk_count;
2142   else
2143     return stored_player[player_nr].inventory_size;
2144 }
2145
2146 static void InitGameControlValues(void)
2147 {
2148   int i;
2149
2150   for (i = 0; game_panel_controls[i].nr != -1; i++)
2151   {
2152     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2153     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2154     struct TextPosInfo *pos = gpc->pos;
2155     int nr = gpc->nr;
2156     int type = gpc->type;
2157
2158     if (nr != i)
2159     {
2160       Error("'game_panel_controls' structure corrupted at %d", i);
2161
2162       Fail("this should not happen -- please debug");
2163     }
2164
2165     // force update of game controls after initialization
2166     gpc->value = gpc->last_value = -1;
2167     gpc->frame = gpc->last_frame = -1;
2168     gpc->gfx_frame = -1;
2169
2170     // determine panel value width for later calculation of alignment
2171     if (type == TYPE_INTEGER || type == TYPE_STRING)
2172     {
2173       pos->width = pos->size * getFontWidth(pos->font);
2174       pos->height = getFontHeight(pos->font);
2175     }
2176     else if (type == TYPE_ELEMENT)
2177     {
2178       pos->width = pos->size;
2179       pos->height = pos->size;
2180     }
2181
2182     // fill structure for game panel draw order
2183     gpo->nr = gpc->nr;
2184     gpo->sort_priority = pos->sort_priority;
2185   }
2186
2187   // sort game panel controls according to sort_priority and control number
2188   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2189         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2190 }
2191
2192 static void UpdatePlayfieldElementCount(void)
2193 {
2194   boolean use_element_count = FALSE;
2195   int i, j, x, y;
2196
2197   // first check if it is needed at all to calculate playfield element count
2198   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2199     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2200       use_element_count = TRUE;
2201
2202   if (!use_element_count)
2203     return;
2204
2205   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2206     element_info[i].element_count = 0;
2207
2208   SCAN_PLAYFIELD(x, y)
2209   {
2210     element_info[Tile[x][y]].element_count++;
2211   }
2212
2213   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2214     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2215       if (IS_IN_GROUP(j, i))
2216         element_info[EL_GROUP_START + i].element_count +=
2217           element_info[j].element_count;
2218 }
2219
2220 static void UpdateGameControlValues(void)
2221 {
2222   int i, k;
2223   int time = (game.LevelSolved ?
2224               game.LevelSolved_CountingTime :
2225               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2226               game_em.lev->time :
2227               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2228               game_sp.time_played :
2229               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2230               game_mm.energy_left :
2231               game.no_time_limit ? TimePlayed : TimeLeft);
2232   int score = (game.LevelSolved ?
2233                game.LevelSolved_CountingScore :
2234                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2235                game_em.lev->score :
2236                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2237                game_sp.score :
2238                level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2239                game_mm.score :
2240                game.score);
2241   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2242               game_em.lev->gems_needed :
2243               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2244               game_sp.infotrons_still_needed :
2245               level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2246               game_mm.kettles_still_needed :
2247               game.gems_still_needed);
2248   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2249                      game_em.lev->gems_needed > 0 :
2250                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2251                      game_sp.infotrons_still_needed > 0 :
2252                      level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2253                      game_mm.kettles_still_needed > 0 ||
2254                      game_mm.lights_still_needed > 0 :
2255                      game.gems_still_needed > 0 ||
2256                      game.sokoban_fields_still_needed > 0 ||
2257                      game.sokoban_objects_still_needed > 0 ||
2258                      game.lights_still_needed > 0);
2259   int health = (game.LevelSolved ?
2260                 game.LevelSolved_CountingHealth :
2261                 level.game_engine_type == GAME_ENGINE_TYPE_MM ?
2262                 MM_HEALTH(game_mm.laser_overload_value) :
2263                 game.health);
2264   int sync_random_frame = INIT_GFX_RANDOM();    // random, but synchronized
2265
2266   UpdatePlayfieldElementCount();
2267
2268   // update game panel control values
2269
2270   // used instead of "level_nr" (for network games)
2271   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = levelset.level_nr;
2272   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2273
2274   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2275   for (i = 0; i < MAX_NUM_KEYS; i++)
2276     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2277   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2278   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2279
2280   if (game.centered_player_nr == -1)
2281   {
2282     for (i = 0; i < MAX_PLAYERS; i++)
2283     {
2284       // only one player in Supaplex game engine
2285       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2286         break;
2287
2288       for (k = 0; k < MAX_NUM_KEYS; k++)
2289       {
2290         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2291         {
2292           if (game_em.ply[i]->keys & (1 << k))
2293             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2294               get_key_element_from_nr(k);
2295         }
2296         else if (stored_player[i].key[k])
2297           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2298             get_key_element_from_nr(k);
2299       }
2300
2301       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2302         getPlayerInventorySize(i);
2303
2304       if (stored_player[i].num_white_keys > 0)
2305         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2306           EL_DC_KEY_WHITE;
2307
2308       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2309         stored_player[i].num_white_keys;
2310     }
2311   }
2312   else
2313   {
2314     int player_nr = game.centered_player_nr;
2315
2316     for (k = 0; k < MAX_NUM_KEYS; k++)
2317     {
2318       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2319       {
2320         if (game_em.ply[player_nr]->keys & (1 << k))
2321           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2322             get_key_element_from_nr(k);
2323       }
2324       else if (stored_player[player_nr].key[k])
2325         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2326           get_key_element_from_nr(k);
2327     }
2328
2329     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2330       getPlayerInventorySize(player_nr);
2331
2332     if (stored_player[player_nr].num_white_keys > 0)
2333       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2334
2335     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2336       stored_player[player_nr].num_white_keys;
2337   }
2338
2339   // re-arrange keys on game panel, if needed or if defined by style settings
2340   for (i = 0; i < MAX_NUM_KEYS + 1; i++)        // all normal keys + white key
2341   {
2342     int nr = GAME_PANEL_KEY_1 + i;
2343     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2344     struct TextPosInfo *pos = gpc->pos;
2345
2346     // skip check if key is not in the player's inventory
2347     if (gpc->value == EL_EMPTY)
2348       continue;
2349
2350     // check if keys should be arranged on panel from left to right
2351     if (pos->style == STYLE_LEFTMOST_POSITION)
2352     {
2353       // check previous key positions (left from current key)
2354       for (k = 0; k < i; k++)
2355       {
2356         int nr_new = GAME_PANEL_KEY_1 + k;
2357
2358         if (game_panel_controls[nr_new].value == EL_EMPTY)
2359         {
2360           game_panel_controls[nr_new].value = gpc->value;
2361           gpc->value = EL_EMPTY;
2362
2363           break;
2364         }
2365       }
2366     }
2367
2368     // check if "undefined" keys can be placed at some other position
2369     if (pos->x == -1 && pos->y == -1)
2370     {
2371       int nr_new = GAME_PANEL_KEY_1 + i % STD_NUM_KEYS;
2372
2373       // 1st try: display key at the same position as normal or EM keys
2374       if (game_panel_controls[nr_new].value == EL_EMPTY)
2375       {
2376         game_panel_controls[nr_new].value = gpc->value;
2377       }
2378       else
2379       {
2380         // 2nd try: display key at the next free position in the key panel
2381         for (k = 0; k < STD_NUM_KEYS; k++)
2382         {
2383           nr_new = GAME_PANEL_KEY_1 + k;
2384
2385           if (game_panel_controls[nr_new].value == EL_EMPTY)
2386           {
2387             game_panel_controls[nr_new].value = gpc->value;
2388
2389             break;
2390           }
2391         }
2392       }
2393     }
2394   }
2395
2396   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2397   {
2398     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2399       get_inventory_element_from_pos(local_player, i);
2400     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2401       get_inventory_element_from_pos(local_player, -i - 1);
2402   }
2403
2404   game_panel_controls[GAME_PANEL_SCORE].value = score;
2405   game_panel_controls[GAME_PANEL_HIGHSCORE].value = scores.entry[0].score;
2406
2407   game_panel_controls[GAME_PANEL_TIME].value = time;
2408
2409   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2410   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2411   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2412
2413   if (level.time == 0)
2414     game_panel_controls[GAME_PANEL_TIME_ANIM].value = 100;
2415   else
2416     game_panel_controls[GAME_PANEL_TIME_ANIM].value = time * 100 / level.time;
2417
2418   game_panel_controls[GAME_PANEL_HEALTH].value = health;
2419   game_panel_controls[GAME_PANEL_HEALTH_ANIM].value = health;
2420
2421   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2422
2423   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2424     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2425      EL_EMPTY);
2426   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2427     local_player->shield_normal_time_left;
2428   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2429     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2430      EL_EMPTY);
2431   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2432     local_player->shield_deadly_time_left;
2433
2434   game_panel_controls[GAME_PANEL_EXIT].value =
2435     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2436
2437   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2438     (game.ball_active ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2439   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2440     (game.ball_active ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2441      EL_EMC_MAGIC_BALL_SWITCH);
2442
2443   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2444     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2445   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2446     game.light_time_left;
2447
2448   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2449     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2450   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2451     game.timegate_time_left;
2452
2453   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2454     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2455
2456   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2457     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2458   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2459     game.lenses_time_left;
2460
2461   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2462     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2463   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2464     game.magnify_time_left;
2465
2466   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2467     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2468      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2469      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2470      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2471      EL_BALLOON_SWITCH_NONE);
2472
2473   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2474     local_player->dynabomb_count;
2475   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2476     local_player->dynabomb_size;
2477   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2478     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2479
2480   game_panel_controls[GAME_PANEL_PENGUINS].value =
2481     game.friends_still_needed;
2482
2483   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2484     game.sokoban_objects_still_needed;
2485   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2486     game.sokoban_fields_still_needed;
2487
2488   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2489     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2490
2491   for (i = 0; i < NUM_BELTS; i++)
2492   {
2493     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2494       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2495        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2496     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2497       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2498   }
2499
2500   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2501     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2502   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2503     game.magic_wall_time_left;
2504
2505   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2506     local_player->gravity;
2507
2508   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2509     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2510
2511   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2512     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2513       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2514        game.panel.element[i].id : EL_UNDEFINED);
2515
2516   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2517     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2518       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2519        element_info[game.panel.element_count[i].id].element_count : 0);
2520
2521   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2522     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2523       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2524        element_info[game.panel.ce_score[i].id].collect_score : 0);
2525
2526   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2527     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2528       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2529        element_info[game.panel.ce_score_element[i].id].collect_score :
2530        EL_UNDEFINED);
2531
2532   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2533   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2534   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2535
2536   // update game panel control frames
2537
2538   for (i = 0; game_panel_controls[i].nr != -1; i++)
2539   {
2540     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2541
2542     if (gpc->type == TYPE_ELEMENT)
2543     {
2544       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2545       {
2546         int last_anim_random_frame = gfx.anim_random_frame;
2547         int element = gpc->value;
2548         int graphic = el2panelimg(element);
2549         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2550                                sync_random_frame : INIT_GFX_RANDOM());
2551
2552         if (gpc->value != gpc->last_value)
2553         {
2554           gpc->gfx_frame = 0;
2555           gpc->gfx_random = init_gfx_random;
2556         }
2557         else
2558         {
2559           gpc->gfx_frame++;
2560
2561           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2562               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2563             gpc->gfx_random = init_gfx_random;
2564         }
2565
2566         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2567           gfx.anim_random_frame = gpc->gfx_random;
2568
2569         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2570           gpc->gfx_frame = element_info[element].collect_score;
2571
2572         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2573
2574         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2575           gfx.anim_random_frame = last_anim_random_frame;
2576       }
2577     }
2578     else if (gpc->type == TYPE_GRAPHIC)
2579     {
2580       if (gpc->graphic != IMG_UNDEFINED)
2581       {
2582         int last_anim_random_frame = gfx.anim_random_frame;
2583         int graphic = gpc->graphic;
2584         int init_gfx_random = (graphic_info[graphic].anim_global_sync ?
2585                                sync_random_frame : INIT_GFX_RANDOM());
2586
2587         if (gpc->value != gpc->last_value)
2588         {
2589           gpc->gfx_frame = 0;
2590           gpc->gfx_random = init_gfx_random;
2591         }
2592         else
2593         {
2594           gpc->gfx_frame++;
2595
2596           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2597               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2598             gpc->gfx_random = init_gfx_random;
2599         }
2600
2601         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2602           gfx.anim_random_frame = gpc->gfx_random;
2603
2604         gpc->frame = getGraphicAnimationFrame(graphic, gpc->gfx_frame);
2605
2606         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2607           gfx.anim_random_frame = last_anim_random_frame;
2608       }
2609     }
2610   }
2611 }
2612
2613 static void DisplayGameControlValues(void)
2614 {
2615   boolean redraw_panel = FALSE;
2616   int i;
2617
2618   for (i = 0; game_panel_controls[i].nr != -1; i++)
2619   {
2620     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2621
2622     if (PANEL_DEACTIVATED(gpc->pos))
2623       continue;
2624
2625     if (gpc->value == gpc->last_value &&
2626         gpc->frame == gpc->last_frame)
2627       continue;
2628
2629     redraw_panel = TRUE;
2630   }
2631
2632   if (!redraw_panel)
2633     return;
2634
2635   // copy default game door content to main double buffer
2636
2637   // !!! CHECK AGAIN !!!
2638   SetPanelBackground();
2639   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2640   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2641
2642   // redraw game control buttons
2643   RedrawGameButtons();
2644
2645   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2646
2647   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2648   {
2649     int nr = game_panel_order[i].nr;
2650     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2651     struct TextPosInfo *pos = gpc->pos;
2652     int type = gpc->type;
2653     int value = gpc->value;
2654     int frame = gpc->frame;
2655     int size = pos->size;
2656     int font = pos->font;
2657     boolean draw_masked = pos->draw_masked;
2658     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2659
2660     if (PANEL_DEACTIVATED(pos))
2661       continue;
2662
2663     if (pos->class == get_hash_from_key("extra_panel_items") &&
2664         !setup.prefer_extra_panel_items)
2665       continue;
2666
2667     gpc->last_value = value;
2668     gpc->last_frame = frame;
2669
2670     if (type == TYPE_INTEGER)
2671     {
2672       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2673           nr == GAME_PANEL_TIME)
2674       {
2675         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2676
2677         if (use_dynamic_size)           // use dynamic number of digits
2678         {
2679           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2680           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2681           int size2 = size1 + 1;
2682           int font1 = pos->font;
2683           int font2 = pos->font_alt;
2684
2685           size = (value < value_change ? size1 : size2);
2686           font = (value < value_change ? font1 : font2);
2687         }
2688       }
2689
2690       // correct text size if "digits" is zero or less
2691       if (size <= 0)
2692         size = strlen(int2str(value, size));
2693
2694       // dynamically correct text alignment
2695       pos->width = size * getFontWidth(font);
2696
2697       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2698                   int2str(value, size), font, mask_mode);
2699     }
2700     else if (type == TYPE_ELEMENT)
2701     {
2702       int element, graphic;
2703       Bitmap *src_bitmap;
2704       int src_x, src_y;
2705       int width, height;
2706       int dst_x = PANEL_XPOS(pos);
2707       int dst_y = PANEL_YPOS(pos);
2708
2709       if (value != EL_UNDEFINED && value != EL_EMPTY)
2710       {
2711         element = value;
2712         graphic = el2panelimg(value);
2713
2714 #if 0
2715         Debug("game:DisplayGameControlValues", "%d, '%s' [%d]",
2716               element, EL_NAME(element), size);
2717 #endif
2718
2719         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2720           size = TILESIZE;
2721
2722         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2723                               &src_x, &src_y);
2724
2725         width  = graphic_info[graphic].width  * size / TILESIZE;
2726         height = graphic_info[graphic].height * size / TILESIZE;
2727
2728         if (draw_masked)
2729           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2730                            dst_x, dst_y);
2731         else
2732           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2733                      dst_x, dst_y);
2734       }
2735     }
2736     else if (type == TYPE_GRAPHIC)
2737     {
2738       int graphic        = gpc->graphic;
2739       int graphic_active = gpc->graphic_active;
2740       Bitmap *src_bitmap;
2741       int src_x, src_y;
2742       int width, height;
2743       int dst_x = PANEL_XPOS(pos);
2744       int dst_y = PANEL_YPOS(pos);
2745       boolean skip = (pos->class == get_hash_from_key("mm_engine_only") &&
2746                       level.game_engine_type != GAME_ENGINE_TYPE_MM);
2747
2748       if (graphic != IMG_UNDEFINED && !skip)
2749       {
2750         if (pos->style == STYLE_REVERSE)
2751           value = 100 - value;
2752
2753         getGraphicSource(graphic_active, frame, &src_bitmap, &src_x, &src_y);
2754
2755         if (pos->direction & MV_HORIZONTAL)
2756         {
2757           width  = graphic_info[graphic_active].width * value / 100;
2758           height = graphic_info[graphic_active].height;
2759
2760           if (pos->direction == MV_LEFT)
2761           {
2762             src_x += graphic_info[graphic_active].width - width;
2763             dst_x += graphic_info[graphic_active].width - width;
2764           }
2765         }
2766         else
2767         {
2768           width  = graphic_info[graphic_active].width;
2769           height = graphic_info[graphic_active].height * value / 100;
2770
2771           if (pos->direction == MV_UP)
2772           {
2773             src_y += graphic_info[graphic_active].height - height;
2774             dst_y += graphic_info[graphic_active].height - height;
2775           }
2776         }
2777
2778         if (draw_masked)
2779           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2780                            dst_x, dst_y);
2781         else
2782           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2783                      dst_x, dst_y);
2784
2785         getGraphicSource(graphic, frame, &src_bitmap, &src_x, &src_y);
2786
2787         if (pos->direction & MV_HORIZONTAL)
2788         {
2789           if (pos->direction == MV_RIGHT)
2790           {
2791             src_x += width;
2792             dst_x += width;
2793           }
2794           else
2795           {
2796             dst_x = PANEL_XPOS(pos);
2797           }
2798
2799           width = graphic_info[graphic].width - width;
2800         }
2801         else
2802         {
2803           if (pos->direction == MV_DOWN)
2804           {
2805             src_y += height;
2806             dst_y += height;
2807           }
2808           else
2809           {
2810             dst_y = PANEL_YPOS(pos);
2811           }
2812
2813           height = graphic_info[graphic].height - height;
2814         }
2815
2816         if (draw_masked)
2817           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2818                            dst_x, dst_y);
2819         else
2820           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2821                      dst_x, dst_y);
2822       }
2823     }
2824     else if (type == TYPE_STRING)
2825     {
2826       boolean active = (value != 0);
2827       char *state_normal = "off";
2828       char *state_active = "on";
2829       char *state = (active ? state_active : state_normal);
2830       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2831                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2832                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2833                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2834
2835       if (nr == GAME_PANEL_GRAVITY_STATE)
2836       {
2837         int font1 = pos->font;          // (used for normal state)
2838         int font2 = pos->font_alt;      // (used for active state)
2839
2840         font = (active ? font2 : font1);
2841       }
2842
2843       if (s != NULL)
2844       {
2845         char *s_cut;
2846
2847         if (size <= 0)
2848         {
2849           // don't truncate output if "chars" is zero or less
2850           size = strlen(s);
2851
2852           // dynamically correct text alignment
2853           pos->width = size * getFontWidth(font);
2854         }
2855
2856         s_cut = getStringCopyN(s, size);
2857
2858         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2859                     s_cut, font, mask_mode);
2860
2861         free(s_cut);
2862       }
2863     }
2864
2865     redraw_mask |= REDRAW_DOOR_1;
2866   }
2867
2868   SetGameStatus(GAME_MODE_PLAYING);
2869 }
2870
2871 void UpdateAndDisplayGameControlValues(void)
2872 {
2873   if (tape.deactivate_display)
2874     return;
2875
2876   UpdateGameControlValues();
2877   DisplayGameControlValues();
2878 }
2879
2880 void UpdateGameDoorValues(void)
2881 {
2882   UpdateGameControlValues();
2883 }
2884
2885 void DrawGameDoorValues(void)
2886 {
2887   DisplayGameControlValues();
2888 }
2889
2890
2891 // ============================================================================
2892 // InitGameEngine()
2893 // ----------------------------------------------------------------------------
2894 // initialize game engine due to level / tape version number
2895 // ============================================================================
2896
2897 static void InitGameEngine(void)
2898 {
2899   int i, j, k, l, x, y;
2900
2901   // set game engine from tape file when re-playing, else from level file
2902   game.engine_version = (tape.playing ? tape.engine_version :
2903                          level.game_version);
2904
2905   // set single or multi-player game mode (needed for re-playing tapes)
2906   game.team_mode = setup.team_mode;
2907
2908   if (tape.playing)
2909   {
2910     int num_players = 0;
2911
2912     for (i = 0; i < MAX_PLAYERS; i++)
2913       if (tape.player_participates[i])
2914         num_players++;
2915
2916     // multi-player tapes contain input data for more than one player
2917     game.team_mode = (num_players > 1);
2918   }
2919
2920 #if 0
2921   Debug("game:init:level", "level %d: level.game_version  == %06d", level_nr,
2922         level.game_version);
2923   Debug("game:init:level", "          tape.file_version   == %06d",
2924         tape.file_version);
2925   Debug("game:init:level", "          tape.game_version   == %06d",
2926         tape.game_version);
2927   Debug("game:init:level", "          tape.engine_version == %06d",
2928         tape.engine_version);
2929   Debug("game:init:level", "       => game.engine_version == %06d [tape mode: %s]",
2930         game.engine_version, (tape.playing ? "PLAYING" : "RECORDING"));
2931 #endif
2932
2933   // --------------------------------------------------------------------------
2934   // set flags for bugs and changes according to active game engine version
2935   // --------------------------------------------------------------------------
2936
2937   /*
2938     Summary of bugfix:
2939     Fixed property "can fall" for run-time element "EL_AMOEBA_DROPPING"
2940
2941     Bug was introduced in version:
2942     2.0.1
2943
2944     Bug was fixed in version:
2945     4.2.0.0
2946
2947     Description:
2948     In version 2.0.1, a new run-time element "EL_AMOEBA_DROPPING" was added,
2949     but the property "can fall" was missing, which caused some levels to be
2950     unsolvable. This was fixed in version 4.2.0.0.
2951
2952     Affected levels/tapes:
2953     An example for a tape that was fixed by this bugfix is tape 029 from the
2954     level set "rnd_sam_bateman".
2955     The wrong behaviour will still be used for all levels or tapes that were
2956     created/recorded with it. An example for this is tape 023 from the level
2957     set "rnd_gerhard_haeusler", which was recorded with a buggy game engine.
2958   */
2959
2960   boolean use_amoeba_dropping_cannot_fall_bug =
2961     ((game.engine_version >= VERSION_IDENT(2,0,1,0) &&
2962       game.engine_version <  VERSION_IDENT(4,2,0,0)) ||
2963      (tape.playing &&
2964       tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2965       tape.game_version <  VERSION_IDENT(4,2,0,0)));
2966
2967   /*
2968     Summary of bugfix/change:
2969     Fixed move speed of elements entering or leaving magic wall.
2970
2971     Fixed/changed in version:
2972     2.0.1
2973
2974     Description:
2975     Before 2.0.1, move speed of elements entering or leaving magic wall was
2976     twice as fast as it is now.
2977     Since 2.0.1, this is set to a lower value by using move_stepsize_list[].
2978
2979     Affected levels/tapes:
2980     The first condition is generally needed for all levels/tapes before version
2981     2.0.1, which might use the old behaviour before it was changed; known tapes
2982     that are affected: Tape 014 from the level set "rnd_conor_mancone".
2983     The second condition is an exception from the above case and is needed for
2984     the special case of tapes recorded with game (not engine!) version 2.0.1 or
2985     above, but before it was known that this change would break tapes like the
2986     above and was fixed in 4.2.0.0, so that the changed behaviour was active
2987     although the engine version while recording maybe was before 2.0.1. There
2988     are a lot of tapes that are affected by this exception, like tape 006 from
2989     the level set "rnd_conor_mancone".
2990   */
2991
2992   boolean use_old_move_stepsize_for_magic_wall =
2993     (game.engine_version < VERSION_IDENT(2,0,1,0) &&
2994      !(tape.playing &&
2995        tape.game_version >= VERSION_IDENT(2,0,1,0) &&
2996        tape.game_version <  VERSION_IDENT(4,2,0,0)));
2997
2998   /*
2999     Summary of bugfix/change:
3000     Fixed handling for custom elements that change when pushed by the player.
3001
3002     Fixed/changed in version:
3003     3.1.0
3004
3005     Description:
3006     Before 3.1.0, custom elements that "change when pushing" changed directly
3007     after the player started pushing them (until then handled in "DigField()").
3008     Since 3.1.0, these custom elements are not changed until the "pushing"
3009     move of the element is finished (now handled in "ContinueMoving()").
3010
3011     Affected levels/tapes:
3012     The first condition is generally needed for all levels/tapes before version
3013     3.1.0, which might use the old behaviour before it was changed; known tapes
3014     that are affected are some tapes from the level set "Walpurgis Gardens" by
3015     Jamie Cullen.
3016     The second condition is an exception from the above case and is needed for
3017     the special case of tapes recorded with game (not engine!) version 3.1.0 or
3018     above (including some development versions of 3.1.0), but before it was
3019     known that this change would break tapes like the above and was fixed in
3020     3.1.1, so that the changed behaviour was active although the engine version
3021     while recording maybe was before 3.1.0. There is at least one tape that is
3022     affected by this exception, which is the tape for the one-level set "Bug
3023     Machine" by Juergen Bonhagen.
3024   */
3025
3026   game.use_change_when_pushing_bug =
3027     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
3028      !(tape.playing &&
3029        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
3030        tape.game_version <  VERSION_IDENT(3,1,1,0)));
3031
3032   /*
3033     Summary of bugfix/change:
3034     Fixed handling for blocking the field the player leaves when moving.
3035
3036     Fixed/changed in version:
3037     3.1.1
3038
3039     Description:
3040     Before 3.1.1, when "block last field when moving" was enabled, the field
3041     the player is leaving when moving was blocked for the time of the move,
3042     and was directly unblocked afterwards. This resulted in the last field
3043     being blocked for exactly one less than the number of frames of one player
3044     move. Additionally, even when blocking was disabled, the last field was
3045     blocked for exactly one frame.
3046     Since 3.1.1, due to changes in player movement handling, the last field
3047     is not blocked at all when blocking is disabled. When blocking is enabled,
3048     the last field is blocked for exactly the number of frames of one player
3049     move. Additionally, if the player is Murphy, the hero of Supaplex, the
3050     last field is blocked for exactly one more than the number of frames of
3051     one player move.
3052
3053     Affected levels/tapes:
3054     (!!! yet to be determined -- probably many !!!)
3055   */
3056
3057   game.use_block_last_field_bug =
3058     (game.engine_version < VERSION_IDENT(3,1,1,0));
3059
3060   /* various special flags and settings for native Emerald Mine game engine */
3061
3062   game_em.use_single_button =
3063     (game.engine_version > VERSION_IDENT(4,0,0,2));
3064
3065   game_em.use_snap_key_bug =
3066     (game.engine_version < VERSION_IDENT(4,0,1,0));
3067
3068   game_em.use_random_bug =
3069     (tape.property_bits & TAPE_PROPERTY_EM_RANDOM_BUG);
3070
3071   boolean use_old_em_engine = (game.engine_version < VERSION_IDENT(4,2,0,0));
3072
3073   game_em.use_old_explosions            = use_old_em_engine;
3074   game_em.use_old_android               = use_old_em_engine;
3075   game_em.use_old_push_elements         = use_old_em_engine;
3076   game_em.use_old_push_into_acid        = use_old_em_engine;
3077
3078   game_em.use_wrap_around               = !use_old_em_engine;
3079
3080   // --------------------------------------------------------------------------
3081
3082   // set maximal allowed number of custom element changes per game frame
3083   game.max_num_changes_per_frame = 1;
3084
3085   // default scan direction: scan playfield from top/left to bottom/right
3086   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
3087
3088   // dynamically adjust element properties according to game engine version
3089   InitElementPropertiesEngine(game.engine_version);
3090
3091   // ---------- initialize special element properties -------------------------
3092
3093   // "EL_AMOEBA_DROPPING" missed property "can fall" in older game versions
3094   if (use_amoeba_dropping_cannot_fall_bug)
3095     SET_PROPERTY(EL_AMOEBA_DROPPING, EP_CAN_FALL, FALSE);
3096
3097   // ---------- initialize player's initial move delay ------------------------
3098
3099   // dynamically adjust player properties according to level information
3100   for (i = 0; i < MAX_PLAYERS; i++)
3101     game.initial_move_delay_value[i] =
3102       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
3103
3104   // dynamically adjust player properties according to game engine version
3105   for (i = 0; i < MAX_PLAYERS; i++)
3106     game.initial_move_delay[i] =
3107       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
3108        game.initial_move_delay_value[i] : 0);
3109
3110   // ---------- initialize player's initial push delay ------------------------
3111
3112   // dynamically adjust player properties according to game engine version
3113   game.initial_push_delay_value =
3114     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
3115
3116   // ---------- initialize changing elements ----------------------------------
3117
3118   // initialize changing elements information
3119   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3120   {
3121     struct ElementInfo *ei = &element_info[i];
3122
3123     // this pointer might have been changed in the level editor
3124     ei->change = &ei->change_page[0];
3125
3126     if (!IS_CUSTOM_ELEMENT(i))
3127     {
3128       ei->change->target_element = EL_EMPTY_SPACE;
3129       ei->change->delay_fixed = 0;
3130       ei->change->delay_random = 0;
3131       ei->change->delay_frames = 1;
3132     }
3133
3134     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3135     {
3136       ei->has_change_event[j] = FALSE;
3137
3138       ei->event_page_nr[j] = 0;
3139       ei->event_page[j] = &ei->change_page[0];
3140     }
3141   }
3142
3143   // add changing elements from pre-defined list
3144   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
3145   {
3146     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
3147     struct ElementInfo *ei = &element_info[ch_delay->element];
3148
3149     ei->change->target_element       = ch_delay->target_element;
3150     ei->change->delay_fixed          = ch_delay->change_delay;
3151
3152     ei->change->pre_change_function  = ch_delay->pre_change_function;
3153     ei->change->change_function      = ch_delay->change_function;
3154     ei->change->post_change_function = ch_delay->post_change_function;
3155
3156     ei->change->can_change = TRUE;
3157     ei->change->can_change_or_has_action = TRUE;
3158
3159     ei->has_change_event[CE_DELAY] = TRUE;
3160
3161     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
3162     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
3163   }
3164
3165   // ---------- initialize internal run-time variables ------------------------
3166
3167   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3168   {
3169     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3170
3171     for (j = 0; j < ei->num_change_pages; j++)
3172     {
3173       ei->change_page[j].can_change_or_has_action =
3174         (ei->change_page[j].can_change |
3175          ei->change_page[j].has_action);
3176     }
3177   }
3178
3179   // add change events from custom element configuration
3180   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3181   {
3182     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3183
3184     for (j = 0; j < ei->num_change_pages; j++)
3185     {
3186       if (!ei->change_page[j].can_change_or_has_action)
3187         continue;
3188
3189       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3190       {
3191         // only add event page for the first page found with this event
3192         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
3193         {
3194           ei->has_change_event[k] = TRUE;
3195
3196           ei->event_page_nr[k] = j;
3197           ei->event_page[k] = &ei->change_page[j];
3198         }
3199       }
3200     }
3201   }
3202
3203   // ---------- initialize reference elements in change conditions ------------
3204
3205   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3206   {
3207     int element = EL_CUSTOM_START + i;
3208     struct ElementInfo *ei = &element_info[element];
3209
3210     for (j = 0; j < ei->num_change_pages; j++)
3211     {
3212       int trigger_element = ei->change_page[j].initial_trigger_element;
3213
3214       if (trigger_element >= EL_PREV_CE_8 &&
3215           trigger_element <= EL_NEXT_CE_8)
3216         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
3217
3218       ei->change_page[j].trigger_element = trigger_element;
3219     }
3220   }
3221
3222   // ---------- initialize run-time trigger player and element ----------------
3223
3224   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3225   {
3226     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
3227
3228     for (j = 0; j < ei->num_change_pages; j++)
3229     {
3230       ei->change_page[j].actual_trigger_element = EL_EMPTY;
3231       ei->change_page[j].actual_trigger_player = EL_EMPTY;
3232       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
3233       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
3234       ei->change_page[j].actual_trigger_ce_value = 0;
3235       ei->change_page[j].actual_trigger_ce_score = 0;
3236     }
3237   }
3238
3239   // ---------- initialize trigger events -------------------------------------
3240
3241   // initialize trigger events information
3242   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3243     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
3244       trigger_events[i][j] = FALSE;
3245
3246   // add trigger events from element change event properties
3247   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3248   {
3249     struct ElementInfo *ei = &element_info[i];
3250
3251     for (j = 0; j < ei->num_change_pages; j++)
3252     {
3253       if (!ei->change_page[j].can_change_or_has_action)
3254         continue;
3255
3256       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
3257       {
3258         int trigger_element = ei->change_page[j].trigger_element;
3259
3260         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
3261         {
3262           if (ei->change_page[j].has_event[k])
3263           {
3264             if (IS_GROUP_ELEMENT(trigger_element))
3265             {
3266               struct ElementGroupInfo *group =
3267                 element_info[trigger_element].group;
3268
3269               for (l = 0; l < group->num_elements_resolved; l++)
3270                 trigger_events[group->element_resolved[l]][k] = TRUE;
3271             }
3272             else if (trigger_element == EL_ANY_ELEMENT)
3273               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
3274                 trigger_events[l][k] = TRUE;
3275             else
3276               trigger_events[trigger_element][k] = TRUE;
3277           }
3278         }
3279       }
3280     }
3281   }
3282
3283   // ---------- initialize push delay -----------------------------------------
3284
3285   // initialize push delay values to default
3286   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3287   {
3288     if (!IS_CUSTOM_ELEMENT(i))
3289     {
3290       // set default push delay values (corrected since version 3.0.7-1)
3291       if (game.engine_version < VERSION_IDENT(3,0,7,1))
3292       {
3293         element_info[i].push_delay_fixed = 2;
3294         element_info[i].push_delay_random = 8;
3295       }
3296       else
3297       {
3298         element_info[i].push_delay_fixed = 8;
3299         element_info[i].push_delay_random = 8;
3300       }
3301     }
3302   }
3303
3304   // set push delay value for certain elements from pre-defined list
3305   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
3306   {
3307     int e = push_delay_list[i].element;
3308
3309     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
3310     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
3311   }
3312
3313   // set push delay value for Supaplex elements for newer engine versions
3314   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
3315   {
3316     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3317     {
3318       if (IS_SP_ELEMENT(i))
3319       {
3320         // set SP push delay to just enough to push under a falling zonk
3321         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
3322
3323         element_info[i].push_delay_fixed  = delay;
3324         element_info[i].push_delay_random = 0;
3325       }
3326     }
3327   }
3328
3329   // ---------- initialize move stepsize --------------------------------------
3330
3331   // initialize move stepsize values to default
3332   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3333     if (!IS_CUSTOM_ELEMENT(i))
3334       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
3335
3336   // set move stepsize value for certain elements from pre-defined list
3337   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
3338   {
3339     int e = move_stepsize_list[i].element;
3340
3341     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
3342
3343     // set move stepsize value for certain elements for older engine versions
3344     if (use_old_move_stepsize_for_magic_wall)
3345     {
3346       if (e == EL_MAGIC_WALL_FILLING ||
3347           e == EL_MAGIC_WALL_EMPTYING ||
3348           e == EL_BD_MAGIC_WALL_FILLING ||
3349           e == EL_BD_MAGIC_WALL_EMPTYING)
3350         element_info[e].move_stepsize *= 2;
3351     }
3352   }
3353
3354   // ---------- initialize collect score --------------------------------------
3355
3356   // initialize collect score values for custom elements from initial value
3357   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3358     if (IS_CUSTOM_ELEMENT(i))
3359       element_info[i].collect_score = element_info[i].collect_score_initial;
3360
3361   // ---------- initialize collect count --------------------------------------
3362
3363   // initialize collect count values for non-custom elements
3364   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3365     if (!IS_CUSTOM_ELEMENT(i))
3366       element_info[i].collect_count_initial = 0;
3367
3368   // add collect count values for all elements from pre-defined list
3369   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
3370     element_info[collect_count_list[i].element].collect_count_initial =
3371       collect_count_list[i].count;
3372
3373   // ---------- initialize access direction -----------------------------------
3374
3375   // initialize access direction values to default (access from every side)
3376   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3377     if (!IS_CUSTOM_ELEMENT(i))
3378       element_info[i].access_direction = MV_ALL_DIRECTIONS;
3379
3380   // set access direction value for certain elements from pre-defined list
3381   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
3382     element_info[access_direction_list[i].element].access_direction =
3383       access_direction_list[i].direction;
3384
3385   // ---------- initialize explosion content ----------------------------------
3386   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3387   {
3388     if (IS_CUSTOM_ELEMENT(i))
3389       continue;
3390
3391     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3392     {
3393       // (content for EL_YAMYAM set at run-time with game.yamyam_content_nr)
3394
3395       element_info[i].content.e[x][y] =
3396         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3397          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3398          i == EL_PLAYER_3 ? EL_EMERALD :
3399          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3400          i == EL_MOLE ? EL_EMERALD_RED :
3401          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3402          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3403          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3404          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3405          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3406          i == EL_WALL_EMERALD ? EL_EMERALD :
3407          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3408          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3409          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3410          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3411          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3412          i == EL_WALL_PEARL ? EL_PEARL :
3413          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3414          EL_EMPTY);
3415     }
3416   }
3417
3418   // ---------- initialize recursion detection --------------------------------
3419   recursion_loop_depth = 0;
3420   recursion_loop_detected = FALSE;
3421   recursion_loop_element = EL_UNDEFINED;
3422
3423   // ---------- initialize graphics engine ------------------------------------
3424   game.scroll_delay_value =
3425     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3426      level.game_engine_type == GAME_ENGINE_TYPE_EM &&
3427      !setup.forced_scroll_delay           ? 0 :
3428      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3429   game.scroll_delay_value =
3430     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3431
3432   // ---------- initialize game engine snapshots ------------------------------
3433   for (i = 0; i < MAX_PLAYERS; i++)
3434     game.snapshot.last_action[i] = 0;
3435   game.snapshot.changed_action = FALSE;
3436   game.snapshot.collected_item = FALSE;
3437   game.snapshot.mode =
3438     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3439      SNAPSHOT_MODE_EVERY_STEP :
3440      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3441      SNAPSHOT_MODE_EVERY_MOVE :
3442      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3443      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3444   game.snapshot.save_snapshot = FALSE;
3445
3446   // ---------- initialize level time for Supaplex engine ---------------------
3447   // Supaplex levels with time limit currently unsupported -- should be added
3448   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3449     level.time = 0;
3450
3451   // ---------- initialize flags for handling game actions --------------------
3452
3453   // set flags for game actions to default values
3454   game.use_key_actions = TRUE;
3455   game.use_mouse_actions = FALSE;
3456
3457   // when using Mirror Magic game engine, handle mouse events only
3458   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
3459   {
3460     game.use_key_actions = FALSE;
3461     game.use_mouse_actions = TRUE;
3462   }
3463
3464   // check for custom elements with mouse click events
3465   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
3466   {
3467     for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
3468     {
3469       int element = EL_CUSTOM_START + i;
3470
3471       if (HAS_CHANGE_EVENT(element, CE_CLICKED_BY_MOUSE) ||
3472           HAS_CHANGE_EVENT(element, CE_PRESSED_BY_MOUSE) ||
3473           HAS_CHANGE_EVENT(element, CE_MOUSE_CLICKED_ON_X) ||
3474           HAS_CHANGE_EVENT(element, CE_MOUSE_PRESSED_ON_X))
3475         game.use_mouse_actions = TRUE;
3476     }
3477   }
3478 }
3479
3480 static int get_num_special_action(int element, int action_first,
3481                                   int action_last)
3482 {
3483   int num_special_action = 0;
3484   int i, j;
3485
3486   for (i = action_first; i <= action_last; i++)
3487   {
3488     boolean found = FALSE;
3489
3490     for (j = 0; j < NUM_DIRECTIONS; j++)
3491       if (el_act_dir2img(element, i, j) !=
3492           el_act_dir2img(element, ACTION_DEFAULT, j))
3493         found = TRUE;
3494
3495     if (found)
3496       num_special_action++;
3497     else
3498       break;
3499   }
3500
3501   return num_special_action;
3502 }
3503
3504
3505 // ============================================================================
3506 // InitGame()
3507 // ----------------------------------------------------------------------------
3508 // initialize and start new game
3509 // ============================================================================
3510
3511 #if DEBUG_INIT_PLAYER
3512 static void DebugPrintPlayerStatus(char *message)
3513 {
3514   int i;
3515
3516   if (!options.debug)
3517     return;
3518
3519   Debug("game:init:player", "%s:", message);
3520
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     Debug("game:init:player",
3526           "- player %d: present == %d, connected == %d [%d/%d], active == %d%s",
3527           i + 1,
3528           player->present,
3529           player->connected,
3530           player->connected_locally,
3531           player->connected_network,
3532           player->active,
3533           (local_player == player ? " (local player)" : ""));
3534   }
3535 }
3536 #endif
3537
3538 void InitGame(void)
3539 {
3540   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3541   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3542   int fade_mask = REDRAW_FIELD;
3543
3544   boolean emulate_bd = TRUE;    // unless non-BOULDERDASH elements found
3545   boolean emulate_sb = TRUE;    // unless non-SOKOBAN     elements found
3546   boolean emulate_sp = TRUE;    // unless non-SUPAPLEX    elements found
3547   int initial_move_dir = MV_DOWN;
3548   int i, j, x, y;
3549
3550   // required here to update video display before fading (FIX THIS)
3551   DrawMaskedBorder(REDRAW_DOOR_2);
3552
3553   if (!game.restart_level)
3554     CloseDoor(DOOR_CLOSE_1);
3555
3556   SetGameStatus(GAME_MODE_PLAYING);
3557
3558   if (level_editor_test_game)
3559     FadeSkipNextFadeOut();
3560   else
3561     FadeSetEnterScreen();
3562
3563   if (CheckFadeAll())
3564     fade_mask = REDRAW_ALL;
3565
3566   FadeLevelSoundsAndMusic();
3567
3568   ExpireSoundLoops(TRUE);
3569
3570   FadeOut(fade_mask);
3571
3572   if (level_editor_test_game)
3573     FadeSkipNextFadeIn();
3574
3575   // needed if different viewport properties defined for playing
3576   ChangeViewportPropertiesIfNeeded();
3577
3578   ClearField();
3579
3580   DrawCompleteVideoDisplay();
3581
3582   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3583
3584   InitGameEngine();
3585   InitGameControlValues();
3586
3587   if (tape.recording)
3588   {
3589     // initialize tape actions from game when recording tape
3590     tape.use_key_actions   = game.use_key_actions;
3591     tape.use_mouse_actions = game.use_mouse_actions;
3592
3593     // initialize visible playfield size when recording tape (for team mode)
3594     tape.scr_fieldx = SCR_FIELDX;
3595     tape.scr_fieldy = SCR_FIELDY;
3596   }
3597
3598   // don't play tapes over network
3599   network_playing = (network.enabled && !tape.playing);
3600
3601   for (i = 0; i < MAX_PLAYERS; i++)
3602   {
3603     struct PlayerInfo *player = &stored_player[i];
3604
3605     player->index_nr = i;
3606     player->index_bit = (1 << i);
3607     player->element_nr = EL_PLAYER_1 + i;
3608
3609     player->present = FALSE;
3610     player->active = FALSE;
3611     player->mapped = FALSE;
3612
3613     player->killed = FALSE;
3614     player->reanimated = FALSE;
3615     player->buried = FALSE;
3616
3617     player->action = 0;
3618     player->effective_action = 0;
3619     player->programmed_action = 0;
3620     player->snap_action = 0;
3621
3622     player->mouse_action.lx = 0;
3623     player->mouse_action.ly = 0;
3624     player->mouse_action.button = 0;
3625     player->mouse_action.button_hint = 0;
3626
3627     player->effective_mouse_action.lx = 0;
3628     player->effective_mouse_action.ly = 0;
3629     player->effective_mouse_action.button = 0;
3630     player->effective_mouse_action.button_hint = 0;
3631
3632     for (j = 0; j < MAX_NUM_KEYS; j++)
3633       player->key[j] = FALSE;
3634
3635     player->num_white_keys = 0;
3636
3637     player->dynabomb_count = 0;
3638     player->dynabomb_size = 1;
3639     player->dynabombs_left = 0;
3640     player->dynabomb_xl = FALSE;
3641
3642     player->MovDir = initial_move_dir;
3643     player->MovPos = 0;
3644     player->GfxPos = 0;
3645     player->GfxDir = initial_move_dir;
3646     player->GfxAction = ACTION_DEFAULT;
3647     player->Frame = 0;
3648     player->StepFrame = 0;
3649
3650     player->initial_element = player->element_nr;
3651     player->artwork_element =
3652       (level.use_artwork_element[i] ? level.artwork_element[i] :
3653        player->element_nr);
3654     player->use_murphy = FALSE;
3655
3656     player->block_last_field = FALSE;   // initialized in InitPlayerField()
3657     player->block_delay_adjustment = 0; // initialized in InitPlayerField()
3658
3659     player->gravity = level.initial_player_gravity[i];
3660
3661     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3662
3663     player->actual_frame_counter = 0;
3664
3665     player->step_counter = 0;
3666
3667     player->last_move_dir = initial_move_dir;
3668
3669     player->is_active = FALSE;
3670
3671     player->is_waiting = FALSE;
3672     player->is_moving = FALSE;
3673     player->is_auto_moving = FALSE;
3674     player->is_digging = FALSE;
3675     player->is_snapping = FALSE;
3676     player->is_collecting = FALSE;
3677     player->is_pushing = FALSE;
3678     player->is_switching = FALSE;
3679     player->is_dropping = FALSE;
3680     player->is_dropping_pressed = FALSE;
3681
3682     player->is_bored = FALSE;
3683     player->is_sleeping = FALSE;
3684
3685     player->was_waiting = TRUE;
3686     player->was_moving = FALSE;
3687     player->was_snapping = FALSE;
3688     player->was_dropping = FALSE;
3689
3690     player->force_dropping = FALSE;
3691
3692     player->frame_counter_bored = -1;
3693     player->frame_counter_sleeping = -1;
3694
3695     player->anim_delay_counter = 0;
3696     player->post_delay_counter = 0;
3697
3698     player->dir_waiting = initial_move_dir;
3699     player->action_waiting = ACTION_DEFAULT;
3700     player->last_action_waiting = ACTION_DEFAULT;
3701     player->special_action_bored = ACTION_DEFAULT;
3702     player->special_action_sleeping = ACTION_DEFAULT;
3703
3704     player->switch_x = -1;
3705     player->switch_y = -1;
3706
3707     player->drop_x = -1;
3708     player->drop_y = -1;
3709
3710     player->show_envelope = 0;
3711
3712     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3713
3714     player->push_delay       = -1;      // initialized when pushing starts
3715     player->push_delay_value = game.initial_push_delay_value;
3716
3717     player->drop_delay = 0;
3718     player->drop_pressed_delay = 0;
3719
3720     player->last_jx = -1;
3721     player->last_jy = -1;
3722     player->jx = -1;
3723     player->jy = -1;
3724
3725     player->shield_normal_time_left = 0;
3726     player->shield_deadly_time_left = 0;
3727
3728     player->last_removed_element = EL_UNDEFINED;
3729
3730     player->inventory_infinite_element = EL_UNDEFINED;
3731     player->inventory_size = 0;
3732
3733     if (level.use_initial_inventory[i])
3734     {
3735       for (j = 0; j < level.initial_inventory_size[i]; j++)
3736       {
3737         int element = level.initial_inventory_content[i][j];
3738         int collect_count = element_info[element].collect_count_initial;
3739         int k;
3740
3741         if (!IS_CUSTOM_ELEMENT(element))
3742           collect_count = 1;
3743
3744         if (collect_count == 0)
3745           player->inventory_infinite_element = element;
3746         else
3747           for (k = 0; k < collect_count; k++)
3748             if (player->inventory_size < MAX_INVENTORY_SIZE)
3749               player->inventory_element[player->inventory_size++] = element;
3750       }
3751     }
3752
3753     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3754     SnapField(player, 0, 0);
3755
3756     map_player_action[i] = i;
3757   }
3758
3759   network_player_action_received = FALSE;
3760
3761   // initial null action
3762   if (network_playing)
3763     SendToServer_MovePlayer(MV_NONE);
3764
3765   FrameCounter = 0;
3766   TimeFrames = 0;
3767   TimePlayed = 0;
3768   TimeLeft = level.time;
3769   TapeTime = 0;
3770
3771   ScreenMovDir = MV_NONE;
3772   ScreenMovPos = 0;
3773   ScreenGfxPos = 0;
3774
3775   ScrollStepSize = 0;   // will be correctly initialized by ScrollScreen()
3776
3777   game.robot_wheel_x = -1;
3778   game.robot_wheel_y = -1;
3779
3780   game.exit_x = -1;
3781   game.exit_y = -1;
3782
3783   game.all_players_gone = FALSE;
3784
3785   game.LevelSolved = FALSE;
3786   game.GameOver = FALSE;
3787
3788   game.GamePlayed = !tape.playing;
3789
3790   game.LevelSolved_GameWon = FALSE;
3791   game.LevelSolved_GameEnd = FALSE;
3792   game.LevelSolved_SaveTape = FALSE;
3793   game.LevelSolved_SaveScore = FALSE;
3794
3795   game.LevelSolved_CountingTime = 0;
3796   game.LevelSolved_CountingScore = 0;
3797   game.LevelSolved_CountingHealth = 0;
3798
3799   game.panel.active = TRUE;
3800
3801   game.no_time_limit = (level.time == 0);
3802
3803   game.yamyam_content_nr = 0;
3804   game.robot_wheel_active = FALSE;
3805   game.magic_wall_active = FALSE;
3806   game.magic_wall_time_left = 0;
3807   game.light_time_left = 0;
3808   game.timegate_time_left = 0;
3809   game.switchgate_pos = 0;
3810   game.wind_direction = level.wind_direction_initial;
3811
3812   game.time_final = 0;
3813   game.score_time_final = 0;
3814
3815   game.score = 0;
3816   game.score_final = 0;
3817
3818   game.health = MAX_HEALTH;
3819   game.health_final = MAX_HEALTH;
3820
3821   game.gems_still_needed = level.gems_needed;
3822   game.sokoban_fields_still_needed = 0;
3823   game.sokoban_objects_still_needed = 0;
3824   game.lights_still_needed = 0;
3825   game.players_still_needed = 0;
3826   game.friends_still_needed = 0;
3827
3828   game.lenses_time_left = 0;
3829   game.magnify_time_left = 0;
3830
3831   game.ball_active = level.ball_active_initial;
3832   game.ball_content_nr = 0;
3833
3834   game.explosions_delayed = TRUE;
3835
3836   game.envelope_active = FALSE;
3837
3838   for (i = 0; i < NUM_BELTS; i++)
3839   {
3840     game.belt_dir[i] = MV_NONE;
3841     game.belt_dir_nr[i] = 3;            // not moving, next moving left
3842   }
3843
3844   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3845     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3846
3847 #if DEBUG_INIT_PLAYER
3848   DebugPrintPlayerStatus("Player status at level initialization");
3849 #endif
3850
3851   SCAN_PLAYFIELD(x, y)
3852   {
3853     Tile[x][y] = Last[x][y] = level.field[x][y];
3854     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3855     ChangeDelay[x][y] = 0;
3856     ChangePage[x][y] = -1;
3857     CustomValue[x][y] = 0;              // initialized in InitField()
3858     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3859     AmoebaNr[x][y] = 0;
3860     WasJustMoving[x][y] = 0;
3861     WasJustFalling[x][y] = 0;
3862     CheckCollision[x][y] = 0;
3863     CheckImpact[x][y] = 0;
3864     Stop[x][y] = FALSE;
3865     Pushed[x][y] = FALSE;
3866
3867     ChangeCount[x][y] = 0;
3868     ChangeEvent[x][y] = -1;
3869
3870     ExplodePhase[x][y] = 0;
3871     ExplodeDelay[x][y] = 0;
3872     ExplodeField[x][y] = EX_TYPE_NONE;
3873
3874     RunnerVisit[x][y] = 0;
3875     PlayerVisit[x][y] = 0;
3876
3877     GfxFrame[x][y] = 0;
3878     GfxRandom[x][y] = INIT_GFX_RANDOM();
3879     GfxElement[x][y] = EL_UNDEFINED;
3880     GfxAction[x][y] = ACTION_DEFAULT;
3881     GfxDir[x][y] = MV_NONE;
3882     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3883   }
3884
3885   SCAN_PLAYFIELD(x, y)
3886   {
3887     if (emulate_bd && !IS_BD_ELEMENT(Tile[x][y]))
3888       emulate_bd = FALSE;
3889     if (emulate_sb && !IS_SB_ELEMENT(Tile[x][y]))
3890       emulate_sb = FALSE;
3891     if (emulate_sp && !IS_SP_ELEMENT(Tile[x][y]))
3892       emulate_sp = FALSE;
3893
3894     InitField(x, y, TRUE);
3895
3896     ResetGfxAnimation(x, y);
3897   }
3898
3899   InitBeltMovement();
3900
3901   for (i = 0; i < MAX_PLAYERS; i++)
3902   {
3903     struct PlayerInfo *player = &stored_player[i];
3904
3905     // set number of special actions for bored and sleeping animation
3906     player->num_special_action_bored =
3907       get_num_special_action(player->artwork_element,
3908                              ACTION_BORING_1, ACTION_BORING_LAST);
3909     player->num_special_action_sleeping =
3910       get_num_special_action(player->artwork_element,
3911                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3912   }
3913
3914   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3915                     emulate_sb ? EMU_SOKOBAN :
3916                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3917
3918   // initialize type of slippery elements
3919   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3920   {
3921     if (!IS_CUSTOM_ELEMENT(i))
3922     {
3923       // default: elements slip down either to the left or right randomly
3924       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3925
3926       // SP style elements prefer to slip down on the left side
3927       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3928         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3929
3930       // BD style elements prefer to slip down on the left side
3931       if (game.emulation == EMU_BOULDERDASH)
3932         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3933     }
3934   }
3935
3936   // initialize explosion and ignition delay
3937   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3938   {
3939     if (!IS_CUSTOM_ELEMENT(i))
3940     {
3941       int num_phase = 8;
3942       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3943                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3944                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3945       int last_phase = (num_phase + 1) * delay;
3946       int half_phase = (num_phase / 2) * delay;
3947
3948       element_info[i].explosion_delay = last_phase - 1;
3949       element_info[i].ignition_delay = half_phase;
3950
3951       if (i == EL_BLACK_ORB)
3952         element_info[i].ignition_delay = 1;
3953     }
3954   }
3955
3956   // correct non-moving belts to start moving left
3957   for (i = 0; i < NUM_BELTS; i++)
3958     if (game.belt_dir[i] == MV_NONE)
3959       game.belt_dir_nr[i] = 3;          // not moving, next moving left
3960
3961 #if USE_NEW_PLAYER_ASSIGNMENTS
3962   // use preferred player also in local single-player mode
3963   if (!network.enabled && !game.team_mode)
3964   {
3965     int new_index_nr = setup.network_player_nr;
3966
3967     if (new_index_nr >= 0 && new_index_nr < MAX_PLAYERS)
3968     {
3969       for (i = 0; i < MAX_PLAYERS; i++)
3970         stored_player[i].connected_locally = FALSE;
3971
3972       stored_player[new_index_nr].connected_locally = TRUE;
3973     }
3974   }
3975
3976   for (i = 0; i < MAX_PLAYERS; i++)
3977   {
3978     stored_player[i].connected = FALSE;
3979
3980     // in network game mode, the local player might not be the first player
3981     if (stored_player[i].connected_locally)
3982       local_player = &stored_player[i];
3983   }
3984
3985   if (!network.enabled)
3986     local_player->connected = TRUE;
3987
3988   if (tape.playing)
3989   {
3990     for (i = 0; i < MAX_PLAYERS; i++)
3991       stored_player[i].connected = tape.player_participates[i];
3992   }
3993   else if (network.enabled)
3994   {
3995     // add team mode players connected over the network (needed for correct
3996     // assignment of player figures from level to locally playing players)
3997
3998     for (i = 0; i < MAX_PLAYERS; i++)
3999       if (stored_player[i].connected_network)
4000         stored_player[i].connected = TRUE;
4001   }
4002   else if (game.team_mode)
4003   {
4004     // try to guess locally connected team mode players (needed for correct
4005     // assignment of player figures from level to locally playing players)
4006
4007     for (i = 0; i < MAX_PLAYERS; i++)
4008       if (setup.input[i].use_joystick ||
4009           setup.input[i].key.left != KSYM_UNDEFINED)
4010         stored_player[i].connected = TRUE;
4011   }
4012
4013 #if DEBUG_INIT_PLAYER
4014   DebugPrintPlayerStatus("Player status after level initialization");
4015 #endif
4016
4017 #if DEBUG_INIT_PLAYER
4018   Debug("game:init:player", "Reassigning players ...");
4019 #endif
4020
4021   // check if any connected player was not found in playfield
4022   for (i = 0; i < MAX_PLAYERS; i++)
4023   {
4024     struct PlayerInfo *player = &stored_player[i];
4025
4026     if (player->connected && !player->present)
4027     {
4028       struct PlayerInfo *field_player = NULL;
4029
4030 #if DEBUG_INIT_PLAYER
4031       Debug("game:init:player",
4032             "- looking for field player for player %d ...", i + 1);
4033 #endif
4034
4035       // assign first free player found that is present in the playfield
4036
4037       // first try: look for unmapped playfield player that is not connected
4038       for (j = 0; j < MAX_PLAYERS; j++)
4039         if (field_player == NULL &&
4040             stored_player[j].present &&
4041             !stored_player[j].mapped &&
4042             !stored_player[j].connected)
4043           field_player = &stored_player[j];
4044
4045       // second try: look for *any* unmapped playfield player
4046       for (j = 0; j < MAX_PLAYERS; j++)
4047         if (field_player == NULL &&
4048             stored_player[j].present &&
4049             !stored_player[j].mapped)
4050           field_player = &stored_player[j];
4051
4052       if (field_player != NULL)
4053       {
4054         int jx = field_player->jx, jy = field_player->jy;
4055
4056 #if DEBUG_INIT_PLAYER
4057         Debug("game:init:player", "- found player %d",
4058               field_player->index_nr + 1);
4059 #endif
4060
4061         player->present = FALSE;
4062         player->active = FALSE;
4063
4064         field_player->present = TRUE;
4065         field_player->active = TRUE;
4066
4067         /*
4068         player->initial_element = field_player->initial_element;
4069         player->artwork_element = field_player->artwork_element;
4070
4071         player->block_last_field       = field_player->block_last_field;
4072         player->block_delay_adjustment = field_player->block_delay_adjustment;
4073         */
4074
4075         StorePlayer[jx][jy] = field_player->element_nr;
4076
4077         field_player->jx = field_player->last_jx = jx;
4078         field_player->jy = field_player->last_jy = jy;
4079
4080         if (local_player == player)
4081           local_player = field_player;
4082
4083         map_player_action[field_player->index_nr] = i;
4084
4085         field_player->mapped = TRUE;
4086
4087 #if DEBUG_INIT_PLAYER
4088         Debug("game:init:player", "- map_player_action[%d] == %d",
4089               field_player->index_nr + 1, i + 1);
4090 #endif
4091       }
4092     }
4093
4094     if (player->connected && player->present)
4095       player->mapped = TRUE;
4096   }
4097
4098 #if DEBUG_INIT_PLAYER
4099   DebugPrintPlayerStatus("Player status after player assignment (first stage)");
4100 #endif
4101
4102 #else
4103
4104   // check if any connected player was not found in playfield
4105   for (i = 0; i < MAX_PLAYERS; i++)
4106   {
4107     struct PlayerInfo *player = &stored_player[i];
4108
4109     if (player->connected && !player->present)
4110     {
4111       for (j = 0; j < MAX_PLAYERS; j++)
4112       {
4113         struct PlayerInfo *field_player = &stored_player[j];
4114         int jx = field_player->jx, jy = field_player->jy;
4115
4116         // assign first free player found that is present in the playfield
4117         if (field_player->present && !field_player->connected)
4118         {
4119           player->present = TRUE;
4120           player->active = TRUE;
4121
4122           field_player->present = FALSE;
4123           field_player->active = FALSE;
4124
4125           player->initial_element = field_player->initial_element;
4126           player->artwork_element = field_player->artwork_element;
4127
4128           player->block_last_field       = field_player->block_last_field;
4129           player->block_delay_adjustment = field_player->block_delay_adjustment;
4130
4131           StorePlayer[jx][jy] = player->element_nr;
4132
4133           player->jx = player->last_jx = jx;
4134           player->jy = player->last_jy = jy;
4135
4136           break;
4137         }
4138       }
4139     }
4140   }
4141 #endif
4142
4143 #if 0
4144   Debug("game:init:player", "local_player->present == %d",
4145         local_player->present);
4146 #endif
4147
4148   // set focus to local player for network games, else to all players
4149   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
4150   game.centered_player_nr_next = game.centered_player_nr;
4151   game.set_centered_player = FALSE;
4152   game.set_centered_player_wrap = FALSE;
4153
4154   if (network_playing && tape.recording)
4155   {
4156     // store client dependent player focus when recording network games
4157     tape.centered_player_nr_next = game.centered_player_nr_next;
4158     tape.set_centered_player = TRUE;
4159   }
4160
4161   if (tape.playing)
4162   {
4163     // when playing a tape, eliminate all players who do not participate
4164
4165 #if USE_NEW_PLAYER_ASSIGNMENTS
4166
4167     if (!game.team_mode)
4168     {
4169       for (i = 0; i < MAX_PLAYERS; i++)
4170       {
4171         if (stored_player[i].active &&
4172             !tape.player_participates[map_player_action[i]])
4173         {
4174           struct PlayerInfo *player = &stored_player[i];
4175           int jx = player->jx, jy = player->jy;
4176
4177 #if DEBUG_INIT_PLAYER
4178           Debug("game:init:player", "Removing player %d at (%d, %d)",
4179                 i + 1, jx, jy);
4180 #endif
4181
4182           player->active = FALSE;
4183           StorePlayer[jx][jy] = 0;
4184           Tile[jx][jy] = EL_EMPTY;
4185         }
4186       }
4187     }
4188
4189 #else
4190
4191     for (i = 0; i < MAX_PLAYERS; i++)
4192     {
4193       if (stored_player[i].active &&
4194           !tape.player_participates[i])
4195       {
4196         struct PlayerInfo *player = &stored_player[i];
4197         int jx = player->jx, jy = player->jy;
4198
4199         player->active = FALSE;
4200         StorePlayer[jx][jy] = 0;
4201         Tile[jx][jy] = EL_EMPTY;
4202       }
4203     }
4204 #endif
4205   }
4206   else if (!network.enabled && !game.team_mode)         // && !tape.playing
4207   {
4208     // when in single player mode, eliminate all but the local player
4209
4210     for (i = 0; i < MAX_PLAYERS; i++)
4211     {
4212       struct PlayerInfo *player = &stored_player[i];
4213
4214       if (player->active && player != local_player)
4215       {
4216         int jx = player->jx, jy = player->jy;
4217
4218         player->active = FALSE;
4219         player->present = FALSE;
4220
4221         StorePlayer[jx][jy] = 0;
4222         Tile[jx][jy] = EL_EMPTY;
4223       }
4224     }
4225   }
4226
4227   for (i = 0; i < MAX_PLAYERS; i++)
4228     if (stored_player[i].active)
4229       game.players_still_needed++;
4230
4231   if (level.solved_by_one_player)
4232     game.players_still_needed = 1;
4233
4234   // when recording the game, store which players take part in the game
4235   if (tape.recording)
4236   {
4237 #if USE_NEW_PLAYER_ASSIGNMENTS
4238     for (i = 0; i < MAX_PLAYERS; i++)
4239       if (stored_player[i].connected)
4240         tape.player_participates[i] = TRUE;
4241 #else
4242     for (i = 0; i < MAX_PLAYERS; i++)
4243       if (stored_player[i].active)
4244         tape.player_participates[i] = TRUE;
4245 #endif
4246   }
4247
4248 #if DEBUG_INIT_PLAYER
4249   DebugPrintPlayerStatus("Player status after player assignment (final stage)");
4250 #endif
4251
4252   if (BorderElement == EL_EMPTY)
4253   {
4254     SBX_Left = 0;
4255     SBX_Right = lev_fieldx - SCR_FIELDX;
4256     SBY_Upper = 0;
4257     SBY_Lower = lev_fieldy - SCR_FIELDY;
4258   }
4259   else
4260   {
4261     SBX_Left = -1;
4262     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
4263     SBY_Upper = -1;
4264     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
4265   }
4266
4267   if (full_lev_fieldx <= SCR_FIELDX)
4268     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
4269   if (full_lev_fieldy <= SCR_FIELDY)
4270     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
4271
4272   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
4273     SBX_Left--;
4274   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
4275     SBY_Upper--;
4276
4277   // if local player not found, look for custom element that might create
4278   // the player (make some assumptions about the right custom element)
4279   if (!local_player->present)
4280   {
4281     int start_x = 0, start_y = 0;
4282     int found_rating = 0;
4283     int found_element = EL_UNDEFINED;
4284     int player_nr = local_player->index_nr;
4285
4286     SCAN_PLAYFIELD(x, y)
4287     {
4288       int element = Tile[x][y];
4289       int content;
4290       int xx, yy;
4291       boolean is_player;
4292
4293       if (level.use_start_element[player_nr] &&
4294           level.start_element[player_nr] == element &&
4295           found_rating < 4)
4296       {
4297         start_x = x;
4298         start_y = y;
4299
4300         found_rating = 4;
4301         found_element = element;
4302       }
4303
4304       if (!IS_CUSTOM_ELEMENT(element))
4305         continue;
4306
4307       if (CAN_CHANGE(element))
4308       {
4309         for (i = 0; i < element_info[element].num_change_pages; i++)
4310         {
4311           // check for player created from custom element as single target
4312           content = element_info[element].change_page[i].target_element;
4313           is_player = ELEM_IS_PLAYER(content);
4314
4315           if (is_player && (found_rating < 3 ||
4316                             (found_rating == 3 && element < found_element)))
4317           {
4318             start_x = x;
4319             start_y = y;
4320
4321             found_rating = 3;
4322             found_element = element;
4323           }
4324         }
4325       }
4326
4327       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
4328       {
4329         // check for player created from custom element as explosion content
4330         content = element_info[element].content.e[xx][yy];
4331         is_player = ELEM_IS_PLAYER(content);
4332
4333         if (is_player && (found_rating < 2 ||
4334                           (found_rating == 2 && element < found_element)))
4335         {
4336           start_x = x + xx - 1;
4337           start_y = y + yy - 1;
4338
4339           found_rating = 2;
4340           found_element = element;
4341         }
4342
4343         if (!CAN_CHANGE(element))
4344           continue;
4345
4346         for (i = 0; i < element_info[element].num_change_pages; i++)
4347         {
4348           // check for player created from custom element as extended target
4349           content =
4350             element_info[element].change_page[i].target_content.e[xx][yy];
4351
4352           is_player = ELEM_IS_PLAYER(content);
4353
4354           if (is_player && (found_rating < 1 ||
4355                             (found_rating == 1 && element < found_element)))
4356           {
4357             start_x = x + xx - 1;
4358             start_y = y + yy - 1;
4359
4360             found_rating = 1;
4361             found_element = element;
4362           }
4363         }
4364       }
4365     }
4366
4367     scroll_x = SCROLL_POSITION_X(start_x);
4368     scroll_y = SCROLL_POSITION_Y(start_y);
4369   }
4370   else
4371   {
4372     scroll_x = SCROLL_POSITION_X(local_player->jx);
4373     scroll_y = SCROLL_POSITION_Y(local_player->jy);
4374   }
4375
4376   // !!! FIX THIS (START) !!!
4377   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4378   {
4379     InitGameEngine_EM();
4380   }
4381   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
4382   {
4383     InitGameEngine_SP();
4384   }
4385   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4386   {
4387     InitGameEngine_MM();
4388   }
4389   else
4390   {
4391     DrawLevel(REDRAW_FIELD);
4392     DrawAllPlayers();
4393
4394     // after drawing the level, correct some elements
4395     if (game.timegate_time_left == 0)
4396       CloseAllOpenTimegates();
4397   }
4398
4399   // blit playfield from scroll buffer to normal back buffer for fading in
4400   BlitScreenToBitmap(backbuffer);
4401   // !!! FIX THIS (END) !!!
4402
4403   DrawMaskedBorder(fade_mask);
4404
4405   FadeIn(fade_mask);
4406
4407 #if 1
4408   // full screen redraw is required at this point in the following cases:
4409   // - special editor door undrawn when game was started from level editor
4410   // - drawing area (playfield) was changed and has to be removed completely
4411   redraw_mask = REDRAW_ALL;
4412   BackToFront();
4413 #endif
4414
4415   if (!game.restart_level)
4416   {
4417     // copy default game door content to main double buffer
4418
4419     // !!! CHECK AGAIN !!!
4420     SetPanelBackground();
4421     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
4422     DrawBackground(DX, DY, DXSIZE, DYSIZE);
4423   }
4424
4425   SetPanelBackground();
4426   SetDrawBackgroundMask(REDRAW_DOOR_1);
4427
4428   UpdateAndDisplayGameControlValues();
4429
4430   if (!game.restart_level)
4431   {
4432     UnmapGameButtons();
4433     UnmapTapeButtons();
4434
4435     FreeGameButtons();
4436     CreateGameButtons();
4437
4438     MapGameButtons();
4439     MapTapeButtons();
4440
4441     // copy actual game door content to door double buffer for OpenDoor()
4442     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
4443
4444     OpenDoor(DOOR_OPEN_ALL);
4445
4446     KeyboardAutoRepeatOffUnlessAutoplay();
4447
4448 #if DEBUG_INIT_PLAYER
4449     DebugPrintPlayerStatus("Player status (final)");
4450 #endif
4451   }
4452
4453   UnmapAllGadgets();
4454
4455   MapGameButtons();
4456   MapTapeButtons();
4457
4458   if (!game.restart_level && !tape.playing)
4459   {
4460     LevelStats_incPlayed(level_nr);
4461
4462     SaveLevelSetup_SeriesInfo();
4463   }
4464
4465   game.restart_level = FALSE;
4466   game.restart_game_message = NULL;
4467
4468   game.request_active = FALSE;
4469   game.request_active_or_moving = FALSE;
4470
4471   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4472     InitGameActions_MM();
4473
4474   SaveEngineSnapshotToListInitial();
4475
4476   if (!game.restart_level)
4477   {
4478     PlaySound(SND_GAME_STARTING);
4479
4480     if (setup.sound_music)
4481       PlayLevelMusic();
4482   }
4483 }
4484
4485 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4486                         int actual_player_x, int actual_player_y)
4487 {
4488   // this is used for non-R'n'D game engines to update certain engine values
4489
4490   // needed to determine if sounds are played within the visible screen area
4491   scroll_x = actual_scroll_x;
4492   scroll_y = actual_scroll_y;
4493
4494   // needed to get player position for "follow finger" playing input method
4495   local_player->jx = actual_player_x;
4496   local_player->jy = actual_player_y;
4497 }
4498
4499 void InitMovDir(int x, int y)
4500 {
4501   int i, element = Tile[x][y];
4502   static int xy[4][2] =
4503   {
4504     {  0, +1 },
4505     { +1,  0 },
4506     {  0, -1 },
4507     { -1,  0 }
4508   };
4509   static int direction[3][4] =
4510   {
4511     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4512     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4513     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4514   };
4515
4516   switch (element)
4517   {
4518     case EL_BUG_RIGHT:
4519     case EL_BUG_UP:
4520     case EL_BUG_LEFT:
4521     case EL_BUG_DOWN:
4522       Tile[x][y] = EL_BUG;
4523       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4524       break;
4525
4526     case EL_SPACESHIP_RIGHT:
4527     case EL_SPACESHIP_UP:
4528     case EL_SPACESHIP_LEFT:
4529     case EL_SPACESHIP_DOWN:
4530       Tile[x][y] = EL_SPACESHIP;
4531       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4532       break;
4533
4534     case EL_BD_BUTTERFLY_RIGHT:
4535     case EL_BD_BUTTERFLY_UP:
4536     case EL_BD_BUTTERFLY_LEFT:
4537     case EL_BD_BUTTERFLY_DOWN:
4538       Tile[x][y] = EL_BD_BUTTERFLY;
4539       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4540       break;
4541
4542     case EL_BD_FIREFLY_RIGHT:
4543     case EL_BD_FIREFLY_UP:
4544     case EL_BD_FIREFLY_LEFT:
4545     case EL_BD_FIREFLY_DOWN:
4546       Tile[x][y] = EL_BD_FIREFLY;
4547       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4548       break;
4549
4550     case EL_PACMAN_RIGHT:
4551     case EL_PACMAN_UP:
4552     case EL_PACMAN_LEFT:
4553     case EL_PACMAN_DOWN:
4554       Tile[x][y] = EL_PACMAN;
4555       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4556       break;
4557
4558     case EL_YAMYAM_LEFT:
4559     case EL_YAMYAM_RIGHT:
4560     case EL_YAMYAM_UP:
4561     case EL_YAMYAM_DOWN:
4562       Tile[x][y] = EL_YAMYAM;
4563       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4564       break;
4565
4566     case EL_SP_SNIKSNAK:
4567       MovDir[x][y] = MV_UP;
4568       break;
4569
4570     case EL_SP_ELECTRON:
4571       MovDir[x][y] = MV_LEFT;
4572       break;
4573
4574     case EL_MOLE_LEFT:
4575     case EL_MOLE_RIGHT:
4576     case EL_MOLE_UP:
4577     case EL_MOLE_DOWN:
4578       Tile[x][y] = EL_MOLE;
4579       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4580       break;
4581
4582     case EL_SPRING_LEFT:
4583     case EL_SPRING_RIGHT:
4584       Tile[x][y] = EL_SPRING;
4585       MovDir[x][y] = direction[2][element - EL_SPRING_LEFT];
4586       break;
4587
4588     default:
4589       if (IS_CUSTOM_ELEMENT(element))
4590       {
4591         struct ElementInfo *ei = &element_info[element];
4592         int move_direction_initial = ei->move_direction_initial;
4593         int move_pattern = ei->move_pattern;
4594
4595         if (move_direction_initial == MV_START_PREVIOUS)
4596         {
4597           if (MovDir[x][y] != MV_NONE)
4598             return;
4599
4600           move_direction_initial = MV_START_AUTOMATIC;
4601         }
4602
4603         if (move_direction_initial == MV_START_RANDOM)
4604           MovDir[x][y] = 1 << RND(4);
4605         else if (move_direction_initial & MV_ANY_DIRECTION)
4606           MovDir[x][y] = move_direction_initial;
4607         else if (move_pattern == MV_ALL_DIRECTIONS ||
4608                  move_pattern == MV_TURNING_LEFT ||
4609                  move_pattern == MV_TURNING_RIGHT ||
4610                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4611                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4612                  move_pattern == MV_TURNING_RANDOM)
4613           MovDir[x][y] = 1 << RND(4);
4614         else if (move_pattern == MV_HORIZONTAL)
4615           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4616         else if (move_pattern == MV_VERTICAL)
4617           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4618         else if (move_pattern & MV_ANY_DIRECTION)
4619           MovDir[x][y] = element_info[element].move_pattern;
4620         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4621                  move_pattern == MV_ALONG_RIGHT_SIDE)
4622         {
4623           // use random direction as default start direction
4624           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4625             MovDir[x][y] = 1 << RND(4);
4626
4627           for (i = 0; i < NUM_DIRECTIONS; i++)
4628           {
4629             int x1 = x + xy[i][0];
4630             int y1 = y + xy[i][1];
4631
4632             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4633             {
4634               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4635                 MovDir[x][y] = direction[0][i];
4636               else
4637                 MovDir[x][y] = direction[1][i];
4638
4639               break;
4640             }
4641           }
4642         }                
4643       }
4644       else
4645       {
4646         MovDir[x][y] = 1 << RND(4);
4647
4648         if (element != EL_BUG &&
4649             element != EL_SPACESHIP &&
4650             element != EL_BD_BUTTERFLY &&
4651             element != EL_BD_FIREFLY)
4652           break;
4653
4654         for (i = 0; i < NUM_DIRECTIONS; i++)
4655         {
4656           int x1 = x + xy[i][0];
4657           int y1 = y + xy[i][1];
4658
4659           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4660           {
4661             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4662             {
4663               MovDir[x][y] = direction[0][i];
4664               break;
4665             }
4666             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4667                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4668             {
4669               MovDir[x][y] = direction[1][i];
4670               break;
4671             }
4672           }
4673         }
4674       }
4675       break;
4676   }
4677
4678   GfxDir[x][y] = MovDir[x][y];
4679 }
4680
4681 void InitAmoebaNr(int x, int y)
4682 {
4683   int i;
4684   int group_nr = AmoebaNeighbourNr(x, y);
4685
4686   if (group_nr == 0)
4687   {
4688     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4689     {
4690       if (AmoebaCnt[i] == 0)
4691       {
4692         group_nr = i;
4693         break;
4694       }
4695     }
4696   }
4697
4698   AmoebaNr[x][y] = group_nr;
4699   AmoebaCnt[group_nr]++;
4700   AmoebaCnt2[group_nr]++;
4701 }
4702
4703 static void LevelSolved(void)
4704 {
4705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND &&
4706       game.players_still_needed > 0)
4707     return;
4708
4709   game.LevelSolved = TRUE;
4710   game.GameOver = TRUE;
4711 }
4712
4713 void GameWon(void)
4714 {
4715   static int time_count_steps;
4716   static int time, time_final;
4717   static float score, score_final; // needed for time score < 10 for 10 seconds
4718   static int health, health_final;
4719   static int game_over_delay_1 = 0;
4720   static int game_over_delay_2 = 0;
4721   static int game_over_delay_3 = 0;
4722   int time_score_base = MIN(MAX(1, level.time_score_base), 10);
4723   float time_score = (float)level.score[SC_TIME_BONUS] / time_score_base;
4724
4725   if (!game.LevelSolved_GameWon)
4726   {
4727     int i;
4728
4729     // do not start end game actions before the player stops moving (to exit)
4730     if (local_player->active && local_player->MovPos)
4731       return;
4732
4733     game.time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4734     game.score_time_final = (level.use_step_counter ? TimePlayed :
4735                              TimePlayed * FRAMES_PER_SECOND + TimeFrames);
4736
4737     game.score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4738                         game_em.lev->score :
4739                         level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4740                         game_mm.score :
4741                         game.score);
4742
4743     game.health_final = (level.game_engine_type == GAME_ENGINE_TYPE_MM ?
4744                          MM_HEALTH(game_mm.laser_overload_value) :
4745                          game.health);
4746
4747     game.LevelSolved_CountingTime = game.time_final;
4748     game.LevelSolved_CountingScore = game.score_final;
4749     game.LevelSolved_CountingHealth = game.health_final;
4750
4751     game.LevelSolved_GameWon = TRUE;
4752     game.LevelSolved_SaveTape = tape.recording;
4753     game.LevelSolved_SaveScore = !tape.playing;
4754
4755     if (!tape.playing)
4756     {
4757       LevelStats_incSolved(level_nr);
4758
4759       SaveLevelSetup_SeriesInfo();
4760     }
4761
4762     if (tape.auto_play)         // tape might already be stopped here
4763       tape.auto_play_level_solved = TRUE;
4764
4765     TapeStop();
4766
4767     game_over_delay_1 = FRAMES_PER_SECOND;      // delay before counting time
4768     game_over_delay_2 = FRAMES_PER_SECOND / 2;  // delay before counting health
4769     game_over_delay_3 = FRAMES_PER_SECOND;      // delay before ending the game
4770
4771     time = time_final = game.time_final;
4772     score = score_final = game.score_final;
4773     health = health_final = game.health_final;
4774
4775     if (time_score > 0)
4776     {
4777       int time_final_max = 999;
4778       int time_frames_final_max = time_final_max * FRAMES_PER_SECOND;
4779       int time_frames = 0;
4780       int time_frames_left = TimeLeft * FRAMES_PER_SECOND - TimeFrames;
4781       int time_frames_played = TimePlayed * FRAMES_PER_SECOND + TimeFrames;
4782
4783       if (TimeLeft > 0)
4784       {
4785         time_final = 0;
4786         time_frames = time_frames_left;
4787       }
4788       else if (game.no_time_limit && TimePlayed < time_final_max)
4789       {
4790         time_final = time_final_max;
4791         time_frames = time_frames_final_max - time_frames_played;
4792       }
4793
4794       score_final += time_score * time_frames / FRAMES_PER_SECOND + 0.5;
4795
4796       time_count_steps = MAX(1, ABS(time_final - time) / 100);
4797
4798       if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
4799       {
4800         health_final = 0;
4801         score_final += health * time_score;
4802       }
4803
4804       game.score_final = score_final;
4805       game.health_final = health_final;
4806     }
4807
4808     if (level_editor_test_game || !setup.count_score_after_game)
4809     {
4810       time = time_final;
4811       score = score_final;
4812
4813       game.LevelSolved_CountingTime = time;
4814       game.LevelSolved_CountingScore = score;
4815
4816       game_panel_controls[GAME_PANEL_TIME].value = time;
4817       game_panel_controls[GAME_PANEL_SCORE].value = score;
4818
4819       DisplayGameControlValues();
4820     }
4821
4822     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4823     {
4824       // check if last player has left the level
4825       if (game.exit_x >= 0 &&
4826           game.exit_y >= 0)
4827       {
4828         int x = game.exit_x;
4829         int y = game.exit_y;
4830         int element = Tile[x][y];
4831
4832         // close exit door after last player
4833         if ((game.all_players_gone &&
4834              (element == EL_EXIT_OPEN ||
4835               element == EL_SP_EXIT_OPEN ||
4836               element == EL_STEEL_EXIT_OPEN)) ||
4837             element == EL_EM_EXIT_OPEN ||
4838             element == EL_EM_STEEL_EXIT_OPEN)
4839         {
4840
4841           Tile[x][y] =
4842             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4843              element == EL_EM_EXIT_OPEN         ? EL_EM_EXIT_CLOSING :
4844              element == EL_SP_EXIT_OPEN         ? EL_SP_EXIT_CLOSING:
4845              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4846              EL_EM_STEEL_EXIT_CLOSING);
4847
4848           PlayLevelSoundElementAction(x, y, element, ACTION_CLOSING);
4849         }
4850
4851         // player disappears
4852         DrawLevelField(x, y);
4853       }
4854
4855       for (i = 0; i < MAX_PLAYERS; i++)
4856       {
4857         struct PlayerInfo *player = &stored_player[i];
4858
4859         if (player->present)
4860         {
4861           RemovePlayer(player);
4862
4863           // player disappears
4864           DrawLevelField(player->jx, player->jy);
4865         }
4866       }
4867     }
4868
4869     PlaySound(SND_GAME_WINNING);
4870   }
4871
4872   if (setup.count_score_after_game)
4873   {
4874     if (time != time_final)
4875     {
4876       if (game_over_delay_1 > 0)
4877       {
4878         game_over_delay_1--;
4879
4880         return;
4881       }
4882
4883       int time_to_go = ABS(time_final - time);
4884       int time_count_dir = (time < time_final ? +1 : -1);
4885
4886       if (time_to_go < time_count_steps)
4887         time_count_steps = 1;
4888
4889       time  += time_count_steps * time_count_dir;
4890       score += time_count_steps * time_score;
4891
4892       // set final score to correct rounding differences after counting score
4893       if (time == time_final)
4894         score = score_final;
4895
4896       game.LevelSolved_CountingTime = time;
4897       game.LevelSolved_CountingScore = score;
4898
4899       game_panel_controls[GAME_PANEL_TIME].value = time;
4900       game_panel_controls[GAME_PANEL_SCORE].value = score;
4901
4902       DisplayGameControlValues();
4903
4904       if (time == time_final)
4905         StopSound(SND_GAME_LEVELTIME_BONUS);
4906       else if (setup.sound_loops)
4907         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4908       else
4909         PlaySound(SND_GAME_LEVELTIME_BONUS);
4910
4911       return;
4912     }
4913
4914     if (health != health_final)
4915     {
4916       if (game_over_delay_2 > 0)
4917       {
4918         game_over_delay_2--;
4919
4920         return;
4921       }
4922
4923       int health_count_dir = (health < health_final ? +1 : -1);
4924
4925       health += health_count_dir;
4926       score  += time_score;
4927
4928       game.LevelSolved_CountingHealth = health;
4929       game.LevelSolved_CountingScore = score;
4930
4931       game_panel_controls[GAME_PANEL_HEALTH].value = health;
4932       game_panel_controls[GAME_PANEL_SCORE].value = score;
4933
4934       DisplayGameControlValues();
4935
4936       if (health == health_final)
4937         StopSound(SND_GAME_LEVELTIME_BONUS);
4938       else if (setup.sound_loops)
4939         PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4940       else
4941         PlaySound(SND_GAME_LEVELTIME_BONUS);
4942
4943       return;
4944     }
4945   }
4946
4947   game.panel.active = FALSE;
4948
4949   if (game_over_delay_3 > 0)
4950   {
4951     game_over_delay_3--;
4952
4953     return;
4954   }
4955
4956   GameEnd();
4957 }
4958
4959 void GameEnd(void)
4960 {
4961   // used instead of "level_nr" (needed for network games)
4962   int last_level_nr = levelset.level_nr;
4963   int hi_pos;
4964
4965   game.LevelSolved_GameEnd = TRUE;
4966
4967   if (game.LevelSolved_SaveTape)
4968   {
4969     // make sure that request dialog to save tape does not open door again
4970     if (!global.use_envelope_request)
4971       CloseDoor(DOOR_CLOSE_1);
4972
4973     SaveTapeChecked_LevelSolved(tape.level_nr);         // ask to save tape
4974   }
4975
4976   // if no tape is to be saved, close both doors simultaneously
4977   CloseDoor(DOOR_CLOSE_ALL);
4978
4979   if (level_editor_test_game)
4980   {
4981     SetGameStatus(GAME_MODE_MAIN);
4982
4983     DrawMainMenu();
4984
4985     return;
4986   }
4987
4988   if (!game.LevelSolved_SaveScore)
4989   {
4990     SetGameStatus(GAME_MODE_MAIN);
4991
4992     DrawMainMenu();
4993
4994     return;
4995   }
4996
4997   if (level_nr == leveldir_current->handicap_level)
4998   {
4999     leveldir_current->handicap_level++;
5000
5001     SaveLevelSetup_SeriesInfo();
5002   }
5003
5004   if (setup.increment_levels &&
5005       level_nr < leveldir_current->last_level &&
5006       !network_playing)
5007   {
5008     level_nr++;         // advance to next level
5009     TapeErase();        // start with empty tape
5010
5011     if (setup.auto_play_next_level)
5012     {
5013       LoadLevel(level_nr);
5014
5015       SaveLevelSetup_SeriesInfo();
5016     }
5017   }
5018
5019   hi_pos = NewHiScore(last_level_nr);
5020
5021   if (hi_pos >= 0 && setup.show_scores_after_game)
5022   {
5023     SetGameStatus(GAME_MODE_SCORES);
5024
5025     DrawHallOfFame(last_level_nr, hi_pos);
5026   }
5027   else if (setup.auto_play_next_level && setup.increment_levels &&
5028            last_level_nr < leveldir_current->last_level &&
5029            !network_playing)
5030   {
5031     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
5032   }
5033   else
5034   {
5035     SetGameStatus(GAME_MODE_MAIN);
5036
5037     DrawMainMenu();
5038   }
5039 }
5040
5041 int NewHiScore(int level_nr)
5042 {
5043   int k, l;
5044   int position = -1;
5045   boolean one_score_entry_per_name = !program.many_scores_per_name;
5046
5047   LoadScore(level_nr);
5048
5049   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
5050       game.score_final < scores.entry[MAX_SCORE_ENTRIES - 1].score)
5051     return -1;
5052
5053   for (k = 0; k < MAX_SCORE_ENTRIES; k++)
5054   {
5055     boolean score_is_better = (game.score_final      >  scores.entry[k].score);
5056     boolean score_is_equal  = (game.score_final      == scores.entry[k].score);
5057     boolean time_is_better  = (game.score_time_final <  scores.entry[k].time);
5058     boolean time_is_equal   = (game.score_time_final == scores.entry[k].time);
5059     boolean better_by_score = (score_is_better ||
5060                                (score_is_equal && time_is_better));
5061     boolean better_by_time  = (time_is_better ||
5062                                (time_is_equal && score_is_better));
5063     boolean is_better = (level.rate_time_over_score ? better_by_time :
5064                          better_by_score);
5065     boolean entry_is_empty = (scores.entry[k].score == 0 &&
5066                               scores.entry[k].time == 0);
5067
5068     if (is_better || entry_is_empty)
5069     {
5070       // player has made it to the hall of fame
5071
5072       if (k < MAX_SCORE_ENTRIES - 1)
5073       {
5074         int m = MAX_SCORE_ENTRIES - 1;
5075
5076         if (one_score_entry_per_name)
5077         {
5078           for (l = k; l < MAX_SCORE_ENTRIES; l++)
5079             if (strEqual(setup.player_name, scores.entry[l].name))
5080               m = l;
5081
5082           if (m == k)   // player's new highscore overwrites his old one
5083             goto put_into_list;
5084         }
5085
5086         for (l = m; l > k; l--)
5087         {
5088           strcpy(scores.entry[l].name, scores.entry[l - 1].name);
5089           scores.entry[l].score = scores.entry[l - 1].score;
5090           scores.entry[l].time  = scores.entry[l - 1].time;
5091         }
5092       }
5093
5094       put_into_list:
5095
5096       strncpy(scores.entry[k].name, setup.player_name, MAX_PLAYER_NAME_LEN);
5097       scores.entry[k].name[MAX_PLAYER_NAME_LEN] = '\0';
5098       scores.entry[k].score = game.score_final;
5099       scores.entry[k].time = game.score_time_final;
5100       position = k;
5101
5102       break;
5103     }
5104     else if (one_score_entry_per_name &&
5105              !strncmp(setup.player_name, scores.entry[k].name,
5106                       MAX_PLAYER_NAME_LEN))
5107       break;    // player already there with a higher score
5108   }
5109
5110   if (position >= 0) 
5111     SaveScore(level_nr);
5112
5113   return position;
5114 }
5115
5116 static int getElementMoveStepsizeExt(int x, int y, int direction)
5117 {
5118   int element = Tile[x][y];
5119   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5120   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5121   int horiz_move = (dx != 0);
5122   int sign = (horiz_move ? dx : dy);
5123   int step = sign * element_info[element].move_stepsize;
5124
5125   // special values for move stepsize for spring and things on conveyor belt
5126   if (horiz_move)
5127   {
5128     if (CAN_FALL(element) &&
5129         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Tile[x][y + 1]))
5130       step = sign * MOVE_STEPSIZE_NORMAL / 2;
5131     else if (element == EL_SPRING)
5132       step = sign * MOVE_STEPSIZE_NORMAL * 2;
5133   }
5134
5135   return step;
5136 }
5137
5138 static int getElementMoveStepsize(int x, int y)
5139 {
5140   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
5141 }
5142
5143 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
5144 {
5145   if (player->GfxAction != action || player->GfxDir != dir)
5146   {
5147     player->GfxAction = action;
5148     player->GfxDir = dir;
5149     player->Frame = 0;
5150     player->StepFrame = 0;
5151   }
5152 }
5153
5154 static void ResetGfxFrame(int x, int y)
5155 {
5156   // profiling showed that "autotest" spends 10~20% of its time in this function
5157   if (DrawingDeactivatedField())
5158     return;
5159
5160   int element = Tile[x][y];
5161   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
5162
5163   if (graphic_info[graphic].anim_global_sync)
5164     GfxFrame[x][y] = FrameCounter;
5165   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
5166     GfxFrame[x][y] = CustomValue[x][y];
5167   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
5168     GfxFrame[x][y] = element_info[element].collect_score;
5169   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
5170     GfxFrame[x][y] = ChangeDelay[x][y];
5171 }
5172
5173 static void ResetGfxAnimation(int x, int y)
5174 {
5175   GfxAction[x][y] = ACTION_DEFAULT;
5176   GfxDir[x][y] = MovDir[x][y];
5177   GfxFrame[x][y] = 0;
5178
5179   ResetGfxFrame(x, y);
5180 }
5181
5182 static void ResetRandomAnimationValue(int x, int y)
5183 {
5184   GfxRandom[x][y] = INIT_GFX_RANDOM();
5185 }
5186
5187 static void InitMovingField(int x, int y, int direction)
5188 {
5189   int element = Tile[x][y];
5190   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
5191   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
5192   int newx = x + dx;
5193   int newy = y + dy;
5194   boolean is_moving_before, is_moving_after;
5195
5196   // check if element was/is moving or being moved before/after mode change
5197   is_moving_before = (WasJustMoving[x][y] != 0);
5198   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
5199
5200   // reset animation only for moving elements which change direction of moving
5201   // or which just started or stopped moving
5202   // (else CEs with property "can move" / "not moving" are reset each frame)
5203   if (is_moving_before != is_moving_after ||
5204       direction != MovDir[x][y])
5205     ResetGfxAnimation(x, y);
5206
5207   MovDir[x][y] = direction;
5208   GfxDir[x][y] = direction;
5209
5210   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
5211                      direction == MV_DOWN && CAN_FALL(element) ?
5212                      ACTION_FALLING : ACTION_MOVING);
5213
5214   // this is needed for CEs with property "can move" / "not moving"
5215
5216   if (is_moving_after)
5217   {
5218     if (Tile[newx][newy] == EL_EMPTY)
5219       Tile[newx][newy] = EL_BLOCKED;
5220
5221     MovDir[newx][newy] = MovDir[x][y];
5222
5223     CustomValue[newx][newy] = CustomValue[x][y];
5224
5225     GfxFrame[newx][newy] = GfxFrame[x][y];
5226     GfxRandom[newx][newy] = GfxRandom[x][y];
5227     GfxAction[newx][newy] = GfxAction[x][y];
5228     GfxDir[newx][newy] = GfxDir[x][y];
5229   }
5230 }
5231
5232 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
5233 {
5234   int direction = MovDir[x][y];
5235   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
5236   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
5237
5238   *goes_to_x = newx;
5239   *goes_to_y = newy;
5240 }
5241
5242 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
5243 {
5244   int oldx = x, oldy = y;
5245   int direction = MovDir[x][y];
5246
5247   if (direction == MV_LEFT)
5248     oldx++;
5249   else if (direction == MV_RIGHT)
5250     oldx--;
5251   else if (direction == MV_UP)
5252     oldy++;
5253   else if (direction == MV_DOWN)
5254     oldy--;
5255
5256   *comes_from_x = oldx;
5257   *comes_from_y = oldy;
5258 }
5259
5260 static int MovingOrBlocked2Element(int x, int y)
5261 {
5262   int element = Tile[x][y];
5263
5264   if (element == EL_BLOCKED)
5265   {
5266     int oldx, oldy;
5267
5268     Blocked2Moving(x, y, &oldx, &oldy);
5269     return Tile[oldx][oldy];
5270   }
5271   else
5272     return element;
5273 }
5274
5275 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
5276 {
5277   // like MovingOrBlocked2Element(), but if element is moving
5278   // and (x,y) is the field the moving element is just leaving,
5279   // return EL_BLOCKED instead of the element value
5280   int element = Tile[x][y];
5281
5282   if (IS_MOVING(x, y))
5283   {
5284     if (element == EL_BLOCKED)
5285     {
5286       int oldx, oldy;
5287
5288       Blocked2Moving(x, y, &oldx, &oldy);
5289       return Tile[oldx][oldy];
5290     }
5291     else
5292       return EL_BLOCKED;
5293   }
5294   else
5295     return element;
5296 }
5297
5298 static void RemoveField(int x, int y)
5299 {
5300   Tile[x][y] = EL_EMPTY;
5301
5302   MovPos[x][y] = 0;
5303   MovDir[x][y] = 0;
5304   MovDelay[x][y] = 0;
5305
5306   CustomValue[x][y] = 0;
5307
5308   AmoebaNr[x][y] = 0;
5309   ChangeDelay[x][y] = 0;
5310   ChangePage[x][y] = -1;
5311   Pushed[x][y] = FALSE;
5312
5313   GfxElement[x][y] = EL_UNDEFINED;
5314   GfxAction[x][y] = ACTION_DEFAULT;
5315   GfxDir[x][y] = MV_NONE;
5316 }
5317
5318 static void RemoveMovingField(int x, int y)
5319 {
5320   int oldx = x, oldy = y, newx = x, newy = y;
5321   int element = Tile[x][y];
5322   int next_element = EL_UNDEFINED;
5323
5324   if (element != EL_BLOCKED && !IS_MOVING(x, y))
5325     return;
5326
5327   if (IS_MOVING(x, y))
5328   {
5329     Moving2Blocked(x, y, &newx, &newy);
5330
5331     if (Tile[newx][newy] != EL_BLOCKED)
5332     {
5333       // element is moving, but target field is not free (blocked), but
5334       // already occupied by something different (example: acid pool);
5335       // in this case, only remove the moving field, but not the target
5336
5337       RemoveField(oldx, oldy);
5338
5339       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5340
5341       TEST_DrawLevelField(oldx, oldy);
5342
5343       return;
5344     }
5345   }
5346   else if (element == EL_BLOCKED)
5347   {
5348     Blocked2Moving(x, y, &oldx, &oldy);
5349     if (!IS_MOVING(oldx, oldy))
5350       return;
5351   }
5352
5353   if (element == EL_BLOCKED &&
5354       (Tile[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
5355        Tile[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
5356        Tile[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
5357        Tile[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
5358        Tile[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
5359        Tile[oldx][oldy] == EL_AMOEBA_DROPPING))
5360     next_element = get_next_element(Tile[oldx][oldy]);
5361
5362   RemoveField(oldx, oldy);
5363   RemoveField(newx, newy);
5364
5365   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
5366
5367   if (next_element != EL_UNDEFINED)
5368     Tile[oldx][oldy] = next_element;
5369
5370   TEST_DrawLevelField(oldx, oldy);
5371   TEST_DrawLevelField(newx, newy);
5372 }
5373
5374 void DrawDynamite(int x, int y)
5375 {
5376   int sx = SCREENX(x), sy = SCREENY(y);
5377   int graphic = el2img(Tile[x][y]);
5378   int frame;
5379
5380   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
5381     return;
5382
5383   if (IS_WALKABLE_INSIDE(Back[x][y]))
5384     return;
5385
5386   if (Back[x][y])
5387     DrawLevelElement(x, y, Back[x][y]);
5388   else if (Store[x][y])
5389     DrawLevelElement(x, y, Store[x][y]);
5390   else if (game.use_masked_elements)
5391     DrawLevelElement(x, y, EL_EMPTY);
5392
5393   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5394
5395   if (Back[x][y] || Store[x][y] || game.use_masked_elements)
5396     DrawGraphicThruMask(sx, sy, graphic, frame);
5397   else
5398     DrawGraphic(sx, sy, graphic, frame);
5399 }
5400
5401 static void CheckDynamite(int x, int y)
5402 {
5403   if (MovDelay[x][y] != 0)      // dynamite is still waiting to explode
5404   {
5405     MovDelay[x][y]--;
5406
5407     if (MovDelay[x][y] != 0)
5408     {
5409       DrawDynamite(x, y);
5410       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5411
5412       return;
5413     }
5414   }
5415
5416   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
5417
5418   Bang(x, y);
5419 }
5420
5421 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
5422 {
5423   boolean num_checked_players = 0;
5424   int i;
5425
5426   for (i = 0; i < MAX_PLAYERS; i++)
5427   {
5428     if (stored_player[i].active)
5429     {
5430       int sx = stored_player[i].jx;
5431       int sy = stored_player[i].jy;
5432
5433       if (num_checked_players == 0)
5434       {
5435         *sx1 = *sx2 = sx;
5436         *sy1 = *sy2 = sy;
5437       }
5438       else
5439       {
5440         *sx1 = MIN(*sx1, sx);
5441         *sy1 = MIN(*sy1, sy);
5442         *sx2 = MAX(*sx2, sx);
5443         *sy2 = MAX(*sy2, sy);
5444       }
5445
5446       num_checked_players++;
5447     }
5448   }
5449 }
5450
5451 static boolean checkIfAllPlayersFitToScreen_RND(void)
5452 {
5453   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
5454
5455   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5456
5457   return (sx2 - sx1 < SCR_FIELDX &&
5458           sy2 - sy1 < SCR_FIELDY);
5459 }
5460
5461 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
5462 {
5463   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
5464
5465   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
5466
5467   *sx = (sx1 + sx2) / 2;
5468   *sy = (sy1 + sy2) / 2;
5469 }
5470
5471 static void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
5472                                boolean center_screen, boolean quick_relocation)
5473 {
5474   unsigned int frame_delay_value_old = GetVideoFrameDelay();
5475   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5476   boolean no_delay = (tape.warp_forward);
5477   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5478   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5479   int new_scroll_x, new_scroll_y;
5480
5481   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
5482   {
5483     // case 1: quick relocation inside visible screen (without scrolling)
5484
5485     RedrawPlayfield();
5486
5487     return;
5488   }
5489
5490   if (!level.shifted_relocation || center_screen)
5491   {
5492     // relocation _with_ centering of screen
5493
5494     new_scroll_x = SCROLL_POSITION_X(x);
5495     new_scroll_y = SCROLL_POSITION_Y(y);
5496   }
5497   else
5498   {
5499     // relocation _without_ centering of screen
5500
5501     int center_scroll_x = SCROLL_POSITION_X(old_x);
5502     int center_scroll_y = SCROLL_POSITION_Y(old_y);
5503     int offset_x = x + (scroll_x - center_scroll_x);
5504     int offset_y = y + (scroll_y - center_scroll_y);
5505
5506     // for new screen position, apply previous offset to center position
5507     new_scroll_x = SCROLL_POSITION_X(offset_x);
5508     new_scroll_y = SCROLL_POSITION_Y(offset_y);
5509   }
5510
5511   if (quick_relocation)
5512   {
5513     // case 2: quick relocation (redraw without visible scrolling)
5514
5515     scroll_x = new_scroll_x;
5516     scroll_y = new_scroll_y;
5517
5518     RedrawPlayfield();
5519
5520     return;
5521   }
5522
5523   // case 3: visible relocation (with scrolling to new position)
5524
5525   ScrollScreen(NULL, SCROLL_GO_ON);     // scroll last frame to full tile
5526
5527   SetVideoFrameDelay(wait_delay_value);
5528
5529   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
5530   {
5531     int dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
5532     int dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
5533
5534     if (dx == 0 && dy == 0)             // no scrolling needed at all
5535       break;
5536
5537     scroll_x -= dx;
5538     scroll_y -= dy;
5539
5540     // set values for horizontal/vertical screen scrolling (half tile size)
5541     int dir_x = (dx != 0 ? MV_HORIZONTAL : 0);
5542     int dir_y = (dy != 0 ? MV_VERTICAL   : 0);
5543     int pos_x = dx * TILEX / 2;
5544     int pos_y = dy * TILEY / 2;
5545     int fx = getFieldbufferOffsetX_RND(dir_x, pos_x);
5546     int fy = getFieldbufferOffsetY_RND(dir_y, pos_y);
5547
5548     ScrollLevel(dx, dy);
5549     DrawAllPlayers();
5550
5551     // scroll in two steps of half tile size to make things smoother
5552     BlitScreenToBitmapExt_RND(window, fx, fy);
5553
5554     // scroll second step to align at full tile size
5555     BlitScreenToBitmap(window);
5556   }
5557
5558   DrawAllPlayers();
5559   BackToFront();
5560
5561   SetVideoFrameDelay(frame_delay_value_old);
5562 }
5563
5564 static void RelocatePlayer(int jx, int jy, int el_player_raw)
5565 {
5566   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5567   int player_nr = GET_PLAYER_NR(el_player);
5568   struct PlayerInfo *player = &stored_player[player_nr];
5569   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5570   boolean no_delay = (tape.warp_forward);
5571   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5572   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5573   int old_jx = player->jx;
5574   int old_jy = player->jy;
5575   int old_element = Tile[old_jx][old_jy];
5576   int element = Tile[jx][jy];
5577   boolean player_relocated = (old_jx != jx || old_jy != jy);
5578
5579   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5580   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5581   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5582   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5583   int leave_side_horiz = move_dir_horiz;
5584   int leave_side_vert  = move_dir_vert;
5585   int enter_side = enter_side_horiz | enter_side_vert;
5586   int leave_side = leave_side_horiz | leave_side_vert;
5587
5588   if (player->buried)           // do not reanimate dead player
5589     return;
5590
5591   if (!player_relocated)        // no need to relocate the player
5592     return;
5593
5594   if (IS_PLAYER(jx, jy))        // player already placed at new position
5595   {
5596     RemoveField(jx, jy);        // temporarily remove newly placed player
5597     DrawLevelField(jx, jy);
5598   }
5599
5600   if (player->present)
5601   {
5602     while (player->MovPos)
5603     {
5604       ScrollPlayer(player, SCROLL_GO_ON);
5605       ScrollScreen(NULL, SCROLL_GO_ON);
5606
5607       AdvanceFrameAndPlayerCounters(player->index_nr);
5608
5609       DrawPlayer(player);
5610
5611       BackToFront_WithFrameDelay(wait_delay_value);
5612     }
5613
5614     DrawPlayer(player);         // needed here only to cleanup last field
5615     DrawLevelField(player->jx, player->jy);     // remove player graphic
5616
5617     player->is_moving = FALSE;
5618   }
5619
5620   if (IS_CUSTOM_ELEMENT(old_element))
5621     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5622                                CE_LEFT_BY_PLAYER,
5623                                player->index_bit, leave_side);
5624
5625   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5626                                       CE_PLAYER_LEAVES_X,
5627                                       player->index_bit, leave_side);
5628
5629   Tile[jx][jy] = el_player;
5630   InitPlayerField(jx, jy, el_player, TRUE);
5631
5632   /* "InitPlayerField()" above sets Tile[jx][jy] to EL_EMPTY, but it may be
5633      possible that the relocation target field did not contain a player element,
5634      but a walkable element, to which the new player was relocated -- in this
5635      case, restore that (already initialized!) element on the player field */
5636   if (!ELEM_IS_PLAYER(element)) // player may be set on walkable element
5637   {
5638     Tile[jx][jy] = element;     // restore previously existing element
5639   }
5640
5641   // only visually relocate centered player
5642   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5643                      FALSE, level.instant_relocation);
5644
5645   TestIfPlayerTouchesBadThing(jx, jy);
5646   TestIfPlayerTouchesCustomElement(jx, jy);
5647
5648   if (IS_CUSTOM_ELEMENT(element))
5649     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5650                                player->index_bit, enter_side);
5651
5652   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5653                                       player->index_bit, enter_side);
5654
5655   if (player->is_switching)
5656   {
5657     /* ensure that relocation while still switching an element does not cause
5658        a new element to be treated as also switched directly after relocation
5659        (this is important for teleporter switches that teleport the player to
5660        a place where another teleporter switch is in the same direction, which
5661        would then incorrectly be treated as immediately switched before the
5662        direction key that caused the switch was released) */
5663
5664     player->switch_x += jx - old_jx;
5665     player->switch_y += jy - old_jy;
5666   }
5667 }
5668
5669 static void Explode(int ex, int ey, int phase, int mode)
5670 {
5671   int x, y;
5672   int last_phase;
5673   int border_element;
5674
5675   // !!! eliminate this variable !!!
5676   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5677
5678   if (game.explosions_delayed)
5679   {
5680     ExplodeField[ex][ey] = mode;
5681     return;
5682   }
5683
5684   if (phase == EX_PHASE_START)          // initialize 'Store[][]' field
5685   {
5686     int center_element = Tile[ex][ey];
5687     int artwork_element, explosion_element;     // set these values later
5688
5689     // remove things displayed in background while burning dynamite
5690     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5691       Back[ex][ey] = 0;
5692
5693     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5694     {
5695       // put moving element to center field (and let it explode there)
5696       center_element = MovingOrBlocked2Element(ex, ey);
5697       RemoveMovingField(ex, ey);
5698       Tile[ex][ey] = center_element;
5699     }
5700
5701     // now "center_element" is finally determined -- set related values now
5702     artwork_element = center_element;           // for custom player artwork
5703     explosion_element = center_element;         // for custom player artwork
5704
5705     if (IS_PLAYER(ex, ey))
5706     {
5707       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5708
5709       artwork_element = stored_player[player_nr].artwork_element;
5710
5711       if (level.use_explosion_element[player_nr])
5712       {
5713         explosion_element = level.explosion_element[player_nr];
5714         artwork_element = explosion_element;
5715       }
5716     }
5717
5718     if (mode == EX_TYPE_NORMAL ||
5719         mode == EX_TYPE_CENTER ||
5720         mode == EX_TYPE_CROSS)
5721       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5722
5723     last_phase = element_info[explosion_element].explosion_delay + 1;
5724
5725     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5726     {
5727       int xx = x - ex + 1;
5728       int yy = y - ey + 1;
5729       int element;
5730
5731       if (!IN_LEV_FIELD(x, y) ||
5732           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5733           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5734         continue;
5735
5736       element = Tile[x][y];
5737
5738       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5739       {
5740         element = MovingOrBlocked2Element(x, y);
5741
5742         if (!IS_EXPLOSION_PROOF(element))
5743           RemoveMovingField(x, y);
5744       }
5745
5746       // indestructible elements can only explode in center (but not flames)
5747       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5748                                            mode == EX_TYPE_BORDER)) ||
5749           element == EL_FLAMES)
5750         continue;
5751
5752       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5753          behaviour, for example when touching a yamyam that explodes to rocks
5754          with active deadly shield, a rock is created under the player !!! */
5755       // (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8)
5756 #if 0
5757       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5758           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5759            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5760 #else
5761       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5762 #endif
5763       {
5764         if (IS_ACTIVE_BOMB(element))
5765         {
5766           // re-activate things under the bomb like gate or penguin
5767           Tile[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5768           Back[x][y] = 0;
5769         }
5770
5771         continue;
5772       }
5773
5774       // save walkable background elements while explosion on same tile
5775       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5776           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5777         Back[x][y] = element;
5778
5779       // ignite explodable elements reached by other explosion
5780       if (element == EL_EXPLOSION)
5781         element = Store2[x][y];
5782
5783       if (AmoebaNr[x][y] &&
5784           (element == EL_AMOEBA_FULL ||
5785            element == EL_BD_AMOEBA ||
5786            element == EL_AMOEBA_GROWING))
5787       {
5788         AmoebaCnt[AmoebaNr[x][y]]--;
5789         AmoebaCnt2[AmoebaNr[x][y]]--;
5790       }
5791
5792       RemoveField(x, y);
5793
5794       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5795       {
5796         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5797
5798         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5799
5800         if (PLAYERINFO(ex, ey)->use_murphy)
5801           Store[x][y] = EL_EMPTY;
5802       }
5803
5804       // !!! check this case -- currently needed for rnd_rado_negundo_v,
5805       // !!! levels 015 018 019 020 021 022 023 026 027 028 !!!
5806       else if (ELEM_IS_PLAYER(center_element))
5807         Store[x][y] = EL_EMPTY;
5808       else if (center_element == EL_YAMYAM)
5809         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5810       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5811         Store[x][y] = element_info[center_element].content.e[xx][yy];
5812 #if 1
5813       // needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5814       // (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5815       // otherwise) -- FIX THIS !!!
5816       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5817         Store[x][y] = element_info[element].content.e[1][1];
5818 #else
5819       else if (!CAN_EXPLODE(element))
5820         Store[x][y] = element_info[element].content.e[1][1];
5821 #endif
5822       else
5823         Store[x][y] = EL_EMPTY;
5824
5825       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5826           center_element == EL_AMOEBA_TO_DIAMOND)
5827         Store2[x][y] = element;
5828
5829       Tile[x][y] = EL_EXPLOSION;
5830       GfxElement[x][y] = artwork_element;
5831
5832       ExplodePhase[x][y] = 1;
5833       ExplodeDelay[x][y] = last_phase;
5834
5835       Stop[x][y] = TRUE;
5836     }
5837
5838     if (center_element == EL_YAMYAM)
5839       game.yamyam_content_nr =
5840         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5841
5842     return;
5843   }
5844
5845   if (Stop[ex][ey])
5846     return;
5847
5848   x = ex;
5849   y = ey;
5850
5851   if (phase == 1)
5852     GfxFrame[x][y] = 0;         // restart explosion animation
5853
5854   last_phase = ExplodeDelay[x][y];
5855
5856   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5857
5858   // this can happen if the player leaves an explosion just in time
5859   if (GfxElement[x][y] == EL_UNDEFINED)
5860     GfxElement[x][y] = EL_EMPTY;
5861
5862   border_element = Store2[x][y];
5863   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5864     border_element = StorePlayer[x][y];
5865
5866   if (phase == element_info[border_element].ignition_delay ||
5867       phase == last_phase)
5868   {
5869     boolean border_explosion = FALSE;
5870
5871     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5872         !PLAYER_EXPLOSION_PROTECTED(x, y))
5873     {
5874       KillPlayerUnlessExplosionProtected(x, y);
5875       border_explosion = TRUE;
5876     }
5877     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5878     {
5879       Tile[x][y] = Store2[x][y];
5880       Store2[x][y] = 0;
5881       Bang(x, y);
5882       border_explosion = TRUE;
5883     }
5884     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5885     {
5886       AmoebaToDiamond(x, y);
5887       Store2[x][y] = 0;
5888       border_explosion = TRUE;
5889     }
5890
5891     // if an element just explodes due to another explosion (chain-reaction),
5892     // do not immediately end the new explosion when it was the last frame of
5893     // the explosion (as it would be done in the following "if"-statement!)
5894     if (border_explosion && phase == last_phase)
5895       return;
5896   }
5897
5898   if (phase == last_phase)
5899   {
5900     int element;
5901
5902     element = Tile[x][y] = Store[x][y];
5903     Store[x][y] = Store2[x][y] = 0;
5904     GfxElement[x][y] = EL_UNDEFINED;
5905
5906     // player can escape from explosions and might therefore be still alive
5907     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5908         element <= EL_PLAYER_IS_EXPLODING_4)
5909     {
5910       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5911       int explosion_element = EL_PLAYER_1 + player_nr;
5912       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5913       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5914
5915       if (level.use_explosion_element[player_nr])
5916         explosion_element = level.explosion_element[player_nr];
5917
5918       Tile[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5919                     element_info[explosion_element].content.e[xx][yy]);
5920     }
5921
5922     // restore probably existing indestructible background element
5923     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5924       element = Tile[x][y] = Back[x][y];
5925     Back[x][y] = 0;
5926
5927     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5928     GfxDir[x][y] = MV_NONE;
5929     ChangeDelay[x][y] = 0;
5930     ChangePage[x][y] = -1;
5931
5932     CustomValue[x][y] = 0;
5933
5934     InitField_WithBug2(x, y, FALSE);
5935
5936     TEST_DrawLevelField(x, y);
5937
5938     TestIfElementTouchesCustomElement(x, y);
5939
5940     if (GFX_CRUMBLED(element))
5941       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5942
5943     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5944       StorePlayer[x][y] = 0;
5945
5946     if (ELEM_IS_PLAYER(element))
5947       RelocatePlayer(x, y, element);
5948   }
5949   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5950   {
5951     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5952     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5953
5954     if (phase == delay)
5955       TEST_DrawLevelFieldCrumbled(x, y);
5956
5957     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5958     {
5959       DrawLevelElement(x, y, Back[x][y]);
5960       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5961     }
5962     else if (IS_WALKABLE_UNDER(Back[x][y]))
5963     {
5964       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5965       DrawLevelElementThruMask(x, y, Back[x][y]);
5966     }
5967     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5968       DrawScreenGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5969   }
5970 }
5971
5972 static void DynaExplode(int ex, int ey)
5973 {
5974   int i, j;
5975   int dynabomb_element = Tile[ex][ey];
5976   int dynabomb_size = 1;
5977   boolean dynabomb_xl = FALSE;
5978   struct PlayerInfo *player;
5979   static int xy[4][2] =
5980   {
5981     { 0, -1 },
5982     { -1, 0 },
5983     { +1, 0 },
5984     { 0, +1 }
5985   };
5986
5987   if (IS_ACTIVE_BOMB(dynabomb_element))
5988   {
5989     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5990     dynabomb_size = player->dynabomb_size;
5991     dynabomb_xl = player->dynabomb_xl;
5992     player->dynabombs_left++;
5993   }
5994
5995   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5996
5997   for (i = 0; i < NUM_DIRECTIONS; i++)
5998   {
5999     for (j = 1; j <= dynabomb_size; j++)
6000     {
6001       int x = ex + j * xy[i][0];
6002       int y = ey + j * xy[i][1];
6003       int element;
6004
6005       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Tile[x][y]))
6006         break;
6007
6008       element = Tile[x][y];
6009
6010       // do not restart explosions of fields with active bombs
6011       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
6012         continue;
6013
6014       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
6015
6016       if (element != EL_EMPTY && element != EL_EXPLOSION &&
6017           !IS_DIGGABLE(element) && !dynabomb_xl)
6018         break;
6019     }
6020   }
6021 }
6022
6023 void Bang(int x, int y)
6024 {
6025   int element = MovingOrBlocked2Element(x, y);
6026   int explosion_type = EX_TYPE_NORMAL;
6027
6028   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
6029   {
6030     struct PlayerInfo *player = PLAYERINFO(x, y);
6031
6032     element = Tile[x][y] = player->initial_element;
6033
6034     if (level.use_explosion_element[player->index_nr])
6035     {
6036       int explosion_element = level.explosion_element[player->index_nr];
6037
6038       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
6039         explosion_type = EX_TYPE_CROSS;
6040       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
6041         explosion_type = EX_TYPE_CENTER;
6042     }
6043   }
6044
6045   switch (element)
6046   {
6047     case EL_BUG:
6048     case EL_SPACESHIP:
6049     case EL_BD_BUTTERFLY:
6050     case EL_BD_FIREFLY:
6051     case EL_YAMYAM:
6052     case EL_DARK_YAMYAM:
6053     case EL_ROBOT:
6054     case EL_PACMAN:
6055     case EL_MOLE:
6056       RaiseScoreElement(element);
6057       break;
6058
6059     case EL_DYNABOMB_PLAYER_1_ACTIVE:
6060     case EL_DYNABOMB_PLAYER_2_ACTIVE:
6061     case EL_DYNABOMB_PLAYER_3_ACTIVE:
6062     case EL_DYNABOMB_PLAYER_4_ACTIVE:
6063     case EL_DYNABOMB_INCREASE_NUMBER:
6064     case EL_DYNABOMB_INCREASE_SIZE:
6065     case EL_DYNABOMB_INCREASE_POWER:
6066       explosion_type = EX_TYPE_DYNA;
6067       break;
6068
6069     case EL_DC_LANDMINE:
6070       explosion_type = EX_TYPE_CENTER;
6071       break;
6072
6073     case EL_PENGUIN:
6074     case EL_LAMP:
6075     case EL_LAMP_ACTIVE:
6076     case EL_AMOEBA_TO_DIAMOND:
6077       if (!IS_PLAYER(x, y))     // penguin and player may be at same field
6078         explosion_type = EX_TYPE_CENTER;
6079       break;
6080
6081     default:
6082       if (element_info[element].explosion_type == EXPLODES_CROSS)
6083         explosion_type = EX_TYPE_CROSS;
6084       else if (element_info[element].explosion_type == EXPLODES_1X1)
6085         explosion_type = EX_TYPE_CENTER;
6086       break;
6087   }
6088
6089   if (explosion_type == EX_TYPE_DYNA)
6090     DynaExplode(x, y);
6091   else
6092     Explode(x, y, EX_PHASE_START, explosion_type);
6093
6094   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
6095 }
6096
6097 static void SplashAcid(int x, int y)
6098 {
6099   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
6100       (!IN_LEV_FIELD(x - 1, y - 2) ||
6101        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
6102     Tile[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
6103
6104   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
6105       (!IN_LEV_FIELD(x + 1, y - 2) ||
6106        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
6107     Tile[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
6108
6109   PlayLevelSound(x, y, SND_ACID_SPLASHING);
6110 }
6111
6112 static void InitBeltMovement(void)
6113 {
6114   static int belt_base_element[4] =
6115   {
6116     EL_CONVEYOR_BELT_1_LEFT,
6117     EL_CONVEYOR_BELT_2_LEFT,
6118     EL_CONVEYOR_BELT_3_LEFT,
6119     EL_CONVEYOR_BELT_4_LEFT
6120   };
6121   static int belt_base_active_element[4] =
6122   {
6123     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6124     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6125     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6126     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6127   };
6128
6129   int x, y, i, j;
6130
6131   // set frame order for belt animation graphic according to belt direction
6132   for (i = 0; i < NUM_BELTS; i++)
6133   {
6134     int belt_nr = i;
6135
6136     for (j = 0; j < NUM_BELT_PARTS; j++)
6137     {
6138       int element = belt_base_active_element[belt_nr] + j;
6139       int graphic_1 = el2img(element);
6140       int graphic_2 = el2panelimg(element);
6141
6142       if (game.belt_dir[i] == MV_LEFT)
6143       {
6144         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6145         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6146       }
6147       else
6148       {
6149         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6150         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6151       }
6152     }
6153   }
6154
6155   SCAN_PLAYFIELD(x, y)
6156   {
6157     int element = Tile[x][y];
6158
6159     for (i = 0; i < NUM_BELTS; i++)
6160     {
6161       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
6162       {
6163         int e_belt_nr = getBeltNrFromBeltElement(element);
6164         int belt_nr = i;
6165
6166         if (e_belt_nr == belt_nr)
6167         {
6168           int belt_part = Tile[x][y] - belt_base_element[belt_nr];
6169
6170           Tile[x][y] = belt_base_active_element[belt_nr] + belt_part;
6171         }
6172       }
6173     }
6174   }
6175 }
6176
6177 static void ToggleBeltSwitch(int x, int y)
6178 {
6179   static int belt_base_element[4] =
6180   {
6181     EL_CONVEYOR_BELT_1_LEFT,
6182     EL_CONVEYOR_BELT_2_LEFT,
6183     EL_CONVEYOR_BELT_3_LEFT,
6184     EL_CONVEYOR_BELT_4_LEFT
6185   };
6186   static int belt_base_active_element[4] =
6187   {
6188     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
6189     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
6190     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
6191     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
6192   };
6193   static int belt_base_switch_element[4] =
6194   {
6195     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
6196     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
6197     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
6198     EL_CONVEYOR_BELT_4_SWITCH_LEFT
6199   };
6200   static int belt_move_dir[4] =
6201   {
6202     MV_LEFT,
6203     MV_NONE,
6204     MV_RIGHT,
6205     MV_NONE,
6206   };
6207
6208   int element = Tile[x][y];
6209   int belt_nr = getBeltNrFromBeltSwitchElement(element);
6210   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
6211   int belt_dir = belt_move_dir[belt_dir_nr];
6212   int xx, yy, i;
6213
6214   if (!IS_BELT_SWITCH(element))
6215     return;
6216
6217   game.belt_dir_nr[belt_nr] = belt_dir_nr;
6218   game.belt_dir[belt_nr] = belt_dir;
6219
6220   if (belt_dir_nr == 3)
6221     belt_dir_nr = 1;
6222
6223   // set frame order for belt animation graphic according to belt direction
6224   for (i = 0; i < NUM_BELT_PARTS; i++)
6225   {
6226     int element = belt_base_active_element[belt_nr] + i;
6227     int graphic_1 = el2img(element);
6228     int graphic_2 = el2panelimg(element);
6229
6230     if (belt_dir == MV_LEFT)
6231     {
6232       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
6233       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
6234     }
6235     else
6236     {
6237       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
6238       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
6239     }
6240   }
6241
6242   SCAN_PLAYFIELD(xx, yy)
6243   {
6244     int element = Tile[xx][yy];
6245
6246     if (IS_BELT_SWITCH(element))
6247     {
6248       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
6249
6250       if (e_belt_nr == belt_nr)
6251       {
6252         Tile[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
6253         TEST_DrawLevelField(xx, yy);
6254       }
6255     }
6256     else if (IS_BELT(element) && belt_dir != MV_NONE)
6257     {
6258       int e_belt_nr = getBeltNrFromBeltElement(element);
6259
6260       if (e_belt_nr == belt_nr)
6261       {
6262         int belt_part = Tile[xx][yy] - belt_base_element[belt_nr];
6263
6264         Tile[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
6265         TEST_DrawLevelField(xx, yy);
6266       }
6267     }
6268     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
6269     {
6270       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
6271
6272       if (e_belt_nr == belt_nr)
6273       {
6274         int belt_part = Tile[xx][yy] - belt_base_active_element[belt_nr];
6275
6276         Tile[xx][yy] = belt_base_element[belt_nr] + belt_part;
6277         TEST_DrawLevelField(xx, yy);
6278       }
6279     }
6280   }
6281 }
6282
6283 static void ToggleSwitchgateSwitch(int x, int y)
6284 {
6285   int xx, yy;
6286
6287   game.switchgate_pos = !game.switchgate_pos;
6288
6289   SCAN_PLAYFIELD(xx, yy)
6290   {
6291     int element = Tile[xx][yy];
6292
6293     if (element == EL_SWITCHGATE_SWITCH_UP)
6294     {
6295       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
6296       TEST_DrawLevelField(xx, yy);
6297     }
6298     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
6299     {
6300       Tile[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
6301       TEST_DrawLevelField(xx, yy);
6302     }
6303     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
6304     {
6305       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
6306       TEST_DrawLevelField(xx, yy);
6307     }
6308     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
6309     {
6310       Tile[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
6311       TEST_DrawLevelField(xx, yy);
6312     }
6313     else if (element == EL_SWITCHGATE_OPEN ||
6314              element == EL_SWITCHGATE_OPENING)
6315     {
6316       Tile[xx][yy] = EL_SWITCHGATE_CLOSING;
6317
6318       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
6319     }
6320     else if (element == EL_SWITCHGATE_CLOSED ||
6321              element == EL_SWITCHGATE_CLOSING)
6322     {
6323       Tile[xx][yy] = EL_SWITCHGATE_OPENING;
6324
6325       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
6326     }
6327   }
6328 }
6329
6330 static int getInvisibleActiveFromInvisibleElement(int element)
6331 {
6332   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
6333           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
6334           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
6335           element);
6336 }
6337
6338 static int getInvisibleFromInvisibleActiveElement(int element)
6339 {
6340   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
6341           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
6342           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
6343           element);
6344 }
6345
6346 static void RedrawAllLightSwitchesAndInvisibleElements(void)
6347 {
6348   int x, y;
6349
6350   SCAN_PLAYFIELD(x, y)
6351   {
6352     int element = Tile[x][y];
6353
6354     if (element == EL_LIGHT_SWITCH &&
6355         game.light_time_left > 0)
6356     {
6357       Tile[x][y] = EL_LIGHT_SWITCH_ACTIVE;
6358       TEST_DrawLevelField(x, y);
6359     }
6360     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
6361              game.light_time_left == 0)
6362     {
6363       Tile[x][y] = EL_LIGHT_SWITCH;
6364       TEST_DrawLevelField(x, y);
6365     }
6366     else if (element == EL_EMC_DRIPPER &&
6367              game.light_time_left > 0)
6368     {
6369       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6370       TEST_DrawLevelField(x, y);
6371     }
6372     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6373              game.light_time_left == 0)
6374     {
6375       Tile[x][y] = EL_EMC_DRIPPER;
6376       TEST_DrawLevelField(x, y);
6377     }
6378     else if (element == EL_INVISIBLE_STEELWALL ||
6379              element == EL_INVISIBLE_WALL ||
6380              element == EL_INVISIBLE_SAND)
6381     {
6382       if (game.light_time_left > 0)
6383         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6384
6385       TEST_DrawLevelField(x, y);
6386
6387       // uncrumble neighbour fields, if needed
6388       if (element == EL_INVISIBLE_SAND)
6389         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6390     }
6391     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6392              element == EL_INVISIBLE_WALL_ACTIVE ||
6393              element == EL_INVISIBLE_SAND_ACTIVE)
6394     {
6395       if (game.light_time_left == 0)
6396         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6397
6398       TEST_DrawLevelField(x, y);
6399
6400       // re-crumble neighbour fields, if needed
6401       if (element == EL_INVISIBLE_SAND)
6402         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6403     }
6404   }
6405 }
6406
6407 static void RedrawAllInvisibleElementsForLenses(void)
6408 {
6409   int x, y;
6410
6411   SCAN_PLAYFIELD(x, y)
6412   {
6413     int element = Tile[x][y];
6414
6415     if (element == EL_EMC_DRIPPER &&
6416         game.lenses_time_left > 0)
6417     {
6418       Tile[x][y] = EL_EMC_DRIPPER_ACTIVE;
6419       TEST_DrawLevelField(x, y);
6420     }
6421     else if (element == EL_EMC_DRIPPER_ACTIVE &&
6422              game.lenses_time_left == 0)
6423     {
6424       Tile[x][y] = EL_EMC_DRIPPER;
6425       TEST_DrawLevelField(x, y);
6426     }
6427     else if (element == EL_INVISIBLE_STEELWALL ||
6428              element == EL_INVISIBLE_WALL ||
6429              element == EL_INVISIBLE_SAND)
6430     {
6431       if (game.lenses_time_left > 0)
6432         Tile[x][y] = getInvisibleActiveFromInvisibleElement(element);
6433
6434       TEST_DrawLevelField(x, y);
6435
6436       // uncrumble neighbour fields, if needed
6437       if (element == EL_INVISIBLE_SAND)
6438         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6439     }
6440     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
6441              element == EL_INVISIBLE_WALL_ACTIVE ||
6442              element == EL_INVISIBLE_SAND_ACTIVE)
6443     {
6444       if (game.lenses_time_left == 0)
6445         Tile[x][y] = getInvisibleFromInvisibleActiveElement(element);
6446
6447       TEST_DrawLevelField(x, y);
6448
6449       // re-crumble neighbour fields, if needed
6450       if (element == EL_INVISIBLE_SAND)
6451         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
6452     }
6453   }
6454 }
6455
6456 static void RedrawAllInvisibleElementsForMagnifier(void)
6457 {
6458   int x, y;
6459
6460   SCAN_PLAYFIELD(x, y)
6461   {
6462     int element = Tile[x][y];
6463
6464     if (element == EL_EMC_FAKE_GRASS &&
6465         game.magnify_time_left > 0)
6466     {
6467       Tile[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
6468       TEST_DrawLevelField(x, y);
6469     }
6470     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
6471              game.magnify_time_left == 0)
6472     {
6473       Tile[x][y] = EL_EMC_FAKE_GRASS;
6474       TEST_DrawLevelField(x, y);
6475     }
6476     else if (IS_GATE_GRAY(element) &&
6477              game.magnify_time_left > 0)
6478     {
6479       Tile[x][y] = (IS_RND_GATE_GRAY(element) ?
6480                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
6481                     IS_EM_GATE_GRAY(element) ?
6482                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
6483                     IS_EMC_GATE_GRAY(element) ?
6484                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
6485                     IS_DC_GATE_GRAY(element) ?
6486                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
6487                     element);
6488       TEST_DrawLevelField(x, y);
6489     }
6490     else if (IS_GATE_GRAY_ACTIVE(element) &&
6491              game.magnify_time_left == 0)
6492     {
6493       Tile[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
6494                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
6495                     IS_EM_GATE_GRAY_ACTIVE(element) ?
6496                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
6497                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
6498                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
6499                     IS_DC_GATE_GRAY_ACTIVE(element) ?
6500                     EL_DC_GATE_WHITE_GRAY :
6501                     element);
6502       TEST_DrawLevelField(x, y);
6503     }
6504   }
6505 }
6506
6507 static void ToggleLightSwitch(int x, int y)
6508 {
6509   int element = Tile[x][y];
6510
6511   game.light_time_left =
6512     (element == EL_LIGHT_SWITCH ?
6513      level.time_light * FRAMES_PER_SECOND : 0);
6514
6515   RedrawAllLightSwitchesAndInvisibleElements();
6516 }
6517
6518 static void ActivateTimegateSwitch(int x, int y)
6519 {
6520   int xx, yy;
6521
6522   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6523
6524   SCAN_PLAYFIELD(xx, yy)
6525   {
6526     int element = Tile[xx][yy];
6527
6528     if (element == EL_TIMEGATE_CLOSED ||
6529         element == EL_TIMEGATE_CLOSING)
6530     {
6531       Tile[xx][yy] = EL_TIMEGATE_OPENING;
6532       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6533     }
6534
6535     /*
6536     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6537     {
6538       Tile[xx][yy] = EL_TIMEGATE_SWITCH;
6539       TEST_DrawLevelField(xx, yy);
6540     }
6541     */
6542
6543   }
6544
6545   Tile[x][y] = (Tile[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6546                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6547 }
6548
6549 static void Impact(int x, int y)
6550 {
6551   boolean last_line = (y == lev_fieldy - 1);
6552   boolean object_hit = FALSE;
6553   boolean impact = (last_line || object_hit);
6554   int element = Tile[x][y];
6555   int smashed = EL_STEELWALL;
6556
6557   if (!last_line)       // check if element below was hit
6558   {
6559     if (Tile[x][y + 1] == EL_PLAYER_IS_LEAVING)
6560       return;
6561
6562     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6563                                          MovDir[x][y + 1] != MV_DOWN ||
6564                                          MovPos[x][y + 1] <= TILEY / 2));
6565
6566     // do not smash moving elements that left the smashed field in time
6567     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6568         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6569       object_hit = FALSE;
6570
6571 #if USE_QUICKSAND_IMPACT_BUGFIX
6572     if (Tile[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6573     {
6574       RemoveMovingField(x, y + 1);
6575       Tile[x][y + 1] = EL_QUICKSAND_EMPTY;
6576       Tile[x][y + 2] = EL_ROCK;
6577       TEST_DrawLevelField(x, y + 2);
6578
6579       object_hit = TRUE;
6580     }
6581
6582     if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6583     {
6584       RemoveMovingField(x, y + 1);
6585       Tile[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6586       Tile[x][y + 2] = EL_ROCK;
6587       TEST_DrawLevelField(x, y + 2);
6588
6589       object_hit = TRUE;
6590     }
6591 #endif
6592
6593     if (object_hit)
6594       smashed = MovingOrBlocked2Element(x, y + 1);
6595
6596     impact = (last_line || object_hit);
6597   }
6598
6599   if (!last_line && smashed == EL_ACID) // element falls into acid
6600   {
6601     SplashAcid(x, y + 1);
6602     return;
6603   }
6604
6605   // !!! not sufficient for all cases -- see EL_PEARL below !!!
6606   // only reset graphic animation if graphic really changes after impact
6607   if (impact &&
6608       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6609   {
6610     ResetGfxAnimation(x, y);
6611     TEST_DrawLevelField(x, y);
6612   }
6613
6614   if (impact && CAN_EXPLODE_IMPACT(element))
6615   {
6616     Bang(x, y);
6617     return;
6618   }
6619   else if (impact && element == EL_PEARL &&
6620            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6621   {
6622     ResetGfxAnimation(x, y);
6623
6624     Tile[x][y] = EL_PEARL_BREAKING;
6625     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6626     return;
6627   }
6628   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6629   {
6630     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6631
6632     return;
6633   }
6634
6635   if (impact && element == EL_AMOEBA_DROP)
6636   {
6637     if (object_hit && IS_PLAYER(x, y + 1))
6638       KillPlayerUnlessEnemyProtected(x, y + 1);
6639     else if (object_hit && smashed == EL_PENGUIN)
6640       Bang(x, y + 1);
6641     else
6642     {
6643       Tile[x][y] = EL_AMOEBA_GROWING;
6644       Store[x][y] = EL_AMOEBA_WET;
6645
6646       ResetRandomAnimationValue(x, y);
6647     }
6648     return;
6649   }
6650
6651   if (object_hit)               // check which object was hit
6652   {
6653     if ((CAN_PASS_MAGIC_WALL(element) && 
6654          (smashed == EL_MAGIC_WALL ||
6655           smashed == EL_BD_MAGIC_WALL)) ||
6656         (CAN_PASS_DC_MAGIC_WALL(element) &&
6657          smashed == EL_DC_MAGIC_WALL))
6658     {
6659       int xx, yy;
6660       int activated_magic_wall =
6661         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6662          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6663          EL_DC_MAGIC_WALL_ACTIVE);
6664
6665       // activate magic wall / mill
6666       SCAN_PLAYFIELD(xx, yy)
6667       {
6668         if (Tile[xx][yy] == smashed)
6669           Tile[xx][yy] = activated_magic_wall;
6670       }
6671
6672       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6673       game.magic_wall_active = TRUE;
6674
6675       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6676                             SND_MAGIC_WALL_ACTIVATING :
6677                             smashed == EL_BD_MAGIC_WALL ?
6678                             SND_BD_MAGIC_WALL_ACTIVATING :
6679                             SND_DC_MAGIC_WALL_ACTIVATING));
6680     }
6681
6682     if (IS_PLAYER(x, y + 1))
6683     {
6684       if (CAN_SMASH_PLAYER(element))
6685       {
6686         KillPlayerUnlessEnemyProtected(x, y + 1);
6687         return;
6688       }
6689     }
6690     else if (smashed == EL_PENGUIN)
6691     {
6692       if (CAN_SMASH_PLAYER(element))
6693       {
6694         Bang(x, y + 1);
6695         return;
6696       }
6697     }
6698     else if (element == EL_BD_DIAMOND)
6699     {
6700       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6701       {
6702         Bang(x, y + 1);
6703         return;
6704       }
6705     }
6706     else if (((element == EL_SP_INFOTRON ||
6707                element == EL_SP_ZONK) &&
6708               (smashed == EL_SP_SNIKSNAK ||
6709                smashed == EL_SP_ELECTRON ||
6710                smashed == EL_SP_DISK_ORANGE)) ||
6711              (element == EL_SP_INFOTRON &&
6712               smashed == EL_SP_DISK_YELLOW))
6713     {
6714       Bang(x, y + 1);
6715       return;
6716     }
6717     else if (CAN_SMASH_EVERYTHING(element))
6718     {
6719       if (IS_CLASSIC_ENEMY(smashed) ||
6720           CAN_EXPLODE_SMASHED(smashed))
6721       {
6722         Bang(x, y + 1);
6723         return;
6724       }
6725       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6726       {
6727         if (smashed == EL_LAMP ||
6728             smashed == EL_LAMP_ACTIVE)
6729         {
6730           Bang(x, y + 1);
6731           return;
6732         }
6733         else if (smashed == EL_NUT)
6734         {
6735           Tile[x][y + 1] = EL_NUT_BREAKING;
6736           PlayLevelSound(x, y, SND_NUT_BREAKING);
6737           RaiseScoreElement(EL_NUT);
6738           return;
6739         }
6740         else if (smashed == EL_PEARL)
6741         {
6742           ResetGfxAnimation(x, y);
6743
6744           Tile[x][y + 1] = EL_PEARL_BREAKING;
6745           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6746           return;
6747         }
6748         else if (smashed == EL_DIAMOND)
6749         {
6750           Tile[x][y + 1] = EL_DIAMOND_BREAKING;
6751           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6752           return;
6753         }
6754         else if (IS_BELT_SWITCH(smashed))
6755         {
6756           ToggleBeltSwitch(x, y + 1);
6757         }
6758         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6759                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6760                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6761                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6762         {
6763           ToggleSwitchgateSwitch(x, y + 1);
6764         }
6765         else if (smashed == EL_LIGHT_SWITCH ||
6766                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6767         {
6768           ToggleLightSwitch(x, y + 1);
6769         }
6770         else
6771         {
6772           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6773
6774           CheckElementChangeBySide(x, y + 1, smashed, element,
6775                                    CE_SWITCHED, CH_SIDE_TOP);
6776           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6777                                             CH_SIDE_TOP);
6778         }
6779       }
6780       else
6781       {
6782         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6783       }
6784     }
6785   }
6786
6787   // play sound of magic wall / mill
6788   if (!last_line &&
6789       (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6790        Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6791        Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6792   {
6793     if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6794       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6795     else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6796       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6797     else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6798       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6799
6800     return;
6801   }
6802
6803   // play sound of object that hits the ground
6804   if (last_line || object_hit)
6805     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6806 }
6807
6808 static void TurnRoundExt(int x, int y)
6809 {
6810   static struct
6811   {
6812     int dx, dy;
6813   } move_xy[] =
6814   {
6815     {  0,  0 },
6816     { -1,  0 },
6817     { +1,  0 },
6818     {  0,  0 },
6819     {  0, -1 },
6820     {  0,  0 }, { 0, 0 }, { 0, 0 },
6821     {  0, +1 }
6822   };
6823   static struct
6824   {
6825     int left, right, back;
6826   } turn[] =
6827   {
6828     { 0,        0,              0        },
6829     { MV_DOWN,  MV_UP,          MV_RIGHT },
6830     { MV_UP,    MV_DOWN,        MV_LEFT  },
6831     { 0,        0,              0        },
6832     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6833     { 0,        0,              0        },
6834     { 0,        0,              0        },
6835     { 0,        0,              0        },
6836     { MV_RIGHT, MV_LEFT,        MV_UP    }
6837   };
6838
6839   int element = Tile[x][y];
6840   int move_pattern = element_info[element].move_pattern;
6841
6842   int old_move_dir = MovDir[x][y];
6843   int left_dir  = turn[old_move_dir].left;
6844   int right_dir = turn[old_move_dir].right;
6845   int back_dir  = turn[old_move_dir].back;
6846
6847   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6848   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6849   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6850   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6851
6852   int left_x  = x + left_dx,  left_y  = y + left_dy;
6853   int right_x = x + right_dx, right_y = y + right_dy;
6854   int move_x  = x + move_dx,  move_y  = y + move_dy;
6855
6856   int xx, yy;
6857
6858   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6859   {
6860     TestIfBadThingTouchesOtherBadThing(x, y);
6861
6862     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6863       MovDir[x][y] = right_dir;
6864     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6865       MovDir[x][y] = left_dir;
6866
6867     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6868       MovDelay[x][y] = 9;
6869     else if (element == EL_BD_BUTTERFLY)     // && MovDir[x][y] == left_dir)
6870       MovDelay[x][y] = 1;
6871   }
6872   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6873   {
6874     TestIfBadThingTouchesOtherBadThing(x, y);
6875
6876     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6877       MovDir[x][y] = left_dir;
6878     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6879       MovDir[x][y] = right_dir;
6880
6881     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6882       MovDelay[x][y] = 9;
6883     else if (element == EL_BD_FIREFLY)      // && MovDir[x][y] == right_dir)
6884       MovDelay[x][y] = 1;
6885   }
6886   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6887   {
6888     TestIfBadThingTouchesOtherBadThing(x, y);
6889
6890     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6891       MovDir[x][y] = left_dir;
6892     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6893       MovDir[x][y] = right_dir;
6894
6895     if (MovDir[x][y] != old_move_dir)
6896       MovDelay[x][y] = 9;
6897   }
6898   else if (element == EL_YAMYAM)
6899   {
6900     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6901     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6902
6903     if (can_turn_left && can_turn_right)
6904       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6905     else if (can_turn_left)
6906       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6907     else if (can_turn_right)
6908       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6909     else
6910       MovDir[x][y] = back_dir;
6911
6912     MovDelay[x][y] = 16 + 16 * RND(3);
6913   }
6914   else if (element == EL_DARK_YAMYAM)
6915   {
6916     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6917                                                          left_x, left_y);
6918     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6919                                                          right_x, right_y);
6920
6921     if (can_turn_left && can_turn_right)
6922       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6923     else if (can_turn_left)
6924       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6925     else if (can_turn_right)
6926       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6927     else
6928       MovDir[x][y] = back_dir;
6929
6930     MovDelay[x][y] = 16 + 16 * RND(3);
6931   }
6932   else if (element == EL_PACMAN)
6933   {
6934     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6935     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6936
6937     if (can_turn_left && can_turn_right)
6938       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6939     else if (can_turn_left)
6940       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6941     else if (can_turn_right)
6942       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6943     else
6944       MovDir[x][y] = back_dir;
6945
6946     MovDelay[x][y] = 6 + RND(40);
6947   }
6948   else if (element == EL_PIG)
6949   {
6950     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6951     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6952     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6953     boolean should_turn_left, should_turn_right, should_move_on;
6954     int rnd_value = 24;
6955     int rnd = RND(rnd_value);
6956
6957     should_turn_left = (can_turn_left &&
6958                         (!can_move_on ||
6959                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6960                                                    y + back_dy + left_dy)));
6961     should_turn_right = (can_turn_right &&
6962                          (!can_move_on ||
6963                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6964                                                     y + back_dy + right_dy)));
6965     should_move_on = (can_move_on &&
6966                       (!can_turn_left ||
6967                        !can_turn_right ||
6968                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6969                                                  y + move_dy + left_dy) ||
6970                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6971                                                  y + move_dy + right_dy)));
6972
6973     if (should_turn_left || should_turn_right || should_move_on)
6974     {
6975       if (should_turn_left && should_turn_right && should_move_on)
6976         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6977                         rnd < 2 * rnd_value / 3 ? right_dir :
6978                         old_move_dir);
6979       else if (should_turn_left && should_turn_right)
6980         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6981       else if (should_turn_left && should_move_on)
6982         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6983       else if (should_turn_right && should_move_on)
6984         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6985       else if (should_turn_left)
6986         MovDir[x][y] = left_dir;
6987       else if (should_turn_right)
6988         MovDir[x][y] = right_dir;
6989       else if (should_move_on)
6990         MovDir[x][y] = old_move_dir;
6991     }
6992     else if (can_move_on && rnd > rnd_value / 8)
6993       MovDir[x][y] = old_move_dir;
6994     else if (can_turn_left && can_turn_right)
6995       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6996     else if (can_turn_left && rnd > rnd_value / 8)
6997       MovDir[x][y] = left_dir;
6998     else if (can_turn_right && rnd > rnd_value/8)
6999       MovDir[x][y] = right_dir;
7000     else
7001       MovDir[x][y] = back_dir;
7002
7003     xx = x + move_xy[MovDir[x][y]].dx;
7004     yy = y + move_xy[MovDir[x][y]].dy;
7005
7006     if (!IN_LEV_FIELD(xx, yy) ||
7007         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Tile[xx][yy])))
7008       MovDir[x][y] = old_move_dir;
7009
7010     MovDelay[x][y] = 0;
7011   }
7012   else if (element == EL_DRAGON)
7013   {
7014     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
7015     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
7016     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
7017     int rnd_value = 24;
7018     int rnd = RND(rnd_value);
7019
7020     if (can_move_on && rnd > rnd_value / 8)
7021       MovDir[x][y] = old_move_dir;
7022     else if (can_turn_left && can_turn_right)
7023       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
7024     else if (can_turn_left && rnd > rnd_value / 8)
7025       MovDir[x][y] = left_dir;
7026     else if (can_turn_right && rnd > rnd_value / 8)
7027       MovDir[x][y] = right_dir;
7028     else
7029       MovDir[x][y] = back_dir;
7030
7031     xx = x + move_xy[MovDir[x][y]].dx;
7032     yy = y + move_xy[MovDir[x][y]].dy;
7033
7034     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
7035       MovDir[x][y] = old_move_dir;
7036
7037     MovDelay[x][y] = 0;
7038   }
7039   else if (element == EL_MOLE)
7040   {
7041     boolean can_move_on =
7042       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
7043                             IS_AMOEBOID(Tile[move_x][move_y]) ||
7044                             Tile[move_x][move_y] == EL_AMOEBA_SHRINKING));
7045     if (!can_move_on)
7046     {
7047       boolean can_turn_left =
7048         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
7049                               IS_AMOEBOID(Tile[left_x][left_y])));
7050
7051       boolean can_turn_right =
7052         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
7053                               IS_AMOEBOID(Tile[right_x][right_y])));
7054
7055       if (can_turn_left && can_turn_right)
7056         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
7057       else if (can_turn_left)
7058         MovDir[x][y] = left_dir;
7059       else
7060         MovDir[x][y] = right_dir;
7061     }
7062
7063     if (MovDir[x][y] != old_move_dir)
7064       MovDelay[x][y] = 9;
7065   }
7066   else if (element == EL_BALLOON)
7067   {
7068     MovDir[x][y] = game.wind_direction;
7069     MovDelay[x][y] = 0;
7070   }
7071   else if (element == EL_SPRING)
7072   {
7073     if (MovDir[x][y] & MV_HORIZONTAL)
7074     {
7075       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
7076           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7077       {
7078         Tile[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
7079         ResetGfxAnimation(move_x, move_y);
7080         TEST_DrawLevelField(move_x, move_y);
7081
7082         MovDir[x][y] = back_dir;
7083       }
7084       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
7085                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
7086         MovDir[x][y] = MV_NONE;
7087     }
7088
7089     MovDelay[x][y] = 0;
7090   }
7091   else if (element == EL_ROBOT ||
7092            element == EL_SATELLITE ||
7093            element == EL_PENGUIN ||
7094            element == EL_EMC_ANDROID)
7095   {
7096     int attr_x = -1, attr_y = -1;
7097
7098     if (game.all_players_gone)
7099     {
7100       attr_x = game.exit_x;
7101       attr_y = game.exit_y;
7102     }
7103     else
7104     {
7105       int i;
7106
7107       for (i = 0; i < MAX_PLAYERS; i++)
7108       {
7109         struct PlayerInfo *player = &stored_player[i];
7110         int jx = player->jx, jy = player->jy;
7111
7112         if (!player->active)
7113           continue;
7114
7115         if (attr_x == -1 ||
7116             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7117         {
7118           attr_x = jx;
7119           attr_y = jy;
7120         }
7121       }
7122     }
7123
7124     if (element == EL_ROBOT &&
7125         game.robot_wheel_x >= 0 &&
7126         game.robot_wheel_y >= 0 &&
7127         (Tile[game.robot_wheel_x][game.robot_wheel_y] == EL_ROBOT_WHEEL_ACTIVE ||
7128          game.engine_version < VERSION_IDENT(3,1,0,0)))
7129     {
7130       attr_x = game.robot_wheel_x;
7131       attr_y = game.robot_wheel_y;
7132     }
7133
7134     if (element == EL_PENGUIN)
7135     {
7136       int i;
7137       static int xy[4][2] =
7138       {
7139         { 0, -1 },
7140         { -1, 0 },
7141         { +1, 0 },
7142         { 0, +1 }
7143       };
7144
7145       for (i = 0; i < NUM_DIRECTIONS; i++)
7146       {
7147         int ex = x + xy[i][0];
7148         int ey = y + xy[i][1];
7149
7150         if (IN_LEV_FIELD(ex, ey) && (Tile[ex][ey] == EL_EXIT_OPEN ||
7151                                      Tile[ex][ey] == EL_EM_EXIT_OPEN ||
7152                                      Tile[ex][ey] == EL_STEEL_EXIT_OPEN ||
7153                                      Tile[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
7154         {
7155           attr_x = ex;
7156           attr_y = ey;
7157           break;
7158         }
7159       }
7160     }
7161
7162     MovDir[x][y] = MV_NONE;
7163     if (attr_x < x)
7164       MovDir[x][y] |= (game.all_players_gone ? MV_RIGHT : MV_LEFT);
7165     else if (attr_x > x)
7166       MovDir[x][y] |= (game.all_players_gone ? MV_LEFT : MV_RIGHT);
7167     if (attr_y < y)
7168       MovDir[x][y] |= (game.all_players_gone ? MV_DOWN : MV_UP);
7169     else if (attr_y > y)
7170       MovDir[x][y] |= (game.all_players_gone ? MV_UP : MV_DOWN);
7171
7172     if (element == EL_ROBOT)
7173     {
7174       int newx, newy;
7175
7176       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7177         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
7178       Moving2Blocked(x, y, &newx, &newy);
7179
7180       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
7181         MovDelay[x][y] = 8 + 8 * !RND(3);
7182       else
7183         MovDelay[x][y] = 16;
7184     }
7185     else if (element == EL_PENGUIN)
7186     {
7187       int newx, newy;
7188
7189       MovDelay[x][y] = 1;
7190
7191       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7192       {
7193         boolean first_horiz = RND(2);
7194         int new_move_dir = MovDir[x][y];
7195
7196         MovDir[x][y] =
7197           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7198         Moving2Blocked(x, y, &newx, &newy);
7199
7200         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7201           return;
7202
7203         MovDir[x][y] =
7204           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7205         Moving2Blocked(x, y, &newx, &newy);
7206
7207         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
7208           return;
7209
7210         MovDir[x][y] = old_move_dir;
7211         return;
7212       }
7213     }
7214     else if (element == EL_SATELLITE)
7215     {
7216       int newx, newy;
7217
7218       MovDelay[x][y] = 1;
7219
7220       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7221       {
7222         boolean first_horiz = RND(2);
7223         int new_move_dir = MovDir[x][y];
7224
7225         MovDir[x][y] =
7226           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7227         Moving2Blocked(x, y, &newx, &newy);
7228
7229         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7230           return;
7231
7232         MovDir[x][y] =
7233           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7234         Moving2Blocked(x, y, &newx, &newy);
7235
7236         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
7237           return;
7238
7239         MovDir[x][y] = old_move_dir;
7240         return;
7241       }
7242     }
7243     else if (element == EL_EMC_ANDROID)
7244     {
7245       static int check_pos[16] =
7246       {
7247         -1,             //  0 => (invalid)
7248         7,              //  1 => MV_LEFT
7249         3,              //  2 => MV_RIGHT
7250         -1,             //  3 => (invalid)
7251         1,              //  4 =>            MV_UP
7252         0,              //  5 => MV_LEFT  | MV_UP
7253         2,              //  6 => MV_RIGHT | MV_UP
7254         -1,             //  7 => (invalid)
7255         5,              //  8 =>            MV_DOWN
7256         6,              //  9 => MV_LEFT  | MV_DOWN
7257         4,              // 10 => MV_RIGHT | MV_DOWN
7258         -1,             // 11 => (invalid)
7259         -1,             // 12 => (invalid)
7260         -1,             // 13 => (invalid)
7261         -1,             // 14 => (invalid)
7262         -1,             // 15 => (invalid)
7263       };
7264       static struct
7265       {
7266         int dx, dy;
7267         int dir;
7268       } check_xy[8] =
7269       {
7270         { -1, -1,       MV_LEFT  | MV_UP   },
7271         {  0, -1,                  MV_UP   },
7272         { +1, -1,       MV_RIGHT | MV_UP   },
7273         { +1,  0,       MV_RIGHT           },
7274         { +1, +1,       MV_RIGHT | MV_DOWN },
7275         {  0, +1,                  MV_DOWN },
7276         { -1, +1,       MV_LEFT  | MV_DOWN },
7277         { -1,  0,       MV_LEFT            },
7278       };
7279       int start_pos, check_order;
7280       boolean can_clone = FALSE;
7281       int i;
7282
7283       // check if there is any free field around current position
7284       for (i = 0; i < 8; i++)
7285       {
7286         int newx = x + check_xy[i].dx;
7287         int newy = y + check_xy[i].dy;
7288
7289         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7290         {
7291           can_clone = TRUE;
7292
7293           break;
7294         }
7295       }
7296
7297       if (can_clone)            // randomly find an element to clone
7298       {
7299         can_clone = FALSE;
7300
7301         start_pos = check_pos[RND(8)];
7302         check_order = (RND(2) ? -1 : +1);
7303
7304         for (i = 0; i < 8; i++)
7305         {
7306           int pos_raw = start_pos + i * check_order;
7307           int pos = (pos_raw + 8) % 8;
7308           int newx = x + check_xy[pos].dx;
7309           int newy = y + check_xy[pos].dy;
7310
7311           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
7312           {
7313             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
7314             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
7315
7316             Store[x][y] = Tile[newx][newy];
7317
7318             can_clone = TRUE;
7319
7320             break;
7321           }
7322         }
7323       }
7324
7325       if (can_clone)            // randomly find a direction to move
7326       {
7327         can_clone = FALSE;
7328
7329         start_pos = check_pos[RND(8)];
7330         check_order = (RND(2) ? -1 : +1);
7331
7332         for (i = 0; i < 8; i++)
7333         {
7334           int pos_raw = start_pos + i * check_order;
7335           int pos = (pos_raw + 8) % 8;
7336           int newx = x + check_xy[pos].dx;
7337           int newy = y + check_xy[pos].dy;
7338           int new_move_dir = check_xy[pos].dir;
7339
7340           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7341           {
7342             MovDir[x][y] = new_move_dir;
7343             MovDelay[x][y] = level.android_clone_time * 8 + 1;
7344
7345             can_clone = TRUE;
7346
7347             break;
7348           }
7349         }
7350       }
7351
7352       if (can_clone)            // cloning and moving successful
7353         return;
7354
7355       // cannot clone -- try to move towards player
7356
7357       start_pos = check_pos[MovDir[x][y] & 0x0f];
7358       check_order = (RND(2) ? -1 : +1);
7359
7360       for (i = 0; i < 3; i++)
7361       {
7362         // first check start_pos, then previous/next or (next/previous) pos
7363         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
7364         int pos = (pos_raw + 8) % 8;
7365         int newx = x + check_xy[pos].dx;
7366         int newy = y + check_xy[pos].dy;
7367         int new_move_dir = check_xy[pos].dir;
7368
7369         if (IS_PLAYER(newx, newy))
7370           break;
7371
7372         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7373         {
7374           MovDir[x][y] = new_move_dir;
7375           MovDelay[x][y] = level.android_move_time * 8 + 1;
7376
7377           break;
7378         }
7379       }
7380     }
7381   }
7382   else if (move_pattern == MV_TURNING_LEFT ||
7383            move_pattern == MV_TURNING_RIGHT ||
7384            move_pattern == MV_TURNING_LEFT_RIGHT ||
7385            move_pattern == MV_TURNING_RIGHT_LEFT ||
7386            move_pattern == MV_TURNING_RANDOM ||
7387            move_pattern == MV_ALL_DIRECTIONS)
7388   {
7389     boolean can_turn_left =
7390       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
7391     boolean can_turn_right =
7392       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
7393
7394     if (element_info[element].move_stepsize == 0)       // "not moving"
7395       return;
7396
7397     if (move_pattern == MV_TURNING_LEFT)
7398       MovDir[x][y] = left_dir;
7399     else if (move_pattern == MV_TURNING_RIGHT)
7400       MovDir[x][y] = right_dir;
7401     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
7402       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
7403     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
7404       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
7405     else if (move_pattern == MV_TURNING_RANDOM)
7406       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
7407                       can_turn_right && !can_turn_left ? right_dir :
7408                       RND(2) ? left_dir : right_dir);
7409     else if (can_turn_left && can_turn_right)
7410       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
7411     else if (can_turn_left)
7412       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
7413     else if (can_turn_right)
7414       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
7415     else
7416       MovDir[x][y] = back_dir;
7417
7418     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7419   }
7420   else if (move_pattern == MV_HORIZONTAL ||
7421            move_pattern == MV_VERTICAL)
7422   {
7423     if (move_pattern & old_move_dir)
7424       MovDir[x][y] = back_dir;
7425     else if (move_pattern == MV_HORIZONTAL)
7426       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
7427     else if (move_pattern == MV_VERTICAL)
7428       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
7429
7430     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7431   }
7432   else if (move_pattern & MV_ANY_DIRECTION)
7433   {
7434     MovDir[x][y] = move_pattern;
7435     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7436   }
7437   else if (move_pattern & MV_WIND_DIRECTION)
7438   {
7439     MovDir[x][y] = game.wind_direction;
7440     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7441   }
7442   else if (move_pattern == MV_ALONG_LEFT_SIDE)
7443   {
7444     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
7445       MovDir[x][y] = left_dir;
7446     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7447       MovDir[x][y] = right_dir;
7448
7449     if (MovDir[x][y] != old_move_dir)
7450       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7451   }
7452   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
7453   {
7454     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
7455       MovDir[x][y] = right_dir;
7456     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7457       MovDir[x][y] = left_dir;
7458
7459     if (MovDir[x][y] != old_move_dir)
7460       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7461   }
7462   else if (move_pattern == MV_TOWARDS_PLAYER ||
7463            move_pattern == MV_AWAY_FROM_PLAYER)
7464   {
7465     int attr_x = -1, attr_y = -1;
7466     int newx, newy;
7467     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
7468
7469     if (game.all_players_gone)
7470     {
7471       attr_x = game.exit_x;
7472       attr_y = game.exit_y;
7473     }
7474     else
7475     {
7476       int i;
7477
7478       for (i = 0; i < MAX_PLAYERS; i++)
7479       {
7480         struct PlayerInfo *player = &stored_player[i];
7481         int jx = player->jx, jy = player->jy;
7482
7483         if (!player->active)
7484           continue;
7485
7486         if (attr_x == -1 ||
7487             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
7488         {
7489           attr_x = jx;
7490           attr_y = jy;
7491         }
7492       }
7493     }
7494
7495     MovDir[x][y] = MV_NONE;
7496     if (attr_x < x)
7497       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
7498     else if (attr_x > x)
7499       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
7500     if (attr_y < y)
7501       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
7502     else if (attr_y > y)
7503       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7504
7505     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7506
7507     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7508     {
7509       boolean first_horiz = RND(2);
7510       int new_move_dir = MovDir[x][y];
7511
7512       if (element_info[element].move_stepsize == 0)     // "not moving"
7513       {
7514         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7515         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7516
7517         return;
7518       }
7519
7520       MovDir[x][y] =
7521         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7522       Moving2Blocked(x, y, &newx, &newy);
7523
7524       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7525         return;
7526
7527       MovDir[x][y] =
7528         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7529       Moving2Blocked(x, y, &newx, &newy);
7530
7531       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7532         return;
7533
7534       MovDir[x][y] = old_move_dir;
7535     }
7536   }
7537   else if (move_pattern == MV_WHEN_PUSHED ||
7538            move_pattern == MV_WHEN_DROPPED)
7539   {
7540     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7541       MovDir[x][y] = MV_NONE;
7542
7543     MovDelay[x][y] = 0;
7544   }
7545   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7546   {
7547     static int test_xy[7][2] =
7548     {
7549       { 0, -1 },
7550       { -1, 0 },
7551       { +1, 0 },
7552       { 0, +1 },
7553       { 0, -1 },
7554       { -1, 0 },
7555       { +1, 0 },
7556     };
7557     static int test_dir[7] =
7558     {
7559       MV_UP,
7560       MV_LEFT,
7561       MV_RIGHT,
7562       MV_DOWN,
7563       MV_UP,
7564       MV_LEFT,
7565       MV_RIGHT,
7566     };
7567     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7568     int move_preference = -1000000;     // start with very low preference
7569     int new_move_dir = MV_NONE;
7570     int start_test = RND(4);
7571     int i;
7572
7573     for (i = 0; i < NUM_DIRECTIONS; i++)
7574     {
7575       int move_dir = test_dir[start_test + i];
7576       int move_dir_preference;
7577
7578       xx = x + test_xy[start_test + i][0];
7579       yy = y + test_xy[start_test + i][1];
7580
7581       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7582           (IS_PLAYER(xx, yy) || Tile[xx][yy] == EL_PLAYER_IS_LEAVING))
7583       {
7584         new_move_dir = move_dir;
7585
7586         break;
7587       }
7588
7589       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7590         continue;
7591
7592       move_dir_preference = -1 * RunnerVisit[xx][yy];
7593       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7594         move_dir_preference = PlayerVisit[xx][yy];
7595
7596       if (move_dir_preference > move_preference)
7597       {
7598         // prefer field that has not been visited for the longest time
7599         move_preference = move_dir_preference;
7600         new_move_dir = move_dir;
7601       }
7602       else if (move_dir_preference == move_preference &&
7603                move_dir == old_move_dir)
7604       {
7605         // prefer last direction when all directions are preferred equally
7606         move_preference = move_dir_preference;
7607         new_move_dir = move_dir;
7608       }
7609     }
7610
7611     MovDir[x][y] = new_move_dir;
7612     if (old_move_dir != new_move_dir)
7613       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7614   }
7615 }
7616
7617 static void TurnRound(int x, int y)
7618 {
7619   int direction = MovDir[x][y];
7620
7621   TurnRoundExt(x, y);
7622
7623   GfxDir[x][y] = MovDir[x][y];
7624
7625   if (direction != MovDir[x][y])
7626     GfxFrame[x][y] = 0;
7627
7628   if (MovDelay[x][y])
7629     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7630
7631   ResetGfxFrame(x, y);
7632 }
7633
7634 static boolean JustBeingPushed(int x, int y)
7635 {
7636   int i;
7637
7638   for (i = 0; i < MAX_PLAYERS; i++)
7639   {
7640     struct PlayerInfo *player = &stored_player[i];
7641
7642     if (player->active && player->is_pushing && player->MovPos)
7643     {
7644       int next_jx = player->jx + (player->jx - player->last_jx);
7645       int next_jy = player->jy + (player->jy - player->last_jy);
7646
7647       if (x == next_jx && y == next_jy)
7648         return TRUE;
7649     }
7650   }
7651
7652   return FALSE;
7653 }
7654
7655 static void StartMoving(int x, int y)
7656 {
7657   boolean started_moving = FALSE;       // some elements can fall _and_ move
7658   int element = Tile[x][y];
7659
7660   if (Stop[x][y])
7661     return;
7662
7663   if (MovDelay[x][y] == 0)
7664     GfxAction[x][y] = ACTION_DEFAULT;
7665
7666   if (CAN_FALL(element) && y < lev_fieldy - 1)
7667   {
7668     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7669         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7670       if (JustBeingPushed(x, y))
7671         return;
7672
7673     if (element == EL_QUICKSAND_FULL)
7674     {
7675       if (IS_FREE(x, y + 1))
7676       {
7677         InitMovingField(x, y, MV_DOWN);
7678         started_moving = TRUE;
7679
7680         Tile[x][y] = EL_QUICKSAND_EMPTYING;
7681 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7682         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7683           Store[x][y] = EL_ROCK;
7684 #else
7685         Store[x][y] = EL_ROCK;
7686 #endif
7687
7688         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7689       }
7690       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7691       {
7692         if (!MovDelay[x][y])
7693         {
7694           MovDelay[x][y] = TILEY + 1;
7695
7696           ResetGfxAnimation(x, y);
7697           ResetGfxAnimation(x, y + 1);
7698         }
7699
7700         if (MovDelay[x][y])
7701         {
7702           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7703           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7704
7705           MovDelay[x][y]--;
7706           if (MovDelay[x][y])
7707             return;
7708         }
7709
7710         Tile[x][y] = EL_QUICKSAND_EMPTY;
7711         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7712         Store[x][y + 1] = Store[x][y];
7713         Store[x][y] = 0;
7714
7715         PlayLevelSoundAction(x, y, ACTION_FILLING);
7716       }
7717       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7718       {
7719         if (!MovDelay[x][y])
7720         {
7721           MovDelay[x][y] = TILEY + 1;
7722
7723           ResetGfxAnimation(x, y);
7724           ResetGfxAnimation(x, y + 1);
7725         }
7726
7727         if (MovDelay[x][y])
7728         {
7729           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7730           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7731
7732           MovDelay[x][y]--;
7733           if (MovDelay[x][y])
7734             return;
7735         }
7736
7737         Tile[x][y] = EL_QUICKSAND_EMPTY;
7738         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7739         Store[x][y + 1] = Store[x][y];
7740         Store[x][y] = 0;
7741
7742         PlayLevelSoundAction(x, y, ACTION_FILLING);
7743       }
7744     }
7745     else if (element == EL_QUICKSAND_FAST_FULL)
7746     {
7747       if (IS_FREE(x, y + 1))
7748       {
7749         InitMovingField(x, y, MV_DOWN);
7750         started_moving = TRUE;
7751
7752         Tile[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7753 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7754         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7755           Store[x][y] = EL_ROCK;
7756 #else
7757         Store[x][y] = EL_ROCK;
7758 #endif
7759
7760         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7761       }
7762       else if (Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7763       {
7764         if (!MovDelay[x][y])
7765         {
7766           MovDelay[x][y] = TILEY + 1;
7767
7768           ResetGfxAnimation(x, y);
7769           ResetGfxAnimation(x, y + 1);
7770         }
7771
7772         if (MovDelay[x][y])
7773         {
7774           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7775           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7776
7777           MovDelay[x][y]--;
7778           if (MovDelay[x][y])
7779             return;
7780         }
7781
7782         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7783         Tile[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7784         Store[x][y + 1] = Store[x][y];
7785         Store[x][y] = 0;
7786
7787         PlayLevelSoundAction(x, y, ACTION_FILLING);
7788       }
7789       else if (Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7790       {
7791         if (!MovDelay[x][y])
7792         {
7793           MovDelay[x][y] = TILEY + 1;
7794
7795           ResetGfxAnimation(x, y);
7796           ResetGfxAnimation(x, y + 1);
7797         }
7798
7799         if (MovDelay[x][y])
7800         {
7801           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7802           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7803
7804           MovDelay[x][y]--;
7805           if (MovDelay[x][y])
7806             return;
7807         }
7808
7809         Tile[x][y] = EL_QUICKSAND_FAST_EMPTY;
7810         Tile[x][y + 1] = EL_QUICKSAND_FULL;
7811         Store[x][y + 1] = Store[x][y];
7812         Store[x][y] = 0;
7813
7814         PlayLevelSoundAction(x, y, ACTION_FILLING);
7815       }
7816     }
7817     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7818              Tile[x][y + 1] == EL_QUICKSAND_EMPTY)
7819     {
7820       InitMovingField(x, y, MV_DOWN);
7821       started_moving = TRUE;
7822
7823       Tile[x][y] = EL_QUICKSAND_FILLING;
7824       Store[x][y] = element;
7825
7826       PlayLevelSoundAction(x, y, ACTION_FILLING);
7827     }
7828     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7829              Tile[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7830     {
7831       InitMovingField(x, y, MV_DOWN);
7832       started_moving = TRUE;
7833
7834       Tile[x][y] = EL_QUICKSAND_FAST_FILLING;
7835       Store[x][y] = element;
7836
7837       PlayLevelSoundAction(x, y, ACTION_FILLING);
7838     }
7839     else if (element == EL_MAGIC_WALL_FULL)
7840     {
7841       if (IS_FREE(x, y + 1))
7842       {
7843         InitMovingField(x, y, MV_DOWN);
7844         started_moving = TRUE;
7845
7846         Tile[x][y] = EL_MAGIC_WALL_EMPTYING;
7847         Store[x][y] = EL_CHANGED(Store[x][y]);
7848       }
7849       else if (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7850       {
7851         if (!MovDelay[x][y])
7852           MovDelay[x][y] = TILEY / 4 + 1;
7853
7854         if (MovDelay[x][y])
7855         {
7856           MovDelay[x][y]--;
7857           if (MovDelay[x][y])
7858             return;
7859         }
7860
7861         Tile[x][y] = EL_MAGIC_WALL_ACTIVE;
7862         Tile[x][y + 1] = EL_MAGIC_WALL_FULL;
7863         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7864         Store[x][y] = 0;
7865       }
7866     }
7867     else if (element == EL_BD_MAGIC_WALL_FULL)
7868     {
7869       if (IS_FREE(x, y + 1))
7870       {
7871         InitMovingField(x, y, MV_DOWN);
7872         started_moving = TRUE;
7873
7874         Tile[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7875         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7876       }
7877       else if (Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7878       {
7879         if (!MovDelay[x][y])
7880           MovDelay[x][y] = TILEY / 4 + 1;
7881
7882         if (MovDelay[x][y])
7883         {
7884           MovDelay[x][y]--;
7885           if (MovDelay[x][y])
7886             return;
7887         }
7888
7889         Tile[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7890         Tile[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7891         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7892         Store[x][y] = 0;
7893       }
7894     }
7895     else if (element == EL_DC_MAGIC_WALL_FULL)
7896     {
7897       if (IS_FREE(x, y + 1))
7898       {
7899         InitMovingField(x, y, MV_DOWN);
7900         started_moving = TRUE;
7901
7902         Tile[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7903         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7904       }
7905       else if (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7906       {
7907         if (!MovDelay[x][y])
7908           MovDelay[x][y] = TILEY / 4 + 1;
7909
7910         if (MovDelay[x][y])
7911         {
7912           MovDelay[x][y]--;
7913           if (MovDelay[x][y])
7914             return;
7915         }
7916
7917         Tile[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7918         Tile[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7919         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7920         Store[x][y] = 0;
7921       }
7922     }
7923     else if ((CAN_PASS_MAGIC_WALL(element) &&
7924               (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7925                Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7926              (CAN_PASS_DC_MAGIC_WALL(element) &&
7927               (Tile[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7928
7929     {
7930       InitMovingField(x, y, MV_DOWN);
7931       started_moving = TRUE;
7932
7933       Tile[x][y] =
7934         (Tile[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7935          Tile[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7936          EL_DC_MAGIC_WALL_FILLING);
7937       Store[x][y] = element;
7938     }
7939     else if (CAN_FALL(element) && Tile[x][y + 1] == EL_ACID)
7940     {
7941       SplashAcid(x, y + 1);
7942
7943       InitMovingField(x, y, MV_DOWN);
7944       started_moving = TRUE;
7945
7946       Store[x][y] = EL_ACID;
7947     }
7948     else if (
7949              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7950               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7951              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7952               CAN_FALL(element) && WasJustFalling[x][y] &&
7953               (Tile[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7954
7955              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7956               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7957               (Tile[x][y + 1] == EL_BLOCKED)))
7958     {
7959       /* this is needed for a special case not covered by calling "Impact()"
7960          from "ContinueMoving()": if an element moves to a tile directly below
7961          another element which was just falling on that tile (which was empty
7962          in the previous frame), the falling element above would just stop
7963          instead of smashing the element below (in previous version, the above
7964          element was just checked for "moving" instead of "falling", resulting
7965          in incorrect smashes caused by horizontal movement of the above
7966          element; also, the case of the player being the element to smash was
7967          simply not covered here... :-/ ) */
7968
7969       CheckCollision[x][y] = 0;
7970       CheckImpact[x][y] = 0;
7971
7972       Impact(x, y);
7973     }
7974     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7975     {
7976       if (MovDir[x][y] == MV_NONE)
7977       {
7978         InitMovingField(x, y, MV_DOWN);
7979         started_moving = TRUE;
7980       }
7981     }
7982     else if (IS_FREE(x, y + 1) || Tile[x][y + 1] == EL_DIAMOND_BREAKING)
7983     {
7984       if (WasJustFalling[x][y]) // prevent animation from being restarted
7985         MovDir[x][y] = MV_DOWN;
7986
7987       InitMovingField(x, y, MV_DOWN);
7988       started_moving = TRUE;
7989     }
7990     else if (element == EL_AMOEBA_DROP)
7991     {
7992       Tile[x][y] = EL_AMOEBA_GROWING;
7993       Store[x][y] = EL_AMOEBA_WET;
7994     }
7995     else if (((IS_SLIPPERY(Tile[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7996               (IS_EM_SLIPPERY_WALL(Tile[x][y + 1]) && IS_GEM(element))) &&
7997              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7998              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7999     {
8000       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
8001                                 (IS_FREE(x - 1, y + 1) ||
8002                                  Tile[x - 1][y + 1] == EL_ACID));
8003       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
8004                                 (IS_FREE(x + 1, y + 1) ||
8005                                  Tile[x + 1][y + 1] == EL_ACID));
8006       boolean can_fall_any  = (can_fall_left || can_fall_right);
8007       boolean can_fall_both = (can_fall_left && can_fall_right);
8008       int slippery_type = element_info[Tile[x][y + 1]].slippery_type;
8009
8010       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
8011       {
8012         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
8013           can_fall_right = FALSE;
8014         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
8015           can_fall_left = FALSE;
8016         else if (slippery_type == SLIPPERY_ONLY_LEFT)
8017           can_fall_right = FALSE;
8018         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
8019           can_fall_left = FALSE;
8020
8021         can_fall_any  = (can_fall_left || can_fall_right);
8022         can_fall_both = FALSE;
8023       }
8024
8025       if (can_fall_both)
8026       {
8027         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
8028           can_fall_right = FALSE;       // slip down on left side
8029         else
8030           can_fall_left = !(can_fall_right = RND(2));
8031
8032         can_fall_both = FALSE;
8033       }
8034
8035       if (can_fall_any)
8036       {
8037         // if not determined otherwise, prefer left side for slipping down
8038         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
8039         started_moving = TRUE;
8040       }
8041     }
8042     else if (IS_BELT_ACTIVE(Tile[x][y + 1]))
8043     {
8044       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
8045       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
8046       int belt_nr = getBeltNrFromBeltActiveElement(Tile[x][y + 1]);
8047       int belt_dir = game.belt_dir[belt_nr];
8048
8049       if ((belt_dir == MV_LEFT  && left_is_free) ||
8050           (belt_dir == MV_RIGHT && right_is_free))
8051       {
8052         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
8053
8054         InitMovingField(x, y, belt_dir);
8055         started_moving = TRUE;
8056
8057         Pushed[x][y] = TRUE;
8058         Pushed[nextx][y] = TRUE;
8059
8060         GfxAction[x][y] = ACTION_DEFAULT;
8061       }
8062       else
8063       {
8064         MovDir[x][y] = 0;       // if element was moving, stop it
8065       }
8066     }
8067   }
8068
8069   // not "else if" because of elements that can fall and move (EL_SPRING)
8070   if (CAN_MOVE(element) && !started_moving)
8071   {
8072     int move_pattern = element_info[element].move_pattern;
8073     int newx, newy;
8074
8075     Moving2Blocked(x, y, &newx, &newy);
8076
8077     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
8078       return;
8079
8080     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
8081         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
8082     {
8083       WasJustMoving[x][y] = 0;
8084       CheckCollision[x][y] = 0;
8085
8086       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
8087
8088       if (Tile[x][y] != element)        // element has changed
8089         return;
8090     }
8091
8092     if (!MovDelay[x][y])        // start new movement phase
8093     {
8094       // all objects that can change their move direction after each step
8095       // (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall
8096
8097       if (element != EL_YAMYAM &&
8098           element != EL_DARK_YAMYAM &&
8099           element != EL_PACMAN &&
8100           !(move_pattern & MV_ANY_DIRECTION) &&
8101           move_pattern != MV_TURNING_LEFT &&
8102           move_pattern != MV_TURNING_RIGHT &&
8103           move_pattern != MV_TURNING_LEFT_RIGHT &&
8104           move_pattern != MV_TURNING_RIGHT_LEFT &&
8105           move_pattern != MV_TURNING_RANDOM)
8106       {
8107         TurnRound(x, y);
8108
8109         if (MovDelay[x][y] && (element == EL_BUG ||
8110                                element == EL_SPACESHIP ||
8111                                element == EL_SP_SNIKSNAK ||
8112                                element == EL_SP_ELECTRON ||
8113                                element == EL_MOLE))
8114           TEST_DrawLevelField(x, y);
8115       }
8116     }
8117
8118     if (MovDelay[x][y])         // wait some time before next movement
8119     {
8120       MovDelay[x][y]--;
8121
8122       if (element == EL_ROBOT ||
8123           element == EL_YAMYAM ||
8124           element == EL_DARK_YAMYAM)
8125       {
8126         DrawLevelElementAnimationIfNeeded(x, y, element);
8127         PlayLevelSoundAction(x, y, ACTION_WAITING);
8128       }
8129       else if (element == EL_SP_ELECTRON)
8130         DrawLevelElementAnimationIfNeeded(x, y, element);
8131       else if (element == EL_DRAGON)
8132       {
8133         int i;
8134         int dir = MovDir[x][y];
8135         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
8136         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
8137         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
8138                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
8139                        dir == MV_UP     ? IMG_FLAMES_1_UP :
8140                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
8141         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
8142
8143         GfxAction[x][y] = ACTION_ATTACKING;
8144
8145         if (IS_PLAYER(x, y))
8146           DrawPlayerField(x, y);
8147         else
8148           TEST_DrawLevelField(x, y);
8149
8150         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
8151
8152         for (i = 1; i <= 3; i++)
8153         {
8154           int xx = x + i * dx;
8155           int yy = y + i * dy;
8156           int sx = SCREENX(xx);
8157           int sy = SCREENY(yy);
8158           int flame_graphic = graphic + (i - 1);
8159
8160           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Tile[xx][yy]))
8161             break;
8162
8163           if (MovDelay[x][y])
8164           {
8165             int flamed = MovingOrBlocked2Element(xx, yy);
8166
8167             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
8168               Bang(xx, yy);
8169             else
8170               RemoveMovingField(xx, yy);
8171
8172             ChangeDelay[xx][yy] = 0;
8173
8174             Tile[xx][yy] = EL_FLAMES;
8175
8176             if (IN_SCR_FIELD(sx, sy))
8177             {
8178               TEST_DrawLevelFieldCrumbled(xx, yy);
8179               DrawGraphic(sx, sy, flame_graphic, frame);
8180             }
8181           }
8182           else
8183           {
8184             if (Tile[xx][yy] == EL_FLAMES)
8185               Tile[xx][yy] = EL_EMPTY;
8186             TEST_DrawLevelField(xx, yy);
8187           }
8188         }
8189       }
8190
8191       if (MovDelay[x][y])       // element still has to wait some time
8192       {
8193         PlayLevelSoundAction(x, y, ACTION_WAITING);
8194
8195         return;
8196       }
8197     }
8198
8199     // now make next step
8200
8201     Moving2Blocked(x, y, &newx, &newy); // get next screen position
8202
8203     if (DONT_COLLIDE_WITH(element) &&
8204         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
8205         !PLAYER_ENEMY_PROTECTED(newx, newy))
8206     {
8207       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
8208
8209       return;
8210     }
8211
8212     else if (CAN_MOVE_INTO_ACID(element) &&
8213              IN_LEV_FIELD(newx, newy) && Tile[newx][newy] == EL_ACID &&
8214              !IS_MV_DIAGONAL(MovDir[x][y]) &&
8215              (MovDir[x][y] == MV_DOWN ||
8216               game.engine_version >= VERSION_IDENT(3,1,0,0)))
8217     {
8218       SplashAcid(newx, newy);
8219       Store[x][y] = EL_ACID;
8220     }
8221     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
8222     {
8223       if (Tile[newx][newy] == EL_EXIT_OPEN ||
8224           Tile[newx][newy] == EL_EM_EXIT_OPEN ||
8225           Tile[newx][newy] == EL_STEEL_EXIT_OPEN ||
8226           Tile[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
8227       {
8228         RemoveField(x, y);
8229         TEST_DrawLevelField(x, y);
8230
8231         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
8232         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
8233           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
8234
8235         game.friends_still_needed--;
8236         if (!game.friends_still_needed &&
8237             !game.GameOver &&
8238             game.all_players_gone)
8239           LevelSolved();
8240
8241         return;
8242       }
8243       else if (IS_FOOD_PENGUIN(Tile[newx][newy]))
8244       {
8245         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
8246           TEST_DrawLevelField(newx, newy);
8247         else
8248           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8249       }
8250       else if (!IS_FREE(newx, newy))
8251       {
8252         GfxAction[x][y] = ACTION_WAITING;
8253
8254         if (IS_PLAYER(x, y))
8255           DrawPlayerField(x, y);
8256         else
8257           TEST_DrawLevelField(x, y);
8258
8259         return;
8260       }
8261     }
8262     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
8263     {
8264       if (IS_FOOD_PIG(Tile[newx][newy]))
8265       {
8266         if (IS_MOVING(newx, newy))
8267           RemoveMovingField(newx, newy);
8268         else
8269         {
8270           Tile[newx][newy] = EL_EMPTY;
8271           TEST_DrawLevelField(newx, newy);
8272         }
8273
8274         PlayLevelSound(x, y, SND_PIG_DIGGING);
8275       }
8276       else if (!IS_FREE(newx, newy))
8277       {
8278         if (IS_PLAYER(x, y))
8279           DrawPlayerField(x, y);
8280         else
8281           TEST_DrawLevelField(x, y);
8282
8283         return;
8284       }
8285     }
8286     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
8287     {
8288       if (Store[x][y] != EL_EMPTY)
8289       {
8290         boolean can_clone = FALSE;
8291         int xx, yy;
8292
8293         // check if element to clone is still there
8294         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
8295         {
8296           if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == Store[x][y])
8297           {
8298             can_clone = TRUE;
8299
8300             break;
8301           }
8302         }
8303
8304         // cannot clone or target field not free anymore -- do not clone
8305         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8306           Store[x][y] = EL_EMPTY;
8307       }
8308
8309       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
8310       {
8311         if (IS_MV_DIAGONAL(MovDir[x][y]))
8312         {
8313           int diagonal_move_dir = MovDir[x][y];
8314           int stored = Store[x][y];
8315           int change_delay = 8;
8316           int graphic;
8317
8318           // android is moving diagonally
8319
8320           CreateField(x, y, EL_DIAGONAL_SHRINKING);
8321
8322           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
8323           GfxElement[x][y] = EL_EMC_ANDROID;
8324           GfxAction[x][y] = ACTION_SHRINKING;
8325           GfxDir[x][y] = diagonal_move_dir;
8326           ChangeDelay[x][y] = change_delay;
8327
8328           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
8329                                    GfxDir[x][y]);
8330
8331           DrawLevelGraphicAnimation(x, y, graphic);
8332           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
8333
8334           if (Tile[newx][newy] == EL_ACID)
8335           {
8336             SplashAcid(newx, newy);
8337
8338             return;
8339           }
8340
8341           CreateField(newx, newy, EL_DIAGONAL_GROWING);
8342
8343           Store[newx][newy] = EL_EMC_ANDROID;
8344           GfxElement[newx][newy] = EL_EMC_ANDROID;
8345           GfxAction[newx][newy] = ACTION_GROWING;
8346           GfxDir[newx][newy] = diagonal_move_dir;
8347           ChangeDelay[newx][newy] = change_delay;
8348
8349           graphic = el_act_dir2img(GfxElement[newx][newy],
8350                                    GfxAction[newx][newy], GfxDir[newx][newy]);
8351
8352           DrawLevelGraphicAnimation(newx, newy, graphic);
8353           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
8354
8355           return;
8356         }
8357         else
8358         {
8359           Tile[newx][newy] = EL_EMPTY;
8360           TEST_DrawLevelField(newx, newy);
8361
8362           PlayLevelSoundAction(x, y, ACTION_DIGGING);
8363         }
8364       }
8365       else if (!IS_FREE(newx, newy))
8366       {
8367         return;
8368       }
8369     }
8370     else if (IS_CUSTOM_ELEMENT(element) &&
8371              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
8372     {
8373       if (!DigFieldByCE(newx, newy, element))
8374         return;
8375
8376       if (move_pattern & MV_MAZE_RUNNER_STYLE)
8377       {
8378         RunnerVisit[x][y] = FrameCounter;
8379         PlayerVisit[x][y] /= 8;         // expire player visit path
8380       }
8381     }
8382     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
8383     {
8384       if (!IS_FREE(newx, newy))
8385       {
8386         if (IS_PLAYER(x, y))
8387           DrawPlayerField(x, y);
8388         else
8389           TEST_DrawLevelField(x, y);
8390
8391         return;
8392       }
8393       else
8394       {
8395         boolean wanna_flame = !RND(10);
8396         int dx = newx - x, dy = newy - y;
8397         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
8398         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
8399         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
8400                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
8401         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
8402                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
8403
8404         if ((wanna_flame ||
8405              IS_CLASSIC_ENEMY(element1) ||
8406              IS_CLASSIC_ENEMY(element2)) &&
8407             element1 != EL_DRAGON && element2 != EL_DRAGON &&
8408             element1 != EL_FLAMES && element2 != EL_FLAMES)
8409         {
8410           ResetGfxAnimation(x, y);
8411           GfxAction[x][y] = ACTION_ATTACKING;
8412
8413           if (IS_PLAYER(x, y))
8414             DrawPlayerField(x, y);
8415           else
8416             TEST_DrawLevelField(x, y);
8417
8418           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
8419
8420           MovDelay[x][y] = 50;
8421
8422           Tile[newx][newy] = EL_FLAMES;
8423           if (IN_LEV_FIELD(newx1, newy1) && Tile[newx1][newy1] == EL_EMPTY)
8424             Tile[newx1][newy1] = EL_FLAMES;
8425           if (IN_LEV_FIELD(newx2, newy2) && Tile[newx2][newy2] == EL_EMPTY)
8426             Tile[newx2][newy2] = EL_FLAMES;
8427
8428           return;
8429         }
8430       }
8431     }
8432     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8433              Tile[newx][newy] == EL_DIAMOND)
8434     {
8435       if (IS_MOVING(newx, newy))
8436         RemoveMovingField(newx, newy);
8437       else
8438       {
8439         Tile[newx][newy] = EL_EMPTY;
8440         TEST_DrawLevelField(newx, newy);
8441       }
8442
8443       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
8444     }
8445     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
8446              IS_FOOD_DARK_YAMYAM(Tile[newx][newy]))
8447     {
8448       if (AmoebaNr[newx][newy])
8449       {
8450         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8451         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8452             Tile[newx][newy] == EL_BD_AMOEBA)
8453           AmoebaCnt[AmoebaNr[newx][newy]]--;
8454       }
8455
8456       if (IS_MOVING(newx, newy))
8457       {
8458         RemoveMovingField(newx, newy);
8459       }
8460       else
8461       {
8462         Tile[newx][newy] = EL_EMPTY;
8463         TEST_DrawLevelField(newx, newy);
8464       }
8465
8466       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
8467     }
8468     else if ((element == EL_PACMAN || element == EL_MOLE)
8469              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Tile[newx][newy]))
8470     {
8471       if (AmoebaNr[newx][newy])
8472       {
8473         AmoebaCnt2[AmoebaNr[newx][newy]]--;
8474         if (Tile[newx][newy] == EL_AMOEBA_FULL ||
8475             Tile[newx][newy] == EL_BD_AMOEBA)
8476           AmoebaCnt[AmoebaNr[newx][newy]]--;
8477       }
8478
8479       if (element == EL_MOLE)
8480       {
8481         Tile[newx][newy] = EL_AMOEBA_SHRINKING;
8482         PlayLevelSound(x, y, SND_MOLE_DIGGING);
8483
8484         ResetGfxAnimation(x, y);
8485         GfxAction[x][y] = ACTION_DIGGING;
8486         TEST_DrawLevelField(x, y);
8487
8488         MovDelay[newx][newy] = 0;       // start amoeba shrinking delay
8489
8490         return;                         // wait for shrinking amoeba
8491       }
8492       else      // element == EL_PACMAN
8493       {
8494         Tile[newx][newy] = EL_EMPTY;
8495         TEST_DrawLevelField(newx, newy);
8496         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
8497       }
8498     }
8499     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
8500              (Tile[newx][newy] == EL_AMOEBA_SHRINKING ||
8501               (Tile[newx][newy] == EL_EMPTY && Stop[newx][newy])))
8502     {
8503       // wait for shrinking amoeba to completely disappear
8504       return;
8505     }
8506     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8507     {
8508       // object was running against a wall
8509
8510       TurnRound(x, y);
8511
8512       if (GFX_ELEMENT(element) != EL_SAND)     // !!! FIX THIS (crumble) !!!
8513         DrawLevelElementAnimation(x, y, element);
8514
8515       if (DONT_TOUCH(element))
8516         TestIfBadThingTouchesPlayer(x, y);
8517
8518       return;
8519     }
8520
8521     InitMovingField(x, y, MovDir[x][y]);
8522
8523     PlayLevelSoundAction(x, y, ACTION_MOVING);
8524   }
8525
8526   if (MovDir[x][y])
8527     ContinueMoving(x, y);
8528 }
8529
8530 void ContinueMoving(int x, int y)
8531 {
8532   int element = Tile[x][y];
8533   struct ElementInfo *ei = &element_info[element];
8534   int direction = MovDir[x][y];
8535   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8536   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8537   int newx = x + dx, newy = y + dy;
8538   int stored = Store[x][y];
8539   int stored_new = Store[newx][newy];
8540   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8541   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8542   boolean last_line = (newy == lev_fieldy - 1);
8543   boolean use_step_delay = (GET_MAX_STEP_DELAY(element) != 0);
8544
8545   if (pushed_by_player)         // special case: moving object pushed by player
8546   {
8547     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8548   }
8549   else if (use_step_delay)      // special case: moving object has step delay
8550   {
8551     if (!MovDelay[x][y])
8552       MovPos[x][y] += getElementMoveStepsize(x, y);
8553
8554     if (MovDelay[x][y])
8555       MovDelay[x][y]--;
8556     else
8557       MovDelay[x][y] = GET_NEW_STEP_DELAY(element);
8558
8559     if (MovDelay[x][y])
8560     {
8561       TEST_DrawLevelField(x, y);
8562
8563       return;   // element is still waiting
8564     }
8565   }
8566   else                          // normal case: generically moving object
8567   {
8568     MovPos[x][y] += getElementMoveStepsize(x, y);
8569   }
8570
8571   if (ABS(MovPos[x][y]) < TILEX)
8572   {
8573     TEST_DrawLevelField(x, y);
8574
8575     return;     // element is still moving
8576   }
8577
8578   // element reached destination field
8579
8580   Tile[x][y] = EL_EMPTY;
8581   Tile[newx][newy] = element;
8582   MovPos[x][y] = 0;     // force "not moving" for "crumbled sand"
8583
8584   if (Store[x][y] == EL_ACID)   // element is moving into acid pool
8585   {
8586     element = Tile[newx][newy] = EL_ACID;
8587   }
8588   else if (element == EL_MOLE)
8589   {
8590     Tile[x][y] = EL_SAND;
8591
8592     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8593   }
8594   else if (element == EL_QUICKSAND_FILLING)
8595   {
8596     element = Tile[newx][newy] = get_next_element(element);
8597     Store[newx][newy] = Store[x][y];
8598   }
8599   else if (element == EL_QUICKSAND_EMPTYING)
8600   {
8601     Tile[x][y] = get_next_element(element);
8602     element = Tile[newx][newy] = Store[x][y];
8603   }
8604   else if (element == EL_QUICKSAND_FAST_FILLING)
8605   {
8606     element = Tile[newx][newy] = get_next_element(element);
8607     Store[newx][newy] = Store[x][y];
8608   }
8609   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8610   {
8611     Tile[x][y] = get_next_element(element);
8612     element = Tile[newx][newy] = Store[x][y];
8613   }
8614   else if (element == EL_MAGIC_WALL_FILLING)
8615   {
8616     element = Tile[newx][newy] = get_next_element(element);
8617     if (!game.magic_wall_active)
8618       element = Tile[newx][newy] = EL_MAGIC_WALL_DEAD;
8619     Store[newx][newy] = Store[x][y];
8620   }
8621   else if (element == EL_MAGIC_WALL_EMPTYING)
8622   {
8623     Tile[x][y] = get_next_element(element);
8624     if (!game.magic_wall_active)
8625       Tile[x][y] = EL_MAGIC_WALL_DEAD;
8626     element = Tile[newx][newy] = Store[x][y];
8627
8628     InitField(newx, newy, FALSE);
8629   }
8630   else if (element == EL_BD_MAGIC_WALL_FILLING)
8631   {
8632     element = Tile[newx][newy] = get_next_element(element);
8633     if (!game.magic_wall_active)
8634       element = Tile[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8635     Store[newx][newy] = Store[x][y];
8636   }
8637   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8638   {
8639     Tile[x][y] = get_next_element(element);
8640     if (!game.magic_wall_active)
8641       Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
8642     element = Tile[newx][newy] = Store[x][y];
8643
8644     InitField(newx, newy, FALSE);
8645   }
8646   else if (element == EL_DC_MAGIC_WALL_FILLING)
8647   {
8648     element = Tile[newx][newy] = get_next_element(element);
8649     if (!game.magic_wall_active)
8650       element = Tile[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8651     Store[newx][newy] = Store[x][y];
8652   }
8653   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8654   {
8655     Tile[x][y] = get_next_element(element);
8656     if (!game.magic_wall_active)
8657       Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
8658     element = Tile[newx][newy] = Store[x][y];
8659
8660     InitField(newx, newy, FALSE);
8661   }
8662   else if (element == EL_AMOEBA_DROPPING)
8663   {
8664     Tile[x][y] = get_next_element(element);
8665     element = Tile[newx][newy] = Store[x][y];
8666   }
8667   else if (element == EL_SOKOBAN_OBJECT)
8668   {
8669     if (Back[x][y])
8670       Tile[x][y] = Back[x][y];
8671
8672     if (Back[newx][newy])
8673       Tile[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8674
8675     Back[x][y] = Back[newx][newy] = 0;
8676   }
8677
8678   Store[x][y] = EL_EMPTY;
8679   MovPos[x][y] = 0;
8680   MovDir[x][y] = 0;
8681   MovDelay[x][y] = 0;
8682
8683   MovDelay[newx][newy] = 0;
8684
8685   if (CAN_CHANGE_OR_HAS_ACTION(element))
8686   {
8687     // copy element change control values to new field
8688     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8689     ChangePage[newx][newy]  = ChangePage[x][y];
8690     ChangeCount[newx][newy] = ChangeCount[x][y];
8691     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8692   }
8693
8694   CustomValue[newx][newy] = CustomValue[x][y];
8695
8696   ChangeDelay[x][y] = 0;
8697   ChangePage[x][y] = -1;
8698   ChangeCount[x][y] = 0;
8699   ChangeEvent[x][y] = -1;
8700
8701   CustomValue[x][y] = 0;
8702
8703   // copy animation control values to new field
8704   GfxFrame[newx][newy]  = GfxFrame[x][y];
8705   GfxRandom[newx][newy] = GfxRandom[x][y];      // keep same random value
8706   GfxAction[newx][newy] = GfxAction[x][y];      // keep action one frame
8707   GfxDir[newx][newy]    = GfxDir[x][y];         // keep element direction
8708
8709   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8710
8711   // some elements can leave other elements behind after moving
8712   if (ei->move_leave_element != EL_EMPTY &&
8713       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8714       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8715   {
8716     int move_leave_element = ei->move_leave_element;
8717
8718     // this makes it possible to leave the removed element again
8719     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8720       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8721
8722     Tile[x][y] = move_leave_element;
8723
8724     if (element_info[Tile[x][y]].move_direction_initial == MV_START_PREVIOUS)
8725       MovDir[x][y] = direction;
8726
8727     InitField(x, y, FALSE);
8728
8729     if (GFX_CRUMBLED(Tile[x][y]))
8730       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8731
8732     if (ELEM_IS_PLAYER(move_leave_element))
8733       RelocatePlayer(x, y, move_leave_element);
8734   }
8735
8736   // do this after checking for left-behind element
8737   ResetGfxAnimation(x, y);      // reset animation values for old field
8738
8739   if (!CAN_MOVE(element) ||
8740       (CAN_FALL(element) && direction == MV_DOWN &&
8741        (element == EL_SPRING ||
8742         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8743         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8744     GfxDir[x][y] = MovDir[newx][newy] = 0;
8745
8746   TEST_DrawLevelField(x, y);
8747   TEST_DrawLevelField(newx, newy);
8748
8749   Stop[newx][newy] = TRUE;      // ignore this element until the next frame
8750
8751   // prevent pushed element from moving on in pushed direction
8752   if (pushed_by_player && CAN_MOVE(element) &&
8753       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8754       !(element_info[element].move_pattern & direction))
8755     TurnRound(newx, newy);
8756
8757   // prevent elements on conveyor belt from moving on in last direction
8758   if (pushed_by_conveyor && CAN_FALL(element) &&
8759       direction & MV_HORIZONTAL)
8760     MovDir[newx][newy] = 0;
8761
8762   if (!pushed_by_player)
8763   {
8764     int nextx = newx + dx, nexty = newy + dy;
8765     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8766
8767     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8768
8769     if (CAN_FALL(element) && direction == MV_DOWN)
8770       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8771
8772     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8773       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8774
8775     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8776       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8777   }
8778
8779   if (DONT_TOUCH(element))      // object may be nasty to player or others
8780   {
8781     TestIfBadThingTouchesPlayer(newx, newy);
8782     TestIfBadThingTouchesFriend(newx, newy);
8783
8784     if (!IS_CUSTOM_ELEMENT(element))
8785       TestIfBadThingTouchesOtherBadThing(newx, newy);
8786   }
8787   else if (element == EL_PENGUIN)
8788     TestIfFriendTouchesBadThing(newx, newy);
8789
8790   if (DONT_GET_HIT_BY(element))
8791   {
8792     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8793   }
8794
8795   // give the player one last chance (one more frame) to move away
8796   if (CAN_FALL(element) && direction == MV_DOWN &&
8797       (last_line || (!IS_FREE(x, newy + 1) &&
8798                      (!IS_PLAYER(x, newy + 1) ||
8799                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8800     Impact(x, newy);
8801
8802   if (pushed_by_player && !game.use_change_when_pushing_bug)
8803   {
8804     int push_side = MV_DIR_OPPOSITE(direction);
8805     struct PlayerInfo *player = PLAYERINFO(x, y);
8806
8807     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8808                                player->index_bit, push_side);
8809     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8810                                         player->index_bit, push_side);
8811   }
8812
8813   if (element == EL_EMC_ANDROID && pushed_by_player)    // make another move
8814     MovDelay[newx][newy] = 1;
8815
8816   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8817
8818   TestIfElementTouchesCustomElement(x, y);      // empty or new element
8819   TestIfElementHitsCustomElement(newx, newy, direction);
8820   TestIfPlayerTouchesCustomElement(newx, newy);
8821   TestIfElementTouchesCustomElement(newx, newy);
8822
8823   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8824       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8825     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8826                              MV_DIR_OPPOSITE(direction));
8827 }
8828
8829 int AmoebaNeighbourNr(int ax, int ay)
8830 {
8831   int i;
8832   int element = Tile[ax][ay];
8833   int group_nr = 0;
8834   static int xy[4][2] =
8835   {
8836     { 0, -1 },
8837     { -1, 0 },
8838     { +1, 0 },
8839     { 0, +1 }
8840   };
8841
8842   for (i = 0; i < NUM_DIRECTIONS; i++)
8843   {
8844     int x = ax + xy[i][0];
8845     int y = ay + xy[i][1];
8846
8847     if (!IN_LEV_FIELD(x, y))
8848       continue;
8849
8850     if (Tile[x][y] == element && AmoebaNr[x][y] > 0)
8851       group_nr = AmoebaNr[x][y];
8852   }
8853
8854   return group_nr;
8855 }
8856
8857 static void AmoebaMerge(int ax, int ay)
8858 {
8859   int i, x, y, xx, yy;
8860   int new_group_nr = AmoebaNr[ax][ay];
8861   static int xy[4][2] =
8862   {
8863     { 0, -1 },
8864     { -1, 0 },
8865     { +1, 0 },
8866     { 0, +1 }
8867   };
8868
8869   if (new_group_nr == 0)
8870     return;
8871
8872   for (i = 0; i < NUM_DIRECTIONS; i++)
8873   {
8874     x = ax + xy[i][0];
8875     y = ay + xy[i][1];
8876
8877     if (!IN_LEV_FIELD(x, y))
8878       continue;
8879
8880     if ((Tile[x][y] == EL_AMOEBA_FULL ||
8881          Tile[x][y] == EL_BD_AMOEBA ||
8882          Tile[x][y] == EL_AMOEBA_DEAD) &&
8883         AmoebaNr[x][y] != new_group_nr)
8884     {
8885       int old_group_nr = AmoebaNr[x][y];
8886
8887       if (old_group_nr == 0)
8888         return;
8889
8890       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8891       AmoebaCnt[old_group_nr] = 0;
8892       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8893       AmoebaCnt2[old_group_nr] = 0;
8894
8895       SCAN_PLAYFIELD(xx, yy)
8896       {
8897         if (AmoebaNr[xx][yy] == old_group_nr)
8898           AmoebaNr[xx][yy] = new_group_nr;
8899       }
8900     }
8901   }
8902 }
8903
8904 void AmoebaToDiamond(int ax, int ay)
8905 {
8906   int i, x, y;
8907
8908   if (Tile[ax][ay] == EL_AMOEBA_DEAD)
8909   {
8910     int group_nr = AmoebaNr[ax][ay];
8911
8912 #ifdef DEBUG
8913     if (group_nr == 0)
8914     {
8915       Debug("game:playing:AmoebaToDiamond", "ax = %d, ay = %d", ax, ay);
8916       Debug("game:playing:AmoebaToDiamond", "This should never happen!");
8917
8918       return;
8919     }
8920 #endif
8921
8922     SCAN_PLAYFIELD(x, y)
8923     {
8924       if (Tile[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8925       {
8926         AmoebaNr[x][y] = 0;
8927         Tile[x][y] = EL_AMOEBA_TO_DIAMOND;
8928       }
8929     }
8930
8931     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8932                             SND_AMOEBA_TURNING_TO_GEM :
8933                             SND_AMOEBA_TURNING_TO_ROCK));
8934     Bang(ax, ay);
8935   }
8936   else
8937   {
8938     static int xy[4][2] =
8939     {
8940       { 0, -1 },
8941       { -1, 0 },
8942       { +1, 0 },
8943       { 0, +1 }
8944     };
8945
8946     for (i = 0; i < NUM_DIRECTIONS; i++)
8947     {
8948       x = ax + xy[i][0];
8949       y = ay + xy[i][1];
8950
8951       if (!IN_LEV_FIELD(x, y))
8952         continue;
8953
8954       if (Tile[x][y] == EL_AMOEBA_TO_DIAMOND)
8955       {
8956         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8957                               SND_AMOEBA_TURNING_TO_GEM :
8958                               SND_AMOEBA_TURNING_TO_ROCK));
8959         Bang(x, y);
8960       }
8961     }
8962   }
8963 }
8964
8965 static void AmoebaToDiamondBD(int ax, int ay, int new_element)
8966 {
8967   int x, y;
8968   int group_nr = AmoebaNr[ax][ay];
8969   boolean done = FALSE;
8970
8971 #ifdef DEBUG
8972   if (group_nr == 0)
8973   {
8974     Debug("game:playing:AmoebaToDiamondBD", "ax = %d, ay = %d", ax, ay);
8975     Debug("game:playing:AmoebaToDiamondBD", "This should never happen!");
8976
8977     return;
8978   }
8979 #endif
8980
8981   SCAN_PLAYFIELD(x, y)
8982   {
8983     if (AmoebaNr[x][y] == group_nr &&
8984         (Tile[x][y] == EL_AMOEBA_DEAD ||
8985          Tile[x][y] == EL_BD_AMOEBA ||
8986          Tile[x][y] == EL_AMOEBA_GROWING))
8987     {
8988       AmoebaNr[x][y] = 0;
8989       Tile[x][y] = new_element;
8990       InitField(x, y, FALSE);
8991       TEST_DrawLevelField(x, y);
8992       done = TRUE;
8993     }
8994   }
8995
8996   if (done)
8997     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8998                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8999                             SND_BD_AMOEBA_TURNING_TO_GEM));
9000 }
9001
9002 static void AmoebaGrowing(int x, int y)
9003 {
9004   static unsigned int sound_delay = 0;
9005   static unsigned int sound_delay_value = 0;
9006
9007   if (!MovDelay[x][y])          // start new growing cycle
9008   {
9009     MovDelay[x][y] = 7;
9010
9011     if (DelayReached(&sound_delay, sound_delay_value))
9012     {
9013       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
9014       sound_delay_value = 30;
9015     }
9016   }
9017
9018   if (MovDelay[x][y])           // wait some time before growing bigger
9019   {
9020     MovDelay[x][y]--;
9021     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9022     {
9023       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
9024                                            6 - MovDelay[x][y]);
9025
9026       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
9027     }
9028
9029     if (!MovDelay[x][y])
9030     {
9031       Tile[x][y] = Store[x][y];
9032       Store[x][y] = 0;
9033       TEST_DrawLevelField(x, y);
9034     }
9035   }
9036 }
9037
9038 static void AmoebaShrinking(int x, int y)
9039 {
9040   static unsigned int sound_delay = 0;
9041   static unsigned int sound_delay_value = 0;
9042
9043   if (!MovDelay[x][y])          // start new shrinking cycle
9044   {
9045     MovDelay[x][y] = 7;
9046
9047     if (DelayReached(&sound_delay, sound_delay_value))
9048       sound_delay_value = 30;
9049   }
9050
9051   if (MovDelay[x][y])           // wait some time before shrinking
9052   {
9053     MovDelay[x][y]--;
9054     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9055     {
9056       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
9057                                            6 - MovDelay[x][y]);
9058
9059       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
9060     }
9061
9062     if (!MovDelay[x][y])
9063     {
9064       Tile[x][y] = EL_EMPTY;
9065       TEST_DrawLevelField(x, y);
9066
9067       // don't let mole enter this field in this cycle;
9068       // (give priority to objects falling to this field from above)
9069       Stop[x][y] = TRUE;
9070     }
9071   }
9072 }
9073
9074 static void AmoebaReproduce(int ax, int ay)
9075 {
9076   int i;
9077   int element = Tile[ax][ay];
9078   int graphic = el2img(element);
9079   int newax = ax, neway = ay;
9080   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
9081   static int xy[4][2] =
9082   {
9083     { 0, -1 },
9084     { -1, 0 },
9085     { +1, 0 },
9086     { 0, +1 }
9087   };
9088
9089   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
9090   {
9091     Tile[ax][ay] = EL_AMOEBA_DEAD;
9092     TEST_DrawLevelField(ax, ay);
9093     return;
9094   }
9095
9096   if (IS_ANIMATED(graphic))
9097     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9098
9099   if (!MovDelay[ax][ay])        // start making new amoeba field
9100     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
9101
9102   if (MovDelay[ax][ay])         // wait some time before making new amoeba
9103   {
9104     MovDelay[ax][ay]--;
9105     if (MovDelay[ax][ay])
9106       return;
9107   }
9108
9109   if (can_drop)                 // EL_AMOEBA_WET or EL_EMC_DRIPPER
9110   {
9111     int start = RND(4);
9112     int x = ax + xy[start][0];
9113     int y = ay + xy[start][1];
9114
9115     if (!IN_LEV_FIELD(x, y))
9116       return;
9117
9118     if (IS_FREE(x, y) ||
9119         CAN_GROW_INTO(Tile[x][y]) ||
9120         Tile[x][y] == EL_QUICKSAND_EMPTY ||
9121         Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9122     {
9123       newax = x;
9124       neway = y;
9125     }
9126
9127     if (newax == ax && neway == ay)
9128       return;
9129   }
9130   else                          // normal or "filled" (BD style) amoeba
9131   {
9132     int start = RND(4);
9133     boolean waiting_for_player = FALSE;
9134
9135     for (i = 0; i < NUM_DIRECTIONS; i++)
9136     {
9137       int j = (start + i) % 4;
9138       int x = ax + xy[j][0];
9139       int y = ay + xy[j][1];
9140
9141       if (!IN_LEV_FIELD(x, y))
9142         continue;
9143
9144       if (IS_FREE(x, y) ||
9145           CAN_GROW_INTO(Tile[x][y]) ||
9146           Tile[x][y] == EL_QUICKSAND_EMPTY ||
9147           Tile[x][y] == EL_QUICKSAND_FAST_EMPTY)
9148       {
9149         newax = x;
9150         neway = y;
9151         break;
9152       }
9153       else if (IS_PLAYER(x, y))
9154         waiting_for_player = TRUE;
9155     }
9156
9157     if (newax == ax && neway == ay)             // amoeba cannot grow
9158     {
9159       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
9160       {
9161         Tile[ax][ay] = EL_AMOEBA_DEAD;
9162         TEST_DrawLevelField(ax, ay);
9163         AmoebaCnt[AmoebaNr[ax][ay]]--;
9164
9165         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   // amoeba is completely dead
9166         {
9167           if (element == EL_AMOEBA_FULL)
9168             AmoebaToDiamond(ax, ay);
9169           else if (element == EL_BD_AMOEBA)
9170             AmoebaToDiamondBD(ax, ay, level.amoeba_content);
9171         }
9172       }
9173       return;
9174     }
9175     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
9176     {
9177       // amoeba gets larger by growing in some direction
9178
9179       int new_group_nr = AmoebaNr[ax][ay];
9180
9181 #ifdef DEBUG
9182   if (new_group_nr == 0)
9183   {
9184     Debug("game:playing:AmoebaReproduce", "newax = %d, neway = %d",
9185           newax, neway);
9186     Debug("game:playing:AmoebaReproduce", "This should never happen!");
9187
9188     return;
9189   }
9190 #endif
9191
9192       AmoebaNr[newax][neway] = new_group_nr;
9193       AmoebaCnt[new_group_nr]++;
9194       AmoebaCnt2[new_group_nr]++;
9195
9196       // if amoeba touches other amoeba(s) after growing, unify them
9197       AmoebaMerge(newax, neway);
9198
9199       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
9200       {
9201         AmoebaToDiamondBD(newax, neway, EL_BD_ROCK);
9202         return;
9203       }
9204     }
9205   }
9206
9207   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
9208       (neway == lev_fieldy - 1 && newax != ax))
9209   {
9210     Tile[newax][neway] = EL_AMOEBA_GROWING;     // creation of new amoeba
9211     Store[newax][neway] = element;
9212   }
9213   else if (neway == ay || element == EL_EMC_DRIPPER)
9214   {
9215     Tile[newax][neway] = EL_AMOEBA_DROP;        // drop left/right of amoeba
9216
9217     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
9218   }
9219   else
9220   {
9221     InitMovingField(ax, ay, MV_DOWN);           // drop dripping from amoeba
9222     Tile[ax][ay] = EL_AMOEBA_DROPPING;
9223     Store[ax][ay] = EL_AMOEBA_DROP;
9224     ContinueMoving(ax, ay);
9225     return;
9226   }
9227
9228   TEST_DrawLevelField(newax, neway);
9229 }
9230
9231 static void Life(int ax, int ay)
9232 {
9233   int x1, y1, x2, y2;
9234   int life_time = 40;
9235   int element = Tile[ax][ay];
9236   int graphic = el2img(element);
9237   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
9238                          level.biomaze);
9239   boolean changed = FALSE;
9240
9241   if (IS_ANIMATED(graphic))
9242     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9243
9244   if (Stop[ax][ay])
9245     return;
9246
9247   if (!MovDelay[ax][ay])        // start new "game of life" cycle
9248     MovDelay[ax][ay] = life_time;
9249
9250   if (MovDelay[ax][ay])         // wait some time before next cycle
9251   {
9252     MovDelay[ax][ay]--;
9253     if (MovDelay[ax][ay])
9254       return;
9255   }
9256
9257   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
9258   {
9259     int xx = ax+x1, yy = ay+y1;
9260     int old_element = Tile[xx][yy];
9261     int num_neighbours = 0;
9262
9263     if (!IN_LEV_FIELD(xx, yy))
9264       continue;
9265
9266     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
9267     {
9268       int x = xx+x2, y = yy+y2;
9269
9270       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
9271         continue;
9272
9273       boolean is_player_cell = (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y));
9274       boolean is_neighbour = FALSE;
9275
9276       if (level.use_life_bugs)
9277         is_neighbour =
9278           (((Tile[x][y] == element || is_player_cell) && !Stop[x][y]) ||
9279            (IS_FREE(x, y)                             &&  Stop[x][y]));
9280       else
9281         is_neighbour =
9282           (Last[x][y] == element || is_player_cell);
9283
9284       if (is_neighbour)
9285         num_neighbours++;
9286     }
9287
9288     boolean is_free = FALSE;
9289
9290     if (level.use_life_bugs)
9291       is_free = (IS_FREE(xx, yy));
9292     else
9293       is_free = (IS_FREE(xx, yy) && Last[xx][yy] == EL_EMPTY);
9294
9295     if (xx == ax && yy == ay)           // field in the middle
9296     {
9297       if (num_neighbours < life_parameter[0] ||
9298           num_neighbours > life_parameter[1])
9299       {
9300         Tile[xx][yy] = EL_EMPTY;
9301         if (Tile[xx][yy] != old_element)
9302           TEST_DrawLevelField(xx, yy);
9303         Stop[xx][yy] = TRUE;
9304         changed = TRUE;
9305       }
9306     }
9307     else if (is_free || CAN_GROW_INTO(Tile[xx][yy]))
9308     {                                   // free border field
9309       if (num_neighbours >= life_parameter[2] &&
9310           num_neighbours <= life_parameter[3])
9311       {
9312         Tile[xx][yy] = element;
9313         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
9314         if (Tile[xx][yy] != old_element)
9315           TEST_DrawLevelField(xx, yy);
9316         Stop[xx][yy] = TRUE;
9317         changed = TRUE;
9318       }
9319     }
9320   }
9321
9322   if (changed)
9323     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
9324                    SND_GAME_OF_LIFE_GROWING);
9325 }
9326
9327 static void InitRobotWheel(int x, int y)
9328 {
9329   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
9330 }
9331
9332 static void RunRobotWheel(int x, int y)
9333 {
9334   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
9335 }
9336
9337 static void StopRobotWheel(int x, int y)
9338 {
9339   if (game.robot_wheel_x == x &&
9340       game.robot_wheel_y == y)
9341   {
9342     game.robot_wheel_x = -1;
9343     game.robot_wheel_y = -1;
9344     game.robot_wheel_active = FALSE;
9345   }
9346 }
9347
9348 static void InitTimegateWheel(int x, int y)
9349 {
9350   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
9351 }
9352
9353 static void RunTimegateWheel(int x, int y)
9354 {
9355   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
9356 }
9357
9358 static void InitMagicBallDelay(int x, int y)
9359 {
9360   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
9361 }
9362
9363 static void ActivateMagicBall(int bx, int by)
9364 {
9365   int x, y;
9366
9367   if (level.ball_random)
9368   {
9369     int pos_border = RND(8);    // select one of the eight border elements
9370     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
9371     int xx = pos_content % 3;
9372     int yy = pos_content / 3;
9373
9374     x = bx - 1 + xx;
9375     y = by - 1 + yy;
9376
9377     if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9378       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9379   }
9380   else
9381   {
9382     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
9383     {
9384       int xx = x - bx + 1;
9385       int yy = y - by + 1;
9386
9387       if (IN_LEV_FIELD(x, y) && Tile[x][y] == EL_EMPTY)
9388         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
9389     }
9390   }
9391
9392   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
9393 }
9394
9395 static void CheckExit(int x, int y)
9396 {
9397   if (game.gems_still_needed > 0 ||
9398       game.sokoban_fields_still_needed > 0 ||
9399       game.sokoban_objects_still_needed > 0 ||
9400       game.lights_still_needed > 0)
9401   {
9402     int element = Tile[x][y];
9403     int graphic = el2img(element);
9404
9405     if (IS_ANIMATED(graphic))
9406       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9407
9408     return;
9409   }
9410
9411   // do not re-open exit door closed after last player
9412   if (game.all_players_gone)
9413     return;
9414
9415   Tile[x][y] = EL_EXIT_OPENING;
9416
9417   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
9418 }
9419
9420 static void CheckExitEM(int x, int y)
9421 {
9422   if (game.gems_still_needed > 0 ||
9423       game.sokoban_fields_still_needed > 0 ||
9424       game.sokoban_objects_still_needed > 0 ||
9425       game.lights_still_needed > 0)
9426   {
9427     int element = Tile[x][y];
9428     int graphic = el2img(element);
9429
9430     if (IS_ANIMATED(graphic))
9431       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9432
9433     return;
9434   }
9435
9436   // do not re-open exit door closed after last player
9437   if (game.all_players_gone)
9438     return;
9439
9440   Tile[x][y] = EL_EM_EXIT_OPENING;
9441
9442   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
9443 }
9444
9445 static void CheckExitSteel(int x, int y)
9446 {
9447   if (game.gems_still_needed > 0 ||
9448       game.sokoban_fields_still_needed > 0 ||
9449       game.sokoban_objects_still_needed > 0 ||
9450       game.lights_still_needed > 0)
9451   {
9452     int element = Tile[x][y];
9453     int graphic = el2img(element);
9454
9455     if (IS_ANIMATED(graphic))
9456       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9457
9458     return;
9459   }
9460
9461   // do not re-open exit door closed after last player
9462   if (game.all_players_gone)
9463     return;
9464
9465   Tile[x][y] = EL_STEEL_EXIT_OPENING;
9466
9467   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
9468 }
9469
9470 static void CheckExitSteelEM(int x, int y)
9471 {
9472   if (game.gems_still_needed > 0 ||
9473       game.sokoban_fields_still_needed > 0 ||
9474       game.sokoban_objects_still_needed > 0 ||
9475       game.lights_still_needed > 0)
9476   {
9477     int element = Tile[x][y];
9478     int graphic = el2img(element);
9479
9480     if (IS_ANIMATED(graphic))
9481       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9482
9483     return;
9484   }
9485
9486   // do not re-open exit door closed after last player
9487   if (game.all_players_gone)
9488     return;
9489
9490   Tile[x][y] = EL_EM_STEEL_EXIT_OPENING;
9491
9492   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
9493 }
9494
9495 static void CheckExitSP(int x, int y)
9496 {
9497   if (game.gems_still_needed > 0)
9498   {
9499     int element = Tile[x][y];
9500     int graphic = el2img(element);
9501
9502     if (IS_ANIMATED(graphic))
9503       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
9504
9505     return;
9506   }
9507
9508   // do not re-open exit door closed after last player
9509   if (game.all_players_gone)
9510     return;
9511
9512   Tile[x][y] = EL_SP_EXIT_OPENING;
9513
9514   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
9515 }
9516
9517 static void CloseAllOpenTimegates(void)
9518 {
9519   int x, y;
9520
9521   SCAN_PLAYFIELD(x, y)
9522   {
9523     int element = Tile[x][y];
9524
9525     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
9526     {
9527       Tile[x][y] = EL_TIMEGATE_CLOSING;
9528
9529       PlayLevelSoundAction(x, y, ACTION_CLOSING);
9530     }
9531   }
9532 }
9533
9534 static void DrawTwinkleOnField(int x, int y)
9535 {
9536   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
9537     return;
9538
9539   if (Tile[x][y] == EL_BD_DIAMOND)
9540     return;
9541
9542   if (MovDelay[x][y] == 0)      // next animation frame
9543     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
9544
9545   if (MovDelay[x][y] != 0)      // wait some time before next frame
9546   {
9547     MovDelay[x][y]--;
9548
9549     DrawLevelElementAnimation(x, y, Tile[x][y]);
9550
9551     if (MovDelay[x][y] != 0)
9552     {
9553       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
9554                                            10 - MovDelay[x][y]);
9555
9556       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9557     }
9558   }
9559 }
9560
9561 static void MauerWaechst(int x, int y)
9562 {
9563   int delay = 6;
9564
9565   if (!MovDelay[x][y])          // next animation frame
9566     MovDelay[x][y] = 3 * delay;
9567
9568   if (MovDelay[x][y])           // wait some time before next frame
9569   {
9570     MovDelay[x][y]--;
9571
9572     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9573     {
9574       int graphic = el_dir2img(Tile[x][y], GfxDir[x][y]);
9575       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9576
9577       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9578     }
9579
9580     if (!MovDelay[x][y])
9581     {
9582       if (MovDir[x][y] == MV_LEFT)
9583       {
9584         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Tile[x - 1][y]))
9585           TEST_DrawLevelField(x - 1, y);
9586       }
9587       else if (MovDir[x][y] == MV_RIGHT)
9588       {
9589         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Tile[x + 1][y]))
9590           TEST_DrawLevelField(x + 1, y);
9591       }
9592       else if (MovDir[x][y] == MV_UP)
9593       {
9594         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Tile[x][y - 1]))
9595           TEST_DrawLevelField(x, y - 1);
9596       }
9597       else
9598       {
9599         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Tile[x][y + 1]))
9600           TEST_DrawLevelField(x, y + 1);
9601       }
9602
9603       Tile[x][y] = Store[x][y];
9604       Store[x][y] = 0;
9605       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9606       TEST_DrawLevelField(x, y);
9607     }
9608   }
9609 }
9610
9611 static void MauerAbleger(int ax, int ay)
9612 {
9613   int element = Tile[ax][ay];
9614   int graphic = el2img(element);
9615   boolean oben_frei = FALSE, unten_frei = FALSE;
9616   boolean links_frei = FALSE, rechts_frei = FALSE;
9617   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9618   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9619   boolean new_wall = FALSE;
9620
9621   if (IS_ANIMATED(graphic))
9622     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9623
9624   if (!MovDelay[ax][ay])        // start building new wall
9625     MovDelay[ax][ay] = 6;
9626
9627   if (MovDelay[ax][ay])         // wait some time before building new wall
9628   {
9629     MovDelay[ax][ay]--;
9630     if (MovDelay[ax][ay])
9631       return;
9632   }
9633
9634   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9635     oben_frei = TRUE;
9636   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9637     unten_frei = TRUE;
9638   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9639     links_frei = TRUE;
9640   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9641     rechts_frei = TRUE;
9642
9643   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9644       element == EL_EXPANDABLE_WALL_ANY)
9645   {
9646     if (oben_frei)
9647     {
9648       Tile[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9649       Store[ax][ay-1] = element;
9650       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9651       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9652         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9653                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9654       new_wall = TRUE;
9655     }
9656     if (unten_frei)
9657     {
9658       Tile[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9659       Store[ax][ay+1] = element;
9660       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9661       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9662         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9663                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9664       new_wall = TRUE;
9665     }
9666   }
9667
9668   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9669       element == EL_EXPANDABLE_WALL_ANY ||
9670       element == EL_EXPANDABLE_WALL ||
9671       element == EL_BD_EXPANDABLE_WALL)
9672   {
9673     if (links_frei)
9674     {
9675       Tile[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9676       Store[ax-1][ay] = element;
9677       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9678       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9679         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9680                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9681       new_wall = TRUE;
9682     }
9683
9684     if (rechts_frei)
9685     {
9686       Tile[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9687       Store[ax+1][ay] = element;
9688       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9689       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9690         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9691                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9692       new_wall = TRUE;
9693     }
9694   }
9695
9696   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9697     TEST_DrawLevelField(ax, ay);
9698
9699   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9700     oben_massiv = TRUE;
9701   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9702     unten_massiv = TRUE;
9703   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9704     links_massiv = TRUE;
9705   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9706     rechts_massiv = TRUE;
9707
9708   if (((oben_massiv && unten_massiv) ||
9709        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9710        element == EL_EXPANDABLE_WALL) &&
9711       ((links_massiv && rechts_massiv) ||
9712        element == EL_EXPANDABLE_WALL_VERTICAL))
9713     Tile[ax][ay] = EL_WALL;
9714
9715   if (new_wall)
9716     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9717 }
9718
9719 static void MauerAblegerStahl(int ax, int ay)
9720 {
9721   int element = Tile[ax][ay];
9722   int graphic = el2img(element);
9723   boolean oben_frei = FALSE, unten_frei = FALSE;
9724   boolean links_frei = FALSE, rechts_frei = FALSE;
9725   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9726   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9727   boolean new_wall = FALSE;
9728
9729   if (IS_ANIMATED(graphic))
9730     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9731
9732   if (!MovDelay[ax][ay])        // start building new wall
9733     MovDelay[ax][ay] = 6;
9734
9735   if (MovDelay[ax][ay])         // wait some time before building new wall
9736   {
9737     MovDelay[ax][ay]--;
9738     if (MovDelay[ax][ay])
9739       return;
9740   }
9741
9742   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9743     oben_frei = TRUE;
9744   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9745     unten_frei = TRUE;
9746   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9747     links_frei = TRUE;
9748   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9749     rechts_frei = TRUE;
9750
9751   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9752       element == EL_EXPANDABLE_STEELWALL_ANY)
9753   {
9754     if (oben_frei)
9755     {
9756       Tile[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9757       Store[ax][ay-1] = element;
9758       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9759       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9760         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9761                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9762       new_wall = TRUE;
9763     }
9764     if (unten_frei)
9765     {
9766       Tile[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9767       Store[ax][ay+1] = element;
9768       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9769       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9770         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9771                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9772       new_wall = TRUE;
9773     }
9774   }
9775
9776   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9777       element == EL_EXPANDABLE_STEELWALL_ANY)
9778   {
9779     if (links_frei)
9780     {
9781       Tile[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9782       Store[ax-1][ay] = element;
9783       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9784       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9785         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9786                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9787       new_wall = TRUE;
9788     }
9789
9790     if (rechts_frei)
9791     {
9792       Tile[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9793       Store[ax+1][ay] = element;
9794       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9795       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9796         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9797                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9798       new_wall = TRUE;
9799     }
9800   }
9801
9802   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Tile[ax][ay-1]))
9803     oben_massiv = TRUE;
9804   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Tile[ax][ay+1]))
9805     unten_massiv = TRUE;
9806   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Tile[ax-1][ay]))
9807     links_massiv = TRUE;
9808   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Tile[ax+1][ay]))
9809     rechts_massiv = TRUE;
9810
9811   if (((oben_massiv && unten_massiv) ||
9812        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9813       ((links_massiv && rechts_massiv) ||
9814        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9815     Tile[ax][ay] = EL_STEELWALL;
9816
9817   if (new_wall)
9818     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9819 }
9820
9821 static void CheckForDragon(int x, int y)
9822 {
9823   int i, j;
9824   boolean dragon_found = FALSE;
9825   static int xy[4][2] =
9826   {
9827     { 0, -1 },
9828     { -1, 0 },
9829     { +1, 0 },
9830     { 0, +1 }
9831   };
9832
9833   for (i = 0; i < NUM_DIRECTIONS; i++)
9834   {
9835     for (j = 0; j < 4; j++)
9836     {
9837       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9838
9839       if (IN_LEV_FIELD(xx, yy) &&
9840           (Tile[xx][yy] == EL_FLAMES || Tile[xx][yy] == EL_DRAGON))
9841       {
9842         if (Tile[xx][yy] == EL_DRAGON)
9843           dragon_found = TRUE;
9844       }
9845       else
9846         break;
9847     }
9848   }
9849
9850   if (!dragon_found)
9851   {
9852     for (i = 0; i < NUM_DIRECTIONS; i++)
9853     {
9854       for (j = 0; j < 3; j++)
9855       {
9856         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9857   
9858         if (IN_LEV_FIELD(xx, yy) && Tile[xx][yy] == EL_FLAMES)
9859         {
9860           Tile[xx][yy] = EL_EMPTY;
9861           TEST_DrawLevelField(xx, yy);
9862         }
9863         else
9864           break;
9865       }
9866     }
9867   }
9868 }
9869
9870 static void InitBuggyBase(int x, int y)
9871 {
9872   int element = Tile[x][y];
9873   int activating_delay = FRAMES_PER_SECOND / 4;
9874
9875   ChangeDelay[x][y] =
9876     (element == EL_SP_BUGGY_BASE ?
9877      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9878      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9879      activating_delay :
9880      element == EL_SP_BUGGY_BASE_ACTIVE ?
9881      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9882 }
9883
9884 static void WarnBuggyBase(int x, int y)
9885 {
9886   int i;
9887   static int xy[4][2] =
9888   {
9889     { 0, -1 },
9890     { -1, 0 },
9891     { +1, 0 },
9892     { 0, +1 }
9893   };
9894
9895   for (i = 0; i < NUM_DIRECTIONS; i++)
9896   {
9897     int xx = x + xy[i][0];
9898     int yy = y + xy[i][1];
9899
9900     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9901     {
9902       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9903
9904       break;
9905     }
9906   }
9907 }
9908
9909 static void InitTrap(int x, int y)
9910 {
9911   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9912 }
9913
9914 static void ActivateTrap(int x, int y)
9915 {
9916   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9917 }
9918
9919 static void ChangeActiveTrap(int x, int y)
9920 {
9921   int graphic = IMG_TRAP_ACTIVE;
9922
9923   // if new animation frame was drawn, correct crumbled sand border
9924   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9925     TEST_DrawLevelFieldCrumbled(x, y);
9926 }
9927
9928 static int getSpecialActionElement(int element, int number, int base_element)
9929 {
9930   return (element != EL_EMPTY ? element :
9931           number != -1 ? base_element + number - 1 :
9932           EL_EMPTY);
9933 }
9934
9935 static int getModifiedActionNumber(int value_old, int operator, int operand,
9936                                    int value_min, int value_max)
9937 {
9938   int value_new = (operator == CA_MODE_SET      ? operand :
9939                    operator == CA_MODE_ADD      ? value_old + operand :
9940                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9941                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9942                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9943                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9944                    value_old);
9945
9946   return (value_new < value_min ? value_min :
9947           value_new > value_max ? value_max :
9948           value_new);
9949 }
9950
9951 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9952 {
9953   struct ElementInfo *ei = &element_info[element];
9954   struct ElementChangeInfo *change = &ei->change_page[page];
9955   int target_element = change->target_element;
9956   int action_type = change->action_type;
9957   int action_mode = change->action_mode;
9958   int action_arg = change->action_arg;
9959   int action_element = change->action_element;
9960   int i;
9961
9962   if (!change->has_action)
9963     return;
9964
9965   // ---------- determine action paramater values -----------------------------
9966
9967   int level_time_value =
9968     (level.time > 0 ? TimeLeft :
9969      TimePlayed);
9970
9971   int action_arg_element_raw =
9972     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9973      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9974      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9975      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9976      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9977      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9978      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9979      EL_EMPTY);
9980   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9981
9982   int action_arg_direction =
9983     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9984      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9985      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9986      change->actual_trigger_side :
9987      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9988      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9989      MV_NONE);
9990
9991   int action_arg_number_min =
9992     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9993      CA_ARG_MIN);
9994
9995   int action_arg_number_max =
9996     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9997      action_type == CA_SET_LEVEL_GEMS ? 999 :
9998      action_type == CA_SET_LEVEL_TIME ? 9999 :
9999      action_type == CA_SET_LEVEL_SCORE ? 99999 :
10000      action_type == CA_SET_CE_VALUE ? 9999 :
10001      action_type == CA_SET_CE_SCORE ? 9999 :
10002      CA_ARG_MAX);
10003
10004   int action_arg_number_reset =
10005     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
10006      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
10007      action_type == CA_SET_LEVEL_TIME ? level.time :
10008      action_type == CA_SET_LEVEL_SCORE ? 0 :
10009      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
10010      action_type == CA_SET_CE_SCORE ? 0 :
10011      0);
10012
10013   int action_arg_number =
10014     (action_arg <= CA_ARG_MAX ? action_arg :
10015      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
10016      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
10017      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
10018      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
10019      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
10020      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
10021      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
10022      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
10023      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
10024      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
10025      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? game.gems_still_needed :
10026      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? game.score :
10027      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
10028      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
10029      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
10030      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
10031      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
10032      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
10033      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
10034      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
10035      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
10036      -1);
10037
10038   int action_arg_number_old =
10039     (action_type == CA_SET_LEVEL_GEMS ? game.gems_still_needed :
10040      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
10041      action_type == CA_SET_LEVEL_SCORE ? game.score :
10042      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
10043      action_type == CA_SET_CE_SCORE ? ei->collect_score :
10044      0);
10045
10046   int action_arg_number_new =
10047     getModifiedActionNumber(action_arg_number_old,
10048                             action_mode, action_arg_number,
10049                             action_arg_number_min, action_arg_number_max);
10050
10051   int trigger_player_bits =
10052     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
10053      change->actual_trigger_player_bits : change->trigger_player);
10054
10055   int action_arg_player_bits =
10056     (action_arg >= CA_ARG_PLAYER_1 &&
10057      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
10058      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
10059      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
10060      PLAYER_BITS_ANY);
10061
10062   // ---------- execute action  -----------------------------------------------
10063
10064   switch (action_type)
10065   {
10066     case CA_NO_ACTION:
10067     {
10068       return;
10069     }
10070
10071     // ---------- level actions  ----------------------------------------------
10072
10073     case CA_RESTART_LEVEL:
10074     {
10075       game.restart_level = TRUE;
10076
10077       break;
10078     }
10079
10080     case CA_SHOW_ENVELOPE:
10081     {
10082       int element = getSpecialActionElement(action_arg_element,
10083                                             action_arg_number, EL_ENVELOPE_1);
10084
10085       if (IS_ENVELOPE(element))
10086         local_player->show_envelope = element;
10087
10088       break;
10089     }
10090
10091     case CA_SET_LEVEL_TIME:
10092     {
10093       if (level.time > 0)       // only modify limited time value
10094       {
10095         TimeLeft = action_arg_number_new;
10096
10097         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10098
10099         DisplayGameControlValues();
10100
10101         if (!TimeLeft && setup.time_limit)
10102           for (i = 0; i < MAX_PLAYERS; i++)
10103             KillPlayer(&stored_player[i]);
10104       }
10105
10106       break;
10107     }
10108
10109     case CA_SET_LEVEL_SCORE:
10110     {
10111       game.score = action_arg_number_new;
10112
10113       game_panel_controls[GAME_PANEL_SCORE].value = game.score;
10114
10115       DisplayGameControlValues();
10116
10117       break;
10118     }
10119
10120     case CA_SET_LEVEL_GEMS:
10121     {
10122       game.gems_still_needed = action_arg_number_new;
10123
10124       game.snapshot.collected_item = TRUE;
10125
10126       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
10127
10128       DisplayGameControlValues();
10129
10130       break;
10131     }
10132
10133     case CA_SET_LEVEL_WIND:
10134     {
10135       game.wind_direction = action_arg_direction;
10136
10137       break;
10138     }
10139
10140     case CA_SET_LEVEL_RANDOM_SEED:
10141     {
10142       // ensure that setting a new random seed while playing is predictable
10143       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
10144
10145       break;
10146     }
10147
10148     // ---------- player actions  ---------------------------------------------
10149
10150     case CA_MOVE_PLAYER:
10151     case CA_MOVE_PLAYER_NEW:
10152     {
10153       // automatically move to the next field in specified direction
10154       for (i = 0; i < MAX_PLAYERS; i++)
10155         if (trigger_player_bits & (1 << i))
10156           if (action_type == CA_MOVE_PLAYER ||
10157               stored_player[i].MovPos == 0)
10158             stored_player[i].programmed_action = action_arg_direction;
10159
10160       break;
10161     }
10162
10163     case CA_EXIT_PLAYER:
10164     {
10165       for (i = 0; i < MAX_PLAYERS; i++)
10166         if (action_arg_player_bits & (1 << i))
10167           ExitPlayer(&stored_player[i]);
10168
10169       if (game.players_still_needed == 0)
10170         LevelSolved();
10171
10172       break;
10173     }
10174
10175     case CA_KILL_PLAYER:
10176     {
10177       for (i = 0; i < MAX_PLAYERS; i++)
10178         if (action_arg_player_bits & (1 << i))
10179           KillPlayer(&stored_player[i]);
10180
10181       break;
10182     }
10183
10184     case CA_SET_PLAYER_KEYS:
10185     {
10186       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
10187       int element = getSpecialActionElement(action_arg_element,
10188                                             action_arg_number, EL_KEY_1);
10189
10190       if (IS_KEY(element))
10191       {
10192         for (i = 0; i < MAX_PLAYERS; i++)
10193         {
10194           if (trigger_player_bits & (1 << i))
10195           {
10196             stored_player[i].key[KEY_NR(element)] = key_state;
10197
10198             DrawGameDoorValues();
10199           }
10200         }
10201       }
10202
10203       break;
10204     }
10205
10206     case CA_SET_PLAYER_SPEED:
10207     {
10208       for (i = 0; i < MAX_PLAYERS; i++)
10209       {
10210         if (trigger_player_bits & (1 << i))
10211         {
10212           int move_stepsize = TILEX / stored_player[i].move_delay_value;
10213
10214           if (action_arg == CA_ARG_SPEED_FASTER &&
10215               stored_player[i].cannot_move)
10216           {
10217             action_arg_number = STEPSIZE_VERY_SLOW;
10218           }
10219           else if (action_arg == CA_ARG_SPEED_SLOWER ||
10220                    action_arg == CA_ARG_SPEED_FASTER)
10221           {
10222             action_arg_number = 2;
10223             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
10224                            CA_MODE_MULTIPLY);
10225           }
10226           else if (action_arg == CA_ARG_NUMBER_RESET)
10227           {
10228             action_arg_number = level.initial_player_stepsize[i];
10229           }
10230
10231           move_stepsize =
10232             getModifiedActionNumber(move_stepsize,
10233                                     action_mode,
10234                                     action_arg_number,
10235                                     action_arg_number_min,
10236                                     action_arg_number_max);
10237
10238           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
10239         }
10240       }
10241
10242       break;
10243     }
10244
10245     case CA_SET_PLAYER_SHIELD:
10246     {
10247       for (i = 0; i < MAX_PLAYERS; i++)
10248       {
10249         if (trigger_player_bits & (1 << i))
10250         {
10251           if (action_arg == CA_ARG_SHIELD_OFF)
10252           {
10253             stored_player[i].shield_normal_time_left = 0;
10254             stored_player[i].shield_deadly_time_left = 0;
10255           }
10256           else if (action_arg == CA_ARG_SHIELD_NORMAL)
10257           {
10258             stored_player[i].shield_normal_time_left = 999999;
10259           }
10260           else if (action_arg == CA_ARG_SHIELD_DEADLY)
10261           {
10262             stored_player[i].shield_normal_time_left = 999999;
10263             stored_player[i].shield_deadly_time_left = 999999;
10264           }
10265         }
10266       }
10267
10268       break;
10269     }
10270
10271     case CA_SET_PLAYER_GRAVITY:
10272     {
10273       for (i = 0; i < MAX_PLAYERS; i++)
10274       {
10275         if (trigger_player_bits & (1 << i))
10276         {
10277           stored_player[i].gravity =
10278             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
10279              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
10280              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
10281              stored_player[i].gravity);
10282         }
10283       }
10284
10285       break;
10286     }
10287
10288     case CA_SET_PLAYER_ARTWORK:
10289     {
10290       for (i = 0; i < MAX_PLAYERS; i++)
10291       {
10292         if (trigger_player_bits & (1 << i))
10293         {
10294           int artwork_element = action_arg_element;
10295
10296           if (action_arg == CA_ARG_ELEMENT_RESET)
10297             artwork_element =
10298               (level.use_artwork_element[i] ? level.artwork_element[i] :
10299                stored_player[i].element_nr);
10300
10301           if (stored_player[i].artwork_element != artwork_element)
10302             stored_player[i].Frame = 0;
10303
10304           stored_player[i].artwork_element = artwork_element;
10305
10306           SetPlayerWaiting(&stored_player[i], FALSE);
10307
10308           // set number of special actions for bored and sleeping animation
10309           stored_player[i].num_special_action_bored =
10310             get_num_special_action(artwork_element,
10311                                    ACTION_BORING_1, ACTION_BORING_LAST);
10312           stored_player[i].num_special_action_sleeping =
10313             get_num_special_action(artwork_element,
10314                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
10315         }
10316       }
10317
10318       break;
10319     }
10320
10321     case CA_SET_PLAYER_INVENTORY:
10322     {
10323       for (i = 0; i < MAX_PLAYERS; i++)
10324       {
10325         struct PlayerInfo *player = &stored_player[i];
10326         int j, k;
10327
10328         if (trigger_player_bits & (1 << i))
10329         {
10330           int inventory_element = action_arg_element;
10331
10332           if (action_arg == CA_ARG_ELEMENT_TARGET ||
10333               action_arg == CA_ARG_ELEMENT_TRIGGER ||
10334               action_arg == CA_ARG_ELEMENT_ACTION)
10335           {
10336             int element = inventory_element;
10337             int collect_count = element_info[element].collect_count_initial;
10338
10339             if (!IS_CUSTOM_ELEMENT(element))
10340               collect_count = 1;
10341
10342             if (collect_count == 0)
10343               player->inventory_infinite_element = element;
10344             else
10345               for (k = 0; k < collect_count; k++)
10346                 if (player->inventory_size < MAX_INVENTORY_SIZE)
10347                   player->inventory_element[player->inventory_size++] =
10348                     element;
10349           }
10350           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
10351                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
10352                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
10353           {
10354             if (player->inventory_infinite_element != EL_UNDEFINED &&
10355                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
10356                                      action_arg_element_raw))
10357               player->inventory_infinite_element = EL_UNDEFINED;
10358
10359             for (k = 0, j = 0; j < player->inventory_size; j++)
10360             {
10361               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
10362                                         action_arg_element_raw))
10363                 player->inventory_element[k++] = player->inventory_element[j];
10364             }
10365
10366             player->inventory_size = k;
10367           }
10368           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
10369           {
10370             if (player->inventory_size > 0)
10371             {
10372               for (j = 0; j < player->inventory_size - 1; j++)
10373                 player->inventory_element[j] = player->inventory_element[j + 1];
10374
10375               player->inventory_size--;
10376             }
10377           }
10378           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
10379           {
10380             if (player->inventory_size > 0)
10381               player->inventory_size--;
10382           }
10383           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
10384           {
10385             player->inventory_infinite_element = EL_UNDEFINED;
10386             player->inventory_size = 0;
10387           }
10388           else if (action_arg == CA_ARG_INVENTORY_RESET)
10389           {
10390             player->inventory_infinite_element = EL_UNDEFINED;
10391             player->inventory_size = 0;
10392
10393             if (level.use_initial_inventory[i])
10394             {
10395               for (j = 0; j < level.initial_inventory_size[i]; j++)
10396               {
10397                 int element = level.initial_inventory_content[i][j];
10398                 int collect_count = element_info[element].collect_count_initial;
10399
10400                 if (!IS_CUSTOM_ELEMENT(element))
10401                   collect_count = 1;
10402
10403                 if (collect_count == 0)
10404                   player->inventory_infinite_element = element;
10405                 else
10406                   for (k = 0; k < collect_count; k++)
10407                     if (player->inventory_size < MAX_INVENTORY_SIZE)
10408                       player->inventory_element[player->inventory_size++] =
10409                         element;
10410               }
10411             }
10412           }
10413         }
10414       }
10415
10416       break;
10417     }
10418
10419     // ---------- CE actions  -------------------------------------------------
10420
10421     case CA_SET_CE_VALUE:
10422     {
10423       int last_ce_value = CustomValue[x][y];
10424
10425       CustomValue[x][y] = action_arg_number_new;
10426
10427       if (CustomValue[x][y] != last_ce_value)
10428       {
10429         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
10430         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
10431
10432         if (CustomValue[x][y] == 0)
10433         {
10434           // reset change counter (else CE_VALUE_GETS_ZERO would not work)
10435           ChangeCount[x][y] = 0;        // allow at least one more change
10436
10437           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
10438           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
10439         }
10440       }
10441
10442       break;
10443     }
10444
10445     case CA_SET_CE_SCORE:
10446     {
10447       int last_ce_score = ei->collect_score;
10448
10449       ei->collect_score = action_arg_number_new;
10450
10451       if (ei->collect_score != last_ce_score)
10452       {
10453         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
10454         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
10455
10456         if (ei->collect_score == 0)
10457         {
10458           int xx, yy;
10459
10460           // reset change counter (else CE_SCORE_GETS_ZERO would not work)
10461           ChangeCount[x][y] = 0;        // allow at least one more change
10462
10463           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
10464           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
10465
10466           /*
10467             This is a very special case that seems to be a mixture between
10468             CheckElementChange() and CheckTriggeredElementChange(): while
10469             the first one only affects single elements that are triggered
10470             directly, the second one affects multiple elements in the playfield
10471             that are triggered indirectly by another element. This is a third
10472             case: Changing the CE score always affects multiple identical CEs,
10473             so every affected CE must be checked, not only the single CE for
10474             which the CE score was changed in the first place (as every instance
10475             of that CE shares the same CE score, and therefore also can change)!
10476           */
10477           SCAN_PLAYFIELD(xx, yy)
10478           {
10479             if (Tile[xx][yy] == element)
10480               CheckElementChange(xx, yy, element, EL_UNDEFINED,
10481                                  CE_SCORE_GETS_ZERO);
10482           }
10483         }
10484       }
10485
10486       break;
10487     }
10488
10489     case CA_SET_CE_ARTWORK:
10490     {
10491       int artwork_element = action_arg_element;
10492       boolean reset_frame = FALSE;
10493       int xx, yy;
10494
10495       if (action_arg == CA_ARG_ELEMENT_RESET)
10496         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
10497                            element);
10498
10499       if (ei->gfx_element != artwork_element)
10500         reset_frame = TRUE;
10501
10502       ei->gfx_element = artwork_element;
10503
10504       SCAN_PLAYFIELD(xx, yy)
10505       {
10506         if (Tile[xx][yy] == element)
10507         {
10508           if (reset_frame)
10509           {
10510             ResetGfxAnimation(xx, yy);
10511             ResetRandomAnimationValue(xx, yy);
10512           }
10513
10514           TEST_DrawLevelField(xx, yy);
10515         }
10516       }
10517
10518       break;
10519     }
10520
10521     // ---------- engine actions  ---------------------------------------------
10522
10523     case CA_SET_ENGINE_SCAN_MODE:
10524     {
10525       InitPlayfieldScanMode(action_arg);
10526
10527       break;
10528     }
10529
10530     default:
10531       break;
10532   }
10533 }
10534
10535 static void CreateFieldExt(int x, int y, int element, boolean is_change)
10536 {
10537   int old_element = Tile[x][y];
10538   int new_element = GetElementFromGroupElement(element);
10539   int previous_move_direction = MovDir[x][y];
10540   int last_ce_value = CustomValue[x][y];
10541   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
10542   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
10543   boolean add_player_onto_element = (new_element_is_player &&
10544                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
10545                                      IS_WALKABLE(old_element));
10546
10547   if (!add_player_onto_element)
10548   {
10549     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
10550       RemoveMovingField(x, y);
10551     else
10552       RemoveField(x, y);
10553
10554     Tile[x][y] = new_element;
10555
10556     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
10557       MovDir[x][y] = previous_move_direction;
10558
10559     if (element_info[new_element].use_last_ce_value)
10560       CustomValue[x][y] = last_ce_value;
10561
10562     InitField_WithBug1(x, y, FALSE);
10563
10564     new_element = Tile[x][y];   // element may have changed
10565
10566     ResetGfxAnimation(x, y);
10567     ResetRandomAnimationValue(x, y);
10568
10569     TEST_DrawLevelField(x, y);
10570
10571     if (GFX_CRUMBLED(new_element))
10572       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10573   }
10574
10575   // check if element under the player changes from accessible to unaccessible
10576   // (needed for special case of dropping element which then changes)
10577   // (must be checked after creating new element for walkable group elements)
10578   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10579       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10580   {
10581     Bang(x, y);
10582
10583     return;
10584   }
10585
10586   // "ChangeCount" not set yet to allow "entered by player" change one time
10587   if (new_element_is_player)
10588     RelocatePlayer(x, y, new_element);
10589
10590   if (is_change)
10591     ChangeCount[x][y]++;        // count number of changes in the same frame
10592
10593   TestIfBadThingTouchesPlayer(x, y);
10594   TestIfPlayerTouchesCustomElement(x, y);
10595   TestIfElementTouchesCustomElement(x, y);
10596 }
10597
10598 static void CreateField(int x, int y, int element)
10599 {
10600   CreateFieldExt(x, y, element, FALSE);
10601 }
10602
10603 static void CreateElementFromChange(int x, int y, int element)
10604 {
10605   element = GET_VALID_RUNTIME_ELEMENT(element);
10606
10607   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10608   {
10609     int old_element = Tile[x][y];
10610
10611     // prevent changed element from moving in same engine frame
10612     // unless both old and new element can either fall or move
10613     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10614         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10615       Stop[x][y] = TRUE;
10616   }
10617
10618   CreateFieldExt(x, y, element, TRUE);
10619 }
10620
10621 static boolean ChangeElement(int x, int y, int element, int page)
10622 {
10623   struct ElementInfo *ei = &element_info[element];
10624   struct ElementChangeInfo *change = &ei->change_page[page];
10625   int ce_value = CustomValue[x][y];
10626   int ce_score = ei->collect_score;
10627   int target_element;
10628   int old_element = Tile[x][y];
10629
10630   // always use default change event to prevent running into a loop
10631   if (ChangeEvent[x][y] == -1)
10632     ChangeEvent[x][y] = CE_DELAY;
10633
10634   if (ChangeEvent[x][y] == CE_DELAY)
10635   {
10636     // reset actual trigger element, trigger player and action element
10637     change->actual_trigger_element = EL_EMPTY;
10638     change->actual_trigger_player = EL_EMPTY;
10639     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10640     change->actual_trigger_side = CH_SIDE_NONE;
10641     change->actual_trigger_ce_value = 0;
10642     change->actual_trigger_ce_score = 0;
10643   }
10644
10645   // do not change elements more than a specified maximum number of changes
10646   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10647     return FALSE;
10648
10649   ChangeCount[x][y]++;          // count number of changes in the same frame
10650
10651   if (change->explode)
10652   {
10653     Bang(x, y);
10654
10655     return TRUE;
10656   }
10657
10658   if (change->use_target_content)
10659   {
10660     boolean complete_replace = TRUE;
10661     boolean can_replace[3][3];
10662     int xx, yy;
10663
10664     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10665     {
10666       boolean is_empty;
10667       boolean is_walkable;
10668       boolean is_diggable;
10669       boolean is_collectible;
10670       boolean is_removable;
10671       boolean is_destructible;
10672       int ex = x + xx - 1;
10673       int ey = y + yy - 1;
10674       int content_element = change->target_content.e[xx][yy];
10675       int e;
10676
10677       can_replace[xx][yy] = TRUE;
10678
10679       if (ex == x && ey == y)   // do not check changing element itself
10680         continue;
10681
10682       if (content_element == EL_EMPTY_SPACE)
10683       {
10684         can_replace[xx][yy] = FALSE;    // do not replace border with space
10685
10686         continue;
10687       }
10688
10689       if (!IN_LEV_FIELD(ex, ey))
10690       {
10691         can_replace[xx][yy] = FALSE;
10692         complete_replace = FALSE;
10693
10694         continue;
10695       }
10696
10697       e = Tile[ex][ey];
10698
10699       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10700         e = MovingOrBlocked2Element(ex, ey);
10701
10702       is_empty = (IS_FREE(ex, ey) ||
10703                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10704
10705       is_walkable     = (is_empty || IS_WALKABLE(e));
10706       is_diggable     = (is_empty || IS_DIGGABLE(e));
10707       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10708       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10709       is_removable    = (is_diggable || is_collectible);
10710
10711       can_replace[xx][yy] =
10712         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10713           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10714           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10715           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10716           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10717           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10718          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10719
10720       if (!can_replace[xx][yy])
10721         complete_replace = FALSE;
10722     }
10723
10724     if (!change->only_if_complete || complete_replace)
10725     {
10726       boolean something_has_changed = FALSE;
10727
10728       if (change->only_if_complete && change->use_random_replace &&
10729           RND(100) < change->random_percentage)
10730         return FALSE;
10731
10732       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10733       {
10734         int ex = x + xx - 1;
10735         int ey = y + yy - 1;
10736         int content_element;
10737
10738         if (can_replace[xx][yy] && (!change->use_random_replace ||
10739                                     RND(100) < change->random_percentage))
10740         {
10741           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10742             RemoveMovingField(ex, ey);
10743
10744           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10745
10746           content_element = change->target_content.e[xx][yy];
10747           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10748                                               ce_value, ce_score);
10749
10750           CreateElementFromChange(ex, ey, target_element);
10751
10752           something_has_changed = TRUE;
10753
10754           // for symmetry reasons, freeze newly created border elements
10755           if (ex != x || ey != y)
10756             Stop[ex][ey] = TRUE;        // no more moving in this frame
10757         }
10758       }
10759
10760       if (something_has_changed)
10761       {
10762         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10763         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10764       }
10765     }
10766   }
10767   else
10768   {
10769     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10770                                         ce_value, ce_score);
10771
10772     if (element == EL_DIAGONAL_GROWING ||
10773         element == EL_DIAGONAL_SHRINKING)
10774     {
10775       target_element = Store[x][y];
10776
10777       Store[x][y] = EL_EMPTY;
10778     }
10779
10780     CreateElementFromChange(x, y, target_element);
10781
10782     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10783     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10784   }
10785
10786   // this uses direct change before indirect change
10787   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10788
10789   return TRUE;
10790 }
10791
10792 static void HandleElementChange(int x, int y, int page)
10793 {
10794   int element = MovingOrBlocked2Element(x, y);
10795   struct ElementInfo *ei = &element_info[element];
10796   struct ElementChangeInfo *change = &ei->change_page[page];
10797   boolean handle_action_before_change = FALSE;
10798
10799 #ifdef DEBUG
10800   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10801       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10802   {
10803     Debug("game:playing:HandleElementChange", "%d,%d: element = %d ('%s')",
10804           x, y, element, element_info[element].token_name);
10805     Debug("game:playing:HandleElementChange", "This should never happen!");
10806   }
10807 #endif
10808
10809   // this can happen with classic bombs on walkable, changing elements
10810   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10811   {
10812     return;
10813   }
10814
10815   if (ChangeDelay[x][y] == 0)           // initialize element change
10816   {
10817     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10818
10819     if (change->can_change)
10820     {
10821       // !!! not clear why graphic animation should be reset at all here !!!
10822       // !!! UPDATE: but is needed for correct Snake Bite tail animation !!!
10823       // !!! SOLUTION: do not reset if graphics engine set to 4 or above !!!
10824
10825       /*
10826         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10827
10828         When using an animation frame delay of 1 (this only happens with
10829         "sp_zonk.moving.left/right" in the classic graphics), the default
10830         (non-moving) animation shows wrong animation frames (while the
10831         moving animation, like "sp_zonk.moving.left/right", is correct,
10832         so this graphical bug never shows up with the classic graphics).
10833         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10834         be drawn instead of the correct frames 0,1,2,3. This is caused by
10835         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10836         an element change: First when the change delay ("ChangeDelay[][]")
10837         counter has reached zero after decrementing, then a second time in
10838         the next frame (after "GfxFrame[][]" was already incremented) when
10839         "ChangeDelay[][]" is reset to the initial delay value again.
10840
10841         This causes frame 0 to be drawn twice, while the last frame won't
10842         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10843
10844         As some animations may already be cleverly designed around this bug
10845         (at least the "Snake Bite" snake tail animation does this), it cannot
10846         simply be fixed here without breaking such existing animations.
10847         Unfortunately, it cannot easily be detected if a graphics set was
10848         designed "before" or "after" the bug was fixed. As a workaround,
10849         a new graphics set option "game.graphics_engine_version" was added
10850         to be able to specify the game's major release version for which the
10851         graphics set was designed, which can then be used to decide if the
10852         bugfix should be used (version 4 and above) or not (version 3 or
10853         below, or if no version was specified at all, as with old sets).
10854
10855         (The wrong/fixed animation frames can be tested with the test level set
10856         "test_gfxframe" and level "000", which contains a specially prepared
10857         custom element at level position (x/y) == (11/9) which uses the zonk
10858         animation mentioned above. Using "game.graphics_engine_version: 4"
10859         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10860         This can also be seen from the debug output for this test element.)
10861       */
10862
10863       // when a custom element is about to change (for example by change delay),
10864       // do not reset graphic animation when the custom element is moving
10865       if (game.graphics_engine_version < 4 &&
10866           !IS_MOVING(x, y))
10867       {
10868         ResetGfxAnimation(x, y);
10869         ResetRandomAnimationValue(x, y);
10870       }
10871
10872       if (change->pre_change_function)
10873         change->pre_change_function(x, y);
10874     }
10875   }
10876
10877   ChangeDelay[x][y]--;
10878
10879   if (ChangeDelay[x][y] != 0)           // continue element change
10880   {
10881     if (change->can_change)
10882     {
10883       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10884
10885       if (IS_ANIMATED(graphic))
10886         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10887
10888       if (change->change_function)
10889         change->change_function(x, y);
10890     }
10891   }
10892   else                                  // finish element change
10893   {
10894     if (ChangePage[x][y] != -1)         // remember page from delayed change
10895     {
10896       page = ChangePage[x][y];
10897       ChangePage[x][y] = -1;
10898
10899       change = &ei->change_page[page];
10900     }
10901
10902     if (IS_MOVING(x, y))                // never change a running system ;-)
10903     {
10904       ChangeDelay[x][y] = 1;            // try change after next move step
10905       ChangePage[x][y] = page;          // remember page to use for change
10906
10907       return;
10908     }
10909
10910     // special case: set new level random seed before changing element
10911     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10912       handle_action_before_change = TRUE;
10913
10914     if (change->has_action && handle_action_before_change)
10915       ExecuteCustomElementAction(x, y, element, page);
10916
10917     if (change->can_change)
10918     {
10919       if (ChangeElement(x, y, element, page))
10920       {
10921         if (change->post_change_function)
10922           change->post_change_function(x, y);
10923       }
10924     }
10925
10926     if (change->has_action && !handle_action_before_change)
10927       ExecuteCustomElementAction(x, y, element, page);
10928   }
10929 }
10930
10931 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10932                                               int trigger_element,
10933                                               int trigger_event,
10934                                               int trigger_player,
10935                                               int trigger_side,
10936                                               int trigger_page)
10937 {
10938   boolean change_done_any = FALSE;
10939   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10940   int i;
10941
10942   if (!(trigger_events[trigger_element][trigger_event]))
10943     return FALSE;
10944
10945   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10946
10947   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10948   {
10949     int element = EL_CUSTOM_START + i;
10950     boolean change_done = FALSE;
10951     int p;
10952
10953     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10954         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10955       continue;
10956
10957     for (p = 0; p < element_info[element].num_change_pages; p++)
10958     {
10959       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10960
10961       if (change->can_change_or_has_action &&
10962           change->has_event[trigger_event] &&
10963           change->trigger_side & trigger_side &&
10964           change->trigger_player & trigger_player &&
10965           change->trigger_page & trigger_page_bits &&
10966           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10967       {
10968         change->actual_trigger_element = trigger_element;
10969         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10970         change->actual_trigger_player_bits = trigger_player;
10971         change->actual_trigger_side = trigger_side;
10972         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10973         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10974
10975         if ((change->can_change && !change_done) || change->has_action)
10976         {
10977           int x, y;
10978
10979           SCAN_PLAYFIELD(x, y)
10980           {
10981             if (Tile[x][y] == element)
10982             {
10983               if (change->can_change && !change_done)
10984               {
10985                 // if element already changed in this frame, not only prevent
10986                 // another element change (checked in ChangeElement()), but
10987                 // also prevent additional element actions for this element
10988
10989                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10990                     !level.use_action_after_change_bug)
10991                   continue;
10992
10993                 ChangeDelay[x][y] = 1;
10994                 ChangeEvent[x][y] = trigger_event;
10995
10996                 HandleElementChange(x, y, p);
10997               }
10998               else if (change->has_action)
10999               {
11000                 // if element already changed in this frame, not only prevent
11001                 // another element change (checked in ChangeElement()), but
11002                 // also prevent additional element actions for this element
11003
11004                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
11005                     !level.use_action_after_change_bug)
11006                   continue;
11007
11008                 ExecuteCustomElementAction(x, y, element, p);
11009                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11010               }
11011             }
11012           }
11013
11014           if (change->can_change)
11015           {
11016             change_done = TRUE;
11017             change_done_any = TRUE;
11018           }
11019         }
11020       }
11021     }
11022   }
11023
11024   RECURSION_LOOP_DETECTION_END();
11025
11026   return change_done_any;
11027 }
11028
11029 static boolean CheckElementChangeExt(int x, int y,
11030                                      int element,
11031                                      int trigger_element,
11032                                      int trigger_event,
11033                                      int trigger_player,
11034                                      int trigger_side)
11035 {
11036   boolean change_done = FALSE;
11037   int p;
11038
11039   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
11040       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
11041     return FALSE;
11042
11043   if (Tile[x][y] == EL_BLOCKED)
11044   {
11045     Blocked2Moving(x, y, &x, &y);
11046     element = Tile[x][y];
11047   }
11048
11049   // check if element has already changed or is about to change after moving
11050   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
11051        Tile[x][y] != element) ||
11052
11053       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
11054        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
11055         ChangePage[x][y] != -1)))
11056     return FALSE;
11057
11058   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
11059
11060   for (p = 0; p < element_info[element].num_change_pages; p++)
11061   {
11062     struct ElementChangeInfo *change = &element_info[element].change_page[p];
11063
11064     /* check trigger element for all events where the element that is checked
11065        for changing interacts with a directly adjacent element -- this is
11066        different to element changes that affect other elements to change on the
11067        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
11068     boolean check_trigger_element =
11069       (trigger_event == CE_TOUCHING_X ||
11070        trigger_event == CE_HITTING_X ||
11071        trigger_event == CE_HIT_BY_X ||
11072        trigger_event == CE_DIGGING_X); // this one was forgotten until 3.2.3
11073
11074     if (change->can_change_or_has_action &&
11075         change->has_event[trigger_event] &&
11076         change->trigger_side & trigger_side &&
11077         change->trigger_player & trigger_player &&
11078         (!check_trigger_element ||
11079          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
11080     {
11081       change->actual_trigger_element = trigger_element;
11082       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
11083       change->actual_trigger_player_bits = trigger_player;
11084       change->actual_trigger_side = trigger_side;
11085       change->actual_trigger_ce_value = CustomValue[x][y];
11086       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11087
11088       // special case: trigger element not at (x,y) position for some events
11089       if (check_trigger_element)
11090       {
11091         static struct
11092         {
11093           int dx, dy;
11094         } move_xy[] =
11095           {
11096             {  0,  0 },
11097             { -1,  0 },
11098             { +1,  0 },
11099             {  0,  0 },
11100             {  0, -1 },
11101             {  0,  0 }, { 0, 0 }, { 0, 0 },
11102             {  0, +1 }
11103           };
11104
11105         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
11106         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
11107
11108         change->actual_trigger_ce_value = CustomValue[xx][yy];
11109         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
11110       }
11111
11112       if (change->can_change && !change_done)
11113       {
11114         ChangeDelay[x][y] = 1;
11115         ChangeEvent[x][y] = trigger_event;
11116
11117         HandleElementChange(x, y, p);
11118
11119         change_done = TRUE;
11120       }
11121       else if (change->has_action)
11122       {
11123         ExecuteCustomElementAction(x, y, element, p);
11124         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
11125       }
11126     }
11127   }
11128
11129   RECURSION_LOOP_DETECTION_END();
11130
11131   return change_done;
11132 }
11133
11134 static void PlayPlayerSound(struct PlayerInfo *player)
11135 {
11136   int jx = player->jx, jy = player->jy;
11137   int sound_element = player->artwork_element;
11138   int last_action = player->last_action_waiting;
11139   int action = player->action_waiting;
11140
11141   if (player->is_waiting)
11142   {
11143     if (action != last_action)
11144       PlayLevelSoundElementAction(jx, jy, sound_element, action);
11145     else
11146       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
11147   }
11148   else
11149   {
11150     if (action != last_action)
11151       StopSound(element_info[sound_element].sound[last_action]);
11152
11153     if (last_action == ACTION_SLEEPING)
11154       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
11155   }
11156 }
11157
11158 static void PlayAllPlayersSound(void)
11159 {
11160   int i;
11161
11162   for (i = 0; i < MAX_PLAYERS; i++)
11163     if (stored_player[i].active)
11164       PlayPlayerSound(&stored_player[i]);
11165 }
11166
11167 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
11168 {
11169   boolean last_waiting = player->is_waiting;
11170   int move_dir = player->MovDir;
11171
11172   player->dir_waiting = move_dir;
11173   player->last_action_waiting = player->action_waiting;
11174
11175   if (is_waiting)
11176   {
11177     if (!last_waiting)          // not waiting -> waiting
11178     {
11179       player->is_waiting = TRUE;
11180
11181       player->frame_counter_bored =
11182         FrameCounter +
11183         game.player_boring_delay_fixed +
11184         GetSimpleRandom(game.player_boring_delay_random);
11185       player->frame_counter_sleeping =
11186         FrameCounter +
11187         game.player_sleeping_delay_fixed +
11188         GetSimpleRandom(game.player_sleeping_delay_random);
11189
11190       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
11191     }
11192
11193     if (game.player_sleeping_delay_fixed +
11194         game.player_sleeping_delay_random > 0 &&
11195         player->anim_delay_counter == 0 &&
11196         player->post_delay_counter == 0 &&
11197         FrameCounter >= player->frame_counter_sleeping)
11198       player->is_sleeping = TRUE;
11199     else if (game.player_boring_delay_fixed +
11200              game.player_boring_delay_random > 0 &&
11201              FrameCounter >= player->frame_counter_bored)
11202       player->is_bored = TRUE;
11203
11204     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
11205                               player->is_bored ? ACTION_BORING :
11206                               ACTION_WAITING);
11207
11208     if (player->is_sleeping && player->use_murphy)
11209     {
11210       // special case for sleeping Murphy when leaning against non-free tile
11211
11212       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
11213           (Tile[player->jx - 1][player->jy] != EL_EMPTY &&
11214            !IS_MOVING(player->jx - 1, player->jy)))
11215         move_dir = MV_LEFT;
11216       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
11217                (Tile[player->jx + 1][player->jy] != EL_EMPTY &&
11218                 !IS_MOVING(player->jx + 1, player->jy)))
11219         move_dir = MV_RIGHT;
11220       else
11221         player->is_sleeping = FALSE;
11222
11223       player->dir_waiting = move_dir;
11224     }
11225
11226     if (player->is_sleeping)
11227     {
11228       if (player->num_special_action_sleeping > 0)
11229       {
11230         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11231         {
11232           int last_special_action = player->special_action_sleeping;
11233           int num_special_action = player->num_special_action_sleeping;
11234           int special_action =
11235             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
11236              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
11237              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
11238              last_special_action + 1 : ACTION_SLEEPING);
11239           int special_graphic =
11240             el_act_dir2img(player->artwork_element, special_action, move_dir);
11241
11242           player->anim_delay_counter =
11243             graphic_info[special_graphic].anim_delay_fixed +
11244             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11245           player->post_delay_counter =
11246             graphic_info[special_graphic].post_delay_fixed +
11247             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11248
11249           player->special_action_sleeping = special_action;
11250         }
11251
11252         if (player->anim_delay_counter > 0)
11253         {
11254           player->action_waiting = player->special_action_sleeping;
11255           player->anim_delay_counter--;
11256         }
11257         else if (player->post_delay_counter > 0)
11258         {
11259           player->post_delay_counter--;
11260         }
11261       }
11262     }
11263     else if (player->is_bored)
11264     {
11265       if (player->num_special_action_bored > 0)
11266       {
11267         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
11268         {
11269           int special_action =
11270             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
11271           int special_graphic =
11272             el_act_dir2img(player->artwork_element, special_action, move_dir);
11273
11274           player->anim_delay_counter =
11275             graphic_info[special_graphic].anim_delay_fixed +
11276             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
11277           player->post_delay_counter =
11278             graphic_info[special_graphic].post_delay_fixed +
11279             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
11280
11281           player->special_action_bored = special_action;
11282         }
11283
11284         if (player->anim_delay_counter > 0)
11285         {
11286           player->action_waiting = player->special_action_bored;
11287           player->anim_delay_counter--;
11288         }
11289         else if (player->post_delay_counter > 0)
11290         {
11291           player->post_delay_counter--;
11292         }
11293       }
11294     }
11295   }
11296   else if (last_waiting)        // waiting -> not waiting
11297   {
11298     player->is_waiting = FALSE;
11299     player->is_bored = FALSE;
11300     player->is_sleeping = FALSE;
11301
11302     player->frame_counter_bored = -1;
11303     player->frame_counter_sleeping = -1;
11304
11305     player->anim_delay_counter = 0;
11306     player->post_delay_counter = 0;
11307
11308     player->dir_waiting = player->MovDir;
11309     player->action_waiting = ACTION_DEFAULT;
11310
11311     player->special_action_bored = ACTION_DEFAULT;
11312     player->special_action_sleeping = ACTION_DEFAULT;
11313   }
11314 }
11315
11316 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
11317 {
11318   if ((!player->is_moving  && player->was_moving) ||
11319       (player->MovPos == 0 && player->was_moving) ||
11320       (player->is_snapping && !player->was_snapping) ||
11321       (player->is_dropping && !player->was_dropping))
11322   {
11323     if (!CheckSaveEngineSnapshotToList())
11324       return;
11325
11326     player->was_moving = FALSE;
11327     player->was_snapping = TRUE;
11328     player->was_dropping = TRUE;
11329   }
11330   else
11331   {
11332     if (player->is_moving)
11333       player->was_moving = TRUE;
11334
11335     if (!player->is_snapping)
11336       player->was_snapping = FALSE;
11337
11338     if (!player->is_dropping)
11339       player->was_dropping = FALSE;
11340   }
11341
11342   static struct MouseActionInfo mouse_action_last = { 0 };
11343   struct MouseActionInfo mouse_action = player->effective_mouse_action;
11344   boolean new_released = (!mouse_action.button && mouse_action_last.button);
11345
11346   if (new_released)
11347     CheckSaveEngineSnapshotToList();
11348
11349   mouse_action_last = mouse_action;
11350 }
11351
11352 static void CheckSingleStepMode(struct PlayerInfo *player)
11353 {
11354   if (tape.single_step && tape.recording && !tape.pausing)
11355   {
11356     // as it is called "single step mode", just return to pause mode when the
11357     // player stopped moving after one tile (or never starts moving at all)
11358     // (reverse logic needed here in case single step mode used in team mode)
11359     if (player->is_moving ||
11360         player->is_pushing ||
11361         player->is_dropping_pressed ||
11362         player->effective_mouse_action.button)
11363       game.enter_single_step_mode = FALSE;
11364   }
11365
11366   CheckSaveEngineSnapshot(player);
11367 }
11368
11369 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
11370 {
11371   int left      = player_action & JOY_LEFT;
11372   int right     = player_action & JOY_RIGHT;
11373   int up        = player_action & JOY_UP;
11374   int down      = player_action & JOY_DOWN;
11375   int button1   = player_action & JOY_BUTTON_1;
11376   int button2   = player_action & JOY_BUTTON_2;
11377   int dx        = (left ? -1 : right ? 1 : 0);
11378   int dy        = (up   ? -1 : down  ? 1 : 0);
11379
11380   if (!player->active || tape.pausing)
11381     return 0;
11382
11383   if (player_action)
11384   {
11385     if (button1)
11386       SnapField(player, dx, dy);
11387     else
11388     {
11389       if (button2)
11390         DropElement(player);
11391
11392       MovePlayer(player, dx, dy);
11393     }
11394
11395     CheckSingleStepMode(player);
11396
11397     SetPlayerWaiting(player, FALSE);
11398
11399     return player_action;
11400   }
11401   else
11402   {
11403     // no actions for this player (no input at player's configured device)
11404
11405     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
11406     SnapField(player, 0, 0);
11407     CheckGravityMovementWhenNotMoving(player);
11408
11409     if (player->MovPos == 0)
11410       SetPlayerWaiting(player, TRUE);
11411
11412     if (player->MovPos == 0)    // needed for tape.playing
11413       player->is_moving = FALSE;
11414
11415     player->is_dropping = FALSE;
11416     player->is_dropping_pressed = FALSE;
11417     player->drop_pressed_delay = 0;
11418
11419     CheckSingleStepMode(player);
11420
11421     return 0;
11422   }
11423 }
11424
11425 static void SetMouseActionFromTapeAction(struct MouseActionInfo *mouse_action,
11426                                          byte *tape_action)
11427 {
11428   if (!tape.use_mouse_actions)
11429     return;
11430
11431   mouse_action->lx     = tape_action[TAPE_ACTION_LX];
11432   mouse_action->ly     = tape_action[TAPE_ACTION_LY];
11433   mouse_action->button = tape_action[TAPE_ACTION_BUTTON];
11434 }
11435
11436 static void SetTapeActionFromMouseAction(byte *tape_action,
11437                                          struct MouseActionInfo *mouse_action)
11438 {
11439   if (!tape.use_mouse_actions)
11440     return;
11441
11442   tape_action[TAPE_ACTION_LX]     = mouse_action->lx;
11443   tape_action[TAPE_ACTION_LY]     = mouse_action->ly;
11444   tape_action[TAPE_ACTION_BUTTON] = mouse_action->button;
11445 }
11446
11447 static void CheckLevelSolved(void)
11448 {
11449   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11450   {
11451     if (game_em.level_solved &&
11452         !game_em.game_over)                             // game won
11453     {
11454       LevelSolved();
11455
11456       game_em.game_over = TRUE;
11457
11458       game.all_players_gone = TRUE;
11459     }
11460
11461     if (game_em.game_over)                              // game lost
11462       game.all_players_gone = TRUE;
11463   }
11464   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11465   {
11466     if (game_sp.level_solved &&
11467         !game_sp.game_over)                             // game won
11468     {
11469       LevelSolved();
11470
11471       game_sp.game_over = TRUE;
11472
11473       game.all_players_gone = TRUE;
11474     }
11475
11476     if (game_sp.game_over)                              // game lost
11477       game.all_players_gone = TRUE;
11478   }
11479   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11480   {
11481     if (game_mm.level_solved &&
11482         !game_mm.game_over)                             // game won
11483     {
11484       LevelSolved();
11485
11486       game_mm.game_over = TRUE;
11487
11488       game.all_players_gone = TRUE;
11489     }
11490
11491     if (game_mm.game_over)                              // game lost
11492       game.all_players_gone = TRUE;
11493   }
11494 }
11495
11496 static void CheckLevelTime(void)
11497 {
11498   int i;
11499
11500   if (TimeFrames >= FRAMES_PER_SECOND)
11501   {
11502     TimeFrames = 0;
11503     TapeTime++;
11504
11505     for (i = 0; i < MAX_PLAYERS; i++)
11506     {
11507       struct PlayerInfo *player = &stored_player[i];
11508
11509       if (SHIELD_ON(player))
11510       {
11511         player->shield_normal_time_left--;
11512
11513         if (player->shield_deadly_time_left > 0)
11514           player->shield_deadly_time_left--;
11515       }
11516     }
11517
11518     if (!game.LevelSolved && !level.use_step_counter)
11519     {
11520       TimePlayed++;
11521
11522       if (TimeLeft > 0)
11523       {
11524         TimeLeft--;
11525
11526         if (TimeLeft <= 10 && setup.time_limit)
11527           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
11528
11529         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
11530            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
11531
11532         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
11533
11534         if (!TimeLeft && setup.time_limit)
11535         {
11536           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11537             game_em.lev->killed_out_of_time = TRUE;
11538           else
11539             for (i = 0; i < MAX_PLAYERS; i++)
11540               KillPlayer(&stored_player[i]);
11541         }
11542       }
11543       else if (game.no_time_limit && !game.all_players_gone)
11544       {
11545         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
11546       }
11547
11548       game_em.lev->time = (game.no_time_limit ? TimePlayed : TimeLeft);
11549     }
11550
11551     if (tape.recording || tape.playing)
11552       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
11553   }
11554
11555   if (tape.recording || tape.playing)
11556     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
11557
11558   UpdateAndDisplayGameControlValues();
11559 }
11560
11561 void AdvanceFrameAndPlayerCounters(int player_nr)
11562 {
11563   int i;
11564
11565   // advance frame counters (global frame counter and time frame counter)
11566   FrameCounter++;
11567   TimeFrames++;
11568
11569   // advance player counters (counters for move delay, move animation etc.)
11570   for (i = 0; i < MAX_PLAYERS; i++)
11571   {
11572     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
11573     int move_delay_value = stored_player[i].move_delay_value;
11574     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
11575
11576     if (!advance_player_counters)       // not all players may be affected
11577       continue;
11578
11579     if (move_frames == 0)       // less than one move per game frame
11580     {
11581       int stepsize = TILEX / move_delay_value;
11582       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
11583       int count = (stored_player[i].is_moving ?
11584                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
11585
11586       if (count % delay == 0)
11587         move_frames = 1;
11588     }
11589
11590     stored_player[i].Frame += move_frames;
11591
11592     if (stored_player[i].MovPos != 0)
11593       stored_player[i].StepFrame += move_frames;
11594
11595     if (stored_player[i].move_delay > 0)
11596       stored_player[i].move_delay--;
11597
11598     // due to bugs in previous versions, counter must count up, not down
11599     if (stored_player[i].push_delay != -1)
11600       stored_player[i].push_delay++;
11601
11602     if (stored_player[i].drop_delay > 0)
11603       stored_player[i].drop_delay--;
11604
11605     if (stored_player[i].is_dropping_pressed)
11606       stored_player[i].drop_pressed_delay++;
11607   }
11608 }
11609
11610 void StartGameActions(boolean init_network_game, boolean record_tape,
11611                       int random_seed)
11612 {
11613   unsigned int new_random_seed = InitRND(random_seed);
11614
11615   if (record_tape)
11616     TapeStartRecording(new_random_seed);
11617
11618   if (init_network_game)
11619   {
11620     SendToServer_LevelFile();
11621     SendToServer_StartPlaying();
11622
11623     return;
11624   }
11625
11626   InitGame();
11627 }
11628
11629 static void GameActionsExt(void)
11630 {
11631 #if 0
11632   static unsigned int game_frame_delay = 0;
11633 #endif
11634   unsigned int game_frame_delay_value;
11635   byte *recorded_player_action;
11636   byte summarized_player_action = 0;
11637   byte tape_action[MAX_TAPE_ACTIONS] = { 0 };
11638   int i;
11639
11640   // detect endless loops, caused by custom element programming
11641   if (recursion_loop_detected && recursion_loop_depth == 0)
11642   {
11643     char *message = getStringCat3("Internal Error! Element ",
11644                                   EL_NAME(recursion_loop_element),
11645                                   " caused endless loop! Quit the game?");
11646
11647     Warn("element '%s' caused endless loop in game engine",
11648          EL_NAME(recursion_loop_element));
11649
11650     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11651
11652     recursion_loop_detected = FALSE;    // if game should be continued
11653
11654     free(message);
11655
11656     return;
11657   }
11658
11659   if (game.restart_level)
11660     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
11661
11662   CheckLevelSolved();
11663
11664   if (game.LevelSolved && !game.LevelSolved_GameEnd)
11665     GameWon();
11666
11667   if (game.all_players_gone && !TAPE_IS_STOPPED(tape))
11668     TapeStop();
11669
11670   if (game_status != GAME_MODE_PLAYING)         // status might have changed
11671     return;
11672
11673   game_frame_delay_value =
11674     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11675
11676   if (tape.playing && tape.warp_forward && !tape.pausing)
11677     game_frame_delay_value = 0;
11678
11679   SetVideoFrameDelay(game_frame_delay_value);
11680
11681   // (de)activate virtual buttons depending on current game status
11682   if (strEqual(setup.touch.control_type, TOUCH_CONTROL_VIRTUAL_BUTTONS))
11683   {
11684     if (game.all_players_gone)  // if no players there to be controlled anymore
11685       SetOverlayActive(FALSE);
11686     else if (!tape.playing)     // if game continues after tape stopped playing
11687       SetOverlayActive(TRUE);
11688   }
11689
11690 #if 0
11691 #if 0
11692   // ---------- main game synchronization point ----------
11693
11694   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11695
11696   Debug("game:playing:skip", "skip == %d", skip);
11697
11698 #else
11699   // ---------- main game synchronization point ----------
11700
11701   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11702 #endif
11703 #endif
11704
11705   if (network_playing && !network_player_action_received)
11706   {
11707     // try to get network player actions in time
11708
11709     // last chance to get network player actions without main loop delay
11710     HandleNetworking();
11711
11712     // game was quit by network peer
11713     if (game_status != GAME_MODE_PLAYING)
11714       return;
11715
11716     // check if network player actions still missing and game still running
11717     if (!network_player_action_received && !checkGameEnded())
11718       return;           // failed to get network player actions in time
11719
11720     // do not yet reset "network_player_action_received" (for tape.pausing)
11721   }
11722
11723   if (tape.pausing)
11724     return;
11725
11726   // at this point we know that we really continue executing the game
11727
11728   network_player_action_received = FALSE;
11729
11730   // when playing tape, read previously recorded player input from tape data
11731   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11732
11733   local_player->effective_mouse_action = local_player->mouse_action;
11734
11735   if (recorded_player_action != NULL)
11736     SetMouseActionFromTapeAction(&local_player->effective_mouse_action,
11737                                  recorded_player_action);
11738
11739   // TapePlayAction() may return NULL when toggling to "pause before death"
11740   if (tape.pausing)
11741     return;
11742
11743   if (tape.set_centered_player)
11744   {
11745     game.centered_player_nr_next = tape.centered_player_nr_next;
11746     game.set_centered_player = TRUE;
11747   }
11748
11749   for (i = 0; i < MAX_PLAYERS; i++)
11750   {
11751     summarized_player_action |= stored_player[i].action;
11752
11753     if (!network_playing && (game.team_mode || tape.playing))
11754       stored_player[i].effective_action = stored_player[i].action;
11755   }
11756
11757   if (network_playing && !checkGameEnded())
11758     SendToServer_MovePlayer(summarized_player_action);
11759
11760   // summarize all actions at local players mapped input device position
11761   // (this allows using different input devices in single player mode)
11762   if (!network.enabled && !game.team_mode)
11763     stored_player[map_player_action[local_player->index_nr]].effective_action =
11764       summarized_player_action;
11765
11766   // summarize all actions at centered player in local team mode
11767   if (tape.recording &&
11768       setup.team_mode && !network.enabled &&
11769       setup.input_on_focus &&
11770       game.centered_player_nr != -1)
11771   {
11772     for (i = 0; i < MAX_PLAYERS; i++)
11773       stored_player[map_player_action[i]].effective_action =
11774         (i == game.centered_player_nr ? summarized_player_action : 0);
11775   }
11776
11777   if (recorded_player_action != NULL)
11778     for (i = 0; i < MAX_PLAYERS; i++)
11779       stored_player[i].effective_action = recorded_player_action[i];
11780
11781   for (i = 0; i < MAX_PLAYERS; i++)
11782   {
11783     tape_action[i] = stored_player[i].effective_action;
11784
11785     /* (this may happen in the RND game engine if a player was not present on
11786        the playfield on level start, but appeared later from a custom element */
11787     if (setup.team_mode &&
11788         tape.recording &&
11789         tape_action[i] &&
11790         !tape.player_participates[i])
11791       tape.player_participates[i] = TRUE;
11792   }
11793
11794   SetTapeActionFromMouseAction(tape_action,
11795                                &local_player->effective_mouse_action);
11796
11797   // only record actions from input devices, but not programmed actions
11798   if (tape.recording)
11799     TapeRecordAction(tape_action);
11800
11801   // remember if game was played (especially after tape stopped playing)
11802   if (!tape.playing && summarized_player_action)
11803     game.GamePlayed = TRUE;
11804
11805 #if USE_NEW_PLAYER_ASSIGNMENTS
11806   // !!! also map player actions in single player mode !!!
11807   // if (game.team_mode)
11808   if (1)
11809   {
11810     byte mapped_action[MAX_PLAYERS];
11811
11812 #if DEBUG_PLAYER_ACTIONS
11813     for (i = 0; i < MAX_PLAYERS; i++)
11814       DebugContinued("", "%d, ", stored_player[i].effective_action);
11815 #endif
11816
11817     for (i = 0; i < MAX_PLAYERS; i++)
11818       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11819
11820     for (i = 0; i < MAX_PLAYERS; i++)
11821       stored_player[i].effective_action = mapped_action[i];
11822
11823 #if DEBUG_PLAYER_ACTIONS
11824     DebugContinued("", "=> ");
11825     for (i = 0; i < MAX_PLAYERS; i++)
11826       DebugContinued("", "%d, ", stored_player[i].effective_action);
11827     DebugContinued("game:playing:player", "\n");
11828 #endif
11829   }
11830 #if DEBUG_PLAYER_ACTIONS
11831   else
11832   {
11833     for (i = 0; i < MAX_PLAYERS; i++)
11834       DebugContinued("", "%d, ", stored_player[i].effective_action);
11835     DebugContinued("game:playing:player", "\n");
11836   }
11837 #endif
11838 #endif
11839
11840   for (i = 0; i < MAX_PLAYERS; i++)
11841   {
11842     // allow engine snapshot in case of changed movement attempt
11843     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11844         (stored_player[i].effective_action & KEY_MOTION))
11845       game.snapshot.changed_action = TRUE;
11846
11847     // allow engine snapshot in case of snapping/dropping attempt
11848     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11849         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11850       game.snapshot.changed_action = TRUE;
11851
11852     game.snapshot.last_action[i] = stored_player[i].effective_action;
11853   }
11854
11855   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11856   {
11857     GameActions_EM_Main();
11858   }
11859   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11860   {
11861     GameActions_SP_Main();
11862   }
11863   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
11864   {
11865     GameActions_MM_Main();
11866   }
11867   else
11868   {
11869     GameActions_RND_Main();
11870   }
11871
11872   BlitScreenToBitmap(backbuffer);
11873
11874   CheckLevelSolved();
11875   CheckLevelTime();
11876
11877   AdvanceFrameAndPlayerCounters(-1);    // advance counters for all players
11878
11879   if (global.show_frames_per_second)
11880   {
11881     static unsigned int fps_counter = 0;
11882     static int fps_frames = 0;
11883     unsigned int fps_delay_ms = Counter() - fps_counter;
11884
11885     fps_frames++;
11886
11887     if (fps_delay_ms >= 500)    // calculate FPS every 0.5 seconds
11888     {
11889       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11890
11891       fps_frames = 0;
11892       fps_counter = Counter();
11893
11894       // always draw FPS to screen after FPS value was updated
11895       redraw_mask |= REDRAW_FPS;
11896     }
11897
11898     // only draw FPS if no screen areas are deactivated (invisible warp mode)
11899     if (GetDrawDeactivationMask() == REDRAW_NONE)
11900       redraw_mask |= REDRAW_FPS;
11901   }
11902 }
11903
11904 static void GameActions_CheckSaveEngineSnapshot(void)
11905 {
11906   if (!game.snapshot.save_snapshot)
11907     return;
11908
11909   // clear flag for saving snapshot _before_ saving snapshot
11910   game.snapshot.save_snapshot = FALSE;
11911
11912   SaveEngineSnapshotToList();
11913 }
11914
11915 void GameActions(void)
11916 {
11917   GameActionsExt();
11918
11919   GameActions_CheckSaveEngineSnapshot();
11920 }
11921
11922 void GameActions_EM_Main(void)
11923 {
11924   byte effective_action[MAX_PLAYERS];
11925   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11926   int i;
11927
11928   for (i = 0; i < MAX_PLAYERS; i++)
11929     effective_action[i] = stored_player[i].effective_action;
11930
11931   GameActions_EM(effective_action, warp_mode);
11932 }
11933
11934 void GameActions_SP_Main(void)
11935 {
11936   byte effective_action[MAX_PLAYERS];
11937   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11938   int i;
11939
11940   for (i = 0; i < MAX_PLAYERS; i++)
11941     effective_action[i] = stored_player[i].effective_action;
11942
11943   GameActions_SP(effective_action, warp_mode);
11944
11945   for (i = 0; i < MAX_PLAYERS; i++)
11946   {
11947     if (stored_player[i].force_dropping)
11948       stored_player[i].action |= KEY_BUTTON_DROP;
11949
11950     stored_player[i].force_dropping = FALSE;
11951   }
11952 }
11953
11954 void GameActions_MM_Main(void)
11955 {
11956   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11957
11958   GameActions_MM(local_player->effective_mouse_action, warp_mode);
11959 }
11960
11961 void GameActions_RND_Main(void)
11962 {
11963   GameActions_RND();
11964 }
11965
11966 void GameActions_RND(void)
11967 {
11968   static struct MouseActionInfo mouse_action_last = { 0 };
11969   struct MouseActionInfo mouse_action = local_player->effective_mouse_action;
11970   int magic_wall_x = 0, magic_wall_y = 0;
11971   int i, x, y, element, graphic, last_gfx_frame;
11972
11973   InitPlayfieldScanModeVars();
11974
11975   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11976   {
11977     SCAN_PLAYFIELD(x, y)
11978     {
11979       ChangeCount[x][y] = 0;
11980       ChangeEvent[x][y] = -1;
11981     }
11982   }
11983
11984   if (game.set_centered_player)
11985   {
11986     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11987
11988     // switching to "all players" only possible if all players fit to screen
11989     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11990     {
11991       game.centered_player_nr_next = game.centered_player_nr;
11992       game.set_centered_player = FALSE;
11993     }
11994
11995     // do not switch focus to non-existing (or non-active) player
11996     if (game.centered_player_nr_next >= 0 &&
11997         !stored_player[game.centered_player_nr_next].active)
11998     {
11999       game.centered_player_nr_next = game.centered_player_nr;
12000       game.set_centered_player = FALSE;
12001     }
12002   }
12003
12004   if (game.set_centered_player &&
12005       ScreenMovPos == 0)        // screen currently aligned at tile position
12006   {
12007     int sx, sy;
12008
12009     if (game.centered_player_nr_next == -1)
12010     {
12011       setScreenCenteredToAllPlayers(&sx, &sy);
12012     }
12013     else
12014     {
12015       sx = stored_player[game.centered_player_nr_next].jx;
12016       sy = stored_player[game.centered_player_nr_next].jy;
12017     }
12018
12019     game.centered_player_nr = game.centered_player_nr_next;
12020     game.set_centered_player = FALSE;
12021
12022     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
12023     DrawGameDoorValues();
12024   }
12025
12026   // check single step mode (set flag and clear again if any player is active)
12027   game.enter_single_step_mode =
12028     (tape.single_step && tape.recording && !tape.pausing);
12029
12030   for (i = 0; i < MAX_PLAYERS; i++)
12031   {
12032     int actual_player_action = stored_player[i].effective_action;
12033
12034 #if 1
12035     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
12036        - rnd_equinox_tetrachloride 048
12037        - rnd_equinox_tetrachloride_ii 096
12038        - rnd_emanuel_schmieg 002
12039        - doctor_sloan_ww 001, 020
12040     */
12041     if (stored_player[i].MovPos == 0)
12042       CheckGravityMovement(&stored_player[i]);
12043 #endif
12044
12045     // overwrite programmed action with tape action
12046     if (stored_player[i].programmed_action)
12047       actual_player_action = stored_player[i].programmed_action;
12048
12049     PlayerActions(&stored_player[i], actual_player_action);
12050
12051     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
12052   }
12053
12054   // single step pause mode may already have been toggled by "ScrollPlayer()"
12055   if (game.enter_single_step_mode && !tape.pausing)
12056     TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12057
12058   ScrollScreen(NULL, SCROLL_GO_ON);
12059
12060   /* for backwards compatibility, the following code emulates a fixed bug that
12061      occured when pushing elements (causing elements that just made their last
12062      pushing step to already (if possible) make their first falling step in the
12063      same game frame, which is bad); this code is also needed to use the famous
12064      "spring push bug" which is used in older levels and might be wanted to be
12065      used also in newer levels, but in this case the buggy pushing code is only
12066      affecting the "spring" element and no other elements */
12067
12068   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
12069   {
12070     for (i = 0; i < MAX_PLAYERS; i++)
12071     {
12072       struct PlayerInfo *player = &stored_player[i];
12073       int x = player->jx;
12074       int y = player->jy;
12075
12076       if (player->active && player->is_pushing && player->is_moving &&
12077           IS_MOVING(x, y) &&
12078           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
12079            Tile[x][y] == EL_SPRING))
12080       {
12081         ContinueMoving(x, y);
12082
12083         // continue moving after pushing (this is actually a bug)
12084         if (!IS_MOVING(x, y))
12085           Stop[x][y] = FALSE;
12086       }
12087     }
12088   }
12089
12090   SCAN_PLAYFIELD(x, y)
12091   {
12092     Last[x][y] = Tile[x][y];
12093
12094     ChangeCount[x][y] = 0;
12095     ChangeEvent[x][y] = -1;
12096
12097     // this must be handled before main playfield loop
12098     if (Tile[x][y] == EL_PLAYER_IS_LEAVING)
12099     {
12100       MovDelay[x][y]--;
12101       if (MovDelay[x][y] <= 0)
12102         RemoveField(x, y);
12103     }
12104
12105     if (Tile[x][y] == EL_ELEMENT_SNAPPING)
12106     {
12107       MovDelay[x][y]--;
12108       if (MovDelay[x][y] <= 0)
12109       {
12110         int element = Store[x][y];
12111         int move_direction = MovDir[x][y];
12112         int player_index_bit = Store2[x][y];
12113
12114         Store[x][y] = 0;
12115         Store2[x][y] = 0;
12116
12117         RemoveField(x, y);
12118         TEST_DrawLevelField(x, y);
12119
12120         TestFieldAfterSnapping(x, y, element, move_direction, player_index_bit);
12121       }
12122     }
12123
12124 #if DEBUG
12125     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
12126     {
12127       Debug("game:playing:GameActions_RND", "x = %d, y = %d: ChangePage != -1",
12128             x, y);
12129       Debug("game:playing:GameActions_RND", "This should never happen!");
12130
12131       ChangePage[x][y] = -1;
12132     }
12133 #endif
12134
12135     Stop[x][y] = FALSE;
12136     if (WasJustMoving[x][y] > 0)
12137       WasJustMoving[x][y]--;
12138     if (WasJustFalling[x][y] > 0)
12139       WasJustFalling[x][y]--;
12140     if (CheckCollision[x][y] > 0)
12141       CheckCollision[x][y]--;
12142     if (CheckImpact[x][y] > 0)
12143       CheckImpact[x][y]--;
12144
12145     GfxFrame[x][y]++;
12146
12147     /* reset finished pushing action (not done in ContinueMoving() to allow
12148        continuous pushing animation for elements with zero push delay) */
12149     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
12150     {
12151       ResetGfxAnimation(x, y);
12152       TEST_DrawLevelField(x, y);
12153     }
12154
12155 #if DEBUG
12156     if (IS_BLOCKED(x, y))
12157     {
12158       int oldx, oldy;
12159
12160       Blocked2Moving(x, y, &oldx, &oldy);
12161       if (!IS_MOVING(oldx, oldy))
12162       {
12163         Debug("game:playing:GameActions_RND", "(BLOCKED => MOVING) context corrupted!");
12164         Debug("game:playing:GameActions_RND", "BLOCKED: x = %d, y = %d", x, y);
12165         Debug("game:playing:GameActions_RND", "!MOVING: oldx = %d, oldy = %d", oldx, oldy);
12166         Debug("game:playing:GameActions_RND", "This should never happen!");
12167       }
12168     }
12169 #endif
12170   }
12171
12172   if (mouse_action.button)
12173   {
12174     int new_button = (mouse_action.button && mouse_action_last.button == 0);
12175     int ch_button = CH_SIDE_FROM_BUTTON(mouse_action.button);
12176
12177     x = mouse_action.lx;
12178     y = mouse_action.ly;
12179     element = Tile[x][y];
12180
12181     if (new_button)
12182     {
12183       CheckElementChangeByMouse(x, y, element, CE_CLICKED_BY_MOUSE, ch_button);
12184       CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_CLICKED_ON_X,
12185                                          ch_button);
12186     }
12187
12188     CheckElementChangeByMouse(x, y, element, CE_PRESSED_BY_MOUSE, ch_button);
12189     CheckTriggeredElementChangeByMouse(x, y, element, CE_MOUSE_PRESSED_ON_X,
12190                                        ch_button);
12191   }
12192
12193   SCAN_PLAYFIELD(x, y)
12194   {
12195     element = Tile[x][y];
12196     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12197     last_gfx_frame = GfxFrame[x][y];
12198
12199     ResetGfxFrame(x, y);
12200
12201     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
12202       DrawLevelGraphicAnimation(x, y, graphic);
12203
12204     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
12205         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
12206       ResetRandomAnimationValue(x, y);
12207
12208     SetRandomAnimationValue(x, y);
12209
12210     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
12211
12212     if (IS_INACTIVE(element))
12213     {
12214       if (IS_ANIMATED(graphic))
12215         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12216
12217       continue;
12218     }
12219
12220     // this may take place after moving, so 'element' may have changed
12221     if (IS_CHANGING(x, y) &&
12222         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
12223     {
12224       int page = element_info[element].event_page_nr[CE_DELAY];
12225
12226       HandleElementChange(x, y, page);
12227
12228       element = Tile[x][y];
12229       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12230     }
12231
12232     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
12233     {
12234       StartMoving(x, y);
12235
12236       element = Tile[x][y];
12237       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
12238
12239       if (IS_ANIMATED(graphic) &&
12240           !IS_MOVING(x, y) &&
12241           !Stop[x][y])
12242         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12243
12244       if (IS_GEM(element) || element == EL_SP_INFOTRON)
12245         TEST_DrawTwinkleOnField(x, y);
12246     }
12247     else if (element == EL_ACID)
12248     {
12249       if (!Stop[x][y])
12250         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12251     }
12252     else if ((element == EL_EXIT_OPEN ||
12253               element == EL_EM_EXIT_OPEN ||
12254               element == EL_SP_EXIT_OPEN ||
12255               element == EL_STEEL_EXIT_OPEN ||
12256               element == EL_EM_STEEL_EXIT_OPEN ||
12257               element == EL_SP_TERMINAL ||
12258               element == EL_SP_TERMINAL_ACTIVE ||
12259               element == EL_EXTRA_TIME ||
12260               element == EL_SHIELD_NORMAL ||
12261               element == EL_SHIELD_DEADLY) &&
12262              IS_ANIMATED(graphic))
12263       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12264     else if (IS_MOVING(x, y))
12265       ContinueMoving(x, y);
12266     else if (IS_ACTIVE_BOMB(element))
12267       CheckDynamite(x, y);
12268     else if (element == EL_AMOEBA_GROWING)
12269       AmoebaGrowing(x, y);
12270     else if (element == EL_AMOEBA_SHRINKING)
12271       AmoebaShrinking(x, y);
12272
12273 #if !USE_NEW_AMOEBA_CODE
12274     else if (IS_AMOEBALIVE(element))
12275       AmoebaReproduce(x, y);
12276 #endif
12277
12278     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
12279       Life(x, y);
12280     else if (element == EL_EXIT_CLOSED)
12281       CheckExit(x, y);
12282     else if (element == EL_EM_EXIT_CLOSED)
12283       CheckExitEM(x, y);
12284     else if (element == EL_STEEL_EXIT_CLOSED)
12285       CheckExitSteel(x, y);
12286     else if (element == EL_EM_STEEL_EXIT_CLOSED)
12287       CheckExitSteelEM(x, y);
12288     else if (element == EL_SP_EXIT_CLOSED)
12289       CheckExitSP(x, y);
12290     else if (element == EL_EXPANDABLE_WALL_GROWING ||
12291              element == EL_EXPANDABLE_STEELWALL_GROWING)
12292       MauerWaechst(x, y);
12293     else if (element == EL_EXPANDABLE_WALL ||
12294              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
12295              element == EL_EXPANDABLE_WALL_VERTICAL ||
12296              element == EL_EXPANDABLE_WALL_ANY ||
12297              element == EL_BD_EXPANDABLE_WALL)
12298       MauerAbleger(x, y);
12299     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
12300              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
12301              element == EL_EXPANDABLE_STEELWALL_ANY)
12302       MauerAblegerStahl(x, y);
12303     else if (element == EL_FLAMES)
12304       CheckForDragon(x, y);
12305     else if (element == EL_EXPLOSION)
12306       ; // drawing of correct explosion animation is handled separately
12307     else if (element == EL_ELEMENT_SNAPPING ||
12308              element == EL_DIAGONAL_SHRINKING ||
12309              element == EL_DIAGONAL_GROWING)
12310     {
12311       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
12312
12313       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12314     }
12315     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
12316       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
12317
12318     if (IS_BELT_ACTIVE(element))
12319       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
12320
12321     if (game.magic_wall_active)
12322     {
12323       int jx = local_player->jx, jy = local_player->jy;
12324
12325       // play the element sound at the position nearest to the player
12326       if ((element == EL_MAGIC_WALL_FULL ||
12327            element == EL_MAGIC_WALL_ACTIVE ||
12328            element == EL_MAGIC_WALL_EMPTYING ||
12329            element == EL_BD_MAGIC_WALL_FULL ||
12330            element == EL_BD_MAGIC_WALL_ACTIVE ||
12331            element == EL_BD_MAGIC_WALL_EMPTYING ||
12332            element == EL_DC_MAGIC_WALL_FULL ||
12333            element == EL_DC_MAGIC_WALL_ACTIVE ||
12334            element == EL_DC_MAGIC_WALL_EMPTYING) &&
12335           ABS(x - jx) + ABS(y - jy) <
12336           ABS(magic_wall_x - jx) + ABS(magic_wall_y - jy))
12337       {
12338         magic_wall_x = x;
12339         magic_wall_y = y;
12340       }
12341     }
12342   }
12343
12344 #if USE_NEW_AMOEBA_CODE
12345   // new experimental amoeba growth stuff
12346   if (!(FrameCounter % 8))
12347   {
12348     static unsigned int random = 1684108901;
12349
12350     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
12351     {
12352       x = RND(lev_fieldx);
12353       y = RND(lev_fieldy);
12354       element = Tile[x][y];
12355
12356       if (!IS_PLAYER(x,y) &&
12357           (element == EL_EMPTY ||
12358            CAN_GROW_INTO(element) ||
12359            element == EL_QUICKSAND_EMPTY ||
12360            element == EL_QUICKSAND_FAST_EMPTY ||
12361            element == EL_ACID_SPLASH_LEFT ||
12362            element == EL_ACID_SPLASH_RIGHT))
12363       {
12364         if ((IN_LEV_FIELD(x, y-1) && Tile[x][y-1] == EL_AMOEBA_WET) ||
12365             (IN_LEV_FIELD(x-1, y) && Tile[x-1][y] == EL_AMOEBA_WET) ||
12366             (IN_LEV_FIELD(x+1, y) && Tile[x+1][y] == EL_AMOEBA_WET) ||
12367             (IN_LEV_FIELD(x, y+1) && Tile[x][y+1] == EL_AMOEBA_WET))
12368           Tile[x][y] = EL_AMOEBA_DROP;
12369       }
12370
12371       random = random * 129 + 1;
12372     }
12373   }
12374 #endif
12375
12376   game.explosions_delayed = FALSE;
12377
12378   SCAN_PLAYFIELD(x, y)
12379   {
12380     element = Tile[x][y];
12381
12382     if (ExplodeField[x][y])
12383       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
12384     else if (element == EL_EXPLOSION)
12385       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
12386
12387     ExplodeField[x][y] = EX_TYPE_NONE;
12388   }
12389
12390   game.explosions_delayed = TRUE;
12391
12392   if (game.magic_wall_active)
12393   {
12394     if (!(game.magic_wall_time_left % 4))
12395     {
12396       int element = Tile[magic_wall_x][magic_wall_y];
12397
12398       if (element == EL_BD_MAGIC_WALL_FULL ||
12399           element == EL_BD_MAGIC_WALL_ACTIVE ||
12400           element == EL_BD_MAGIC_WALL_EMPTYING)
12401         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
12402       else if (element == EL_DC_MAGIC_WALL_FULL ||
12403                element == EL_DC_MAGIC_WALL_ACTIVE ||
12404                element == EL_DC_MAGIC_WALL_EMPTYING)
12405         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
12406       else
12407         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
12408     }
12409
12410     if (game.magic_wall_time_left > 0)
12411     {
12412       game.magic_wall_time_left--;
12413
12414       if (!game.magic_wall_time_left)
12415       {
12416         SCAN_PLAYFIELD(x, y)
12417         {
12418           element = Tile[x][y];
12419
12420           if (element == EL_MAGIC_WALL_ACTIVE ||
12421               element == EL_MAGIC_WALL_FULL)
12422           {
12423             Tile[x][y] = EL_MAGIC_WALL_DEAD;
12424             TEST_DrawLevelField(x, y);
12425           }
12426           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
12427                    element == EL_BD_MAGIC_WALL_FULL)
12428           {
12429             Tile[x][y] = EL_BD_MAGIC_WALL_DEAD;
12430             TEST_DrawLevelField(x, y);
12431           }
12432           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
12433                    element == EL_DC_MAGIC_WALL_FULL)
12434           {
12435             Tile[x][y] = EL_DC_MAGIC_WALL_DEAD;
12436             TEST_DrawLevelField(x, y);
12437           }
12438         }
12439
12440         game.magic_wall_active = FALSE;
12441       }
12442     }
12443   }
12444
12445   if (game.light_time_left > 0)
12446   {
12447     game.light_time_left--;
12448
12449     if (game.light_time_left == 0)
12450       RedrawAllLightSwitchesAndInvisibleElements();
12451   }
12452
12453   if (game.timegate_time_left > 0)
12454   {
12455     game.timegate_time_left--;
12456
12457     if (game.timegate_time_left == 0)
12458       CloseAllOpenTimegates();
12459   }
12460
12461   if (game.lenses_time_left > 0)
12462   {
12463     game.lenses_time_left--;
12464
12465     if (game.lenses_time_left == 0)
12466       RedrawAllInvisibleElementsForLenses();
12467   }
12468
12469   if (game.magnify_time_left > 0)
12470   {
12471     game.magnify_time_left--;
12472
12473     if (game.magnify_time_left == 0)
12474       RedrawAllInvisibleElementsForMagnifier();
12475   }
12476
12477   for (i = 0; i < MAX_PLAYERS; i++)
12478   {
12479     struct PlayerInfo *player = &stored_player[i];
12480
12481     if (SHIELD_ON(player))
12482     {
12483       if (player->shield_deadly_time_left)
12484         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
12485       else if (player->shield_normal_time_left)
12486         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
12487     }
12488   }
12489
12490 #if USE_DELAYED_GFX_REDRAW
12491   SCAN_PLAYFIELD(x, y)
12492   {
12493     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
12494     {
12495       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
12496          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
12497
12498       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
12499         DrawLevelField(x, y);
12500
12501       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
12502         DrawLevelFieldCrumbled(x, y);
12503
12504       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
12505         DrawLevelFieldCrumbledNeighbours(x, y);
12506
12507       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
12508         DrawTwinkleOnField(x, y);
12509     }
12510
12511     GfxRedraw[x][y] = GFX_REDRAW_NONE;
12512   }
12513 #endif
12514
12515   DrawAllPlayers();
12516   PlayAllPlayersSound();
12517
12518   for (i = 0; i < MAX_PLAYERS; i++)
12519   {
12520     struct PlayerInfo *player = &stored_player[i];
12521
12522     if (player->show_envelope != 0 && (!player->active ||
12523                                        player->MovPos == 0))
12524     {
12525       ShowEnvelope(player->show_envelope - EL_ENVELOPE_1);
12526
12527       player->show_envelope = 0;
12528     }
12529   }
12530
12531   // use random number generator in every frame to make it less predictable
12532   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12533     RND(1);
12534
12535   mouse_action_last = mouse_action;
12536 }
12537
12538 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
12539 {
12540   int min_x = x, min_y = y, max_x = x, max_y = y;
12541   int scr_fieldx = getScreenFieldSizeX();
12542   int scr_fieldy = getScreenFieldSizeY();
12543   int i;
12544
12545   for (i = 0; i < MAX_PLAYERS; i++)
12546   {
12547     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12548
12549     if (!stored_player[i].active || &stored_player[i] == player)
12550       continue;
12551
12552     min_x = MIN(min_x, jx);
12553     min_y = MIN(min_y, jy);
12554     max_x = MAX(max_x, jx);
12555     max_y = MAX(max_y, jy);
12556   }
12557
12558   return (max_x - min_x < scr_fieldx && max_y - min_y < scr_fieldy);
12559 }
12560
12561 static boolean AllPlayersInVisibleScreen(void)
12562 {
12563   int i;
12564
12565   for (i = 0; i < MAX_PLAYERS; i++)
12566   {
12567     int jx = stored_player[i].jx, jy = stored_player[i].jy;
12568
12569     if (!stored_player[i].active)
12570       continue;
12571
12572     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12573       return FALSE;
12574   }
12575
12576   return TRUE;
12577 }
12578
12579 void ScrollLevel(int dx, int dy)
12580 {
12581   int scroll_offset = 2 * TILEX_VAR;
12582   int x, y;
12583
12584   BlitBitmap(drawto_field, drawto_field,
12585              FX + TILEX_VAR * (dx == -1) - scroll_offset,
12586              FY + TILEY_VAR * (dy == -1) - scroll_offset,
12587              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
12588              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
12589              FX + TILEX_VAR * (dx == 1) - scroll_offset,
12590              FY + TILEY_VAR * (dy == 1) - scroll_offset);
12591
12592   if (dx != 0)
12593   {
12594     x = (dx == 1 ? BX1 : BX2);
12595     for (y = BY1; y <= BY2; y++)
12596       DrawScreenField(x, y);
12597   }
12598
12599   if (dy != 0)
12600   {
12601     y = (dy == 1 ? BY1 : BY2);
12602     for (x = BX1; x <= BX2; x++)
12603       DrawScreenField(x, y);
12604   }
12605
12606   redraw_mask |= REDRAW_FIELD;
12607 }
12608
12609 static boolean canFallDown(struct PlayerInfo *player)
12610 {
12611   int jx = player->jx, jy = player->jy;
12612
12613   return (IN_LEV_FIELD(jx, jy + 1) &&
12614           (IS_FREE(jx, jy + 1) ||
12615            (Tile[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
12616           IS_WALKABLE_FROM(Tile[jx][jy], MV_DOWN) &&
12617           !IS_WALKABLE_INSIDE(Tile[jx][jy]));
12618 }
12619
12620 static boolean canPassField(int x, int y, int move_dir)
12621 {
12622   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12623   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12624   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12625   int nextx = x + dx;
12626   int nexty = y + dy;
12627   int element = Tile[x][y];
12628
12629   return (IS_PASSABLE_FROM(element, opposite_dir) &&
12630           !CAN_MOVE(element) &&
12631           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
12632           IS_WALKABLE_FROM(Tile[nextx][nexty], move_dir) &&
12633           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
12634 }
12635
12636 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
12637 {
12638   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
12639   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
12640   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
12641   int newx = x + dx;
12642   int newy = y + dy;
12643
12644   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
12645           IS_GRAVITY_REACHABLE(Tile[newx][newy]) &&
12646           (IS_DIGGABLE(Tile[newx][newy]) ||
12647            IS_WALKABLE_FROM(Tile[newx][newy], opposite_dir) ||
12648            canPassField(newx, newy, move_dir)));
12649 }
12650
12651 static void CheckGravityMovement(struct PlayerInfo *player)
12652 {
12653   if (player->gravity && !player->programmed_action)
12654   {
12655     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
12656     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
12657     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
12658     int jx = player->jx, jy = player->jy;
12659     boolean player_is_moving_to_valid_field =
12660       (!player_is_snapping &&
12661        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
12662         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
12663     boolean player_can_fall_down = canFallDown(player);
12664
12665     if (player_can_fall_down &&
12666         !player_is_moving_to_valid_field)
12667       player->programmed_action = MV_DOWN;
12668   }
12669 }
12670
12671 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
12672 {
12673   return CheckGravityMovement(player);
12674
12675   if (player->gravity && !player->programmed_action)
12676   {
12677     int jx = player->jx, jy = player->jy;
12678     boolean field_under_player_is_free =
12679       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
12680     boolean player_is_standing_on_valid_field =
12681       (IS_WALKABLE_INSIDE(Tile[jx][jy]) ||
12682        (IS_WALKABLE(Tile[jx][jy]) &&
12683         !(element_info[Tile[jx][jy]].access_direction & MV_DOWN)));
12684
12685     if (field_under_player_is_free && !player_is_standing_on_valid_field)
12686       player->programmed_action = MV_DOWN;
12687   }
12688 }
12689
12690 /*
12691   MovePlayerOneStep()
12692   -----------------------------------------------------------------------------
12693   dx, dy:               direction (non-diagonal) to try to move the player to
12694   real_dx, real_dy:     direction as read from input device (can be diagonal)
12695 */
12696
12697 boolean MovePlayerOneStep(struct PlayerInfo *player,
12698                           int dx, int dy, int real_dx, int real_dy)
12699 {
12700   int jx = player->jx, jy = player->jy;
12701   int new_jx = jx + dx, new_jy = jy + dy;
12702   int can_move;
12703   boolean player_can_move = !player->cannot_move;
12704
12705   if (!player->active || (!dx && !dy))
12706     return MP_NO_ACTION;
12707
12708   player->MovDir = (dx < 0 ? MV_LEFT :
12709                     dx > 0 ? MV_RIGHT :
12710                     dy < 0 ? MV_UP :
12711                     dy > 0 ? MV_DOWN :  MV_NONE);
12712
12713   if (!IN_LEV_FIELD(new_jx, new_jy))
12714     return MP_NO_ACTION;
12715
12716   if (!player_can_move)
12717   {
12718     if (player->MovPos == 0)
12719     {
12720       player->is_moving = FALSE;
12721       player->is_digging = FALSE;
12722       player->is_collecting = FALSE;
12723       player->is_snapping = FALSE;
12724       player->is_pushing = FALSE;
12725     }
12726   }
12727
12728   if (!network.enabled && game.centered_player_nr == -1 &&
12729       !AllPlayersInSight(player, new_jx, new_jy))
12730     return MP_NO_ACTION;
12731
12732   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12733   if (can_move != MP_MOVING)
12734     return can_move;
12735
12736   // check if DigField() has caused relocation of the player
12737   if (player->jx != jx || player->jy != jy)
12738     return MP_NO_ACTION;        // <-- !!! CHECK THIS [-> MP_ACTION ?] !!!
12739
12740   StorePlayer[jx][jy] = 0;
12741   player->last_jx = jx;
12742   player->last_jy = jy;
12743   player->jx = new_jx;
12744   player->jy = new_jy;
12745   StorePlayer[new_jx][new_jy] = player->element_nr;
12746
12747   if (player->move_delay_value_next != -1)
12748   {
12749     player->move_delay_value = player->move_delay_value_next;
12750     player->move_delay_value_next = -1;
12751   }
12752
12753   player->MovPos =
12754     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12755
12756   player->step_counter++;
12757
12758   PlayerVisit[jx][jy] = FrameCounter;
12759
12760   player->is_moving = TRUE;
12761
12762 #if 1
12763   // should better be called in MovePlayer(), but this breaks some tapes
12764   ScrollPlayer(player, SCROLL_INIT);
12765 #endif
12766
12767   return MP_MOVING;
12768 }
12769
12770 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12771 {
12772   int jx = player->jx, jy = player->jy;
12773   int old_jx = jx, old_jy = jy;
12774   int moved = MP_NO_ACTION;
12775
12776   if (!player->active)
12777     return FALSE;
12778
12779   if (!dx && !dy)
12780   {
12781     if (player->MovPos == 0)
12782     {
12783       player->is_moving = FALSE;
12784       player->is_digging = FALSE;
12785       player->is_collecting = FALSE;
12786       player->is_snapping = FALSE;
12787       player->is_pushing = FALSE;
12788     }
12789
12790     return FALSE;
12791   }
12792
12793   if (player->move_delay > 0)
12794     return FALSE;
12795
12796   player->move_delay = -1;              // set to "uninitialized" value
12797
12798   // store if player is automatically moved to next field
12799   player->is_auto_moving = (player->programmed_action != MV_NONE);
12800
12801   // remove the last programmed player action
12802   player->programmed_action = 0;
12803
12804   if (player->MovPos)
12805   {
12806     // should only happen if pre-1.2 tape recordings are played
12807     // this is only for backward compatibility
12808
12809     int original_move_delay_value = player->move_delay_value;
12810
12811 #if DEBUG
12812     Debug("game:playing:MovePlayer",
12813           "THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]",
12814           tape.counter);
12815 #endif
12816
12817     // scroll remaining steps with finest movement resolution
12818     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12819
12820     while (player->MovPos)
12821     {
12822       ScrollPlayer(player, SCROLL_GO_ON);
12823       ScrollScreen(NULL, SCROLL_GO_ON);
12824
12825       AdvanceFrameAndPlayerCounters(player->index_nr);
12826
12827       DrawAllPlayers();
12828       BackToFront_WithFrameDelay(0);
12829     }
12830
12831     player->move_delay_value = original_move_delay_value;
12832   }
12833
12834   player->is_active = FALSE;
12835
12836   if (player->last_move_dir & MV_HORIZONTAL)
12837   {
12838     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12839       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12840   }
12841   else
12842   {
12843     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12844       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12845   }
12846
12847   if (!moved && !player->is_active)
12848   {
12849     player->is_moving = FALSE;
12850     player->is_digging = FALSE;
12851     player->is_collecting = FALSE;
12852     player->is_snapping = FALSE;
12853     player->is_pushing = FALSE;
12854   }
12855
12856   jx = player->jx;
12857   jy = player->jy;
12858
12859   if (moved & MP_MOVING && !ScreenMovPos &&
12860       (player->index_nr == game.centered_player_nr ||
12861        game.centered_player_nr == -1))
12862   {
12863     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12864
12865     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12866     {
12867       // actual player has left the screen -- scroll in that direction
12868       if (jx != old_jx)         // player has moved horizontally
12869         scroll_x += (jx - old_jx);
12870       else                      // player has moved vertically
12871         scroll_y += (jy - old_jy);
12872     }
12873     else
12874     {
12875       int offset_raw = game.scroll_delay_value;
12876
12877       if (jx != old_jx)         // player has moved horizontally
12878       {
12879         int offset = MIN(offset_raw, (SCR_FIELDX - 2) / 2);
12880         int offset_x = offset * (player->MovDir == MV_LEFT ? +1 : -1);
12881         int new_scroll_x = jx - MIDPOSX + offset_x;
12882
12883         if ((player->MovDir == MV_LEFT  && scroll_x > new_scroll_x) ||
12884             (player->MovDir == MV_RIGHT && scroll_x < new_scroll_x))
12885           scroll_x = new_scroll_x;
12886
12887         // don't scroll over playfield boundaries
12888         scroll_x = MIN(MAX(SBX_Left, scroll_x), SBX_Right);
12889
12890         // don't scroll more than one field at a time
12891         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12892
12893         // don't scroll against the player's moving direction
12894         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12895             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12896           scroll_x = old_scroll_x;
12897       }
12898       else                      // player has moved vertically
12899       {
12900         int offset = MIN(offset_raw, (SCR_FIELDY - 2) / 2);
12901         int offset_y = offset * (player->MovDir == MV_UP ? +1 : -1);
12902         int new_scroll_y = jy - MIDPOSY + offset_y;
12903
12904         if ((player->MovDir == MV_UP   && scroll_y > new_scroll_y) ||
12905             (player->MovDir == MV_DOWN && scroll_y < new_scroll_y))
12906           scroll_y = new_scroll_y;
12907
12908         // don't scroll over playfield boundaries
12909         scroll_y = MIN(MAX(SBY_Upper, scroll_y), SBY_Lower);
12910
12911         // don't scroll more than one field at a time
12912         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12913
12914         // don't scroll against the player's moving direction
12915         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12916             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12917           scroll_y = old_scroll_y;
12918       }
12919     }
12920
12921     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12922     {
12923       if (!network.enabled && game.centered_player_nr == -1 &&
12924           !AllPlayersInVisibleScreen())
12925       {
12926         scroll_x = old_scroll_x;
12927         scroll_y = old_scroll_y;
12928       }
12929       else
12930       {
12931         ScrollScreen(player, SCROLL_INIT);
12932         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12933       }
12934     }
12935   }
12936
12937   player->StepFrame = 0;
12938
12939   if (moved & MP_MOVING)
12940   {
12941     if (old_jx != jx && old_jy == jy)
12942       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12943     else if (old_jx == jx && old_jy != jy)
12944       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12945
12946     TEST_DrawLevelField(jx, jy);        // for "crumbled sand"
12947
12948     player->last_move_dir = player->MovDir;
12949     player->is_moving = TRUE;
12950     player->is_snapping = FALSE;
12951     player->is_switching = FALSE;
12952     player->is_dropping = FALSE;
12953     player->is_dropping_pressed = FALSE;
12954     player->drop_pressed_delay = 0;
12955
12956 #if 0
12957     // should better be called here than above, but this breaks some tapes
12958     ScrollPlayer(player, SCROLL_INIT);
12959 #endif
12960   }
12961   else
12962   {
12963     CheckGravityMovementWhenNotMoving(player);
12964
12965     player->is_moving = FALSE;
12966
12967     /* at this point, the player is allowed to move, but cannot move right now
12968        (e.g. because of something blocking the way) -- ensure that the player
12969        is also allowed to move in the next frame (in old versions before 3.1.1,
12970        the player was forced to wait again for eight frames before next try) */
12971
12972     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12973       player->move_delay = 0;   // allow direct movement in the next frame
12974   }
12975
12976   if (player->move_delay == -1)         // not yet initialized by DigField()
12977     player->move_delay = player->move_delay_value;
12978
12979   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12980   {
12981     TestIfPlayerTouchesBadThing(jx, jy);
12982     TestIfPlayerTouchesCustomElement(jx, jy);
12983   }
12984
12985   if (!player->active)
12986     RemovePlayer(player);
12987
12988   return moved;
12989 }
12990
12991 void ScrollPlayer(struct PlayerInfo *player, int mode)
12992 {
12993   int jx = player->jx, jy = player->jy;
12994   int last_jx = player->last_jx, last_jy = player->last_jy;
12995   int move_stepsize = TILEX / player->move_delay_value;
12996
12997   if (!player->active)
12998     return;
12999
13000   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      // player not moving
13001     return;
13002
13003   if (mode == SCROLL_INIT)
13004   {
13005     player->actual_frame_counter = FrameCounter;
13006     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13007
13008     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
13009         Tile[last_jx][last_jy] == EL_EMPTY)
13010     {
13011       int last_field_block_delay = 0;   // start with no blocking at all
13012       int block_delay_adjustment = player->block_delay_adjustment;
13013
13014       // if player blocks last field, add delay for exactly one move
13015       if (player->block_last_field)
13016       {
13017         last_field_block_delay += player->move_delay_value;
13018
13019         // when blocking enabled, prevent moving up despite gravity
13020         if (player->gravity && player->MovDir == MV_UP)
13021           block_delay_adjustment = -1;
13022       }
13023
13024       // add block delay adjustment (also possible when not blocking)
13025       last_field_block_delay += block_delay_adjustment;
13026
13027       Tile[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
13028       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
13029     }
13030
13031     if (player->MovPos != 0)    // player has not yet reached destination
13032       return;
13033   }
13034   else if (!FrameReached(&player->actual_frame_counter, 1))
13035     return;
13036
13037   if (player->MovPos != 0)
13038   {
13039     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
13040     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
13041
13042     // before DrawPlayer() to draw correct player graphic for this case
13043     if (player->MovPos == 0)
13044       CheckGravityMovement(player);
13045   }
13046
13047   if (player->MovPos == 0)      // player reached destination field
13048   {
13049     if (player->move_delay_reset_counter > 0)
13050     {
13051       player->move_delay_reset_counter--;
13052
13053       if (player->move_delay_reset_counter == 0)
13054       {
13055         // continue with normal speed after quickly moving through gate
13056         HALVE_PLAYER_SPEED(player);
13057
13058         // be able to make the next move without delay
13059         player->move_delay = 0;
13060       }
13061     }
13062
13063     player->last_jx = jx;
13064     player->last_jy = jy;
13065
13066     if (Tile[jx][jy] == EL_EXIT_OPEN ||
13067         Tile[jx][jy] == EL_EM_EXIT_OPEN ||
13068         Tile[jx][jy] == EL_EM_EXIT_OPENING ||
13069         Tile[jx][jy] == EL_STEEL_EXIT_OPEN ||
13070         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
13071         Tile[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
13072         Tile[jx][jy] == EL_SP_EXIT_OPEN ||
13073         Tile[jx][jy] == EL_SP_EXIT_OPENING)     // <-- special case
13074     {
13075       ExitPlayer(player);
13076
13077       if (game.players_still_needed == 0 &&
13078           (game.friends_still_needed == 0 ||
13079            IS_SP_ELEMENT(Tile[jx][jy])))
13080         LevelSolved();
13081     }
13082
13083     // this breaks one level: "machine", level 000
13084     {
13085       int move_direction = player->MovDir;
13086       int enter_side = MV_DIR_OPPOSITE(move_direction);
13087       int leave_side = move_direction;
13088       int old_jx = last_jx;
13089       int old_jy = last_jy;
13090       int old_element = Tile[old_jx][old_jy];
13091       int new_element = Tile[jx][jy];
13092
13093       if (IS_CUSTOM_ELEMENT(old_element))
13094         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
13095                                    CE_LEFT_BY_PLAYER,
13096                                    player->index_bit, leave_side);
13097
13098       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
13099                                           CE_PLAYER_LEAVES_X,
13100                                           player->index_bit, leave_side);
13101
13102       if (IS_CUSTOM_ELEMENT(new_element))
13103         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
13104                                    player->index_bit, enter_side);
13105
13106       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
13107                                           CE_PLAYER_ENTERS_X,
13108                                           player->index_bit, enter_side);
13109
13110       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
13111                                         CE_MOVE_OF_X, move_direction);
13112     }
13113
13114     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13115     {
13116       TestIfPlayerTouchesBadThing(jx, jy);
13117       TestIfPlayerTouchesCustomElement(jx, jy);
13118
13119       /* needed because pushed element has not yet reached its destination,
13120          so it would trigger a change event at its previous field location */
13121       if (!player->is_pushing)
13122         TestIfElementTouchesCustomElement(jx, jy);      // for empty space
13123
13124       if (level.finish_dig_collect &&
13125           (player->is_digging || player->is_collecting))
13126       {
13127         int last_element = player->last_removed_element;
13128         int move_direction = player->MovDir;
13129         int enter_side = MV_DIR_OPPOSITE(move_direction);
13130         int change_event = (player->is_digging ? CE_PLAYER_DIGS_X :
13131                             CE_PLAYER_COLLECTS_X);
13132
13133         CheckTriggeredElementChangeByPlayer(jx, jy, last_element, change_event,
13134                                             player->index_bit, enter_side);
13135
13136         player->last_removed_element = EL_UNDEFINED;
13137       }
13138
13139       if (!player->active)
13140         RemovePlayer(player);
13141     }
13142
13143     if (!game.LevelSolved && level.use_step_counter)
13144     {
13145       int i;
13146
13147       TimePlayed++;
13148
13149       if (TimeLeft > 0)
13150       {
13151         TimeLeft--;
13152
13153         if (TimeLeft <= 10 && setup.time_limit)
13154           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
13155
13156         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13157
13158         DisplayGameControlValues();
13159
13160         if (!TimeLeft && setup.time_limit)
13161           for (i = 0; i < MAX_PLAYERS; i++)
13162             KillPlayer(&stored_player[i]);
13163       }
13164       else if (game.no_time_limit && !game.all_players_gone)
13165       {
13166         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
13167
13168         DisplayGameControlValues();
13169       }
13170     }
13171
13172     if (tape.single_step && tape.recording && !tape.pausing &&
13173         !player->programmed_action)
13174       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
13175
13176     if (!player->programmed_action)
13177       CheckSaveEngineSnapshot(player);
13178   }
13179 }
13180
13181 void ScrollScreen(struct PlayerInfo *player, int mode)
13182 {
13183   static unsigned int screen_frame_counter = 0;
13184
13185   if (mode == SCROLL_INIT)
13186   {
13187     // set scrolling step size according to actual player's moving speed
13188     ScrollStepSize = TILEX / player->move_delay_value;
13189
13190     screen_frame_counter = FrameCounter;
13191     ScreenMovDir = player->MovDir;
13192     ScreenMovPos = player->MovPos;
13193     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13194     return;
13195   }
13196   else if (!FrameReached(&screen_frame_counter, 1))
13197     return;
13198
13199   if (ScreenMovPos)
13200   {
13201     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
13202     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
13203     redraw_mask |= REDRAW_FIELD;
13204   }
13205   else
13206     ScreenMovDir = MV_NONE;
13207 }
13208
13209 void TestIfPlayerTouchesCustomElement(int x, int y)
13210 {
13211   static int xy[4][2] =
13212   {
13213     { 0, -1 },
13214     { -1, 0 },
13215     { +1, 0 },
13216     { 0, +1 }
13217   };
13218   static int trigger_sides[4][2] =
13219   {
13220     // center side       border side
13221     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13222     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13223     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13224     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13225   };
13226   static int touch_dir[4] =
13227   {
13228     MV_LEFT | MV_RIGHT,
13229     MV_UP   | MV_DOWN,
13230     MV_UP   | MV_DOWN,
13231     MV_LEFT | MV_RIGHT
13232   };
13233   int center_element = Tile[x][y];      // should always be non-moving!
13234   int i;
13235
13236   for (i = 0; i < NUM_DIRECTIONS; i++)
13237   {
13238     int xx = x + xy[i][0];
13239     int yy = y + xy[i][1];
13240     int center_side = trigger_sides[i][0];
13241     int border_side = trigger_sides[i][1];
13242     int border_element;
13243
13244     if (!IN_LEV_FIELD(xx, yy))
13245       continue;
13246
13247     if (IS_PLAYER(x, y))                // player found at center element
13248     {
13249       struct PlayerInfo *player = PLAYERINFO(x, y);
13250
13251       if (game.engine_version < VERSION_IDENT(3,0,7,0))
13252         border_element = Tile[xx][yy];          // may be moving!
13253       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13254         border_element = Tile[xx][yy];
13255       else if (MovDir[xx][yy] & touch_dir[i])   // elements are touching
13256         border_element = MovingOrBlocked2Element(xx, yy);
13257       else
13258         continue;               // center and border element do not touch
13259
13260       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
13261                                  player->index_bit, border_side);
13262       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
13263                                           CE_PLAYER_TOUCHES_X,
13264                                           player->index_bit, border_side);
13265
13266       {
13267         /* use player element that is initially defined in the level playfield,
13268            not the player element that corresponds to the runtime player number
13269            (example: a level that contains EL_PLAYER_3 as the only player would
13270            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13271         int player_element = PLAYERINFO(x, y)->initial_element;
13272
13273         CheckElementChangeBySide(xx, yy, border_element, player_element,
13274                                  CE_TOUCHING_X, border_side);
13275       }
13276     }
13277     else if (IS_PLAYER(xx, yy))         // player found at border element
13278     {
13279       struct PlayerInfo *player = PLAYERINFO(xx, yy);
13280
13281       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13282       {
13283         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13284           continue;             // center and border element do not touch
13285       }
13286
13287       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
13288                                  player->index_bit, center_side);
13289       CheckTriggeredElementChangeByPlayer(x, y, center_element,
13290                                           CE_PLAYER_TOUCHES_X,
13291                                           player->index_bit, center_side);
13292
13293       {
13294         /* use player element that is initially defined in the level playfield,
13295            not the player element that corresponds to the runtime player number
13296            (example: a level that contains EL_PLAYER_3 as the only player would
13297            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13298         int player_element = PLAYERINFO(xx, yy)->initial_element;
13299
13300         CheckElementChangeBySide(x, y, center_element, player_element,
13301                                  CE_TOUCHING_X, center_side);
13302       }
13303
13304       break;
13305     }
13306   }
13307 }
13308
13309 void TestIfElementTouchesCustomElement(int x, int y)
13310 {
13311   static int xy[4][2] =
13312   {
13313     { 0, -1 },
13314     { -1, 0 },
13315     { +1, 0 },
13316     { 0, +1 }
13317   };
13318   static int trigger_sides[4][2] =
13319   {
13320     // center side      border side
13321     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      // check top
13322     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      // check left
13323     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      // check right
13324     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       // check bottom
13325   };
13326   static int touch_dir[4] =
13327   {
13328     MV_LEFT | MV_RIGHT,
13329     MV_UP   | MV_DOWN,
13330     MV_UP   | MV_DOWN,
13331     MV_LEFT | MV_RIGHT
13332   };
13333   boolean change_center_element = FALSE;
13334   int center_element = Tile[x][y];      // should always be non-moving!
13335   int border_element_old[NUM_DIRECTIONS];
13336   int i;
13337
13338   for (i = 0; i < NUM_DIRECTIONS; i++)
13339   {
13340     int xx = x + xy[i][0];
13341     int yy = y + xy[i][1];
13342     int border_element;
13343
13344     border_element_old[i] = -1;
13345
13346     if (!IN_LEV_FIELD(xx, yy))
13347       continue;
13348
13349     if (game.engine_version < VERSION_IDENT(3,0,7,0))
13350       border_element = Tile[xx][yy];    // may be moving!
13351     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
13352       border_element = Tile[xx][yy];
13353     else if (MovDir[xx][yy] & touch_dir[i])     // elements are touching
13354       border_element = MovingOrBlocked2Element(xx, yy);
13355     else
13356       continue;                 // center and border element do not touch
13357
13358     border_element_old[i] = border_element;
13359   }
13360
13361   for (i = 0; i < NUM_DIRECTIONS; i++)
13362   {
13363     int xx = x + xy[i][0];
13364     int yy = y + xy[i][1];
13365     int center_side = trigger_sides[i][0];
13366     int border_element = border_element_old[i];
13367
13368     if (border_element == -1)
13369       continue;
13370
13371     // check for change of border element
13372     CheckElementChangeBySide(xx, yy, border_element, center_element,
13373                              CE_TOUCHING_X, center_side);
13374
13375     // (center element cannot be player, so we dont have to check this here)
13376   }
13377
13378   for (i = 0; i < NUM_DIRECTIONS; i++)
13379   {
13380     int xx = x + xy[i][0];
13381     int yy = y + xy[i][1];
13382     int border_side = trigger_sides[i][1];
13383     int border_element = border_element_old[i];
13384
13385     if (border_element == -1)
13386       continue;
13387
13388     // check for change of center element (but change it only once)
13389     if (!change_center_element)
13390       change_center_element =
13391         CheckElementChangeBySide(x, y, center_element, border_element,
13392                                  CE_TOUCHING_X, border_side);
13393
13394     if (IS_PLAYER(xx, yy))
13395     {
13396       /* use player element that is initially defined in the level playfield,
13397          not the player element that corresponds to the runtime player number
13398          (example: a level that contains EL_PLAYER_3 as the only player would
13399          incorrectly give EL_PLAYER_1 for "player->element_nr") */
13400       int player_element = PLAYERINFO(xx, yy)->initial_element;
13401
13402       CheckElementChangeBySide(x, y, center_element, player_element,
13403                                CE_TOUCHING_X, border_side);
13404     }
13405   }
13406 }
13407
13408 void TestIfElementHitsCustomElement(int x, int y, int direction)
13409 {
13410   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
13411   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
13412   int hitx = x + dx, hity = y + dy;
13413   int hitting_element = Tile[x][y];
13414   int touched_element;
13415
13416   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
13417     return;
13418
13419   touched_element = (IN_LEV_FIELD(hitx, hity) ?
13420                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
13421
13422   if (IN_LEV_FIELD(hitx, hity))
13423   {
13424     int opposite_direction = MV_DIR_OPPOSITE(direction);
13425     int hitting_side = direction;
13426     int touched_side = opposite_direction;
13427     boolean object_hit = (!IS_MOVING(hitx, hity) ||
13428                           MovDir[hitx][hity] != direction ||
13429                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
13430
13431     object_hit = TRUE;
13432
13433     if (object_hit)
13434     {
13435       CheckElementChangeBySide(x, y, hitting_element, touched_element,
13436                                CE_HITTING_X, touched_side);
13437
13438       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13439                                CE_HIT_BY_X, hitting_side);
13440
13441       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
13442                                CE_HIT_BY_SOMETHING, opposite_direction);
13443
13444       if (IS_PLAYER(hitx, hity))
13445       {
13446         /* use player element that is initially defined in the level playfield,
13447            not the player element that corresponds to the runtime player number
13448            (example: a level that contains EL_PLAYER_3 as the only player would
13449            incorrectly give EL_PLAYER_1 for "player->element_nr") */
13450         int player_element = PLAYERINFO(hitx, hity)->initial_element;
13451
13452         CheckElementChangeBySide(x, y, hitting_element, player_element,
13453                                  CE_HITTING_X, touched_side);
13454       }
13455     }
13456   }
13457
13458   // "hitting something" is also true when hitting the playfield border
13459   CheckElementChangeBySide(x, y, hitting_element, touched_element,
13460                            CE_HITTING_SOMETHING, direction);
13461 }
13462
13463 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
13464 {
13465   int i, kill_x = -1, kill_y = -1;
13466
13467   int bad_element = -1;
13468   static int test_xy[4][2] =
13469   {
13470     { 0, -1 },
13471     { -1, 0 },
13472     { +1, 0 },
13473     { 0, +1 }
13474   };
13475   static int test_dir[4] =
13476   {
13477     MV_UP,
13478     MV_LEFT,
13479     MV_RIGHT,
13480     MV_DOWN
13481   };
13482
13483   for (i = 0; i < NUM_DIRECTIONS; i++)
13484   {
13485     int test_x, test_y, test_move_dir, test_element;
13486
13487     test_x = good_x + test_xy[i][0];
13488     test_y = good_y + test_xy[i][1];
13489
13490     if (!IN_LEV_FIELD(test_x, test_y))
13491       continue;
13492
13493     test_move_dir =
13494       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13495
13496     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
13497
13498     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13499        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13500     */
13501     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
13502         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
13503     {
13504       kill_x = test_x;
13505       kill_y = test_y;
13506       bad_element = test_element;
13507
13508       break;
13509     }
13510   }
13511
13512   if (kill_x != -1 || kill_y != -1)
13513   {
13514     if (IS_PLAYER(good_x, good_y))
13515     {
13516       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
13517
13518       if (player->shield_deadly_time_left > 0 &&
13519           !IS_INDESTRUCTIBLE(bad_element))
13520         Bang(kill_x, kill_y);
13521       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
13522         KillPlayer(player);
13523     }
13524     else
13525       Bang(good_x, good_y);
13526   }
13527 }
13528
13529 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
13530 {
13531   int i, kill_x = -1, kill_y = -1;
13532   int bad_element = Tile[bad_x][bad_y];
13533   static int test_xy[4][2] =
13534   {
13535     { 0, -1 },
13536     { -1, 0 },
13537     { +1, 0 },
13538     { 0, +1 }
13539   };
13540   static int touch_dir[4] =
13541   {
13542     MV_LEFT | MV_RIGHT,
13543     MV_UP   | MV_DOWN,
13544     MV_UP   | MV_DOWN,
13545     MV_LEFT | MV_RIGHT
13546   };
13547   static int test_dir[4] =
13548   {
13549     MV_UP,
13550     MV_LEFT,
13551     MV_RIGHT,
13552     MV_DOWN
13553   };
13554
13555   if (bad_element == EL_EXPLOSION)      // skip just exploding bad things
13556     return;
13557
13558   for (i = 0; i < NUM_DIRECTIONS; i++)
13559   {
13560     int test_x, test_y, test_move_dir, test_element;
13561
13562     test_x = bad_x + test_xy[i][0];
13563     test_y = bad_y + test_xy[i][1];
13564
13565     if (!IN_LEV_FIELD(test_x, test_y))
13566       continue;
13567
13568     test_move_dir =
13569       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13570
13571     test_element = Tile[test_x][test_y];
13572
13573     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
13574        2nd case: DONT_TOUCH style bad thing does not move away from good thing
13575     */
13576     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
13577         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
13578     {
13579       // good thing is player or penguin that does not move away
13580       if (IS_PLAYER(test_x, test_y))
13581       {
13582         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13583
13584         if (bad_element == EL_ROBOT && player->is_moving)
13585           continue;     // robot does not kill player if he is moving
13586
13587         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
13588         {
13589           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
13590             continue;           // center and border element do not touch
13591         }
13592
13593         kill_x = test_x;
13594         kill_y = test_y;
13595
13596         break;
13597       }
13598       else if (test_element == EL_PENGUIN)
13599       {
13600         kill_x = test_x;
13601         kill_y = test_y;
13602
13603         break;
13604       }
13605     }
13606   }
13607
13608   if (kill_x != -1 || kill_y != -1)
13609   {
13610     if (IS_PLAYER(kill_x, kill_y))
13611     {
13612       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13613
13614       if (player->shield_deadly_time_left > 0 &&
13615           !IS_INDESTRUCTIBLE(bad_element))
13616         Bang(bad_x, bad_y);
13617       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13618         KillPlayer(player);
13619     }
13620     else
13621       Bang(kill_x, kill_y);
13622   }
13623 }
13624
13625 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
13626 {
13627   int bad_element = Tile[bad_x][bad_y];
13628   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
13629   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
13630   int test_x = bad_x + dx, test_y = bad_y + dy;
13631   int test_move_dir, test_element;
13632   int kill_x = -1, kill_y = -1;
13633
13634   if (!IN_LEV_FIELD(test_x, test_y))
13635     return;
13636
13637   test_move_dir =
13638     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
13639
13640   test_element = Tile[test_x][test_y];
13641
13642   if (test_move_dir != bad_move_dir)
13643   {
13644     // good thing can be player or penguin that does not move away
13645     if (IS_PLAYER(test_x, test_y))
13646     {
13647       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
13648
13649       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
13650          player as being hit when he is moving towards the bad thing, because
13651          the "get hit by" condition would be lost after the player stops) */
13652       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
13653         return;         // player moves away from bad thing
13654
13655       kill_x = test_x;
13656       kill_y = test_y;
13657     }
13658     else if (test_element == EL_PENGUIN)
13659     {
13660       kill_x = test_x;
13661       kill_y = test_y;
13662     }
13663   }
13664
13665   if (kill_x != -1 || kill_y != -1)
13666   {
13667     if (IS_PLAYER(kill_x, kill_y))
13668     {
13669       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
13670
13671       if (player->shield_deadly_time_left > 0 &&
13672           !IS_INDESTRUCTIBLE(bad_element))
13673         Bang(bad_x, bad_y);
13674       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
13675         KillPlayer(player);
13676     }
13677     else
13678       Bang(kill_x, kill_y);
13679   }
13680 }
13681
13682 void TestIfPlayerTouchesBadThing(int x, int y)
13683 {
13684   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13685 }
13686
13687 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
13688 {
13689   TestIfGoodThingHitsBadThing(x, y, move_dir);
13690 }
13691
13692 void TestIfBadThingTouchesPlayer(int x, int y)
13693 {
13694   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13695 }
13696
13697 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
13698 {
13699   TestIfBadThingHitsGoodThing(x, y, move_dir);
13700 }
13701
13702 void TestIfFriendTouchesBadThing(int x, int y)
13703 {
13704   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
13705 }
13706
13707 void TestIfBadThingTouchesFriend(int x, int y)
13708 {
13709   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
13710 }
13711
13712 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
13713 {
13714   int i, kill_x = bad_x, kill_y = bad_y;
13715   static int xy[4][2] =
13716   {
13717     { 0, -1 },
13718     { -1, 0 },
13719     { +1, 0 },
13720     { 0, +1 }
13721   };
13722
13723   for (i = 0; i < NUM_DIRECTIONS; i++)
13724   {
13725     int x, y, element;
13726
13727     x = bad_x + xy[i][0];
13728     y = bad_y + xy[i][1];
13729     if (!IN_LEV_FIELD(x, y))
13730       continue;
13731
13732     element = Tile[x][y];
13733     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13734         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13735     {
13736       kill_x = x;
13737       kill_y = y;
13738       break;
13739     }
13740   }
13741
13742   if (kill_x != bad_x || kill_y != bad_y)
13743     Bang(bad_x, bad_y);
13744 }
13745
13746 void KillPlayer(struct PlayerInfo *player)
13747 {
13748   int jx = player->jx, jy = player->jy;
13749
13750   if (!player->active)
13751     return;
13752
13753 #if 0
13754   Debug("game:playing:KillPlayer",
13755         "0: killed == %d, active == %d, reanimated == %d",
13756         player->killed, player->active, player->reanimated);
13757 #endif
13758
13759   /* the following code was introduced to prevent an infinite loop when calling
13760      -> Bang()
13761      -> CheckTriggeredElementChangeExt()
13762      -> ExecuteCustomElementAction()
13763      -> KillPlayer()
13764      -> (infinitely repeating the above sequence of function calls)
13765      which occurs when killing the player while having a CE with the setting
13766      "kill player X when explosion of <player X>"; the solution using a new
13767      field "player->killed" was chosen for backwards compatibility, although
13768      clever use of the fields "player->active" etc. would probably also work */
13769 #if 1
13770   if (player->killed)
13771     return;
13772 #endif
13773
13774   player->killed = TRUE;
13775
13776   // remove accessible field at the player's position
13777   Tile[jx][jy] = EL_EMPTY;
13778
13779   // deactivate shield (else Bang()/Explode() would not work right)
13780   player->shield_normal_time_left = 0;
13781   player->shield_deadly_time_left = 0;
13782
13783 #if 0
13784   Debug("game:playing:KillPlayer",
13785         "1: killed == %d, active == %d, reanimated == %d",
13786         player->killed, player->active, player->reanimated);
13787 #endif
13788
13789   Bang(jx, jy);
13790
13791 #if 0
13792   Debug("game:playing:KillPlayer",
13793         "2: killed == %d, active == %d, reanimated == %d",
13794         player->killed, player->active, player->reanimated);
13795 #endif
13796
13797   if (player->reanimated)       // killed player may have been reanimated
13798     player->killed = player->reanimated = FALSE;
13799   else
13800     BuryPlayer(player);
13801 }
13802
13803 static void KillPlayerUnlessEnemyProtected(int x, int y)
13804 {
13805   if (!PLAYER_ENEMY_PROTECTED(x, y))
13806     KillPlayer(PLAYERINFO(x, y));
13807 }
13808
13809 static void KillPlayerUnlessExplosionProtected(int x, int y)
13810 {
13811   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13812     KillPlayer(PLAYERINFO(x, y));
13813 }
13814
13815 void BuryPlayer(struct PlayerInfo *player)
13816 {
13817   int jx = player->jx, jy = player->jy;
13818
13819   if (!player->active)
13820     return;
13821
13822   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13823   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13824
13825   RemovePlayer(player);
13826
13827   player->buried = TRUE;
13828
13829   if (game.all_players_gone)
13830     game.GameOver = TRUE;
13831 }
13832
13833 void RemovePlayer(struct PlayerInfo *player)
13834 {
13835   int jx = player->jx, jy = player->jy;
13836   int i, found = FALSE;
13837
13838   player->present = FALSE;
13839   player->active = FALSE;
13840
13841   // required for some CE actions (even if the player is not active anymore)
13842   player->MovPos = 0;
13843
13844   if (!ExplodeField[jx][jy])
13845     StorePlayer[jx][jy] = 0;
13846
13847   if (player->is_moving)
13848     TEST_DrawLevelField(player->last_jx, player->last_jy);
13849
13850   for (i = 0; i < MAX_PLAYERS; i++)
13851     if (stored_player[i].active)
13852       found = TRUE;
13853
13854   if (!found)
13855   {
13856     game.all_players_gone = TRUE;
13857     game.GameOver = TRUE;
13858   }
13859
13860   game.exit_x = game.robot_wheel_x = jx;
13861   game.exit_y = game.robot_wheel_y = jy;
13862 }
13863
13864 void ExitPlayer(struct PlayerInfo *player)
13865 {
13866   DrawPlayer(player);   // needed here only to cleanup last field
13867   RemovePlayer(player);
13868
13869   if (game.players_still_needed > 0)
13870     game.players_still_needed--;
13871 }
13872
13873 static void SetFieldForSnapping(int x, int y, int element, int direction,
13874                                 int player_index_bit)
13875 {
13876   struct ElementInfo *ei = &element_info[element];
13877   int direction_bit = MV_DIR_TO_BIT(direction);
13878   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13879   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13880                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13881
13882   Tile[x][y] = EL_ELEMENT_SNAPPING;
13883   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13884   MovDir[x][y] = direction;
13885   Store[x][y] = element;
13886   Store2[x][y] = player_index_bit;
13887
13888   ResetGfxAnimation(x, y);
13889
13890   GfxElement[x][y] = element;
13891   GfxAction[x][y] = action;
13892   GfxDir[x][y] = direction;
13893   GfxFrame[x][y] = -1;
13894 }
13895
13896 static void TestFieldAfterSnapping(int x, int y, int element, int direction,
13897                                    int player_index_bit)
13898 {
13899   TestIfElementTouchesCustomElement(x, y);      // for empty space
13900
13901   if (level.finish_dig_collect)
13902   {
13903     int dig_side = MV_DIR_OPPOSITE(direction);
13904
13905     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13906                                         player_index_bit, dig_side);
13907   }
13908 }
13909
13910 /*
13911   =============================================================================
13912   checkDiagonalPushing()
13913   -----------------------------------------------------------------------------
13914   check if diagonal input device direction results in pushing of object
13915   (by checking if the alternative direction is walkable, diggable, ...)
13916   =============================================================================
13917 */
13918
13919 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13920                                     int x, int y, int real_dx, int real_dy)
13921 {
13922   int jx, jy, dx, dy, xx, yy;
13923
13924   if (real_dx == 0 || real_dy == 0)     // no diagonal direction => push
13925     return TRUE;
13926
13927   // diagonal direction: check alternative direction
13928   jx = player->jx;
13929   jy = player->jy;
13930   dx = x - jx;
13931   dy = y - jy;
13932   xx = jx + (dx == 0 ? real_dx : 0);
13933   yy = jy + (dy == 0 ? real_dy : 0);
13934
13935   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Tile[xx][yy]));
13936 }
13937
13938 /*
13939   =============================================================================
13940   DigField()
13941   -----------------------------------------------------------------------------
13942   x, y:                 field next to player (non-diagonal) to try to dig to
13943   real_dx, real_dy:     direction as read from input device (can be diagonal)
13944   =============================================================================
13945 */
13946
13947 static int DigField(struct PlayerInfo *player,
13948                     int oldx, int oldy, int x, int y,
13949                     int real_dx, int real_dy, int mode)
13950 {
13951   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13952   boolean player_was_pushing = player->is_pushing;
13953   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13954   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13955   int jx = oldx, jy = oldy;
13956   int dx = x - jx, dy = y - jy;
13957   int nextx = x + dx, nexty = y + dy;
13958   int move_direction = (dx == -1 ? MV_LEFT  :
13959                         dx == +1 ? MV_RIGHT :
13960                         dy == -1 ? MV_UP    :
13961                         dy == +1 ? MV_DOWN  : MV_NONE);
13962   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13963   int dig_side = MV_DIR_OPPOSITE(move_direction);
13964   int old_element = Tile[jx][jy];
13965   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13966   int collect_count;
13967
13968   if (is_player)                // function can also be called by EL_PENGUIN
13969   {
13970     if (player->MovPos == 0)
13971     {
13972       player->is_digging = FALSE;
13973       player->is_collecting = FALSE;
13974     }
13975
13976     if (player->MovPos == 0)    // last pushing move finished
13977       player->is_pushing = FALSE;
13978
13979     if (mode == DF_NO_PUSH)     // player just stopped pushing
13980     {
13981       player->is_switching = FALSE;
13982       player->push_delay = -1;
13983
13984       return MP_NO_ACTION;
13985     }
13986   }
13987
13988   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13989     old_element = Back[jx][jy];
13990
13991   // in case of element dropped at player position, check background
13992   else if (Back[jx][jy] != EL_EMPTY &&
13993            game.engine_version >= VERSION_IDENT(2,2,0,0))
13994     old_element = Back[jx][jy];
13995
13996   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13997     return MP_NO_ACTION;        // field has no opening in this direction
13998
13999   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
14000     return MP_NO_ACTION;        // field has no opening in this direction
14001
14002   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
14003   {
14004     SplashAcid(x, y);
14005
14006     Tile[jx][jy] = player->artwork_element;
14007     InitMovingField(jx, jy, MV_DOWN);
14008     Store[jx][jy] = EL_ACID;
14009     ContinueMoving(jx, jy);
14010     BuryPlayer(player);
14011
14012     return MP_DONT_RUN_INTO;
14013   }
14014
14015   if (player_can_move && DONT_RUN_INTO(element))
14016   {
14017     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
14018
14019     return MP_DONT_RUN_INTO;
14020   }
14021
14022   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
14023     return MP_NO_ACTION;
14024
14025   collect_count = element_info[element].collect_count_initial;
14026
14027   if (!is_player && !IS_COLLECTIBLE(element))   // penguin cannot collect it
14028     return MP_NO_ACTION;
14029
14030   if (game.engine_version < VERSION_IDENT(2,2,0,0))
14031     player_can_move = player_can_move_or_snap;
14032
14033   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
14034       game.engine_version >= VERSION_IDENT(2,2,0,0))
14035   {
14036     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
14037                                player->index_bit, dig_side);
14038     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14039                                         player->index_bit, dig_side);
14040
14041     if (element == EL_DC_LANDMINE)
14042       Bang(x, y);
14043
14044     if (Tile[x][y] != element)          // field changed by snapping
14045       return MP_ACTION;
14046
14047     return MP_NO_ACTION;
14048   }
14049
14050   if (player->gravity && is_player && !player->is_auto_moving &&
14051       canFallDown(player) && move_direction != MV_DOWN &&
14052       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
14053     return MP_NO_ACTION;        // player cannot walk here due to gravity
14054
14055   if (player_can_move &&
14056       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
14057   {
14058     int sound_element = SND_ELEMENT(element);
14059     int sound_action = ACTION_WALKING;
14060
14061     if (IS_RND_GATE(element))
14062     {
14063       if (!player->key[RND_GATE_NR(element)])
14064         return MP_NO_ACTION;
14065     }
14066     else if (IS_RND_GATE_GRAY(element))
14067     {
14068       if (!player->key[RND_GATE_GRAY_NR(element)])
14069         return MP_NO_ACTION;
14070     }
14071     else if (IS_RND_GATE_GRAY_ACTIVE(element))
14072     {
14073       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
14074         return MP_NO_ACTION;
14075     }
14076     else if (element == EL_EXIT_OPEN ||
14077              element == EL_EM_EXIT_OPEN ||
14078              element == EL_EM_EXIT_OPENING ||
14079              element == EL_STEEL_EXIT_OPEN ||
14080              element == EL_EM_STEEL_EXIT_OPEN ||
14081              element == EL_EM_STEEL_EXIT_OPENING ||
14082              element == EL_SP_EXIT_OPEN ||
14083              element == EL_SP_EXIT_OPENING)
14084     {
14085       sound_action = ACTION_PASSING;    // player is passing exit
14086     }
14087     else if (element == EL_EMPTY)
14088     {
14089       sound_action = ACTION_MOVING;             // nothing to walk on
14090     }
14091
14092     // play sound from background or player, whatever is available
14093     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
14094       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
14095     else
14096       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
14097   }
14098   else if (player_can_move &&
14099            IS_PASSABLE(element) && canPassField(x, y, move_direction))
14100   {
14101     if (!ACCESS_FROM(element, opposite_direction))
14102       return MP_NO_ACTION;      // field not accessible from this direction
14103
14104     if (CAN_MOVE(element))      // only fixed elements can be passed!
14105       return MP_NO_ACTION;
14106
14107     if (IS_EM_GATE(element))
14108     {
14109       if (!player->key[EM_GATE_NR(element)])
14110         return MP_NO_ACTION;
14111     }
14112     else if (IS_EM_GATE_GRAY(element))
14113     {
14114       if (!player->key[EM_GATE_GRAY_NR(element)])
14115         return MP_NO_ACTION;
14116     }
14117     else if (IS_EM_GATE_GRAY_ACTIVE(element))
14118     {
14119       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
14120         return MP_NO_ACTION;
14121     }
14122     else if (IS_EMC_GATE(element))
14123     {
14124       if (!player->key[EMC_GATE_NR(element)])
14125         return MP_NO_ACTION;
14126     }
14127     else if (IS_EMC_GATE_GRAY(element))
14128     {
14129       if (!player->key[EMC_GATE_GRAY_NR(element)])
14130         return MP_NO_ACTION;
14131     }
14132     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
14133     {
14134       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
14135         return MP_NO_ACTION;
14136     }
14137     else if (element == EL_DC_GATE_WHITE ||
14138              element == EL_DC_GATE_WHITE_GRAY ||
14139              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
14140     {
14141       if (player->num_white_keys == 0)
14142         return MP_NO_ACTION;
14143
14144       player->num_white_keys--;
14145     }
14146     else if (IS_SP_PORT(element))
14147     {
14148       if (element == EL_SP_GRAVITY_PORT_LEFT ||
14149           element == EL_SP_GRAVITY_PORT_RIGHT ||
14150           element == EL_SP_GRAVITY_PORT_UP ||
14151           element == EL_SP_GRAVITY_PORT_DOWN)
14152         player->gravity = !player->gravity;
14153       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
14154                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
14155                element == EL_SP_GRAVITY_ON_PORT_UP ||
14156                element == EL_SP_GRAVITY_ON_PORT_DOWN)
14157         player->gravity = TRUE;
14158       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
14159                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
14160                element == EL_SP_GRAVITY_OFF_PORT_UP ||
14161                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
14162         player->gravity = FALSE;
14163     }
14164
14165     // automatically move to the next field with double speed
14166     player->programmed_action = move_direction;
14167
14168     if (player->move_delay_reset_counter == 0)
14169     {
14170       player->move_delay_reset_counter = 2;     // two double speed steps
14171
14172       DOUBLE_PLAYER_SPEED(player);
14173     }
14174
14175     PlayLevelSoundAction(x, y, ACTION_PASSING);
14176   }
14177   else if (player_can_move_or_snap && IS_DIGGABLE(element))
14178   {
14179     RemoveField(x, y);
14180
14181     if (mode != DF_SNAP)
14182     {
14183       GfxElement[x][y] = GFX_ELEMENT(element);
14184       player->is_digging = TRUE;
14185     }
14186
14187     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14188
14189     // use old behaviour for old levels (digging)
14190     if (!level.finish_dig_collect)
14191     {
14192       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
14193                                           player->index_bit, dig_side);
14194
14195       // if digging triggered player relocation, finish digging tile
14196       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14197         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14198     }
14199
14200     if (mode == DF_SNAP)
14201     {
14202       if (level.block_snap_field)
14203         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14204       else
14205         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14206
14207       // use old behaviour for old levels (snapping)
14208       if (!level.finish_dig_collect)
14209         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14210                                             player->index_bit, dig_side);
14211     }
14212   }
14213   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
14214   {
14215     RemoveField(x, y);
14216
14217     if (is_player && mode != DF_SNAP)
14218     {
14219       GfxElement[x][y] = element;
14220       player->is_collecting = TRUE;
14221     }
14222
14223     if (element == EL_SPEED_PILL)
14224     {
14225       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
14226     }
14227     else if (element == EL_EXTRA_TIME && level.time > 0)
14228     {
14229       TimeLeft += level.extra_time;
14230
14231       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14232
14233       DisplayGameControlValues();
14234     }
14235     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
14236     {
14237       player->shield_normal_time_left += level.shield_normal_time;
14238       if (element == EL_SHIELD_DEADLY)
14239         player->shield_deadly_time_left += level.shield_deadly_time;
14240     }
14241     else if (element == EL_DYNAMITE ||
14242              element == EL_EM_DYNAMITE ||
14243              element == EL_SP_DISK_RED)
14244     {
14245       if (player->inventory_size < MAX_INVENTORY_SIZE)
14246         player->inventory_element[player->inventory_size++] = element;
14247
14248       DrawGameDoorValues();
14249     }
14250     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
14251     {
14252       player->dynabomb_count++;
14253       player->dynabombs_left++;
14254     }
14255     else if (element == EL_DYNABOMB_INCREASE_SIZE)
14256     {
14257       player->dynabomb_size++;
14258     }
14259     else if (element == EL_DYNABOMB_INCREASE_POWER)
14260     {
14261       player->dynabomb_xl = TRUE;
14262     }
14263     else if (IS_KEY(element))
14264     {
14265       player->key[KEY_NR(element)] = TRUE;
14266
14267       DrawGameDoorValues();
14268     }
14269     else if (element == EL_DC_KEY_WHITE)
14270     {
14271       player->num_white_keys++;
14272
14273       // display white keys?
14274       // DrawGameDoorValues();
14275     }
14276     else if (IS_ENVELOPE(element))
14277     {
14278       player->show_envelope = element;
14279     }
14280     else if (element == EL_EMC_LENSES)
14281     {
14282       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
14283
14284       RedrawAllInvisibleElementsForLenses();
14285     }
14286     else if (element == EL_EMC_MAGNIFIER)
14287     {
14288       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
14289
14290       RedrawAllInvisibleElementsForMagnifier();
14291     }
14292     else if (IS_DROPPABLE(element) ||
14293              IS_THROWABLE(element))     // can be collected and dropped
14294     {
14295       int i;
14296
14297       if (collect_count == 0)
14298         player->inventory_infinite_element = element;
14299       else
14300         for (i = 0; i < collect_count; i++)
14301           if (player->inventory_size < MAX_INVENTORY_SIZE)
14302             player->inventory_element[player->inventory_size++] = element;
14303
14304       DrawGameDoorValues();
14305     }
14306     else if (collect_count > 0)
14307     {
14308       game.gems_still_needed -= collect_count;
14309       if (game.gems_still_needed < 0)
14310         game.gems_still_needed = 0;
14311
14312       game.snapshot.collected_item = TRUE;
14313
14314       game_panel_controls[GAME_PANEL_GEMS].value = game.gems_still_needed;
14315
14316       DisplayGameControlValues();
14317     }
14318
14319     RaiseScoreElement(element);
14320     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14321
14322     // use old behaviour for old levels (collecting)
14323     if (!level.finish_dig_collect && is_player)
14324     {
14325       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
14326                                           player->index_bit, dig_side);
14327
14328       // if collecting triggered player relocation, finish collecting tile
14329       if (mode == DF_DIG && (player->jx != jx || player->jy != jy))
14330         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14331     }
14332
14333     if (mode == DF_SNAP)
14334     {
14335       if (level.block_snap_field)
14336         SetFieldForSnapping(x, y, element, move_direction, player->index_bit);
14337       else
14338         TestFieldAfterSnapping(x, y, element, move_direction, player->index_bit);
14339
14340       // use old behaviour for old levels (snapping)
14341       if (!level.finish_dig_collect)
14342         CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
14343                                             player->index_bit, dig_side);
14344     }
14345   }
14346   else if (player_can_move_or_snap && IS_PUSHABLE(element))
14347   {
14348     if (mode == DF_SNAP && element != EL_BD_ROCK)
14349       return MP_NO_ACTION;
14350
14351     if (CAN_FALL(element) && dy)
14352       return MP_NO_ACTION;
14353
14354     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
14355         !(element == EL_SPRING && level.use_spring_bug))
14356       return MP_NO_ACTION;
14357
14358     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
14359         ((move_direction & MV_VERTICAL &&
14360           ((element_info[element].move_pattern & MV_LEFT &&
14361             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
14362            (element_info[element].move_pattern & MV_RIGHT &&
14363             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
14364          (move_direction & MV_HORIZONTAL &&
14365           ((element_info[element].move_pattern & MV_UP &&
14366             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
14367            (element_info[element].move_pattern & MV_DOWN &&
14368             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
14369       return MP_NO_ACTION;
14370
14371     // do not push elements already moving away faster than player
14372     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
14373         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
14374       return MP_NO_ACTION;
14375
14376     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
14377     {
14378       if (player->push_delay_value == -1 || !player_was_pushing)
14379         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14380     }
14381     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14382     {
14383       if (player->push_delay_value == -1)
14384         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14385     }
14386     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
14387     {
14388       if (!player->is_pushing)
14389         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14390     }
14391
14392     player->is_pushing = TRUE;
14393     player->is_active = TRUE;
14394
14395     if (!(IN_LEV_FIELD(nextx, nexty) &&
14396           (IS_FREE(nextx, nexty) ||
14397            (IS_SB_ELEMENT(element) &&
14398             Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
14399            (IS_CUSTOM_ELEMENT(element) &&
14400             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
14401       return MP_NO_ACTION;
14402
14403     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
14404       return MP_NO_ACTION;
14405
14406     if (player->push_delay == -1)       // new pushing; restart delay
14407       player->push_delay = 0;
14408
14409     if (player->push_delay < player->push_delay_value &&
14410         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
14411         element != EL_SPRING && element != EL_BALLOON)
14412     {
14413       // make sure that there is no move delay before next try to push
14414       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
14415         player->move_delay = 0;
14416
14417       return MP_NO_ACTION;
14418     }
14419
14420     if (IS_CUSTOM_ELEMENT(element) &&
14421         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
14422     {
14423       if (!DigFieldByCE(nextx, nexty, element))
14424         return MP_NO_ACTION;
14425     }
14426
14427     if (IS_SB_ELEMENT(element))
14428     {
14429       boolean sokoban_task_solved = FALSE;
14430
14431       if (element == EL_SOKOBAN_FIELD_FULL)
14432       {
14433         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
14434
14435         IncrementSokobanFieldsNeeded();
14436         IncrementSokobanObjectsNeeded();
14437       }
14438
14439       if (Tile[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
14440       {
14441         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
14442
14443         DecrementSokobanFieldsNeeded();
14444         DecrementSokobanObjectsNeeded();
14445
14446         // sokoban object was pushed from empty field to sokoban field
14447         if (Back[x][y] == EL_EMPTY)
14448           sokoban_task_solved = TRUE;
14449       }
14450
14451       Tile[x][y] = EL_SOKOBAN_OBJECT;
14452
14453       if (Back[x][y] == Back[nextx][nexty])
14454         PlayLevelSoundAction(x, y, ACTION_PUSHING);
14455       else if (Back[x][y] != 0)
14456         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
14457                                     ACTION_EMPTYING);
14458       else
14459         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
14460                                     ACTION_FILLING);
14461
14462       if (sokoban_task_solved &&
14463           game.sokoban_fields_still_needed == 0 &&
14464           game.sokoban_objects_still_needed == 0 &&
14465           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
14466       {
14467         game.players_still_needed = 0;
14468
14469         LevelSolved();
14470
14471         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
14472       }
14473     }
14474     else
14475       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14476
14477     InitMovingField(x, y, move_direction);
14478     GfxAction[x][y] = ACTION_PUSHING;
14479
14480     if (mode == DF_SNAP)
14481       ContinueMoving(x, y);
14482     else
14483       MovPos[x][y] = (dx != 0 ? dx : dy);
14484
14485     Pushed[x][y] = TRUE;
14486     Pushed[nextx][nexty] = TRUE;
14487
14488     if (game.engine_version < VERSION_IDENT(2,2,0,7))
14489       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
14490     else
14491       player->push_delay_value = -1;    // get new value later
14492
14493     // check for element change _after_ element has been pushed
14494     if (game.use_change_when_pushing_bug)
14495     {
14496       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
14497                                  player->index_bit, dig_side);
14498       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
14499                                           player->index_bit, dig_side);
14500     }
14501   }
14502   else if (IS_SWITCHABLE(element))
14503   {
14504     if (PLAYER_SWITCHING(player, x, y))
14505     {
14506       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14507                                           player->index_bit, dig_side);
14508
14509       return MP_ACTION;
14510     }
14511
14512     player->is_switching = TRUE;
14513     player->switch_x = x;
14514     player->switch_y = y;
14515
14516     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14517
14518     if (element == EL_ROBOT_WHEEL)
14519     {
14520       Tile[x][y] = EL_ROBOT_WHEEL_ACTIVE;
14521
14522       game.robot_wheel_x = x;
14523       game.robot_wheel_y = y;
14524       game.robot_wheel_active = TRUE;
14525
14526       TEST_DrawLevelField(x, y);
14527     }
14528     else if (element == EL_SP_TERMINAL)
14529     {
14530       int xx, yy;
14531
14532       SCAN_PLAYFIELD(xx, yy)
14533       {
14534         if (Tile[xx][yy] == EL_SP_DISK_YELLOW)
14535         {
14536           Bang(xx, yy);
14537         }
14538         else if (Tile[xx][yy] == EL_SP_TERMINAL)
14539         {
14540           Tile[xx][yy] = EL_SP_TERMINAL_ACTIVE;
14541
14542           ResetGfxAnimation(xx, yy);
14543           TEST_DrawLevelField(xx, yy);
14544         }
14545       }
14546     }
14547     else if (IS_BELT_SWITCH(element))
14548     {
14549       ToggleBeltSwitch(x, y);
14550     }
14551     else if (element == EL_SWITCHGATE_SWITCH_UP ||
14552              element == EL_SWITCHGATE_SWITCH_DOWN ||
14553              element == EL_DC_SWITCHGATE_SWITCH_UP ||
14554              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
14555     {
14556       ToggleSwitchgateSwitch(x, y);
14557     }
14558     else if (element == EL_LIGHT_SWITCH ||
14559              element == EL_LIGHT_SWITCH_ACTIVE)
14560     {
14561       ToggleLightSwitch(x, y);
14562     }
14563     else if (element == EL_TIMEGATE_SWITCH ||
14564              element == EL_DC_TIMEGATE_SWITCH)
14565     {
14566       ActivateTimegateSwitch(x, y);
14567     }
14568     else if (element == EL_BALLOON_SWITCH_LEFT  ||
14569              element == EL_BALLOON_SWITCH_RIGHT ||
14570              element == EL_BALLOON_SWITCH_UP    ||
14571              element == EL_BALLOON_SWITCH_DOWN  ||
14572              element == EL_BALLOON_SWITCH_NONE  ||
14573              element == EL_BALLOON_SWITCH_ANY)
14574     {
14575       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
14576                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
14577                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
14578                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
14579                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
14580                              move_direction);
14581     }
14582     else if (element == EL_LAMP)
14583     {
14584       Tile[x][y] = EL_LAMP_ACTIVE;
14585       game.lights_still_needed--;
14586
14587       ResetGfxAnimation(x, y);
14588       TEST_DrawLevelField(x, y);
14589     }
14590     else if (element == EL_TIME_ORB_FULL)
14591     {
14592       Tile[x][y] = EL_TIME_ORB_EMPTY;
14593
14594       if (level.time > 0 || level.use_time_orb_bug)
14595       {
14596         TimeLeft += level.time_orb_time;
14597         game.no_time_limit = FALSE;
14598
14599         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
14600
14601         DisplayGameControlValues();
14602       }
14603
14604       ResetGfxAnimation(x, y);
14605       TEST_DrawLevelField(x, y);
14606     }
14607     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
14608              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14609     {
14610       int xx, yy;
14611
14612       game.ball_active = !game.ball_active;
14613
14614       SCAN_PLAYFIELD(xx, yy)
14615       {
14616         int e = Tile[xx][yy];
14617
14618         if (game.ball_active)
14619         {
14620           if (e == EL_EMC_MAGIC_BALL)
14621             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
14622           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
14623             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
14624         }
14625         else
14626         {
14627           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
14628             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
14629           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
14630             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
14631         }
14632       }
14633     }
14634
14635     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14636                                         player->index_bit, dig_side);
14637
14638     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14639                                         player->index_bit, dig_side);
14640
14641     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14642                                         player->index_bit, dig_side);
14643
14644     return MP_ACTION;
14645   }
14646   else
14647   {
14648     if (!PLAYER_SWITCHING(player, x, y))
14649     {
14650       player->is_switching = TRUE;
14651       player->switch_x = x;
14652       player->switch_y = y;
14653
14654       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
14655                                  player->index_bit, dig_side);
14656       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
14657                                           player->index_bit, dig_side);
14658
14659       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
14660                                  player->index_bit, dig_side);
14661       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
14662                                           player->index_bit, dig_side);
14663     }
14664
14665     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
14666                                player->index_bit, dig_side);
14667     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
14668                                         player->index_bit, dig_side);
14669
14670     return MP_NO_ACTION;
14671   }
14672
14673   player->push_delay = -1;
14674
14675   if (is_player)                // function can also be called by EL_PENGUIN
14676   {
14677     if (Tile[x][y] != element)          // really digged/collected something
14678     {
14679       player->is_collecting = !player->is_digging;
14680       player->is_active = TRUE;
14681
14682       player->last_removed_element = element;
14683     }
14684   }
14685
14686   return MP_MOVING;
14687 }
14688
14689 static boolean DigFieldByCE(int x, int y, int digging_element)
14690 {
14691   int element = Tile[x][y];
14692
14693   if (!IS_FREE(x, y))
14694   {
14695     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
14696                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
14697                   ACTION_BREAKING);
14698
14699     // no element can dig solid indestructible elements
14700     if (IS_INDESTRUCTIBLE(element) &&
14701         !IS_DIGGABLE(element) &&
14702         !IS_COLLECTIBLE(element))
14703       return FALSE;
14704
14705     if (AmoebaNr[x][y] &&
14706         (element == EL_AMOEBA_FULL ||
14707          element == EL_BD_AMOEBA ||
14708          element == EL_AMOEBA_GROWING))
14709     {
14710       AmoebaCnt[AmoebaNr[x][y]]--;
14711       AmoebaCnt2[AmoebaNr[x][y]]--;
14712     }
14713
14714     if (IS_MOVING(x, y))
14715       RemoveMovingField(x, y);
14716     else
14717     {
14718       RemoveField(x, y);
14719       TEST_DrawLevelField(x, y);
14720     }
14721
14722     // if digged element was about to explode, prevent the explosion
14723     ExplodeField[x][y] = EX_TYPE_NONE;
14724
14725     PlayLevelSoundAction(x, y, action);
14726   }
14727
14728   Store[x][y] = EL_EMPTY;
14729
14730   // this makes it possible to leave the removed element again
14731   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
14732     Store[x][y] = element;
14733
14734   return TRUE;
14735 }
14736
14737 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
14738 {
14739   int jx = player->jx, jy = player->jy;
14740   int x = jx + dx, y = jy + dy;
14741   int snap_direction = (dx == -1 ? MV_LEFT  :
14742                         dx == +1 ? MV_RIGHT :
14743                         dy == -1 ? MV_UP    :
14744                         dy == +1 ? MV_DOWN  : MV_NONE);
14745   boolean can_continue_snapping = (level.continuous_snapping &&
14746                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
14747
14748   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
14749     return FALSE;
14750
14751   if (!player->active || !IN_LEV_FIELD(x, y))
14752     return FALSE;
14753
14754   if (dx && dy)
14755     return FALSE;
14756
14757   if (!dx && !dy)
14758   {
14759     if (player->MovPos == 0)
14760       player->is_pushing = FALSE;
14761
14762     player->is_snapping = FALSE;
14763
14764     if (player->MovPos == 0)
14765     {
14766       player->is_moving = FALSE;
14767       player->is_digging = FALSE;
14768       player->is_collecting = FALSE;
14769     }
14770
14771     return FALSE;
14772   }
14773
14774   // prevent snapping with already pressed snap key when not allowed
14775   if (player->is_snapping && !can_continue_snapping)
14776     return FALSE;
14777
14778   player->MovDir = snap_direction;
14779
14780   if (player->MovPos == 0)
14781   {
14782     player->is_moving = FALSE;
14783     player->is_digging = FALSE;
14784     player->is_collecting = FALSE;
14785   }
14786
14787   player->is_dropping = FALSE;
14788   player->is_dropping_pressed = FALSE;
14789   player->drop_pressed_delay = 0;
14790
14791   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
14792     return FALSE;
14793
14794   player->is_snapping = TRUE;
14795   player->is_active = TRUE;
14796
14797   if (player->MovPos == 0)
14798   {
14799     player->is_moving = FALSE;
14800     player->is_digging = FALSE;
14801     player->is_collecting = FALSE;
14802   }
14803
14804   if (player->MovPos != 0)      // prevent graphic bugs in versions < 2.2.0
14805     TEST_DrawLevelField(player->last_jx, player->last_jy);
14806
14807   TEST_DrawLevelField(x, y);
14808
14809   return TRUE;
14810 }
14811
14812 static boolean DropElement(struct PlayerInfo *player)
14813 {
14814   int old_element, new_element;
14815   int dropx = player->jx, dropy = player->jy;
14816   int drop_direction = player->MovDir;
14817   int drop_side = drop_direction;
14818   int drop_element = get_next_dropped_element(player);
14819
14820   /* do not drop an element on top of another element; when holding drop key
14821      pressed without moving, dropped element must move away before the next
14822      element can be dropped (this is especially important if the next element
14823      is dynamite, which can be placed on background for historical reasons) */
14824   if (PLAYER_DROPPING(player, dropx, dropy) && Tile[dropx][dropy] != EL_EMPTY)
14825     return MP_ACTION;
14826
14827   if (IS_THROWABLE(drop_element))
14828   {
14829     dropx += GET_DX_FROM_DIR(drop_direction);
14830     dropy += GET_DY_FROM_DIR(drop_direction);
14831
14832     if (!IN_LEV_FIELD(dropx, dropy))
14833       return FALSE;
14834   }
14835
14836   old_element = Tile[dropx][dropy];     // old element at dropping position
14837   new_element = drop_element;           // default: no change when dropping
14838
14839   // check if player is active, not moving and ready to drop
14840   if (!player->active || player->MovPos || player->drop_delay > 0)
14841     return FALSE;
14842
14843   // check if player has anything that can be dropped
14844   if (new_element == EL_UNDEFINED)
14845     return FALSE;
14846
14847   // only set if player has anything that can be dropped
14848   player->is_dropping_pressed = TRUE;
14849
14850   // check if drop key was pressed long enough for EM style dynamite
14851   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14852     return FALSE;
14853
14854   // check if anything can be dropped at the current position
14855   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14856     return FALSE;
14857
14858   // collected custom elements can only be dropped on empty fields
14859   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14860     return FALSE;
14861
14862   if (old_element != EL_EMPTY)
14863     Back[dropx][dropy] = old_element;   // store old element on this field
14864
14865   ResetGfxAnimation(dropx, dropy);
14866   ResetRandomAnimationValue(dropx, dropy);
14867
14868   if (player->inventory_size > 0 ||
14869       player->inventory_infinite_element != EL_UNDEFINED)
14870   {
14871     if (player->inventory_size > 0)
14872     {
14873       player->inventory_size--;
14874
14875       DrawGameDoorValues();
14876
14877       if (new_element == EL_DYNAMITE)
14878         new_element = EL_DYNAMITE_ACTIVE;
14879       else if (new_element == EL_EM_DYNAMITE)
14880         new_element = EL_EM_DYNAMITE_ACTIVE;
14881       else if (new_element == EL_SP_DISK_RED)
14882         new_element = EL_SP_DISK_RED_ACTIVE;
14883     }
14884
14885     Tile[dropx][dropy] = new_element;
14886
14887     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14888       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14889                           el2img(Tile[dropx][dropy]), 0);
14890
14891     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14892
14893     // needed if previous element just changed to "empty" in the last frame
14894     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14895
14896     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14897                                player->index_bit, drop_side);
14898     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14899                                         CE_PLAYER_DROPS_X,
14900                                         player->index_bit, drop_side);
14901
14902     TestIfElementTouchesCustomElement(dropx, dropy);
14903   }
14904   else          // player is dropping a dyna bomb
14905   {
14906     player->dynabombs_left--;
14907
14908     Tile[dropx][dropy] = new_element;
14909
14910     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14911       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14912                           el2img(Tile[dropx][dropy]), 0);
14913
14914     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14915   }
14916
14917   if (Tile[dropx][dropy] == new_element) // uninitialized unless CE change
14918     InitField_WithBug1(dropx, dropy, FALSE);
14919
14920   new_element = Tile[dropx][dropy];     // element might have changed
14921
14922   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14923       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14924   {
14925     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14926       MovDir[dropx][dropy] = drop_direction;
14927
14928     ChangeCount[dropx][dropy] = 0;      // allow at least one more change
14929
14930     // do not cause impact style collision by dropping elements that can fall
14931     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14932   }
14933
14934   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14935   player->is_dropping = TRUE;
14936
14937   player->drop_pressed_delay = 0;
14938   player->is_dropping_pressed = FALSE;
14939
14940   player->drop_x = dropx;
14941   player->drop_y = dropy;
14942
14943   return TRUE;
14944 }
14945
14946 // ----------------------------------------------------------------------------
14947 // game sound playing functions
14948 // ----------------------------------------------------------------------------
14949
14950 static int *loop_sound_frame = NULL;
14951 static int *loop_sound_volume = NULL;
14952
14953 void InitPlayLevelSound(void)
14954 {
14955   int num_sounds = getSoundListSize();
14956
14957   checked_free(loop_sound_frame);
14958   checked_free(loop_sound_volume);
14959
14960   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14961   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14962 }
14963
14964 static void PlayLevelSound(int x, int y, int nr)
14965 {
14966   int sx = SCREENX(x), sy = SCREENY(y);
14967   int volume, stereo_position;
14968   int max_distance = 8;
14969   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14970
14971   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14972       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14973     return;
14974
14975   if (!IN_LEV_FIELD(x, y) ||
14976       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14977       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14978     return;
14979
14980   volume = SOUND_MAX_VOLUME;
14981
14982   if (!IN_SCR_FIELD(sx, sy))
14983   {
14984     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14985     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14986
14987     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14988   }
14989
14990   stereo_position = (SOUND_MAX_LEFT +
14991                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14992                      (SCR_FIELDX + 2 * max_distance));
14993
14994   if (IS_LOOP_SOUND(nr))
14995   {
14996     /* This assures that quieter loop sounds do not overwrite louder ones,
14997        while restarting sound volume comparison with each new game frame. */
14998
14999     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
15000       return;
15001
15002     loop_sound_volume[nr] = volume;
15003     loop_sound_frame[nr] = FrameCounter;
15004   }
15005
15006   PlaySoundExt(nr, volume, stereo_position, type);
15007 }
15008
15009 static void PlayLevelSoundNearest(int x, int y, int sound_action)
15010 {
15011   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
15012                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
15013                  y < LEVELY(BY1) ? LEVELY(BY1) :
15014                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
15015                  sound_action);
15016 }
15017
15018 static void PlayLevelSoundAction(int x, int y, int action)
15019 {
15020   PlayLevelSoundElementAction(x, y, Tile[x][y], action);
15021 }
15022
15023 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
15024 {
15025   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15026
15027   if (sound_effect != SND_UNDEFINED)
15028     PlayLevelSound(x, y, sound_effect);
15029 }
15030
15031 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
15032                                               int action)
15033 {
15034   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
15035
15036   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15037     PlayLevelSound(x, y, sound_effect);
15038 }
15039
15040 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
15041 {
15042   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15043
15044   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15045     PlayLevelSound(x, y, sound_effect);
15046 }
15047
15048 static void StopLevelSoundActionIfLoop(int x, int y, int action)
15049 {
15050   int sound_effect = element_info[SND_ELEMENT(Tile[x][y])].sound[action];
15051
15052   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
15053     StopSound(sound_effect);
15054 }
15055
15056 static int getLevelMusicNr(void)
15057 {
15058   if (levelset.music[level_nr] != MUS_UNDEFINED)
15059     return levelset.music[level_nr];            // from config file
15060   else
15061     return MAP_NOCONF_MUSIC(level_nr);          // from music dir
15062 }
15063
15064 static void FadeLevelSounds(void)
15065 {
15066   FadeSounds();
15067 }
15068
15069 static void FadeLevelMusic(void)
15070 {
15071   int music_nr = getLevelMusicNr();
15072   char *curr_music = getCurrentlyPlayingMusicFilename();
15073   char *next_music = getMusicInfoEntryFilename(music_nr);
15074
15075   if (!strEqual(curr_music, next_music))
15076     FadeMusic();
15077 }
15078
15079 void FadeLevelSoundsAndMusic(void)
15080 {
15081   FadeLevelSounds();
15082   FadeLevelMusic();
15083 }
15084
15085 static void PlayLevelMusic(void)
15086 {
15087   int music_nr = getLevelMusicNr();
15088   char *curr_music = getCurrentlyPlayingMusicFilename();
15089   char *next_music = getMusicInfoEntryFilename(music_nr);
15090
15091   if (!strEqual(curr_music, next_music))
15092     PlayMusicLoop(music_nr);
15093 }
15094
15095 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
15096 {
15097   int element = (element_em > -1 ? map_element_EM_to_RND_game(element_em) : 0);
15098   int offset = 0;
15099   int x = xx - offset;
15100   int y = yy - offset;
15101
15102   switch (sample)
15103   {
15104     case SOUND_blank:
15105       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
15106       break;
15107
15108     case SOUND_roll:
15109       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15110       break;
15111
15112     case SOUND_stone:
15113       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15114       break;
15115
15116     case SOUND_nut:
15117       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15118       break;
15119
15120     case SOUND_crack:
15121       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15122       break;
15123
15124     case SOUND_bug:
15125       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15126       break;
15127
15128     case SOUND_tank:
15129       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15130       break;
15131
15132     case SOUND_android_clone:
15133       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15134       break;
15135
15136     case SOUND_android_move:
15137       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15138       break;
15139
15140     case SOUND_spring:
15141       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15142       break;
15143
15144     case SOUND_slurp:
15145       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
15146       break;
15147
15148     case SOUND_eater:
15149       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
15150       break;
15151
15152     case SOUND_eater_eat:
15153       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15154       break;
15155
15156     case SOUND_alien:
15157       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
15158       break;
15159
15160     case SOUND_collect:
15161       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
15162       break;
15163
15164     case SOUND_diamond:
15165       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15166       break;
15167
15168     case SOUND_squash:
15169       // !!! CHECK THIS !!!
15170 #if 1
15171       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
15172 #else
15173       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
15174 #endif
15175       break;
15176
15177     case SOUND_wonderfall:
15178       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
15179       break;
15180
15181     case SOUND_drip:
15182       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
15183       break;
15184
15185     case SOUND_push:
15186       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
15187       break;
15188
15189     case SOUND_dirt:
15190       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
15191       break;
15192
15193     case SOUND_acid:
15194       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
15195       break;
15196
15197     case SOUND_ball:
15198       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15199       break;
15200
15201     case SOUND_slide:
15202       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
15203       break;
15204
15205     case SOUND_wonder:
15206       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15207       break;
15208
15209     case SOUND_door:
15210       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15211       break;
15212
15213     case SOUND_exit_open:
15214       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
15215       break;
15216
15217     case SOUND_exit_leave:
15218       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
15219       break;
15220
15221     case SOUND_dynamite:
15222       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
15223       break;
15224
15225     case SOUND_tick:
15226       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15227       break;
15228
15229     case SOUND_press:
15230       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
15231       break;
15232
15233     case SOUND_wheel:
15234       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
15235       break;
15236
15237     case SOUND_boom:
15238       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
15239       break;
15240
15241     case SOUND_die:
15242       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
15243       break;
15244
15245     case SOUND_time:
15246       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
15247       break;
15248
15249     default:
15250       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
15251       break;
15252   }
15253 }
15254
15255 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
15256 {
15257   int element = map_element_SP_to_RND(element_sp);
15258   int action = map_action_SP_to_RND(action_sp);
15259   int offset = (setup.sp_show_border_elements ? 0 : 1);
15260   int x = xx - offset;
15261   int y = yy - offset;
15262
15263   PlayLevelSoundElementAction(x, y, element, action);
15264 }
15265
15266 void PlayLevelSound_MM(int xx, int yy, int element_mm, int action_mm)
15267 {
15268   int element = map_element_MM_to_RND(element_mm);
15269   int action = map_action_MM_to_RND(action_mm);
15270   int offset = 0;
15271   int x = xx - offset;
15272   int y = yy - offset;
15273
15274   if (!IS_MM_ELEMENT(element))
15275     element = EL_MM_DEFAULT;
15276
15277   PlayLevelSoundElementAction(x, y, element, action);
15278 }
15279
15280 void PlaySound_MM(int sound_mm)
15281 {
15282   int sound = map_sound_MM_to_RND(sound_mm);
15283
15284   if (sound == SND_UNDEFINED)
15285     return;
15286
15287   PlaySound(sound);
15288 }
15289
15290 void PlaySoundLoop_MM(int sound_mm)
15291 {
15292   int sound = map_sound_MM_to_RND(sound_mm);
15293
15294   if (sound == SND_UNDEFINED)
15295     return;
15296
15297   PlaySoundLoop(sound);
15298 }
15299
15300 void StopSound_MM(int sound_mm)
15301 {
15302   int sound = map_sound_MM_to_RND(sound_mm);
15303
15304   if (sound == SND_UNDEFINED)
15305     return;
15306
15307   StopSound(sound);
15308 }
15309
15310 void RaiseScore(int value)
15311 {
15312   game.score += value;
15313
15314   game_panel_controls[GAME_PANEL_SCORE].value = game.score;
15315
15316   DisplayGameControlValues();
15317 }
15318
15319 void RaiseScoreElement(int element)
15320 {
15321   switch (element)
15322   {
15323     case EL_EMERALD:
15324     case EL_BD_DIAMOND:
15325     case EL_EMERALD_YELLOW:
15326     case EL_EMERALD_RED:
15327     case EL_EMERALD_PURPLE:
15328     case EL_SP_INFOTRON:
15329       RaiseScore(level.score[SC_EMERALD]);
15330       break;
15331     case EL_DIAMOND:
15332       RaiseScore(level.score[SC_DIAMOND]);
15333       break;
15334     case EL_CRYSTAL:
15335       RaiseScore(level.score[SC_CRYSTAL]);
15336       break;
15337     case EL_PEARL:
15338       RaiseScore(level.score[SC_PEARL]);
15339       break;
15340     case EL_BUG:
15341     case EL_BD_BUTTERFLY:
15342     case EL_SP_ELECTRON:
15343       RaiseScore(level.score[SC_BUG]);
15344       break;
15345     case EL_SPACESHIP:
15346     case EL_BD_FIREFLY:
15347     case EL_SP_SNIKSNAK:
15348       RaiseScore(level.score[SC_SPACESHIP]);
15349       break;
15350     case EL_YAMYAM:
15351     case EL_DARK_YAMYAM:
15352       RaiseScore(level.score[SC_YAMYAM]);
15353       break;
15354     case EL_ROBOT:
15355       RaiseScore(level.score[SC_ROBOT]);
15356       break;
15357     case EL_PACMAN:
15358       RaiseScore(level.score[SC_PACMAN]);
15359       break;
15360     case EL_NUT:
15361       RaiseScore(level.score[SC_NUT]);
15362       break;
15363     case EL_DYNAMITE:
15364     case EL_EM_DYNAMITE:
15365     case EL_SP_DISK_RED:
15366     case EL_DYNABOMB_INCREASE_NUMBER:
15367     case EL_DYNABOMB_INCREASE_SIZE:
15368     case EL_DYNABOMB_INCREASE_POWER:
15369       RaiseScore(level.score[SC_DYNAMITE]);
15370       break;
15371     case EL_SHIELD_NORMAL:
15372     case EL_SHIELD_DEADLY:
15373       RaiseScore(level.score[SC_SHIELD]);
15374       break;
15375     case EL_EXTRA_TIME:
15376       RaiseScore(level.extra_time_score);
15377       break;
15378     case EL_KEY_1:
15379     case EL_KEY_2:
15380     case EL_KEY_3:
15381     case EL_KEY_4:
15382     case EL_EM_KEY_1:
15383     case EL_EM_KEY_2:
15384     case EL_EM_KEY_3:
15385     case EL_EM_KEY_4:
15386     case EL_EMC_KEY_5:
15387     case EL_EMC_KEY_6:
15388     case EL_EMC_KEY_7:
15389     case EL_EMC_KEY_8:
15390     case EL_DC_KEY_WHITE:
15391       RaiseScore(level.score[SC_KEY]);
15392       break;
15393     default:
15394       RaiseScore(element_info[element].collect_score);
15395       break;
15396   }
15397 }
15398
15399 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
15400 {
15401   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
15402   {
15403     if (!quick_quit)
15404     {
15405       // prevent short reactivation of overlay buttons while closing door
15406       SetOverlayActive(FALSE);
15407
15408       // door may still be open due to skipped or envelope style request
15409       CloseDoor(DOOR_CLOSE_1);
15410     }
15411
15412     if (network.enabled)
15413       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
15414     else
15415     {
15416       if (quick_quit)
15417         FadeSkipNextFadeIn();
15418
15419       SetGameStatus(GAME_MODE_MAIN);
15420
15421       DrawMainMenu();
15422     }
15423   }
15424   else          // continue playing the game
15425   {
15426     if (tape.playing && tape.deactivate_display)
15427       TapeDeactivateDisplayOff(TRUE);
15428
15429     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
15430
15431     if (tape.playing && tape.deactivate_display)
15432       TapeDeactivateDisplayOn();
15433   }
15434 }
15435
15436 void RequestQuitGame(boolean escape_key_pressed)
15437 {
15438   boolean ask_on_escape = (setup.ask_on_escape && setup.ask_on_quit_game);
15439   boolean quick_quit = ((escape_key_pressed && !ask_on_escape) ||
15440                         level_editor_test_game);
15441   boolean skip_request = (game.all_players_gone || !setup.ask_on_quit_game ||
15442                           quick_quit);
15443
15444   RequestQuitGameExt(skip_request, quick_quit,
15445                      "Do you really want to quit the game?");
15446 }
15447
15448 void RequestRestartGame(char *message)
15449 {
15450   game.restart_game_message = NULL;
15451
15452   boolean has_started_game = hasStartedNetworkGame();
15453   int request_mode = (has_started_game ? REQ_ASK : REQ_CONFIRM);
15454
15455   if (Request(message, request_mode | REQ_STAY_CLOSED) && has_started_game)
15456   {
15457     StartGameActions(network.enabled, setup.autorecord, level.random_seed);
15458   }
15459   else
15460   {
15461     // needed in case of envelope request to close game panel
15462     CloseDoor(DOOR_CLOSE_1);
15463
15464     SetGameStatus(GAME_MODE_MAIN);
15465
15466     DrawMainMenu();
15467   }
15468 }
15469
15470 void CheckGameOver(void)
15471 {
15472   static boolean last_game_over = FALSE;
15473   static int game_over_delay = 0;
15474   int game_over_delay_value = 50;
15475   boolean game_over = checkGameFailed();
15476
15477   // do not handle game over if request dialog is already active
15478   if (game.request_active)
15479     return;
15480
15481   // do not ask to play again if game was never actually played
15482   if (!game.GamePlayed)
15483     return;
15484
15485   if (!game_over)
15486   {
15487     last_game_over = FALSE;
15488     game_over_delay = game_over_delay_value;
15489
15490     return;
15491   }
15492
15493   if (game_over_delay > 0)
15494   {
15495     game_over_delay--;
15496
15497     return;
15498   }
15499
15500   if (last_game_over != game_over)
15501     game.restart_game_message = (hasStartedNetworkGame() ?
15502                                  "Game over! Play it again?" :
15503                                  "Game over!");
15504
15505   last_game_over = game_over;
15506 }
15507
15508 boolean checkGameSolved(void)
15509 {
15510   // set for all game engines if level was solved
15511   return game.LevelSolved_GameEnd;
15512 }
15513
15514 boolean checkGameFailed(void)
15515 {
15516   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15517     return (game_em.game_over && !game_em.level_solved);
15518   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15519     return (game_sp.game_over && !game_sp.level_solved);
15520   else if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15521     return (game_mm.game_over && !game_mm.level_solved);
15522   else                          // GAME_ENGINE_TYPE_RND
15523     return (game.GameOver && !game.LevelSolved);
15524 }
15525
15526 boolean checkGameEnded(void)
15527 {
15528   return (checkGameSolved() || checkGameFailed());
15529 }
15530
15531
15532 // ----------------------------------------------------------------------------
15533 // random generator functions
15534 // ----------------------------------------------------------------------------
15535
15536 unsigned int InitEngineRandom_RND(int seed)
15537 {
15538   game.num_random_calls = 0;
15539
15540   return InitEngineRandom(seed);
15541 }
15542
15543 unsigned int RND(int max)
15544 {
15545   if (max > 0)
15546   {
15547     game.num_random_calls++;
15548
15549     return GetEngineRandom(max);
15550   }
15551
15552   return 0;
15553 }
15554
15555
15556 // ----------------------------------------------------------------------------
15557 // game engine snapshot handling functions
15558 // ----------------------------------------------------------------------------
15559
15560 struct EngineSnapshotInfo
15561 {
15562   // runtime values for custom element collect score
15563   int collect_score[NUM_CUSTOM_ELEMENTS];
15564
15565   // runtime values for group element choice position
15566   int choice_pos[NUM_GROUP_ELEMENTS];
15567
15568   // runtime values for belt position animations
15569   int belt_graphic[4][NUM_BELT_PARTS];
15570   int belt_anim_mode[4][NUM_BELT_PARTS];
15571 };
15572
15573 static struct EngineSnapshotInfo engine_snapshot_rnd;
15574 static char *snapshot_level_identifier = NULL;
15575 static int snapshot_level_nr = -1;
15576
15577 static void SaveEngineSnapshotValues_RND(void)
15578 {
15579   static int belt_base_active_element[4] =
15580   {
15581     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
15582     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
15583     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
15584     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
15585   };
15586   int i, j;
15587
15588   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15589   {
15590     int element = EL_CUSTOM_START + i;
15591
15592     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
15593   }
15594
15595   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15596   {
15597     int element = EL_GROUP_START + i;
15598
15599     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
15600   }
15601
15602   for (i = 0; i < 4; i++)
15603   {
15604     for (j = 0; j < NUM_BELT_PARTS; j++)
15605     {
15606       int element = belt_base_active_element[i] + j;
15607       int graphic = el2img(element);
15608       int anim_mode = graphic_info[graphic].anim_mode;
15609
15610       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
15611       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
15612     }
15613   }
15614 }
15615
15616 static void LoadEngineSnapshotValues_RND(void)
15617 {
15618   unsigned int num_random_calls = game.num_random_calls;
15619   int i, j;
15620
15621   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
15622   {
15623     int element = EL_CUSTOM_START + i;
15624
15625     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
15626   }
15627
15628   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
15629   {
15630     int element = EL_GROUP_START + i;
15631
15632     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
15633   }
15634
15635   for (i = 0; i < 4; i++)
15636   {
15637     for (j = 0; j < NUM_BELT_PARTS; j++)
15638     {
15639       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
15640       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
15641
15642       graphic_info[graphic].anim_mode = anim_mode;
15643     }
15644   }
15645
15646   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15647   {
15648     InitRND(tape.random_seed);
15649     for (i = 0; i < num_random_calls; i++)
15650       RND(1);
15651   }
15652
15653   if (game.num_random_calls != num_random_calls)
15654   {
15655     Error("number of random calls out of sync");
15656     Error("number of random calls should be %d", num_random_calls);
15657     Error("number of random calls is %d", game.num_random_calls);
15658
15659     Fail("this should not happen -- please debug");
15660   }
15661 }
15662
15663 void FreeEngineSnapshotSingle(void)
15664 {
15665   FreeSnapshotSingle();
15666
15667   setString(&snapshot_level_identifier, NULL);
15668   snapshot_level_nr = -1;
15669 }
15670
15671 void FreeEngineSnapshotList(void)
15672 {
15673   FreeSnapshotList();
15674 }
15675
15676 static ListNode *SaveEngineSnapshotBuffers(void)
15677 {
15678   ListNode *buffers = NULL;
15679
15680   // copy some special values to a structure better suited for the snapshot
15681
15682   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15683     SaveEngineSnapshotValues_RND();
15684   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15685     SaveEngineSnapshotValues_EM();
15686   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15687     SaveEngineSnapshotValues_SP(&buffers);
15688   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15689     SaveEngineSnapshotValues_MM(&buffers);
15690
15691   // save values stored in special snapshot structure
15692
15693   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15694     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
15695   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15696     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
15697   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15698     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
15699   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15700     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_mm));
15701
15702   // save further RND engine values
15703
15704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
15705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
15706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
15707
15708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
15709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
15710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
15711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
15712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
15713
15714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
15715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
15716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
15717
15718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
15719
15720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
15721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
15722
15723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Tile));
15724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
15725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
15726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
15727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
15728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
15729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
15730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
15731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
15732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
15733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
15734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
15735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
15736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
15737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
15738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
15739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
15740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
15741
15742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
15743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
15744
15745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
15746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
15747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
15748
15749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
15750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
15751
15752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
15753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
15754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
15755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
15756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
15757
15758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
15759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
15760
15761 #if 0
15762   ListNode *node = engine_snapshot_list_rnd;
15763   int num_bytes = 0;
15764
15765   while (node != NULL)
15766   {
15767     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
15768
15769     node = node->next;
15770   }
15771
15772   Debug("game:playing:SaveEngineSnapshotBuffers",
15773         "size of engine snapshot: %d bytes", num_bytes);
15774 #endif
15775
15776   return buffers;
15777 }
15778
15779 void SaveEngineSnapshotSingle(void)
15780 {
15781   ListNode *buffers = SaveEngineSnapshotBuffers();
15782
15783   // finally save all snapshot buffers to single snapshot
15784   SaveSnapshotSingle(buffers);
15785
15786   // save level identification information
15787   setString(&snapshot_level_identifier, leveldir_current->identifier);
15788   snapshot_level_nr = level_nr;
15789 }
15790
15791 boolean CheckSaveEngineSnapshotToList(void)
15792 {
15793   boolean save_snapshot =
15794     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
15795      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
15796       game.snapshot.changed_action) ||
15797      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15798       game.snapshot.collected_item));
15799
15800   game.snapshot.changed_action = FALSE;
15801   game.snapshot.collected_item = FALSE;
15802   game.snapshot.save_snapshot = save_snapshot;
15803
15804   return save_snapshot;
15805 }
15806
15807 void SaveEngineSnapshotToList(void)
15808 {
15809   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
15810       tape.quick_resume)
15811     return;
15812
15813   ListNode *buffers = SaveEngineSnapshotBuffers();
15814
15815   // finally save all snapshot buffers to snapshot list
15816   SaveSnapshotToList(buffers);
15817 }
15818
15819 void SaveEngineSnapshotToListInitial(void)
15820 {
15821   FreeEngineSnapshotList();
15822
15823   SaveEngineSnapshotToList();
15824 }
15825
15826 static void LoadEngineSnapshotValues(void)
15827 {
15828   // restore special values from snapshot structure
15829
15830   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
15831     LoadEngineSnapshotValues_RND();
15832   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
15833     LoadEngineSnapshotValues_EM();
15834   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
15835     LoadEngineSnapshotValues_SP();
15836   if (level.game_engine_type == GAME_ENGINE_TYPE_MM)
15837     LoadEngineSnapshotValues_MM();
15838 }
15839
15840 void LoadEngineSnapshotSingle(void)
15841 {
15842   LoadSnapshotSingle();
15843
15844   LoadEngineSnapshotValues();
15845 }
15846
15847 static void LoadEngineSnapshot_Undo(int steps)
15848 {
15849   LoadSnapshotFromList_Older(steps);
15850
15851   LoadEngineSnapshotValues();
15852 }
15853
15854 static void LoadEngineSnapshot_Redo(int steps)
15855 {
15856   LoadSnapshotFromList_Newer(steps);
15857
15858   LoadEngineSnapshotValues();
15859 }
15860
15861 boolean CheckEngineSnapshotSingle(void)
15862 {
15863   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
15864           snapshot_level_nr == level_nr);
15865 }
15866
15867 boolean CheckEngineSnapshotList(void)
15868 {
15869   return CheckSnapshotList();
15870 }
15871
15872
15873 // ---------- new game button stuff -------------------------------------------
15874
15875 static struct
15876 {
15877   int graphic;
15878   struct XY *pos;
15879   int gadget_id;
15880   boolean *setup_value;
15881   boolean allowed_on_tape;
15882   boolean is_touch_button;
15883   char *infotext;
15884 } gamebutton_info[NUM_GAME_BUTTONS] =
15885 {
15886   {
15887     IMG_GFX_GAME_BUTTON_STOP,                   &game.button.stop,
15888     GAME_CTRL_ID_STOP,                          NULL,
15889     TRUE, FALSE,                                "stop game"
15890   },
15891   {
15892     IMG_GFX_GAME_BUTTON_PAUSE,                  &game.button.pause,
15893     GAME_CTRL_ID_PAUSE,                         NULL,
15894     TRUE, FALSE,                                "pause game"
15895   },
15896   {
15897     IMG_GFX_GAME_BUTTON_PLAY,                   &game.button.play,
15898     GAME_CTRL_ID_PLAY,                          NULL,
15899     TRUE, FALSE,                                "play game"
15900   },
15901   {
15902     IMG_GFX_GAME_BUTTON_UNDO,                   &game.button.undo,
15903     GAME_CTRL_ID_UNDO,                          NULL,
15904     TRUE, FALSE,                                "undo step"
15905   },
15906   {
15907     IMG_GFX_GAME_BUTTON_REDO,                   &game.button.redo,
15908     GAME_CTRL_ID_REDO,                          NULL,
15909     TRUE, FALSE,                                "redo step"
15910   },
15911   {
15912     IMG_GFX_GAME_BUTTON_SAVE,                   &game.button.save,
15913     GAME_CTRL_ID_SAVE,                          NULL,
15914     TRUE, FALSE,                                "save game"
15915   },
15916   {
15917     IMG_GFX_GAME_BUTTON_PAUSE2,                 &game.button.pause2,
15918     GAME_CTRL_ID_PAUSE2,                        NULL,
15919     TRUE, FALSE,                                "pause game"
15920   },
15921   {
15922     IMG_GFX_GAME_BUTTON_LOAD,                   &game.button.load,
15923     GAME_CTRL_ID_LOAD,                          NULL,
15924     TRUE, FALSE,                                "load game"
15925   },
15926   {
15927     IMG_GFX_GAME_BUTTON_PANEL_STOP,             &game.button.panel_stop,
15928     GAME_CTRL_ID_PANEL_STOP,                    NULL,
15929     FALSE, FALSE,                               "stop game"
15930   },
15931   {
15932     IMG_GFX_GAME_BUTTON_PANEL_PAUSE,            &game.button.panel_pause,
15933     GAME_CTRL_ID_PANEL_PAUSE,                   NULL,
15934     FALSE, FALSE,                               "pause game"
15935   },
15936   {
15937     IMG_GFX_GAME_BUTTON_PANEL_PLAY,             &game.button.panel_play,
15938     GAME_CTRL_ID_PANEL_PLAY,                    NULL,
15939     FALSE, FALSE,                               "play game"
15940   },
15941   {
15942     IMG_GFX_GAME_BUTTON_TOUCH_STOP,             &game.button.touch_stop,
15943     GAME_CTRL_ID_TOUCH_STOP,                    NULL,
15944     FALSE, TRUE,                                "stop game"
15945   },
15946   {
15947     IMG_GFX_GAME_BUTTON_TOUCH_PAUSE,            &game.button.touch_pause,
15948     GAME_CTRL_ID_TOUCH_PAUSE,                   NULL,
15949     FALSE, TRUE,                                "pause game"
15950   },
15951   {
15952     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,            &game.button.sound_music,
15953     SOUND_CTRL_ID_MUSIC,                        &setup.sound_music,
15954     TRUE, FALSE,                                "background music on/off"
15955   },
15956   {
15957     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,            &game.button.sound_loops,
15958     SOUND_CTRL_ID_LOOPS,                        &setup.sound_loops,
15959     TRUE, FALSE,                                "sound loops on/off"
15960   },
15961   {
15962     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,           &game.button.sound_simple,
15963     SOUND_CTRL_ID_SIMPLE,                       &setup.sound_simple,
15964     TRUE, FALSE,                                "normal sounds on/off"
15965   },
15966   {
15967     IMG_GFX_GAME_BUTTON_PANEL_SOUND_MUSIC,      &game.button.panel_sound_music,
15968     SOUND_CTRL_ID_PANEL_MUSIC,                  &setup.sound_music,
15969     FALSE, FALSE,                               "background music on/off"
15970   },
15971   {
15972     IMG_GFX_GAME_BUTTON_PANEL_SOUND_LOOPS,      &game.button.panel_sound_loops,
15973     SOUND_CTRL_ID_PANEL_LOOPS,                  &setup.sound_loops,
15974     FALSE, FALSE,                               "sound loops on/off"
15975   },
15976   {
15977     IMG_GFX_GAME_BUTTON_PANEL_SOUND_SIMPLE,     &game.button.panel_sound_simple,
15978     SOUND_CTRL_ID_PANEL_SIMPLE,                 &setup.sound_simple,
15979     FALSE, FALSE,                               "normal sounds on/off"
15980   }
15981 };
15982
15983 void CreateGameButtons(void)
15984 {
15985   int i;
15986
15987   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15988   {
15989     int graphic = gamebutton_info[i].graphic;
15990     struct GraphicInfo *gfx = &graphic_info[graphic];
15991     struct XY *pos = gamebutton_info[i].pos;
15992     struct GadgetInfo *gi;
15993     int button_type;
15994     boolean checked;
15995     unsigned int event_mask;
15996     boolean is_touch_button = gamebutton_info[i].is_touch_button;
15997     boolean allowed_on_tape = gamebutton_info[i].allowed_on_tape;
15998     boolean on_tape = (tape.show_game_buttons && allowed_on_tape);
15999     int base_x = (is_touch_button ? 0 : on_tape ? VX : DX);
16000     int base_y = (is_touch_button ? 0 : on_tape ? VY : DY);
16001     int gd_x   = gfx->src_x;
16002     int gd_y   = gfx->src_y;
16003     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
16004     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
16005     int gd_xa  = gfx->src_x + gfx->active_xoffset;
16006     int gd_ya  = gfx->src_y + gfx->active_yoffset;
16007     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
16008     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
16009     int x = (is_touch_button ? pos->x : GDI_ACTIVE_POS(pos->x));
16010     int y = (is_touch_button ? pos->y : GDI_ACTIVE_POS(pos->y));
16011     int id = i;
16012
16013     if (gfx->bitmap == NULL)
16014     {
16015       game_gadget[id] = NULL;
16016
16017       continue;
16018     }
16019
16020     if (id == GAME_CTRL_ID_STOP ||
16021         id == GAME_CTRL_ID_PANEL_STOP ||
16022         id == GAME_CTRL_ID_TOUCH_STOP ||
16023         id == GAME_CTRL_ID_PLAY ||
16024         id == GAME_CTRL_ID_PANEL_PLAY ||
16025         id == GAME_CTRL_ID_SAVE ||
16026         id == GAME_CTRL_ID_LOAD)
16027     {
16028       button_type = GD_TYPE_NORMAL_BUTTON;
16029       checked = FALSE;
16030       event_mask = GD_EVENT_RELEASED;
16031     }
16032     else if (id == GAME_CTRL_ID_UNDO ||
16033              id == GAME_CTRL_ID_REDO)
16034     {
16035       button_type = GD_TYPE_NORMAL_BUTTON;
16036       checked = FALSE;
16037       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
16038     }
16039     else
16040     {
16041       button_type = GD_TYPE_CHECK_BUTTON;
16042       checked = (gamebutton_info[i].setup_value != NULL ?
16043                  *gamebutton_info[i].setup_value : FALSE);
16044       event_mask = GD_EVENT_PRESSED;
16045     }
16046
16047     gi = CreateGadget(GDI_CUSTOM_ID, id,
16048                       GDI_IMAGE_ID, graphic,
16049                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
16050                       GDI_X, base_x + x,
16051                       GDI_Y, base_y + y,
16052                       GDI_WIDTH, gfx->width,
16053                       GDI_HEIGHT, gfx->height,
16054                       GDI_TYPE, button_type,
16055                       GDI_STATE, GD_BUTTON_UNPRESSED,
16056                       GDI_CHECKED, checked,
16057                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
16058                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
16059                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
16060                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
16061                       GDI_DIRECT_DRAW, FALSE,
16062                       GDI_OVERLAY_TOUCH_BUTTON, is_touch_button,
16063                       GDI_EVENT_MASK, event_mask,
16064                       GDI_CALLBACK_ACTION, HandleGameButtons,
16065                       GDI_END);
16066
16067     if (gi == NULL)
16068       Fail("cannot create gadget");
16069
16070     game_gadget[id] = gi;
16071   }
16072 }
16073
16074 void FreeGameButtons(void)
16075 {
16076   int i;
16077
16078   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16079     FreeGadget(game_gadget[i]);
16080 }
16081
16082 static void UnmapGameButtonsAtSamePosition(int id)
16083 {
16084   int i;
16085
16086   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16087     if (i != id &&
16088         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16089         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16090       UnmapGadget(game_gadget[i]);
16091 }
16092
16093 static void UnmapGameButtonsAtSamePosition_All(void)
16094 {
16095   if (setup.show_snapshot_buttons)
16096   {
16097     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
16098     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
16099     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
16100   }
16101   else
16102   {
16103     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
16104     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
16105     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
16106
16107     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_STOP);
16108     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PAUSE);
16109     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PANEL_PLAY);
16110   }
16111 }
16112
16113 static void MapGameButtonsAtSamePosition(int id)
16114 {
16115   int i;
16116
16117   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16118     if (i != id &&
16119         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
16120         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
16121       MapGadget(game_gadget[i]);
16122
16123   UnmapGameButtonsAtSamePosition_All();
16124 }
16125
16126 void MapUndoRedoButtons(void)
16127 {
16128   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16129   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16130
16131   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16132   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16133 }
16134
16135 void UnmapUndoRedoButtons(void)
16136 {
16137   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
16138   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
16139
16140   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
16141   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
16142 }
16143
16144 void ModifyPauseButtons(void)
16145 {
16146   static int ids[] =
16147   {
16148     GAME_CTRL_ID_PAUSE,
16149     GAME_CTRL_ID_PAUSE2,
16150     GAME_CTRL_ID_PANEL_PAUSE,
16151     GAME_CTRL_ID_TOUCH_PAUSE,
16152     -1
16153   };
16154   int i;
16155
16156   for (i = 0; ids[i] > -1; i++)
16157     ModifyGadget(game_gadget[ids[i]], GDI_CHECKED, tape.pausing, GDI_END);
16158 }
16159
16160 static void MapGameButtonsExt(boolean on_tape)
16161 {
16162   int i;
16163
16164   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16165     if ((!on_tape || gamebutton_info[i].allowed_on_tape) &&
16166         i != GAME_CTRL_ID_UNDO &&
16167         i != GAME_CTRL_ID_REDO)
16168       MapGadget(game_gadget[i]);
16169
16170   UnmapGameButtonsAtSamePosition_All();
16171
16172   RedrawGameButtons();
16173 }
16174
16175 static void UnmapGameButtonsExt(boolean on_tape)
16176 {
16177   int i;
16178
16179   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16180     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16181       UnmapGadget(game_gadget[i]);
16182 }
16183
16184 static void RedrawGameButtonsExt(boolean on_tape)
16185 {
16186   int i;
16187
16188   for (i = 0; i < NUM_GAME_BUTTONS; i++)
16189     if (!on_tape || gamebutton_info[i].allowed_on_tape)
16190       RedrawGadget(game_gadget[i]);
16191 }
16192
16193 static void SetGadgetState(struct GadgetInfo *gi, boolean state)
16194 {
16195   if (gi == NULL)
16196     return;
16197
16198   gi->checked = state;
16199 }
16200
16201 static void RedrawSoundButtonGadget(int id)
16202 {
16203   int id2 = (id == SOUND_CTRL_ID_MUSIC        ? SOUND_CTRL_ID_PANEL_MUSIC :
16204              id == SOUND_CTRL_ID_LOOPS        ? SOUND_CTRL_ID_PANEL_LOOPS :
16205              id == SOUND_CTRL_ID_SIMPLE       ? SOUND_CTRL_ID_PANEL_SIMPLE :
16206              id == SOUND_CTRL_ID_PANEL_MUSIC  ? SOUND_CTRL_ID_MUSIC :
16207              id == SOUND_CTRL_ID_PANEL_LOOPS  ? SOUND_CTRL_ID_LOOPS :
16208              id == SOUND_CTRL_ID_PANEL_SIMPLE ? SOUND_CTRL_ID_SIMPLE :
16209              id);
16210
16211   SetGadgetState(game_gadget[id2], *gamebutton_info[id2].setup_value);
16212   RedrawGadget(game_gadget[id2]);
16213 }
16214
16215 void MapGameButtons(void)
16216 {
16217   MapGameButtonsExt(FALSE);
16218 }
16219
16220 void UnmapGameButtons(void)
16221 {
16222   UnmapGameButtonsExt(FALSE);
16223 }
16224
16225 void RedrawGameButtons(void)
16226 {
16227   RedrawGameButtonsExt(FALSE);
16228 }
16229
16230 void MapGameButtonsOnTape(void)
16231 {
16232   MapGameButtonsExt(TRUE);
16233 }
16234
16235 void UnmapGameButtonsOnTape(void)
16236 {
16237   UnmapGameButtonsExt(TRUE);
16238 }
16239
16240 void RedrawGameButtonsOnTape(void)
16241 {
16242   RedrawGameButtonsExt(TRUE);
16243 }
16244
16245 static void GameUndoRedoExt(void)
16246 {
16247   ClearPlayerAction();
16248
16249   tape.pausing = TRUE;
16250
16251   RedrawPlayfield();
16252   UpdateAndDisplayGameControlValues();
16253
16254   DrawCompleteVideoDisplay();
16255   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
16256   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
16257   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
16258
16259   BackToFront();
16260 }
16261
16262 static void GameUndo(int steps)
16263 {
16264   if (!CheckEngineSnapshotList())
16265     return;
16266
16267   LoadEngineSnapshot_Undo(steps);
16268
16269   GameUndoRedoExt();
16270 }
16271
16272 static void GameRedo(int steps)
16273 {
16274   if (!CheckEngineSnapshotList())
16275     return;
16276
16277   LoadEngineSnapshot_Redo(steps);
16278
16279   GameUndoRedoExt();
16280 }
16281
16282 static void HandleGameButtonsExt(int id, int button)
16283 {
16284   static boolean game_undo_executed = FALSE;
16285   int steps = BUTTON_STEPSIZE(button);
16286   boolean handle_game_buttons =
16287     (game_status == GAME_MODE_PLAYING ||
16288      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
16289
16290   if (!handle_game_buttons)
16291     return;
16292
16293   switch (id)
16294   {
16295     case GAME_CTRL_ID_STOP:
16296     case GAME_CTRL_ID_PANEL_STOP:
16297     case GAME_CTRL_ID_TOUCH_STOP:
16298       if (game_status == GAME_MODE_MAIN)
16299         break;
16300
16301       if (tape.playing)
16302         TapeStop();
16303       else
16304         RequestQuitGame(FALSE);
16305
16306       break;
16307
16308     case GAME_CTRL_ID_PAUSE:
16309     case GAME_CTRL_ID_PAUSE2:
16310     case GAME_CTRL_ID_PANEL_PAUSE:
16311     case GAME_CTRL_ID_TOUCH_PAUSE:
16312       if (network.enabled && game_status == GAME_MODE_PLAYING)
16313       {
16314         if (tape.pausing)
16315           SendToServer_ContinuePlaying();
16316         else
16317           SendToServer_PausePlaying();
16318       }
16319       else
16320         TapeTogglePause(TAPE_TOGGLE_MANUAL);
16321
16322       game_undo_executed = FALSE;
16323
16324       break;
16325
16326     case GAME_CTRL_ID_PLAY:
16327     case GAME_CTRL_ID_PANEL_PLAY:
16328       if (game_status == GAME_MODE_MAIN)
16329       {
16330         StartGameActions(network.enabled, setup.autorecord, level.random_seed);
16331       }
16332       else if (tape.pausing)
16333       {
16334         if (network.enabled)
16335           SendToServer_ContinuePlaying();
16336         else
16337           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
16338       }
16339       break;
16340
16341     case GAME_CTRL_ID_UNDO:
16342       // Important: When using "save snapshot when collecting an item" mode,
16343       // load last (current) snapshot for first "undo" after pressing "pause"
16344       // (else the last-but-one snapshot would be loaded, because the snapshot
16345       // pointer already points to the last snapshot when pressing "pause",
16346       // which is fine for "every step/move" mode, but not for "every collect")
16347       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
16348           !game_undo_executed)
16349         steps--;
16350
16351       game_undo_executed = TRUE;
16352
16353       GameUndo(steps);
16354       break;
16355
16356     case GAME_CTRL_ID_REDO:
16357       GameRedo(steps);
16358       break;
16359
16360     case GAME_CTRL_ID_SAVE:
16361       TapeQuickSave();
16362       break;
16363
16364     case GAME_CTRL_ID_LOAD:
16365       TapeQuickLoad();
16366       break;
16367
16368     case SOUND_CTRL_ID_MUSIC:
16369     case SOUND_CTRL_ID_PANEL_MUSIC:
16370       if (setup.sound_music)
16371       { 
16372         setup.sound_music = FALSE;
16373
16374         FadeMusic();
16375       }
16376       else if (audio.music_available)
16377       { 
16378         setup.sound = setup.sound_music = TRUE;
16379
16380         SetAudioMode(setup.sound);
16381
16382         if (game_status == GAME_MODE_PLAYING)
16383           PlayLevelMusic();
16384       }
16385
16386       RedrawSoundButtonGadget(id);
16387
16388       break;
16389
16390     case SOUND_CTRL_ID_LOOPS:
16391     case SOUND_CTRL_ID_PANEL_LOOPS:
16392       if (setup.sound_loops)
16393         setup.sound_loops = FALSE;
16394       else if (audio.loops_available)
16395       {
16396         setup.sound = setup.sound_loops = TRUE;
16397
16398         SetAudioMode(setup.sound);
16399       }
16400
16401       RedrawSoundButtonGadget(id);
16402
16403       break;
16404
16405     case SOUND_CTRL_ID_SIMPLE:
16406     case SOUND_CTRL_ID_PANEL_SIMPLE:
16407       if (setup.sound_simple)
16408         setup.sound_simple = FALSE;
16409       else if (audio.sound_available)
16410       {
16411         setup.sound = setup.sound_simple = TRUE;
16412
16413         SetAudioMode(setup.sound);
16414       }
16415
16416       RedrawSoundButtonGadget(id);
16417
16418       break;
16419
16420     default:
16421       break;
16422   }
16423 }
16424
16425 static void HandleGameButtons(struct GadgetInfo *gi)
16426 {
16427   HandleGameButtonsExt(gi->custom_id, gi->event.button);
16428 }
16429
16430 void HandleSoundButtonKeys(Key key)
16431 {
16432   if (key == setup.shortcut.sound_simple)
16433     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
16434   else if (key == setup.shortcut.sound_loops)
16435     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
16436   else if (key == setup.shortcut.sound_music)
16437     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
16438 }