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