improved displaying FPS (debug mode only)
[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 //                  http://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()             (local_player->LevelSolved_PanelOff)
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_FRAME                        35
126 #define GAME_PANEL_SHIELD_NORMAL                36
127 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
128 #define GAME_PANEL_SHIELD_DEADLY                38
129 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
130 #define GAME_PANEL_EXIT                         40
131 #define GAME_PANEL_EMC_MAGIC_BALL               41
132 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
133 #define GAME_PANEL_LIGHT_SWITCH                 43
134 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
135 #define GAME_PANEL_TIMEGATE_SWITCH              45
136 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
137 #define GAME_PANEL_SWITCHGATE_SWITCH            47
138 #define GAME_PANEL_EMC_LENSES                   48
139 #define GAME_PANEL_EMC_LENSES_TIME              49
140 #define GAME_PANEL_EMC_MAGNIFIER                50
141 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
142 #define GAME_PANEL_BALLOON_SWITCH               52
143 #define GAME_PANEL_DYNABOMB_NUMBER              53
144 #define GAME_PANEL_DYNABOMB_SIZE                54
145 #define GAME_PANEL_DYNABOMB_POWER               55
146 #define GAME_PANEL_PENGUINS                     56
147 #define GAME_PANEL_SOKOBAN_OBJECTS              57
148 #define GAME_PANEL_SOKOBAN_FIELDS               58
149 #define GAME_PANEL_ROBOT_WHEEL                  59
150 #define GAME_PANEL_CONVEYOR_BELT_1              60
151 #define GAME_PANEL_CONVEYOR_BELT_2              61
152 #define GAME_PANEL_CONVEYOR_BELT_3              62
153 #define GAME_PANEL_CONVEYOR_BELT_4              63
154 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
155 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
156 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
157 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
158 #define GAME_PANEL_MAGIC_WALL                   68
159 #define GAME_PANEL_MAGIC_WALL_TIME              69
160 #define GAME_PANEL_GRAVITY_STATE                70
161 #define GAME_PANEL_GRAPHIC_1                    71
162 #define GAME_PANEL_GRAPHIC_2                    72
163 #define GAME_PANEL_GRAPHIC_3                    73
164 #define GAME_PANEL_GRAPHIC_4                    74
165 #define GAME_PANEL_GRAPHIC_5                    75
166 #define GAME_PANEL_GRAPHIC_6                    76
167 #define GAME_PANEL_GRAPHIC_7                    77
168 #define GAME_PANEL_GRAPHIC_8                    78
169 #define GAME_PANEL_ELEMENT_1                    79
170 #define GAME_PANEL_ELEMENT_2                    80
171 #define GAME_PANEL_ELEMENT_3                    81
172 #define GAME_PANEL_ELEMENT_4                    82
173 #define GAME_PANEL_ELEMENT_5                    83
174 #define GAME_PANEL_ELEMENT_6                    84
175 #define GAME_PANEL_ELEMENT_7                    85
176 #define GAME_PANEL_ELEMENT_8                    86
177 #define GAME_PANEL_ELEMENT_COUNT_1              87
178 #define GAME_PANEL_ELEMENT_COUNT_2              88
179 #define GAME_PANEL_ELEMENT_COUNT_3              89
180 #define GAME_PANEL_ELEMENT_COUNT_4              90
181 #define GAME_PANEL_ELEMENT_COUNT_5              91
182 #define GAME_PANEL_ELEMENT_COUNT_6              92
183 #define GAME_PANEL_ELEMENT_COUNT_7              93
184 #define GAME_PANEL_ELEMENT_COUNT_8              94
185 #define GAME_PANEL_CE_SCORE_1                   95
186 #define GAME_PANEL_CE_SCORE_2                   96
187 #define GAME_PANEL_CE_SCORE_3                   97
188 #define GAME_PANEL_CE_SCORE_4                   98
189 #define GAME_PANEL_CE_SCORE_5                   99
190 #define GAME_PANEL_CE_SCORE_6                   100
191 #define GAME_PANEL_CE_SCORE_7                   101
192 #define GAME_PANEL_CE_SCORE_8                   102
193 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
194 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
195 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
196 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
197 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
198 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
199 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
200 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
201 #define GAME_PANEL_PLAYER_NAME                  111
202 #define GAME_PANEL_LEVEL_NAME                   112
203 #define GAME_PANEL_LEVEL_AUTHOR                 113
204
205 #define NUM_GAME_PANEL_CONTROLS                 114
206
207 struct GamePanelOrderInfo
208 {
209   int nr;
210   int sort_priority;
211 };
212
213 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
214
215 struct GamePanelControlInfo
216 {
217   int nr;
218
219   struct TextPosInfo *pos;
220   int type;
221
222   int value, last_value;
223   int frame, last_frame;
224   int gfx_frame;
225   int gfx_random;
226 };
227
228 static struct GamePanelControlInfo game_panel_controls[] =
229 {
230   {
231     GAME_PANEL_LEVEL_NUMBER,
232     &game.panel.level_number,
233     TYPE_INTEGER,
234   },
235   {
236     GAME_PANEL_GEMS,
237     &game.panel.gems,
238     TYPE_INTEGER,
239   },
240   {
241     GAME_PANEL_INVENTORY_COUNT,
242     &game.panel.inventory_count,
243     TYPE_INTEGER,
244   },
245   {
246     GAME_PANEL_INVENTORY_FIRST_1,
247     &game.panel.inventory_first[0],
248     TYPE_ELEMENT,
249   },
250   {
251     GAME_PANEL_INVENTORY_FIRST_2,
252     &game.panel.inventory_first[1],
253     TYPE_ELEMENT,
254   },
255   {
256     GAME_PANEL_INVENTORY_FIRST_3,
257     &game.panel.inventory_first[2],
258     TYPE_ELEMENT,
259   },
260   {
261     GAME_PANEL_INVENTORY_FIRST_4,
262     &game.panel.inventory_first[3],
263     TYPE_ELEMENT,
264   },
265   {
266     GAME_PANEL_INVENTORY_FIRST_5,
267     &game.panel.inventory_first[4],
268     TYPE_ELEMENT,
269   },
270   {
271     GAME_PANEL_INVENTORY_FIRST_6,
272     &game.panel.inventory_first[5],
273     TYPE_ELEMENT,
274   },
275   {
276     GAME_PANEL_INVENTORY_FIRST_7,
277     &game.panel.inventory_first[6],
278     TYPE_ELEMENT,
279   },
280   {
281     GAME_PANEL_INVENTORY_FIRST_8,
282     &game.panel.inventory_first[7],
283     TYPE_ELEMENT,
284   },
285   {
286     GAME_PANEL_INVENTORY_LAST_1,
287     &game.panel.inventory_last[0],
288     TYPE_ELEMENT,
289   },
290   {
291     GAME_PANEL_INVENTORY_LAST_2,
292     &game.panel.inventory_last[1],
293     TYPE_ELEMENT,
294   },
295   {
296     GAME_PANEL_INVENTORY_LAST_3,
297     &game.panel.inventory_last[2],
298     TYPE_ELEMENT,
299   },
300   {
301     GAME_PANEL_INVENTORY_LAST_4,
302     &game.panel.inventory_last[3],
303     TYPE_ELEMENT,
304   },
305   {
306     GAME_PANEL_INVENTORY_LAST_5,
307     &game.panel.inventory_last[4],
308     TYPE_ELEMENT,
309   },
310   {
311     GAME_PANEL_INVENTORY_LAST_6,
312     &game.panel.inventory_last[5],
313     TYPE_ELEMENT,
314   },
315   {
316     GAME_PANEL_INVENTORY_LAST_7,
317     &game.panel.inventory_last[6],
318     TYPE_ELEMENT,
319   },
320   {
321     GAME_PANEL_INVENTORY_LAST_8,
322     &game.panel.inventory_last[7],
323     TYPE_ELEMENT,
324   },
325   {
326     GAME_PANEL_KEY_1,
327     &game.panel.key[0],
328     TYPE_ELEMENT,
329   },
330   {
331     GAME_PANEL_KEY_2,
332     &game.panel.key[1],
333     TYPE_ELEMENT,
334   },
335   {
336     GAME_PANEL_KEY_3,
337     &game.panel.key[2],
338     TYPE_ELEMENT,
339   },
340   {
341     GAME_PANEL_KEY_4,
342     &game.panel.key[3],
343     TYPE_ELEMENT,
344   },
345   {
346     GAME_PANEL_KEY_5,
347     &game.panel.key[4],
348     TYPE_ELEMENT,
349   },
350   {
351     GAME_PANEL_KEY_6,
352     &game.panel.key[5],
353     TYPE_ELEMENT,
354   },
355   {
356     GAME_PANEL_KEY_7,
357     &game.panel.key[6],
358     TYPE_ELEMENT,
359   },
360   {
361     GAME_PANEL_KEY_8,
362     &game.panel.key[7],
363     TYPE_ELEMENT,
364   },
365   {
366     GAME_PANEL_KEY_WHITE,
367     &game.panel.key_white,
368     TYPE_ELEMENT,
369   },
370   {
371     GAME_PANEL_KEY_WHITE_COUNT,
372     &game.panel.key_white_count,
373     TYPE_INTEGER,
374   },
375   {
376     GAME_PANEL_SCORE,
377     &game.panel.score,
378     TYPE_INTEGER,
379   },
380   {
381     GAME_PANEL_HIGHSCORE,
382     &game.panel.highscore,
383     TYPE_INTEGER,
384   },
385   {
386     GAME_PANEL_TIME,
387     &game.panel.time,
388     TYPE_INTEGER,
389   },
390   {
391     GAME_PANEL_TIME_HH,
392     &game.panel.time_hh,
393     TYPE_INTEGER,
394   },
395   {
396     GAME_PANEL_TIME_MM,
397     &game.panel.time_mm,
398     TYPE_INTEGER,
399   },
400   {
401     GAME_PANEL_TIME_SS,
402     &game.panel.time_ss,
403     TYPE_INTEGER,
404   },
405   {
406     GAME_PANEL_FRAME,
407     &game.panel.frame,
408     TYPE_INTEGER,
409   },
410   {
411     GAME_PANEL_SHIELD_NORMAL,
412     &game.panel.shield_normal,
413     TYPE_ELEMENT,
414   },
415   {
416     GAME_PANEL_SHIELD_NORMAL_TIME,
417     &game.panel.shield_normal_time,
418     TYPE_INTEGER,
419   },
420   {
421     GAME_PANEL_SHIELD_DEADLY,
422     &game.panel.shield_deadly,
423     TYPE_ELEMENT,
424   },
425   {
426     GAME_PANEL_SHIELD_DEADLY_TIME,
427     &game.panel.shield_deadly_time,
428     TYPE_INTEGER,
429   },
430   {
431     GAME_PANEL_EXIT,
432     &game.panel.exit,
433     TYPE_ELEMENT,
434   },
435   {
436     GAME_PANEL_EMC_MAGIC_BALL,
437     &game.panel.emc_magic_ball,
438     TYPE_ELEMENT,
439   },
440   {
441     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
442     &game.panel.emc_magic_ball_switch,
443     TYPE_ELEMENT,
444   },
445   {
446     GAME_PANEL_LIGHT_SWITCH,
447     &game.panel.light_switch,
448     TYPE_ELEMENT,
449   },
450   {
451     GAME_PANEL_LIGHT_SWITCH_TIME,
452     &game.panel.light_switch_time,
453     TYPE_INTEGER,
454   },
455   {
456     GAME_PANEL_TIMEGATE_SWITCH,
457     &game.panel.timegate_switch,
458     TYPE_ELEMENT,
459   },
460   {
461     GAME_PANEL_TIMEGATE_SWITCH_TIME,
462     &game.panel.timegate_switch_time,
463     TYPE_INTEGER,
464   },
465   {
466     GAME_PANEL_SWITCHGATE_SWITCH,
467     &game.panel.switchgate_switch,
468     TYPE_ELEMENT,
469   },
470   {
471     GAME_PANEL_EMC_LENSES,
472     &game.panel.emc_lenses,
473     TYPE_ELEMENT,
474   },
475   {
476     GAME_PANEL_EMC_LENSES_TIME,
477     &game.panel.emc_lenses_time,
478     TYPE_INTEGER,
479   },
480   {
481     GAME_PANEL_EMC_MAGNIFIER,
482     &game.panel.emc_magnifier,
483     TYPE_ELEMENT,
484   },
485   {
486     GAME_PANEL_EMC_MAGNIFIER_TIME,
487     &game.panel.emc_magnifier_time,
488     TYPE_INTEGER,
489   },
490   {
491     GAME_PANEL_BALLOON_SWITCH,
492     &game.panel.balloon_switch,
493     TYPE_ELEMENT,
494   },
495   {
496     GAME_PANEL_DYNABOMB_NUMBER,
497     &game.panel.dynabomb_number,
498     TYPE_INTEGER,
499   },
500   {
501     GAME_PANEL_DYNABOMB_SIZE,
502     &game.panel.dynabomb_size,
503     TYPE_INTEGER,
504   },
505   {
506     GAME_PANEL_DYNABOMB_POWER,
507     &game.panel.dynabomb_power,
508     TYPE_ELEMENT,
509   },
510   {
511     GAME_PANEL_PENGUINS,
512     &game.panel.penguins,
513     TYPE_INTEGER,
514   },
515   {
516     GAME_PANEL_SOKOBAN_OBJECTS,
517     &game.panel.sokoban_objects,
518     TYPE_INTEGER,
519   },
520   {
521     GAME_PANEL_SOKOBAN_FIELDS,
522     &game.panel.sokoban_fields,
523     TYPE_INTEGER,
524   },
525   {
526     GAME_PANEL_ROBOT_WHEEL,
527     &game.panel.robot_wheel,
528     TYPE_ELEMENT,
529   },
530   {
531     GAME_PANEL_CONVEYOR_BELT_1,
532     &game.panel.conveyor_belt[0],
533     TYPE_ELEMENT,
534   },
535   {
536     GAME_PANEL_CONVEYOR_BELT_2,
537     &game.panel.conveyor_belt[1],
538     TYPE_ELEMENT,
539   },
540   {
541     GAME_PANEL_CONVEYOR_BELT_3,
542     &game.panel.conveyor_belt[2],
543     TYPE_ELEMENT,
544   },
545   {
546     GAME_PANEL_CONVEYOR_BELT_4,
547     &game.panel.conveyor_belt[3],
548     TYPE_ELEMENT,
549   },
550   {
551     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
552     &game.panel.conveyor_belt_switch[0],
553     TYPE_ELEMENT,
554   },
555   {
556     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
557     &game.panel.conveyor_belt_switch[1],
558     TYPE_ELEMENT,
559   },
560   {
561     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
562     &game.panel.conveyor_belt_switch[2],
563     TYPE_ELEMENT,
564   },
565   {
566     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
567     &game.panel.conveyor_belt_switch[3],
568     TYPE_ELEMENT,
569   },
570   {
571     GAME_PANEL_MAGIC_WALL,
572     &game.panel.magic_wall,
573     TYPE_ELEMENT,
574   },
575   {
576     GAME_PANEL_MAGIC_WALL_TIME,
577     &game.panel.magic_wall_time,
578     TYPE_INTEGER,
579   },
580   {
581     GAME_PANEL_GRAVITY_STATE,
582     &game.panel.gravity_state,
583     TYPE_STRING,
584   },
585   {
586     GAME_PANEL_GRAPHIC_1,
587     &game.panel.graphic[0],
588     TYPE_ELEMENT,
589   },
590   {
591     GAME_PANEL_GRAPHIC_2,
592     &game.panel.graphic[1],
593     TYPE_ELEMENT,
594   },
595   {
596     GAME_PANEL_GRAPHIC_3,
597     &game.panel.graphic[2],
598     TYPE_ELEMENT,
599   },
600   {
601     GAME_PANEL_GRAPHIC_4,
602     &game.panel.graphic[3],
603     TYPE_ELEMENT,
604   },
605   {
606     GAME_PANEL_GRAPHIC_5,
607     &game.panel.graphic[4],
608     TYPE_ELEMENT,
609   },
610   {
611     GAME_PANEL_GRAPHIC_6,
612     &game.panel.graphic[5],
613     TYPE_ELEMENT,
614   },
615   {
616     GAME_PANEL_GRAPHIC_7,
617     &game.panel.graphic[6],
618     TYPE_ELEMENT,
619   },
620   {
621     GAME_PANEL_GRAPHIC_8,
622     &game.panel.graphic[7],
623     TYPE_ELEMENT,
624   },
625   {
626     GAME_PANEL_ELEMENT_1,
627     &game.panel.element[0],
628     TYPE_ELEMENT,
629   },
630   {
631     GAME_PANEL_ELEMENT_2,
632     &game.panel.element[1],
633     TYPE_ELEMENT,
634   },
635   {
636     GAME_PANEL_ELEMENT_3,
637     &game.panel.element[2],
638     TYPE_ELEMENT,
639   },
640   {
641     GAME_PANEL_ELEMENT_4,
642     &game.panel.element[3],
643     TYPE_ELEMENT,
644   },
645   {
646     GAME_PANEL_ELEMENT_5,
647     &game.panel.element[4],
648     TYPE_ELEMENT,
649   },
650   {
651     GAME_PANEL_ELEMENT_6,
652     &game.panel.element[5],
653     TYPE_ELEMENT,
654   },
655   {
656     GAME_PANEL_ELEMENT_7,
657     &game.panel.element[6],
658     TYPE_ELEMENT,
659   },
660   {
661     GAME_PANEL_ELEMENT_8,
662     &game.panel.element[7],
663     TYPE_ELEMENT,
664   },
665   {
666     GAME_PANEL_ELEMENT_COUNT_1,
667     &game.panel.element_count[0],
668     TYPE_INTEGER,
669   },
670   {
671     GAME_PANEL_ELEMENT_COUNT_2,
672     &game.panel.element_count[1],
673     TYPE_INTEGER,
674   },
675   {
676     GAME_PANEL_ELEMENT_COUNT_3,
677     &game.panel.element_count[2],
678     TYPE_INTEGER,
679   },
680   {
681     GAME_PANEL_ELEMENT_COUNT_4,
682     &game.panel.element_count[3],
683     TYPE_INTEGER,
684   },
685   {
686     GAME_PANEL_ELEMENT_COUNT_5,
687     &game.panel.element_count[4],
688     TYPE_INTEGER,
689   },
690   {
691     GAME_PANEL_ELEMENT_COUNT_6,
692     &game.panel.element_count[5],
693     TYPE_INTEGER,
694   },
695   {
696     GAME_PANEL_ELEMENT_COUNT_7,
697     &game.panel.element_count[6],
698     TYPE_INTEGER,
699   },
700   {
701     GAME_PANEL_ELEMENT_COUNT_8,
702     &game.panel.element_count[7],
703     TYPE_INTEGER,
704   },
705   {
706     GAME_PANEL_CE_SCORE_1,
707     &game.panel.ce_score[0],
708     TYPE_INTEGER,
709   },
710   {
711     GAME_PANEL_CE_SCORE_2,
712     &game.panel.ce_score[1],
713     TYPE_INTEGER,
714   },
715   {
716     GAME_PANEL_CE_SCORE_3,
717     &game.panel.ce_score[2],
718     TYPE_INTEGER,
719   },
720   {
721     GAME_PANEL_CE_SCORE_4,
722     &game.panel.ce_score[3],
723     TYPE_INTEGER,
724   },
725   {
726     GAME_PANEL_CE_SCORE_5,
727     &game.panel.ce_score[4],
728     TYPE_INTEGER,
729   },
730   {
731     GAME_PANEL_CE_SCORE_6,
732     &game.panel.ce_score[5],
733     TYPE_INTEGER,
734   },
735   {
736     GAME_PANEL_CE_SCORE_7,
737     &game.panel.ce_score[6],
738     TYPE_INTEGER,
739   },
740   {
741     GAME_PANEL_CE_SCORE_8,
742     &game.panel.ce_score[7],
743     TYPE_INTEGER,
744   },
745   {
746     GAME_PANEL_CE_SCORE_1_ELEMENT,
747     &game.panel.ce_score_element[0],
748     TYPE_ELEMENT,
749   },
750   {
751     GAME_PANEL_CE_SCORE_2_ELEMENT,
752     &game.panel.ce_score_element[1],
753     TYPE_ELEMENT,
754   },
755   {
756     GAME_PANEL_CE_SCORE_3_ELEMENT,
757     &game.panel.ce_score_element[2],
758     TYPE_ELEMENT,
759   },
760   {
761     GAME_PANEL_CE_SCORE_4_ELEMENT,
762     &game.panel.ce_score_element[3],
763     TYPE_ELEMENT,
764   },
765   {
766     GAME_PANEL_CE_SCORE_5_ELEMENT,
767     &game.panel.ce_score_element[4],
768     TYPE_ELEMENT,
769   },
770   {
771     GAME_PANEL_CE_SCORE_6_ELEMENT,
772     &game.panel.ce_score_element[5],
773     TYPE_ELEMENT,
774   },
775   {
776     GAME_PANEL_CE_SCORE_7_ELEMENT,
777     &game.panel.ce_score_element[6],
778     TYPE_ELEMENT,
779   },
780   {
781     GAME_PANEL_CE_SCORE_8_ELEMENT,
782     &game.panel.ce_score_element[7],
783     TYPE_ELEMENT,
784   },
785   {
786     GAME_PANEL_PLAYER_NAME,
787     &game.panel.player_name,
788     TYPE_STRING,
789   },
790   {
791     GAME_PANEL_LEVEL_NAME,
792     &game.panel.level_name,
793     TYPE_STRING,
794   },
795   {
796     GAME_PANEL_LEVEL_AUTHOR,
797     &game.panel.level_author,
798     TYPE_STRING,
799   },
800
801   {
802     -1,
803     NULL,
804     -1,
805   }
806 };
807
808 /* values for delayed check of falling and moving elements and for collision */
809 #define CHECK_DELAY_MOVING      3
810 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
811 #define CHECK_DELAY_COLLISION   2
812 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
813
814 /* values for initial player move delay (initial delay counter value) */
815 #define INITIAL_MOVE_DELAY_OFF  -1
816 #define INITIAL_MOVE_DELAY_ON   0
817
818 /* values for player movement speed (which is in fact a delay value) */
819 #define MOVE_DELAY_MIN_SPEED    32
820 #define MOVE_DELAY_NORMAL_SPEED 8
821 #define MOVE_DELAY_HIGH_SPEED   4
822 #define MOVE_DELAY_MAX_SPEED    1
823
824 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
825 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
826
827 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
828 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
829
830 /* values for scroll positions */
831 #define SCROLL_POSITION_X(x)    ((x) < SBX_Left  + MIDPOSX ? SBX_Left : \
832                                  (x) > SBX_Right + MIDPOSX ? SBX_Right :\
833                                  (x) - MIDPOSX)
834 #define SCROLL_POSITION_Y(y)    ((y) < SBY_Upper + MIDPOSY ? SBY_Upper :\
835                                  (y) > SBY_Lower + MIDPOSY ? SBY_Lower :\
836                                  (y) - MIDPOSY)
837
838 /* values for other actions */
839 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
840 #define MOVE_STEPSIZE_MIN       (1)
841 #define MOVE_STEPSIZE_MAX       (TILEX)
842
843 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
844 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
845
846 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
847
848 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
849                                  RND(element_info[e].push_delay_random))
850 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
851                                  RND(element_info[e].drop_delay_random))
852 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
853                                  RND(element_info[e].move_delay_random))
854 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
855                                     (element_info[e].move_delay_random))
856 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
857                                  RND(element_info[e].ce_value_random_initial))
858 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
859 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
860                                  RND((c)->delay_random * (c)->delay_frames))
861 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
862                                  RND((c)->delay_random))
863
864
865 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
866          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
867
868 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
869         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
870          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
871          (be) + (e) - EL_SELF)
872
873 #define GET_PLAYER_FROM_BITS(p)                                         \
874         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
875
876 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
877         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
878          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
879          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
880          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
881          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
882          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
883          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
884          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
885          (e))
886
887 #define CAN_GROW_INTO(e)                                                \
888         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
889
890 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
891                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
892                                         (condition)))
893
894 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
895                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
896                                         (CAN_MOVE_INTO_ACID(e) &&       \
897                                          Feld[x][y] == EL_ACID) ||      \
898                                         (condition)))
899
900 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
901                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
902                                         (CAN_MOVE_INTO_ACID(e) &&       \
903                                          Feld[x][y] == EL_ACID) ||      \
904                                         (condition)))
905
906 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
907                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
908                                         (condition) ||                  \
909                                         (CAN_MOVE_INTO_ACID(e) &&       \
910                                          Feld[x][y] == EL_ACID) ||      \
911                                         (DONT_COLLIDE_WITH(e) &&        \
912                                          IS_PLAYER(x, y) &&             \
913                                          !PLAYER_ENEMY_PROTECTED(x, y))))
914
915 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
916         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
917
918 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
919         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
920
921 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
922         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
923
924 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
925         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
926                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
927
928 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
929         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
930
931 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
932         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
933
934 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
935         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
936
937 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
938         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
939
940 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
942
943 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
945                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
946                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
947                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
948                                                  IS_FOOD_PENGUIN(Feld[x][y])))
949 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
950         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
951
952 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
953         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
954
955 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
956         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
957
958 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
959         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
960                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
961
962 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
963
964 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
965                 (!IS_PLAYER(x, y) &&                                    \
966                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
967
968 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
969         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
970
971 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
972 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
973
974 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
975 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
976 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
977 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
978
979 /* game button identifiers */
980 #define GAME_CTRL_ID_STOP               0
981 #define GAME_CTRL_ID_PAUSE              1
982 #define GAME_CTRL_ID_PLAY               2
983 #define GAME_CTRL_ID_UNDO               3
984 #define GAME_CTRL_ID_REDO               4
985 #define GAME_CTRL_ID_SAVE               5
986 #define GAME_CTRL_ID_PAUSE2             6
987 #define GAME_CTRL_ID_LOAD               7
988 #define SOUND_CTRL_ID_MUSIC             8
989 #define SOUND_CTRL_ID_LOOPS             9
990 #define SOUND_CTRL_ID_SIMPLE            10
991
992 #define NUM_GAME_BUTTONS                11
993
994
995 /* forward declaration for internal use */
996
997 static void CreateField(int, int, int);
998
999 static void ResetGfxAnimation(int, int);
1000
1001 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
1002 static void AdvanceFrameAndPlayerCounters(int);
1003
1004 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
1005 static boolean MovePlayer(struct PlayerInfo *, int, int);
1006 static void ScrollPlayer(struct PlayerInfo *, int);
1007 static void ScrollScreen(struct PlayerInfo *, int);
1008
1009 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1010 static boolean DigFieldByCE(int, int, int);
1011 static boolean SnapField(struct PlayerInfo *, int, int);
1012 static boolean DropElement(struct PlayerInfo *);
1013
1014 static void InitBeltMovement(void);
1015 static void CloseAllOpenTimegates(void);
1016 static void CheckGravityMovement(struct PlayerInfo *);
1017 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1018 static void KillPlayerUnlessEnemyProtected(int, int);
1019 static void KillPlayerUnlessExplosionProtected(int, int);
1020
1021 static void TestIfPlayerTouchesCustomElement(int, int);
1022 static void TestIfElementTouchesCustomElement(int, int);
1023 static void TestIfElementHitsCustomElement(int, int, int);
1024
1025 static void HandleElementChange(int, int, int);
1026 static void ExecuteCustomElementAction(int, int, int, int);
1027 static boolean ChangeElement(int, int, int, int);
1028
1029 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1030 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1031         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1032 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1033         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1034 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1035         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1036 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1037         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1038
1039 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1040 #define CheckElementChange(x, y, e, te, ev)                             \
1041         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1042 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1043         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1044 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1045         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1046
1047 static void PlayLevelSound(int, int, int);
1048 static void PlayLevelSoundNearest(int, int, int);
1049 static void PlayLevelSoundAction(int, int, int);
1050 static void PlayLevelSoundElementAction(int, int, int, int);
1051 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1052 static void PlayLevelSoundActionIfLoop(int, int, int);
1053 static void StopLevelSoundActionIfLoop(int, int, int);
1054 static void PlayLevelMusic();
1055
1056 static void HandleGameButtons(struct GadgetInfo *);
1057
1058 int AmoebeNachbarNr(int, int);
1059 void AmoebeUmwandeln(int, int);
1060 void ContinueMoving(int, int);
1061 void Bang(int, int);
1062 void InitMovDir(int, int);
1063 void InitAmoebaNr(int, int);
1064 int NewHiScore(void);
1065
1066 void TestIfGoodThingHitsBadThing(int, int, int);
1067 void TestIfBadThingHitsGoodThing(int, int, int);
1068 void TestIfPlayerTouchesBadThing(int, int);
1069 void TestIfPlayerRunsIntoBadThing(int, int, int);
1070 void TestIfBadThingTouchesPlayer(int, int);
1071 void TestIfBadThingRunsIntoPlayer(int, int, int);
1072 void TestIfFriendTouchesBadThing(int, int);
1073 void TestIfBadThingTouchesFriend(int, int);
1074 void TestIfBadThingTouchesOtherBadThing(int, int);
1075 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1076
1077 void KillPlayer(struct PlayerInfo *);
1078 void BuryPlayer(struct PlayerInfo *);
1079 void RemovePlayer(struct PlayerInfo *);
1080
1081 static int getInvisibleActiveFromInvisibleElement(int);
1082 static int getInvisibleFromInvisibleActiveElement(int);
1083
1084 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1085
1086 /* for detection of endless loops, caused by custom element programming */
1087 /* (using maximal playfield width x 10 is just a rough approximation) */
1088 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1089
1090 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1091 {                                                                       \
1092   if (recursion_loop_detected)                                          \
1093     return (rc);                                                        \
1094                                                                         \
1095   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1096   {                                                                     \
1097     recursion_loop_detected = TRUE;                                     \
1098     recursion_loop_element = (e);                                       \
1099   }                                                                     \
1100                                                                         \
1101   recursion_loop_depth++;                                               \
1102 }
1103
1104 #define RECURSION_LOOP_DETECTION_END()                                  \
1105 {                                                                       \
1106   recursion_loop_depth--;                                               \
1107 }
1108
1109 static int recursion_loop_depth;
1110 static boolean recursion_loop_detected;
1111 static boolean recursion_loop_element;
1112
1113 static int map_player_action[MAX_PLAYERS];
1114
1115
1116 /* ------------------------------------------------------------------------- */
1117 /* definition of elements that automatically change to other elements after  */
1118 /* a specified time, eventually calling a function when changing             */
1119 /* ------------------------------------------------------------------------- */
1120
1121 /* forward declaration for changer functions */
1122 static void InitBuggyBase(int, int);
1123 static void WarnBuggyBase(int, int);
1124
1125 static void InitTrap(int, int);
1126 static void ActivateTrap(int, int);
1127 static void ChangeActiveTrap(int, int);
1128
1129 static void InitRobotWheel(int, int);
1130 static void RunRobotWheel(int, int);
1131 static void StopRobotWheel(int, int);
1132
1133 static void InitTimegateWheel(int, int);
1134 static void RunTimegateWheel(int, int);
1135
1136 static void InitMagicBallDelay(int, int);
1137 static void ActivateMagicBall(int, int);
1138
1139 struct ChangingElementInfo
1140 {
1141   int element;
1142   int target_element;
1143   int change_delay;
1144   void (*pre_change_function)(int x, int y);
1145   void (*change_function)(int x, int y);
1146   void (*post_change_function)(int x, int y);
1147 };
1148
1149 static struct ChangingElementInfo change_delay_list[] =
1150 {
1151   {
1152     EL_NUT_BREAKING,
1153     EL_EMERALD,
1154     6,
1155     NULL,
1156     NULL,
1157     NULL
1158   },
1159   {
1160     EL_PEARL_BREAKING,
1161     EL_EMPTY,
1162     8,
1163     NULL,
1164     NULL,
1165     NULL
1166   },
1167   {
1168     EL_EXIT_OPENING,
1169     EL_EXIT_OPEN,
1170     29,
1171     NULL,
1172     NULL,
1173     NULL
1174   },
1175   {
1176     EL_EXIT_CLOSING,
1177     EL_EXIT_CLOSED,
1178     29,
1179     NULL,
1180     NULL,
1181     NULL
1182   },
1183   {
1184     EL_STEEL_EXIT_OPENING,
1185     EL_STEEL_EXIT_OPEN,
1186     29,
1187     NULL,
1188     NULL,
1189     NULL
1190   },
1191   {
1192     EL_STEEL_EXIT_CLOSING,
1193     EL_STEEL_EXIT_CLOSED,
1194     29,
1195     NULL,
1196     NULL,
1197     NULL
1198   },
1199   {
1200     EL_EM_EXIT_OPENING,
1201     EL_EM_EXIT_OPEN,
1202     29,
1203     NULL,
1204     NULL,
1205     NULL
1206   },
1207   {
1208     EL_EM_EXIT_CLOSING,
1209     EL_EMPTY,
1210     29,
1211     NULL,
1212     NULL,
1213     NULL
1214   },
1215   {
1216     EL_EM_STEEL_EXIT_OPENING,
1217     EL_EM_STEEL_EXIT_OPEN,
1218     29,
1219     NULL,
1220     NULL,
1221     NULL
1222   },
1223   {
1224     EL_EM_STEEL_EXIT_CLOSING,
1225     EL_STEELWALL,
1226     29,
1227     NULL,
1228     NULL,
1229     NULL
1230   },
1231   {
1232     EL_SP_EXIT_OPENING,
1233     EL_SP_EXIT_OPEN,
1234     29,
1235     NULL,
1236     NULL,
1237     NULL
1238   },
1239   {
1240     EL_SP_EXIT_CLOSING,
1241     EL_SP_EXIT_CLOSED,
1242     29,
1243     NULL,
1244     NULL,
1245     NULL
1246   },
1247   {
1248     EL_SWITCHGATE_OPENING,
1249     EL_SWITCHGATE_OPEN,
1250     29,
1251     NULL,
1252     NULL,
1253     NULL
1254   },
1255   {
1256     EL_SWITCHGATE_CLOSING,
1257     EL_SWITCHGATE_CLOSED,
1258     29,
1259     NULL,
1260     NULL,
1261     NULL
1262   },
1263   {
1264     EL_TIMEGATE_OPENING,
1265     EL_TIMEGATE_OPEN,
1266     29,
1267     NULL,
1268     NULL,
1269     NULL
1270   },
1271   {
1272     EL_TIMEGATE_CLOSING,
1273     EL_TIMEGATE_CLOSED,
1274     29,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279
1280   {
1281     EL_ACID_SPLASH_LEFT,
1282     EL_EMPTY,
1283     8,
1284     NULL,
1285     NULL,
1286     NULL
1287   },
1288   {
1289     EL_ACID_SPLASH_RIGHT,
1290     EL_EMPTY,
1291     8,
1292     NULL,
1293     NULL,
1294     NULL
1295   },
1296   {
1297     EL_SP_BUGGY_BASE,
1298     EL_SP_BUGGY_BASE_ACTIVATING,
1299     0,
1300     InitBuggyBase,
1301     NULL,
1302     NULL
1303   },
1304   {
1305     EL_SP_BUGGY_BASE_ACTIVATING,
1306     EL_SP_BUGGY_BASE_ACTIVE,
1307     0,
1308     InitBuggyBase,
1309     NULL,
1310     NULL
1311   },
1312   {
1313     EL_SP_BUGGY_BASE_ACTIVE,
1314     EL_SP_BUGGY_BASE,
1315     0,
1316     InitBuggyBase,
1317     WarnBuggyBase,
1318     NULL
1319   },
1320   {
1321     EL_TRAP,
1322     EL_TRAP_ACTIVE,
1323     0,
1324     InitTrap,
1325     NULL,
1326     ActivateTrap
1327   },
1328   {
1329     EL_TRAP_ACTIVE,
1330     EL_TRAP,
1331     31,
1332     NULL,
1333     ChangeActiveTrap,
1334     NULL
1335   },
1336   {
1337     EL_ROBOT_WHEEL_ACTIVE,
1338     EL_ROBOT_WHEEL,
1339     0,
1340     InitRobotWheel,
1341     RunRobotWheel,
1342     StopRobotWheel
1343   },
1344   {
1345     EL_TIMEGATE_SWITCH_ACTIVE,
1346     EL_TIMEGATE_SWITCH,
1347     0,
1348     InitTimegateWheel,
1349     RunTimegateWheel,
1350     NULL
1351   },
1352   {
1353     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1354     EL_DC_TIMEGATE_SWITCH,
1355     0,
1356     InitTimegateWheel,
1357     RunTimegateWheel,
1358     NULL
1359   },
1360   {
1361     EL_EMC_MAGIC_BALL_ACTIVE,
1362     EL_EMC_MAGIC_BALL_ACTIVE,
1363     0,
1364     InitMagicBallDelay,
1365     NULL,
1366     ActivateMagicBall
1367   },
1368   {
1369     EL_EMC_SPRING_BUMPER_ACTIVE,
1370     EL_EMC_SPRING_BUMPER,
1371     8,
1372     NULL,
1373     NULL,
1374     NULL
1375   },
1376   {
1377     EL_DIAGONAL_SHRINKING,
1378     EL_UNDEFINED,
1379     0,
1380     NULL,
1381     NULL,
1382     NULL
1383   },
1384   {
1385     EL_DIAGONAL_GROWING,
1386     EL_UNDEFINED,
1387     0,
1388     NULL,
1389     NULL,
1390     NULL,
1391   },
1392
1393   {
1394     EL_UNDEFINED,
1395     EL_UNDEFINED,
1396     -1,
1397     NULL,
1398     NULL,
1399     NULL
1400   }
1401 };
1402
1403 struct
1404 {
1405   int element;
1406   int push_delay_fixed, push_delay_random;
1407 }
1408 push_delay_list[] =
1409 {
1410   { EL_SPRING,                  0, 0 },
1411   { EL_BALLOON,                 0, 0 },
1412
1413   { EL_SOKOBAN_OBJECT,          2, 0 },
1414   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1415   { EL_SATELLITE,               2, 0 },
1416   { EL_SP_DISK_YELLOW,          2, 0 },
1417
1418   { EL_UNDEFINED,               0, 0 },
1419 };
1420
1421 struct
1422 {
1423   int element;
1424   int move_stepsize;
1425 }
1426 move_stepsize_list[] =
1427 {
1428   { EL_AMOEBA_DROP,             2 },
1429   { EL_AMOEBA_DROPPING,         2 },
1430   { EL_QUICKSAND_FILLING,       1 },
1431   { EL_QUICKSAND_EMPTYING,      1 },
1432   { EL_QUICKSAND_FAST_FILLING,  2 },
1433   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1434   { EL_MAGIC_WALL_FILLING,      2 },
1435   { EL_MAGIC_WALL_EMPTYING,     2 },
1436   { EL_BD_MAGIC_WALL_FILLING,   2 },
1437   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1438   { EL_DC_MAGIC_WALL_FILLING,   2 },
1439   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1440
1441   { EL_UNDEFINED,               0 },
1442 };
1443
1444 struct
1445 {
1446   int element;
1447   int count;
1448 }
1449 collect_count_list[] =
1450 {
1451   { EL_EMERALD,                 1 },
1452   { EL_BD_DIAMOND,              1 },
1453   { EL_EMERALD_YELLOW,          1 },
1454   { EL_EMERALD_RED,             1 },
1455   { EL_EMERALD_PURPLE,          1 },
1456   { EL_DIAMOND,                 3 },
1457   { EL_SP_INFOTRON,             1 },
1458   { EL_PEARL,                   5 },
1459   { EL_CRYSTAL,                 8 },
1460
1461   { EL_UNDEFINED,               0 },
1462 };
1463
1464 struct
1465 {
1466   int element;
1467   int direction;
1468 }
1469 access_direction_list[] =
1470 {
1471   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1472   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1473   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1474   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1475   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1476   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1477   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1478   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1479   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1480   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1481   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1482
1483   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1484   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1485   { EL_SP_PORT_UP,                                                   MV_DOWN },
1486   { EL_SP_PORT_DOWN,                                         MV_UP           },
1487   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1488   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1489   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1490   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1491   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1492   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1493   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1494   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1495   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1496   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1497   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1498   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1499   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1500   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1501   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1502
1503   { EL_UNDEFINED,                       MV_NONE                              }
1504 };
1505
1506 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1507
1508 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1509 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1510 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1511                                  IS_JUST_CHANGING(x, y))
1512
1513 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1514
1515 /* static variables for playfield scan mode (scanning forward or backward) */
1516 static int playfield_scan_start_x = 0;
1517 static int playfield_scan_start_y = 0;
1518 static int playfield_scan_delta_x = 1;
1519 static int playfield_scan_delta_y = 1;
1520
1521 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1522                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1523                                      (y) += playfield_scan_delta_y)     \
1524                                 for ((x) = playfield_scan_start_x;      \
1525                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1526                                      (x) += playfield_scan_delta_x)
1527
1528 #ifdef DEBUG
1529 void DEBUG_SetMaximumDynamite()
1530 {
1531   int i;
1532
1533   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1534     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1535       local_player->inventory_element[local_player->inventory_size++] =
1536         EL_DYNAMITE;
1537 }
1538 #endif
1539
1540 static void InitPlayfieldScanModeVars()
1541 {
1542   if (game.use_reverse_scan_direction)
1543   {
1544     playfield_scan_start_x = lev_fieldx - 1;
1545     playfield_scan_start_y = lev_fieldy - 1;
1546
1547     playfield_scan_delta_x = -1;
1548     playfield_scan_delta_y = -1;
1549   }
1550   else
1551   {
1552     playfield_scan_start_x = 0;
1553     playfield_scan_start_y = 0;
1554
1555     playfield_scan_delta_x = 1;
1556     playfield_scan_delta_y = 1;
1557   }
1558 }
1559
1560 static void InitPlayfieldScanMode(int mode)
1561 {
1562   game.use_reverse_scan_direction =
1563     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1564
1565   InitPlayfieldScanModeVars();
1566 }
1567
1568 static int get_move_delay_from_stepsize(int move_stepsize)
1569 {
1570   move_stepsize =
1571     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1572
1573   /* make sure that stepsize value is always a power of 2 */
1574   move_stepsize = (1 << log_2(move_stepsize));
1575
1576   return TILEX / move_stepsize;
1577 }
1578
1579 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1580                                boolean init_game)
1581 {
1582   int player_nr = player->index_nr;
1583   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1584   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1585
1586   /* do no immediately change move delay -- the player might just be moving */
1587   player->move_delay_value_next = move_delay;
1588
1589   /* information if player can move must be set separately */
1590   player->cannot_move = cannot_move;
1591
1592   if (init_game)
1593   {
1594     player->move_delay       = game.initial_move_delay[player_nr];
1595     player->move_delay_value = game.initial_move_delay_value[player_nr];
1596
1597     player->move_delay_value_next = -1;
1598
1599     player->move_delay_reset_counter = 0;
1600   }
1601 }
1602
1603 void GetPlayerConfig()
1604 {
1605   GameFrameDelay = setup.game_frame_delay;
1606
1607   if (!audio.sound_available)
1608     setup.sound_simple = FALSE;
1609
1610   if (!audio.loops_available)
1611     setup.sound_loops = FALSE;
1612
1613   if (!audio.music_available)
1614     setup.sound_music = FALSE;
1615
1616   if (!video.fullscreen_available)
1617     setup.fullscreen = FALSE;
1618
1619   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1620
1621   SetAudioMode(setup.sound);
1622 }
1623
1624 int GetElementFromGroupElement(int element)
1625 {
1626   if (IS_GROUP_ELEMENT(element))
1627   {
1628     struct ElementGroupInfo *group = element_info[element].group;
1629     int last_anim_random_frame = gfx.anim_random_frame;
1630     int element_pos;
1631
1632     if (group->choice_mode == ANIM_RANDOM)
1633       gfx.anim_random_frame = RND(group->num_elements_resolved);
1634
1635     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1636                                     group->choice_mode, 0,
1637                                     group->choice_pos);
1638
1639     if (group->choice_mode == ANIM_RANDOM)
1640       gfx.anim_random_frame = last_anim_random_frame;
1641
1642     group->choice_pos++;
1643
1644     element = group->element_resolved[element_pos];
1645   }
1646
1647   return element;
1648 }
1649
1650 static void InitPlayerField(int x, int y, int element, boolean init_game)
1651 {
1652   if (element == EL_SP_MURPHY)
1653   {
1654     if (init_game)
1655     {
1656       if (stored_player[0].present)
1657       {
1658         Feld[x][y] = EL_SP_MURPHY_CLONE;
1659
1660         return;
1661       }
1662       else
1663       {
1664         stored_player[0].initial_element = element;
1665         stored_player[0].use_murphy = TRUE;
1666
1667         if (!level.use_artwork_element[0])
1668           stored_player[0].artwork_element = EL_SP_MURPHY;
1669       }
1670
1671       Feld[x][y] = EL_PLAYER_1;
1672     }
1673   }
1674
1675   if (init_game)
1676   {
1677     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1678     int jx = player->jx, jy = player->jy;
1679
1680     player->present = TRUE;
1681
1682     player->block_last_field = (element == EL_SP_MURPHY ?
1683                                 level.sp_block_last_field :
1684                                 level.block_last_field);
1685
1686     /* ---------- initialize player's last field block delay --------------- */
1687
1688     /* always start with reliable default value (no adjustment needed) */
1689     player->block_delay_adjustment = 0;
1690
1691     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1692     if (player->block_last_field && element == EL_SP_MURPHY)
1693       player->block_delay_adjustment = 1;
1694
1695     /* special case 2: in game engines before 3.1.1, blocking was different */
1696     if (game.use_block_last_field_bug)
1697       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1698
1699     if (!options.network || player->connected)
1700     {
1701       player->active = TRUE;
1702
1703       /* remove potentially duplicate players */
1704       if (StorePlayer[jx][jy] == Feld[x][y])
1705         StorePlayer[jx][jy] = 0;
1706
1707       StorePlayer[x][y] = Feld[x][y];
1708
1709 #if DEBUG_INIT_PLAYER
1710       if (options.debug)
1711       {
1712         printf("- player element %d activated", player->element_nr);
1713         printf(" (local player is %d and currently %s)\n",
1714                local_player->element_nr,
1715                local_player->active ? "active" : "not active");
1716       }
1717     }
1718 #endif
1719
1720     Feld[x][y] = EL_EMPTY;
1721
1722     player->jx = player->last_jx = x;
1723     player->jy = player->last_jy = y;
1724   }
1725
1726   if (!init_game)
1727   {
1728     int player_nr = GET_PLAYER_NR(element);
1729     struct PlayerInfo *player = &stored_player[player_nr];
1730
1731     if (player->active && player->killed)
1732       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1733   }
1734 }
1735
1736 static void InitField(int x, int y, boolean init_game)
1737 {
1738   int element = Feld[x][y];
1739
1740   switch (element)
1741   {
1742     case EL_SP_MURPHY:
1743     case EL_PLAYER_1:
1744     case EL_PLAYER_2:
1745     case EL_PLAYER_3:
1746     case EL_PLAYER_4:
1747       InitPlayerField(x, y, element, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_PLAYER:
1751       element = Feld[x][y] = EL_PLAYER_1;
1752       InitField(x, y, init_game);
1753
1754       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1755       InitField(x, y, init_game);
1756       break;
1757
1758     case EL_SOKOBAN_FIELD_EMPTY:
1759       local_player->sokobanfields_still_needed++;
1760       break;
1761
1762     case EL_STONEBLOCK:
1763       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1764         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1765       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1766         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1767       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1768         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1769       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1770         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1771       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1772         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1773       break;
1774
1775     case EL_BUG:
1776     case EL_BUG_RIGHT:
1777     case EL_BUG_UP:
1778     case EL_BUG_LEFT:
1779     case EL_BUG_DOWN:
1780     case EL_SPACESHIP:
1781     case EL_SPACESHIP_RIGHT:
1782     case EL_SPACESHIP_UP:
1783     case EL_SPACESHIP_LEFT:
1784     case EL_SPACESHIP_DOWN:
1785     case EL_BD_BUTTERFLY:
1786     case EL_BD_BUTTERFLY_RIGHT:
1787     case EL_BD_BUTTERFLY_UP:
1788     case EL_BD_BUTTERFLY_LEFT:
1789     case EL_BD_BUTTERFLY_DOWN:
1790     case EL_BD_FIREFLY:
1791     case EL_BD_FIREFLY_RIGHT:
1792     case EL_BD_FIREFLY_UP:
1793     case EL_BD_FIREFLY_LEFT:
1794     case EL_BD_FIREFLY_DOWN:
1795     case EL_PACMAN_RIGHT:
1796     case EL_PACMAN_UP:
1797     case EL_PACMAN_LEFT:
1798     case EL_PACMAN_DOWN:
1799     case EL_YAMYAM:
1800     case EL_YAMYAM_LEFT:
1801     case EL_YAMYAM_RIGHT:
1802     case EL_YAMYAM_UP:
1803     case EL_YAMYAM_DOWN:
1804     case EL_DARK_YAMYAM:
1805     case EL_ROBOT:
1806     case EL_PACMAN:
1807     case EL_SP_SNIKSNAK:
1808     case EL_SP_ELECTRON:
1809     case EL_MOLE:
1810     case EL_MOLE_LEFT:
1811     case EL_MOLE_RIGHT:
1812     case EL_MOLE_UP:
1813     case EL_MOLE_DOWN:
1814       InitMovDir(x, y);
1815       break;
1816
1817     case EL_AMOEBA_FULL:
1818     case EL_BD_AMOEBA:
1819       InitAmoebaNr(x, y);
1820       break;
1821
1822     case EL_AMOEBA_DROP:
1823       if (y == lev_fieldy - 1)
1824       {
1825         Feld[x][y] = EL_AMOEBA_GROWING;
1826         Store[x][y] = EL_AMOEBA_WET;
1827       }
1828       break;
1829
1830     case EL_DYNAMITE_ACTIVE:
1831     case EL_SP_DISK_RED_ACTIVE:
1832     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1833     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1834     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1835     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1836       MovDelay[x][y] = 96;
1837       break;
1838
1839     case EL_EM_DYNAMITE_ACTIVE:
1840       MovDelay[x][y] = 32;
1841       break;
1842
1843     case EL_LAMP:
1844       local_player->lights_still_needed++;
1845       break;
1846
1847     case EL_PENGUIN:
1848       local_player->friends_still_needed++;
1849       break;
1850
1851     case EL_PIG:
1852     case EL_DRAGON:
1853       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1854       break;
1855
1856     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1859     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1860     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1861     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1862     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1863     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1864     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1865     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1866     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1867     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1868       if (init_game)
1869       {
1870         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1871         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1872         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1873
1874         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1875         {
1876           game.belt_dir[belt_nr] = belt_dir;
1877           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1878         }
1879         else    /* more than one switch -- set it like the first switch */
1880         {
1881           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1882         }
1883       }
1884       break;
1885
1886     case EL_LIGHT_SWITCH_ACTIVE:
1887       if (init_game)
1888         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1889       break;
1890
1891     case EL_INVISIBLE_STEELWALL:
1892     case EL_INVISIBLE_WALL:
1893     case EL_INVISIBLE_SAND:
1894       if (game.light_time_left > 0 ||
1895           game.lenses_time_left > 0)
1896         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1897       break;
1898
1899     case EL_EMC_MAGIC_BALL:
1900       if (game.ball_state)
1901         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1902       break;
1903
1904     case EL_EMC_MAGIC_BALL_SWITCH:
1905       if (game.ball_state)
1906         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1907       break;
1908
1909     case EL_TRIGGER_PLAYER:
1910     case EL_TRIGGER_ELEMENT:
1911     case EL_TRIGGER_CE_VALUE:
1912     case EL_TRIGGER_CE_SCORE:
1913     case EL_SELF:
1914     case EL_ANY_ELEMENT:
1915     case EL_CURRENT_CE_VALUE:
1916     case EL_CURRENT_CE_SCORE:
1917     case EL_PREV_CE_1:
1918     case EL_PREV_CE_2:
1919     case EL_PREV_CE_3:
1920     case EL_PREV_CE_4:
1921     case EL_PREV_CE_5:
1922     case EL_PREV_CE_6:
1923     case EL_PREV_CE_7:
1924     case EL_PREV_CE_8:
1925     case EL_NEXT_CE_1:
1926     case EL_NEXT_CE_2:
1927     case EL_NEXT_CE_3:
1928     case EL_NEXT_CE_4:
1929     case EL_NEXT_CE_5:
1930     case EL_NEXT_CE_6:
1931     case EL_NEXT_CE_7:
1932     case EL_NEXT_CE_8:
1933       /* reference elements should not be used on the playfield */
1934       Feld[x][y] = EL_EMPTY;
1935       break;
1936
1937     default:
1938       if (IS_CUSTOM_ELEMENT(element))
1939       {
1940         if (CAN_MOVE(element))
1941           InitMovDir(x, y);
1942
1943         if (!element_info[element].use_last_ce_value || init_game)
1944           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1945       }
1946       else if (IS_GROUP_ELEMENT(element))
1947       {
1948         Feld[x][y] = GetElementFromGroupElement(element);
1949
1950         InitField(x, y, init_game);
1951       }
1952
1953       break;
1954   }
1955
1956   if (!init_game)
1957     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1958 }
1959
1960 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1961 {
1962   InitField(x, y, init_game);
1963
1964   /* not needed to call InitMovDir() -- already done by InitField()! */
1965   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1966       CAN_MOVE(Feld[x][y]))
1967     InitMovDir(x, y);
1968 }
1969
1970 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1971 {
1972   int old_element = Feld[x][y];
1973
1974   InitField(x, y, init_game);
1975
1976   /* not needed to call InitMovDir() -- already done by InitField()! */
1977   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1978       CAN_MOVE(old_element) &&
1979       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1980     InitMovDir(x, y);
1981
1982   /* this case is in fact a combination of not less than three bugs:
1983      first, it calls InitMovDir() for elements that can move, although this is
1984      already done by InitField(); then, it checks the element that was at this
1985      field _before_ the call to InitField() (which can change it); lastly, it
1986      was not called for "mole with direction" elements, which were treated as
1987      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1988   */
1989 }
1990
1991 static int get_key_element_from_nr(int key_nr)
1992 {
1993   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1994                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1995                           EL_EM_KEY_1 : EL_KEY_1);
1996
1997   return key_base_element + key_nr;
1998 }
1999
2000 static int get_next_dropped_element(struct PlayerInfo *player)
2001 {
2002   return (player->inventory_size > 0 ?
2003           player->inventory_element[player->inventory_size - 1] :
2004           player->inventory_infinite_element != EL_UNDEFINED ?
2005           player->inventory_infinite_element :
2006           player->dynabombs_left > 0 ?
2007           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2008           EL_UNDEFINED);
2009 }
2010
2011 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2012 {
2013   /* pos >= 0: get element from bottom of the stack;
2014      pos <  0: get element from top of the stack */
2015
2016   if (pos < 0)
2017   {
2018     int min_inventory_size = -pos;
2019     int inventory_pos = player->inventory_size - min_inventory_size;
2020     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2021
2022     return (player->inventory_size >= min_inventory_size ?
2023             player->inventory_element[inventory_pos] :
2024             player->inventory_infinite_element != EL_UNDEFINED ?
2025             player->inventory_infinite_element :
2026             player->dynabombs_left >= min_dynabombs_left ?
2027             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2028             EL_UNDEFINED);
2029   }
2030   else
2031   {
2032     int min_dynabombs_left = pos + 1;
2033     int min_inventory_size = pos + 1 - player->dynabombs_left;
2034     int inventory_pos = pos - player->dynabombs_left;
2035
2036     return (player->inventory_infinite_element != EL_UNDEFINED ?
2037             player->inventory_infinite_element :
2038             player->dynabombs_left >= min_dynabombs_left ?
2039             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2040             player->inventory_size >= min_inventory_size ?
2041             player->inventory_element[inventory_pos] :
2042             EL_UNDEFINED);
2043   }
2044 }
2045
2046 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2047 {
2048   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2049   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2050   int compare_result;
2051
2052   if (gpo1->sort_priority != gpo2->sort_priority)
2053     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2054   else
2055     compare_result = gpo1->nr - gpo2->nr;
2056
2057   return compare_result;
2058 }
2059
2060 int getPlayerInventorySize(int player_nr)
2061 {
2062   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2063     return level.native_em_level->ply[player_nr]->dynamite;
2064   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2065     return level.native_sp_level->game_sp->red_disk_count;
2066   else
2067     return stored_player[player_nr].inventory_size;
2068 }
2069
2070 void InitGameControlValues()
2071 {
2072   int i;
2073
2074   for (i = 0; game_panel_controls[i].nr != -1; i++)
2075   {
2076     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2077     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2078     struct TextPosInfo *pos = gpc->pos;
2079     int nr = gpc->nr;
2080     int type = gpc->type;
2081
2082     if (nr != i)
2083     {
2084       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2085       Error(ERR_EXIT, "this should not happen -- please debug");
2086     }
2087
2088     /* force update of game controls after initialization */
2089     gpc->value = gpc->last_value = -1;
2090     gpc->frame = gpc->last_frame = -1;
2091     gpc->gfx_frame = -1;
2092
2093     /* determine panel value width for later calculation of alignment */
2094     if (type == TYPE_INTEGER || type == TYPE_STRING)
2095     {
2096       pos->width = pos->size * getFontWidth(pos->font);
2097       pos->height = getFontHeight(pos->font);
2098     }
2099     else if (type == TYPE_ELEMENT)
2100     {
2101       pos->width = pos->size;
2102       pos->height = pos->size;
2103     }
2104
2105     /* fill structure for game panel draw order */
2106     gpo->nr = gpc->nr;
2107     gpo->sort_priority = pos->sort_priority;
2108   }
2109
2110   /* sort game panel controls according to sort_priority and control number */
2111   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2112         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2113 }
2114
2115 void UpdatePlayfieldElementCount()
2116 {
2117   boolean use_element_count = FALSE;
2118   int i, j, x, y;
2119
2120   /* first check if it is needed at all to calculate playfield element count */
2121   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2122     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2123       use_element_count = TRUE;
2124
2125   if (!use_element_count)
2126     return;
2127
2128   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2129     element_info[i].element_count = 0;
2130
2131   SCAN_PLAYFIELD(x, y)
2132   {
2133     element_info[Feld[x][y]].element_count++;
2134   }
2135
2136   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2137     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2138       if (IS_IN_GROUP(j, i))
2139         element_info[EL_GROUP_START + i].element_count +=
2140           element_info[j].element_count;
2141 }
2142
2143 void UpdateGameControlValues()
2144 {
2145   int i, k;
2146   int time = (local_player->LevelSolved ?
2147               local_player->LevelSolved_CountingTime :
2148               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2149               level.native_em_level->lev->time :
2150               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2151               level.native_sp_level->game_sp->time_played :
2152               game.no_time_limit ? TimePlayed : TimeLeft);
2153   int score = (local_player->LevelSolved ?
2154                local_player->LevelSolved_CountingScore :
2155                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2156                level.native_em_level->lev->score :
2157                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2158                level.native_sp_level->game_sp->score :
2159                local_player->score);
2160   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2161               level.native_em_level->lev->required :
2162               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2163               level.native_sp_level->game_sp->infotrons_still_needed :
2164               local_player->gems_still_needed);
2165   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2166                      level.native_em_level->lev->required > 0 :
2167                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2168                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2169                      local_player->gems_still_needed > 0 ||
2170                      local_player->sokobanfields_still_needed > 0 ||
2171                      local_player->lights_still_needed > 0);
2172
2173   UpdatePlayfieldElementCount();
2174
2175   /* update game panel control values */
2176
2177   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2178   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2179
2180   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2181   for (i = 0; i < MAX_NUM_KEYS; i++)
2182     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2183   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2184   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2185
2186   if (game.centered_player_nr == -1)
2187   {
2188     for (i = 0; i < MAX_PLAYERS; i++)
2189     {
2190       /* only one player in Supaplex game engine */
2191       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2192         break;
2193
2194       for (k = 0; k < MAX_NUM_KEYS; k++)
2195       {
2196         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2197         {
2198           if (level.native_em_level->ply[i]->keys & (1 << k))
2199             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2200               get_key_element_from_nr(k);
2201         }
2202         else if (stored_player[i].key[k])
2203           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2204             get_key_element_from_nr(k);
2205       }
2206
2207       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2208         getPlayerInventorySize(i);
2209
2210       if (stored_player[i].num_white_keys > 0)
2211         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2212           EL_DC_KEY_WHITE;
2213
2214       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2215         stored_player[i].num_white_keys;
2216     }
2217   }
2218   else
2219   {
2220     int player_nr = game.centered_player_nr;
2221
2222     for (k = 0; k < MAX_NUM_KEYS; k++)
2223     {
2224       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       {
2226         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2227           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2228             get_key_element_from_nr(k);
2229       }
2230       else if (stored_player[player_nr].key[k])
2231         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2232           get_key_element_from_nr(k);
2233     }
2234
2235     game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2236       getPlayerInventorySize(player_nr);
2237
2238     if (stored_player[player_nr].num_white_keys > 0)
2239       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2240
2241     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2242       stored_player[player_nr].num_white_keys;
2243   }
2244
2245   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2246   {
2247     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2248       get_inventory_element_from_pos(local_player, i);
2249     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2250       get_inventory_element_from_pos(local_player, -i - 1);
2251   }
2252
2253   game_panel_controls[GAME_PANEL_SCORE].value = score;
2254   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2255
2256   game_panel_controls[GAME_PANEL_TIME].value = time;
2257
2258   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2259   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2260   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2261
2262   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2263
2264   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2265     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2268     local_player->shield_normal_time_left;
2269   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2270     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2271      EL_EMPTY);
2272   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2273     local_player->shield_deadly_time_left;
2274
2275   game_panel_controls[GAME_PANEL_EXIT].value =
2276     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2277
2278   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2279     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2280   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2281     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2282      EL_EMC_MAGIC_BALL_SWITCH);
2283
2284   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2285     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2286   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2287     game.light_time_left;
2288
2289   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2290     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2291   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2292     game.timegate_time_left;
2293
2294   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2295     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2296
2297   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2298     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2300     game.lenses_time_left;
2301
2302   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2303     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2304   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2305     game.magnify_time_left;
2306
2307   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2308     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2309      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2310      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2311      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2312      EL_BALLOON_SWITCH_NONE);
2313
2314   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2315     local_player->dynabomb_count;
2316   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2317     local_player->dynabomb_size;
2318   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2319     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2320
2321   game_panel_controls[GAME_PANEL_PENGUINS].value =
2322     local_player->friends_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2325     local_player->sokobanfields_still_needed;
2326   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2327     local_player->sokobanfields_still_needed;
2328
2329   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2330     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2331
2332   for (i = 0; i < NUM_BELTS; i++)
2333   {
2334     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2335       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2336        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2337     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2338       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2339   }
2340
2341   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2342     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2343   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2344     game.magic_wall_time_left;
2345
2346   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2347     local_player->gravity;
2348
2349   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2350     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2354       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2355        game.panel.element[i].id : EL_UNDEFINED);
2356
2357   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2358     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2359       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2360        element_info[game.panel.element_count[i].id].element_count : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2365        element_info[game.panel.ce_score[i].id].collect_score : 0);
2366
2367   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2368     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2369       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2370        element_info[game.panel.ce_score_element[i].id].collect_score :
2371        EL_UNDEFINED);
2372
2373   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2374   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2375   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2376
2377   /* update game panel control frames */
2378
2379   for (i = 0; game_panel_controls[i].nr != -1; i++)
2380   {
2381     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2382
2383     if (gpc->type == TYPE_ELEMENT)
2384     {
2385       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2386       {
2387         int last_anim_random_frame = gfx.anim_random_frame;
2388         int element = gpc->value;
2389         int graphic = el2panelimg(element);
2390
2391         if (gpc->value != gpc->last_value)
2392         {
2393           gpc->gfx_frame = 0;
2394           gpc->gfx_random = INIT_GFX_RANDOM();
2395         }
2396         else
2397         {
2398           gpc->gfx_frame++;
2399
2400           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2401               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2402             gpc->gfx_random = INIT_GFX_RANDOM();
2403         }
2404
2405         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2406           gfx.anim_random_frame = gpc->gfx_random;
2407
2408         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2409           gpc->gfx_frame = element_info[element].collect_score;
2410
2411         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2412                                               gpc->gfx_frame);
2413
2414         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2415           gfx.anim_random_frame = last_anim_random_frame;
2416       }
2417     }
2418   }
2419 }
2420
2421 void DisplayGameControlValues()
2422 {
2423   boolean redraw_panel = FALSE;
2424   int i;
2425
2426   for (i = 0; game_panel_controls[i].nr != -1; i++)
2427   {
2428     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2429
2430     if (PANEL_DEACTIVATED(gpc->pos))
2431       continue;
2432
2433     if (gpc->value == gpc->last_value &&
2434         gpc->frame == gpc->last_frame)
2435       continue;
2436
2437     redraw_panel = TRUE;
2438   }
2439
2440   if (!redraw_panel)
2441     return;
2442
2443   /* copy default game door content to main double buffer */
2444
2445   /* !!! CHECK AGAIN !!! */
2446   SetPanelBackground();
2447   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2448   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2449
2450   /* redraw game control buttons */
2451   RedrawGameButtons();
2452
2453   SetGameStatus(GAME_MODE_PSEUDO_PANEL);
2454
2455   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2456   {
2457     int nr = game_panel_order[i].nr;
2458     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2459     struct TextPosInfo *pos = gpc->pos;
2460     int type = gpc->type;
2461     int value = gpc->value;
2462     int frame = gpc->frame;
2463     int size = pos->size;
2464     int font = pos->font;
2465     boolean draw_masked = pos->draw_masked;
2466     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2467
2468     if (PANEL_DEACTIVATED(pos))
2469       continue;
2470
2471     gpc->last_value = value;
2472     gpc->last_frame = frame;
2473
2474     if (type == TYPE_INTEGER)
2475     {
2476       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2477           nr == GAME_PANEL_TIME)
2478       {
2479         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2480
2481         if (use_dynamic_size)           /* use dynamic number of digits */
2482         {
2483           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2484           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2485           int size2 = size1 + 1;
2486           int font1 = pos->font;
2487           int font2 = pos->font_alt;
2488
2489           size = (value < value_change ? size1 : size2);
2490           font = (value < value_change ? font1 : font2);
2491         }
2492       }
2493
2494       /* correct text size if "digits" is zero or less */
2495       if (size <= 0)
2496         size = strlen(int2str(value, size));
2497
2498       /* dynamically correct text alignment */
2499       pos->width = size * getFontWidth(font);
2500
2501       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2502                   int2str(value, size), font, mask_mode);
2503     }
2504     else if (type == TYPE_ELEMENT)
2505     {
2506       int element, graphic;
2507       Bitmap *src_bitmap;
2508       int src_x, src_y;
2509       int width, height;
2510       int dst_x = PANEL_XPOS(pos);
2511       int dst_y = PANEL_YPOS(pos);
2512
2513       if (value != EL_UNDEFINED && value != EL_EMPTY)
2514       {
2515         element = value;
2516         graphic = el2panelimg(value);
2517
2518         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2519
2520         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2521           size = TILESIZE;
2522
2523         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2524                               &src_x, &src_y);
2525
2526         width  = graphic_info[graphic].width  * size / TILESIZE;
2527         height = graphic_info[graphic].height * size / TILESIZE;
2528
2529         if (draw_masked)
2530           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2531                            dst_x, dst_y);
2532         else
2533           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2534                      dst_x, dst_y);
2535       }
2536     }
2537     else if (type == TYPE_STRING)
2538     {
2539       boolean active = (value != 0);
2540       char *state_normal = "off";
2541       char *state_active = "on";
2542       char *state = (active ? state_active : state_normal);
2543       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2544                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2545                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2546                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2547
2548       if (nr == GAME_PANEL_GRAVITY_STATE)
2549       {
2550         int font1 = pos->font;          /* (used for normal state) */
2551         int font2 = pos->font_alt;      /* (used for active state) */
2552
2553         font = (active ? font2 : font1);
2554       }
2555
2556       if (s != NULL)
2557       {
2558         char *s_cut;
2559
2560         if (size <= 0)
2561         {
2562           /* don't truncate output if "chars" is zero or less */
2563           size = strlen(s);
2564
2565           /* dynamically correct text alignment */
2566           pos->width = size * getFontWidth(font);
2567         }
2568
2569         s_cut = getStringCopyN(s, size);
2570
2571         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2572                     s_cut, font, mask_mode);
2573
2574         free(s_cut);
2575       }
2576     }
2577
2578     redraw_mask |= REDRAW_DOOR_1;
2579   }
2580
2581   SetGameStatus(GAME_MODE_PLAYING);
2582 }
2583
2584 void UpdateAndDisplayGameControlValues()
2585 {
2586   if (tape.deactivate_display)
2587     return;
2588
2589   UpdateGameControlValues();
2590   DisplayGameControlValues();
2591 }
2592
2593 void UpdateGameDoorValues()
2594 {
2595   UpdateGameControlValues();
2596 }
2597
2598 void DrawGameDoorValues()
2599 {
2600   DisplayGameControlValues();
2601 }
2602
2603
2604 /*
2605   =============================================================================
2606   InitGameEngine()
2607   -----------------------------------------------------------------------------
2608   initialize game engine due to level / tape version number
2609   =============================================================================
2610 */
2611
2612 static void InitGameEngine()
2613 {
2614   int i, j, k, l, x, y;
2615
2616   /* set game engine from tape file when re-playing, else from level file */
2617   game.engine_version = (tape.playing ? tape.engine_version :
2618                          level.game_version);
2619
2620   /* set single or multi-player game mode (needed for re-playing tapes) */
2621   game.team_mode = setup.team_mode;
2622
2623   if (tape.playing)
2624   {
2625     int num_players = 0;
2626
2627     for (i = 0; i < MAX_PLAYERS; i++)
2628       if (tape.player_participates[i])
2629         num_players++;
2630
2631     /* multi-player tapes contain input data for more than one player */
2632     game.team_mode = (num_players > 1);
2633   }
2634
2635   /* ---------------------------------------------------------------------- */
2636   /* set flags for bugs and changes according to active game engine version */
2637   /* ---------------------------------------------------------------------- */
2638
2639   /*
2640     Summary of bugfix/change:
2641     Fixed handling for custom elements that change when pushed by the player.
2642
2643     Fixed/changed in version:
2644     3.1.0
2645
2646     Description:
2647     Before 3.1.0, custom elements that "change when pushing" changed directly
2648     after the player started pushing them (until then handled in "DigField()").
2649     Since 3.1.0, these custom elements are not changed until the "pushing"
2650     move of the element is finished (now handled in "ContinueMoving()").
2651
2652     Affected levels/tapes:
2653     The first condition is generally needed for all levels/tapes before version
2654     3.1.0, which might use the old behaviour before it was changed; known tapes
2655     that are affected are some tapes from the level set "Walpurgis Gardens" by
2656     Jamie Cullen.
2657     The second condition is an exception from the above case and is needed for
2658     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2659     above (including some development versions of 3.1.0), but before it was
2660     known that this change would break tapes like the above and was fixed in
2661     3.1.1, so that the changed behaviour was active although the engine version
2662     while recording maybe was before 3.1.0. There is at least one tape that is
2663     affected by this exception, which is the tape for the one-level set "Bug
2664     Machine" by Juergen Bonhagen.
2665   */
2666
2667   game.use_change_when_pushing_bug =
2668     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2669      !(tape.playing &&
2670        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2671        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2672
2673   /*
2674     Summary of bugfix/change:
2675     Fixed handling for blocking the field the player leaves when moving.
2676
2677     Fixed/changed in version:
2678     3.1.1
2679
2680     Description:
2681     Before 3.1.1, when "block last field when moving" was enabled, the field
2682     the player is leaving when moving was blocked for the time of the move,
2683     and was directly unblocked afterwards. This resulted in the last field
2684     being blocked for exactly one less than the number of frames of one player
2685     move. Additionally, even when blocking was disabled, the last field was
2686     blocked for exactly one frame.
2687     Since 3.1.1, due to changes in player movement handling, the last field
2688     is not blocked at all when blocking is disabled. When blocking is enabled,
2689     the last field is blocked for exactly the number of frames of one player
2690     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2691     last field is blocked for exactly one more than the number of frames of
2692     one player move.
2693
2694     Affected levels/tapes:
2695     (!!! yet to be determined -- probably many !!!)
2696   */
2697
2698   game.use_block_last_field_bug =
2699     (game.engine_version < VERSION_IDENT(3,1,1,0));
2700
2701   game_em.use_single_button =
2702     (game.engine_version > VERSION_IDENT(4,0,0,2));
2703
2704   /* ---------------------------------------------------------------------- */
2705
2706   /* set maximal allowed number of custom element changes per game frame */
2707   game.max_num_changes_per_frame = 1;
2708
2709   /* default scan direction: scan playfield from top/left to bottom/right */
2710   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2711
2712   /* dynamically adjust element properties according to game engine version */
2713   InitElementPropertiesEngine(game.engine_version);
2714
2715 #if 0
2716   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2717   printf("          tape version == %06d [%s] [file: %06d]\n",
2718          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2719          tape.file_version);
2720   printf("       => game.engine_version == %06d\n", game.engine_version);
2721 #endif
2722
2723   /* ---------- initialize player's initial move delay --------------------- */
2724
2725   /* dynamically adjust player properties according to level information */
2726   for (i = 0; i < MAX_PLAYERS; i++)
2727     game.initial_move_delay_value[i] =
2728       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   for (i = 0; i < MAX_PLAYERS; i++)
2732     game.initial_move_delay[i] =
2733       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2734        game.initial_move_delay_value[i] : 0);
2735
2736   /* ---------- initialize player's initial push delay --------------------- */
2737
2738   /* dynamically adjust player properties according to game engine version */
2739   game.initial_push_delay_value =
2740     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2741
2742   /* ---------- initialize changing elements ------------------------------- */
2743
2744   /* initialize changing elements information */
2745   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2746   {
2747     struct ElementInfo *ei = &element_info[i];
2748
2749     /* this pointer might have been changed in the level editor */
2750     ei->change = &ei->change_page[0];
2751
2752     if (!IS_CUSTOM_ELEMENT(i))
2753     {
2754       ei->change->target_element = EL_EMPTY_SPACE;
2755       ei->change->delay_fixed = 0;
2756       ei->change->delay_random = 0;
2757       ei->change->delay_frames = 1;
2758     }
2759
2760     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2761     {
2762       ei->has_change_event[j] = FALSE;
2763
2764       ei->event_page_nr[j] = 0;
2765       ei->event_page[j] = &ei->change_page[0];
2766     }
2767   }
2768
2769   /* add changing elements from pre-defined list */
2770   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2771   {
2772     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2773     struct ElementInfo *ei = &element_info[ch_delay->element];
2774
2775     ei->change->target_element       = ch_delay->target_element;
2776     ei->change->delay_fixed          = ch_delay->change_delay;
2777
2778     ei->change->pre_change_function  = ch_delay->pre_change_function;
2779     ei->change->change_function      = ch_delay->change_function;
2780     ei->change->post_change_function = ch_delay->post_change_function;
2781
2782     ei->change->can_change = TRUE;
2783     ei->change->can_change_or_has_action = TRUE;
2784
2785     ei->has_change_event[CE_DELAY] = TRUE;
2786
2787     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2788     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2789   }
2790
2791   /* ---------- initialize internal run-time variables --------------------- */
2792
2793   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2794   {
2795     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2796
2797     for (j = 0; j < ei->num_change_pages; j++)
2798     {
2799       ei->change_page[j].can_change_or_has_action =
2800         (ei->change_page[j].can_change |
2801          ei->change_page[j].has_action);
2802     }
2803   }
2804
2805   /* add change events from custom element configuration */
2806   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2807   {
2808     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2809
2810     for (j = 0; j < ei->num_change_pages; j++)
2811     {
2812       if (!ei->change_page[j].can_change_or_has_action)
2813         continue;
2814
2815       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2816       {
2817         /* only add event page for the first page found with this event */
2818         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2819         {
2820           ei->has_change_event[k] = TRUE;
2821
2822           ei->event_page_nr[k] = j;
2823           ei->event_page[k] = &ei->change_page[j];
2824         }
2825       }
2826     }
2827   }
2828
2829   /* ---------- initialize reference elements in change conditions --------- */
2830
2831   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2832   {
2833     int element = EL_CUSTOM_START + i;
2834     struct ElementInfo *ei = &element_info[element];
2835
2836     for (j = 0; j < ei->num_change_pages; j++)
2837     {
2838       int trigger_element = ei->change_page[j].initial_trigger_element;
2839
2840       if (trigger_element >= EL_PREV_CE_8 &&
2841           trigger_element <= EL_NEXT_CE_8)
2842         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2843
2844       ei->change_page[j].trigger_element = trigger_element;
2845     }
2846   }
2847
2848   /* ---------- initialize run-time trigger player and element ------------- */
2849
2850   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2851   {
2852     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2853
2854     for (j = 0; j < ei->num_change_pages; j++)
2855     {
2856       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2857       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2858       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2859       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2860       ei->change_page[j].actual_trigger_ce_value = 0;
2861       ei->change_page[j].actual_trigger_ce_score = 0;
2862     }
2863   }
2864
2865   /* ---------- initialize trigger events ---------------------------------- */
2866
2867   /* initialize trigger events information */
2868   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2869     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2870       trigger_events[i][j] = FALSE;
2871
2872   /* add trigger events from element change event properties */
2873   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2874   {
2875     struct ElementInfo *ei = &element_info[i];
2876
2877     for (j = 0; j < ei->num_change_pages; j++)
2878     {
2879       if (!ei->change_page[j].can_change_or_has_action)
2880         continue;
2881
2882       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2883       {
2884         int trigger_element = ei->change_page[j].trigger_element;
2885
2886         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2887         {
2888           if (ei->change_page[j].has_event[k])
2889           {
2890             if (IS_GROUP_ELEMENT(trigger_element))
2891             {
2892               struct ElementGroupInfo *group =
2893                 element_info[trigger_element].group;
2894
2895               for (l = 0; l < group->num_elements_resolved; l++)
2896                 trigger_events[group->element_resolved[l]][k] = TRUE;
2897             }
2898             else if (trigger_element == EL_ANY_ELEMENT)
2899               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2900                 trigger_events[l][k] = TRUE;
2901             else
2902               trigger_events[trigger_element][k] = TRUE;
2903           }
2904         }
2905       }
2906     }
2907   }
2908
2909   /* ---------- initialize push delay -------------------------------------- */
2910
2911   /* initialize push delay values to default */
2912   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2913   {
2914     if (!IS_CUSTOM_ELEMENT(i))
2915     {
2916       /* set default push delay values (corrected since version 3.0.7-1) */
2917       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2918       {
2919         element_info[i].push_delay_fixed = 2;
2920         element_info[i].push_delay_random = 8;
2921       }
2922       else
2923       {
2924         element_info[i].push_delay_fixed = 8;
2925         element_info[i].push_delay_random = 8;
2926       }
2927     }
2928   }
2929
2930   /* set push delay value for certain elements from pre-defined list */
2931   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2932   {
2933     int e = push_delay_list[i].element;
2934
2935     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2936     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2937   }
2938
2939   /* set push delay value for Supaplex elements for newer engine versions */
2940   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2941   {
2942     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2943     {
2944       if (IS_SP_ELEMENT(i))
2945       {
2946         /* set SP push delay to just enough to push under a falling zonk */
2947         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2948
2949         element_info[i].push_delay_fixed  = delay;
2950         element_info[i].push_delay_random = 0;
2951       }
2952     }
2953   }
2954
2955   /* ---------- initialize move stepsize ----------------------------------- */
2956
2957   /* initialize move stepsize values to default */
2958   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2959     if (!IS_CUSTOM_ELEMENT(i))
2960       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2961
2962   /* set move stepsize value for certain elements from pre-defined list */
2963   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2964   {
2965     int e = move_stepsize_list[i].element;
2966
2967     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2968   }
2969
2970   /* ---------- initialize collect score ----------------------------------- */
2971
2972   /* initialize collect score values for custom elements from initial value */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_score = element_info[i].collect_score_initial;
2976
2977   /* ---------- initialize collect count ----------------------------------- */
2978
2979   /* initialize collect count values for non-custom elements */
2980   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2981     if (!IS_CUSTOM_ELEMENT(i))
2982       element_info[i].collect_count_initial = 0;
2983
2984   /* add collect count values for all elements from pre-defined list */
2985   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2986     element_info[collect_count_list[i].element].collect_count_initial =
2987       collect_count_list[i].count;
2988
2989   /* ---------- initialize access direction -------------------------------- */
2990
2991   /* initialize access direction values to default (access from every side) */
2992   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2993     if (!IS_CUSTOM_ELEMENT(i))
2994       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2995
2996   /* set access direction value for certain elements from pre-defined list */
2997   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2998     element_info[access_direction_list[i].element].access_direction =
2999       access_direction_list[i].direction;
3000
3001   /* ---------- initialize explosion content ------------------------------- */
3002   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3003   {
3004     if (IS_CUSTOM_ELEMENT(i))
3005       continue;
3006
3007     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3008     {
3009       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3010
3011       element_info[i].content.e[x][y] =
3012         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3013          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3014          i == EL_PLAYER_3 ? EL_EMERALD :
3015          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3016          i == EL_MOLE ? EL_EMERALD_RED :
3017          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3018          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3019          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3020          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3021          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3022          i == EL_WALL_EMERALD ? EL_EMERALD :
3023          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3024          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3025          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3026          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3027          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3028          i == EL_WALL_PEARL ? EL_PEARL :
3029          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3030          EL_EMPTY);
3031     }
3032   }
3033
3034   /* ---------- initialize recursion detection ------------------------------ */
3035   recursion_loop_depth = 0;
3036   recursion_loop_detected = FALSE;
3037   recursion_loop_element = EL_UNDEFINED;
3038
3039   /* ---------- initialize graphics engine ---------------------------------- */
3040   game.scroll_delay_value =
3041     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3042      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3043   game.scroll_delay_value =
3044     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3045
3046   /* ---------- initialize game engine snapshots ---------------------------- */
3047   for (i = 0; i < MAX_PLAYERS; i++)
3048     game.snapshot.last_action[i] = 0;
3049   game.snapshot.changed_action = FALSE;
3050   game.snapshot.collected_item = FALSE;
3051   game.snapshot.mode =
3052     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3053      SNAPSHOT_MODE_EVERY_STEP :
3054      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3055      SNAPSHOT_MODE_EVERY_MOVE :
3056      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_COLLECT) ?
3057      SNAPSHOT_MODE_EVERY_COLLECT : SNAPSHOT_MODE_OFF);
3058   game.snapshot.save_snapshot = FALSE;
3059 }
3060
3061 int get_num_special_action(int element, int action_first, int action_last)
3062 {
3063   int num_special_action = 0;
3064   int i, j;
3065
3066   for (i = action_first; i <= action_last; i++)
3067   {
3068     boolean found = FALSE;
3069
3070     for (j = 0; j < NUM_DIRECTIONS; j++)
3071       if (el_act_dir2img(element, i, j) !=
3072           el_act_dir2img(element, ACTION_DEFAULT, j))
3073         found = TRUE;
3074
3075     if (found)
3076       num_special_action++;
3077     else
3078       break;
3079   }
3080
3081   return num_special_action;
3082 }
3083
3084
3085 /*
3086   =============================================================================
3087   InitGame()
3088   -----------------------------------------------------------------------------
3089   initialize and start new game
3090   =============================================================================
3091 */
3092
3093 void InitGame()
3094 {
3095   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3096   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3097   int fade_mask = REDRAW_FIELD;
3098
3099   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3100   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3101   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3102   int initial_move_dir = MV_DOWN;
3103   int i, j, x, y;
3104
3105   // required here to update video display before fading (FIX THIS)
3106   DrawMaskedBorder(REDRAW_DOOR_2);
3107
3108   if (!game.restart_level)
3109     CloseDoor(DOOR_CLOSE_1);
3110
3111   SetGameStatus(GAME_MODE_PLAYING);
3112
3113   if (level_editor_test_game)
3114     FadeSkipNextFadeIn();
3115   else
3116     FadeSetEnterScreen();
3117
3118   if (CheckIfGlobalBorderHasChanged())
3119     fade_mask = REDRAW_ALL;
3120
3121   FadeSoundsAndMusic();
3122
3123   ExpireSoundLoops(TRUE);
3124
3125   FadeOut(fade_mask);
3126
3127   /* needed if different viewport properties defined for playing */
3128   ChangeViewportPropertiesIfNeeded();
3129
3130   ClearField();
3131
3132   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3133
3134   DrawCompleteVideoDisplay();
3135
3136   InitGameEngine();
3137   InitGameControlValues();
3138
3139   /* don't play tapes over network */
3140   network_playing = (options.network && !tape.playing);
3141
3142   for (i = 0; i < MAX_PLAYERS; i++)
3143   {
3144     struct PlayerInfo *player = &stored_player[i];
3145
3146     player->index_nr = i;
3147     player->index_bit = (1 << i);
3148     player->element_nr = EL_PLAYER_1 + i;
3149
3150     player->present = FALSE;
3151     player->active = FALSE;
3152     player->mapped = FALSE;
3153
3154     player->killed = FALSE;
3155     player->reanimated = FALSE;
3156
3157     player->action = 0;
3158     player->effective_action = 0;
3159     player->programmed_action = 0;
3160
3161     player->score = 0;
3162     player->score_final = 0;
3163
3164     player->gems_still_needed = level.gems_needed;
3165     player->sokobanfields_still_needed = 0;
3166     player->lights_still_needed = 0;
3167     player->friends_still_needed = 0;
3168
3169     for (j = 0; j < MAX_NUM_KEYS; j++)
3170       player->key[j] = FALSE;
3171
3172     player->num_white_keys = 0;
3173
3174     player->dynabomb_count = 0;
3175     player->dynabomb_size = 1;
3176     player->dynabombs_left = 0;
3177     player->dynabomb_xl = FALSE;
3178
3179     player->MovDir = initial_move_dir;
3180     player->MovPos = 0;
3181     player->GfxPos = 0;
3182     player->GfxDir = initial_move_dir;
3183     player->GfxAction = ACTION_DEFAULT;
3184     player->Frame = 0;
3185     player->StepFrame = 0;
3186
3187     player->initial_element = player->element_nr;
3188     player->artwork_element =
3189       (level.use_artwork_element[i] ? level.artwork_element[i] :
3190        player->element_nr);
3191     player->use_murphy = FALSE;
3192
3193     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3194     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3195
3196     player->gravity = level.initial_player_gravity[i];
3197
3198     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3199
3200     player->actual_frame_counter = 0;
3201
3202     player->step_counter = 0;
3203
3204     player->last_move_dir = initial_move_dir;
3205
3206     player->is_active = FALSE;
3207
3208     player->is_waiting = FALSE;
3209     player->is_moving = FALSE;
3210     player->is_auto_moving = FALSE;
3211     player->is_digging = FALSE;
3212     player->is_snapping = FALSE;
3213     player->is_collecting = FALSE;
3214     player->is_pushing = FALSE;
3215     player->is_switching = FALSE;
3216     player->is_dropping = FALSE;
3217     player->is_dropping_pressed = FALSE;
3218
3219     player->is_bored = FALSE;
3220     player->is_sleeping = FALSE;
3221
3222     player->was_waiting = TRUE;
3223     player->was_moving = FALSE;
3224     player->was_snapping = FALSE;
3225     player->was_dropping = FALSE;
3226
3227     player->force_dropping = FALSE;
3228
3229     player->frame_counter_bored = -1;
3230     player->frame_counter_sleeping = -1;
3231
3232     player->anim_delay_counter = 0;
3233     player->post_delay_counter = 0;
3234
3235     player->dir_waiting = initial_move_dir;
3236     player->action_waiting = ACTION_DEFAULT;
3237     player->last_action_waiting = ACTION_DEFAULT;
3238     player->special_action_bored = ACTION_DEFAULT;
3239     player->special_action_sleeping = ACTION_DEFAULT;
3240
3241     player->switch_x = -1;
3242     player->switch_y = -1;
3243
3244     player->drop_x = -1;
3245     player->drop_y = -1;
3246
3247     player->show_envelope = 0;
3248
3249     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3250
3251     player->push_delay       = -1;      /* initialized when pushing starts */
3252     player->push_delay_value = game.initial_push_delay_value;
3253
3254     player->drop_delay = 0;
3255     player->drop_pressed_delay = 0;
3256
3257     player->last_jx = -1;
3258     player->last_jy = -1;
3259     player->jx = -1;
3260     player->jy = -1;
3261
3262     player->shield_normal_time_left = 0;
3263     player->shield_deadly_time_left = 0;
3264
3265     player->inventory_infinite_element = EL_UNDEFINED;
3266     player->inventory_size = 0;
3267
3268     if (level.use_initial_inventory[i])
3269     {
3270       for (j = 0; j < level.initial_inventory_size[i]; j++)
3271       {
3272         int element = level.initial_inventory_content[i][j];
3273         int collect_count = element_info[element].collect_count_initial;
3274         int k;
3275
3276         if (!IS_CUSTOM_ELEMENT(element))
3277           collect_count = 1;
3278
3279         if (collect_count == 0)
3280           player->inventory_infinite_element = element;
3281         else
3282           for (k = 0; k < collect_count; k++)
3283             if (player->inventory_size < MAX_INVENTORY_SIZE)
3284               player->inventory_element[player->inventory_size++] = element;
3285       }
3286     }
3287
3288     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3289     SnapField(player, 0, 0);
3290
3291     player->LevelSolved = FALSE;
3292     player->GameOver = FALSE;
3293
3294     player->LevelSolved_GameWon = FALSE;
3295     player->LevelSolved_GameEnd = FALSE;
3296     player->LevelSolved_PanelOff = FALSE;
3297     player->LevelSolved_SaveTape = FALSE;
3298     player->LevelSolved_SaveScore = FALSE;
3299     player->LevelSolved_CountingTime = 0;
3300     player->LevelSolved_CountingScore = 0;
3301
3302     map_player_action[i] = i;
3303   }
3304
3305   network_player_action_received = FALSE;
3306
3307 #if defined(NETWORK_AVALIABLE)
3308   /* initial null action */
3309   if (network_playing)
3310     SendToServer_MovePlayer(MV_NONE);
3311 #endif
3312
3313   ZX = ZY = -1;
3314   ExitX = ExitY = -1;
3315
3316   FrameCounter = 0;
3317   TimeFrames = 0;
3318   TimePlayed = 0;
3319   TimeLeft = level.time;
3320   TapeTime = 0;
3321
3322   ScreenMovDir = MV_NONE;
3323   ScreenMovPos = 0;
3324   ScreenGfxPos = 0;
3325
3326   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3327
3328   AllPlayersGone = FALSE;
3329
3330   game.no_time_limit = (level.time == 0);
3331
3332   game.yamyam_content_nr = 0;
3333   game.robot_wheel_active = FALSE;
3334   game.magic_wall_active = FALSE;
3335   game.magic_wall_time_left = 0;
3336   game.light_time_left = 0;
3337   game.timegate_time_left = 0;
3338   game.switchgate_pos = 0;
3339   game.wind_direction = level.wind_direction_initial;
3340
3341   game.lenses_time_left = 0;
3342   game.magnify_time_left = 0;
3343
3344   game.ball_state = level.ball_state_initial;
3345   game.ball_content_nr = 0;
3346
3347   game.envelope_active = FALSE;
3348
3349   /* set focus to local player for network games, else to all players */
3350   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3351   game.centered_player_nr_next = game.centered_player_nr;
3352   game.set_centered_player = FALSE;
3353
3354   if (network_playing && tape.recording)
3355   {
3356     /* store client dependent player focus when recording network games */
3357     tape.centered_player_nr_next = game.centered_player_nr_next;
3358     tape.set_centered_player = TRUE;
3359   }
3360
3361   for (i = 0; i < NUM_BELTS; i++)
3362   {
3363     game.belt_dir[i] = MV_NONE;
3364     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3365   }
3366
3367   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3368     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3369
3370 #if DEBUG_INIT_PLAYER
3371   if (options.debug)
3372   {
3373     printf("Player status at level initialization:\n");
3374   }
3375 #endif
3376
3377   SCAN_PLAYFIELD(x, y)
3378   {
3379     Feld[x][y] = level.field[x][y];
3380     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3381     ChangeDelay[x][y] = 0;
3382     ChangePage[x][y] = -1;
3383     CustomValue[x][y] = 0;              /* initialized in InitField() */
3384     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3385     AmoebaNr[x][y] = 0;
3386     WasJustMoving[x][y] = 0;
3387     WasJustFalling[x][y] = 0;
3388     CheckCollision[x][y] = 0;
3389     CheckImpact[x][y] = 0;
3390     Stop[x][y] = FALSE;
3391     Pushed[x][y] = FALSE;
3392
3393     ChangeCount[x][y] = 0;
3394     ChangeEvent[x][y] = -1;
3395
3396     ExplodePhase[x][y] = 0;
3397     ExplodeDelay[x][y] = 0;
3398     ExplodeField[x][y] = EX_TYPE_NONE;
3399
3400     RunnerVisit[x][y] = 0;
3401     PlayerVisit[x][y] = 0;
3402
3403     GfxFrame[x][y] = 0;
3404     GfxRandom[x][y] = INIT_GFX_RANDOM();
3405     GfxElement[x][y] = EL_UNDEFINED;
3406     GfxAction[x][y] = ACTION_DEFAULT;
3407     GfxDir[x][y] = MV_NONE;
3408     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3409   }
3410
3411   SCAN_PLAYFIELD(x, y)
3412   {
3413     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3414       emulate_bd = FALSE;
3415     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3416       emulate_sb = FALSE;
3417     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3418       emulate_sp = FALSE;
3419
3420     InitField(x, y, TRUE);
3421
3422     ResetGfxAnimation(x, y);
3423   }
3424
3425   InitBeltMovement();
3426
3427   for (i = 0; i < MAX_PLAYERS; i++)
3428   {
3429     struct PlayerInfo *player = &stored_player[i];
3430
3431     /* set number of special actions for bored and sleeping animation */
3432     player->num_special_action_bored =
3433       get_num_special_action(player->artwork_element,
3434                              ACTION_BORING_1, ACTION_BORING_LAST);
3435     player->num_special_action_sleeping =
3436       get_num_special_action(player->artwork_element,
3437                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3438   }
3439
3440   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3441                     emulate_sb ? EMU_SOKOBAN :
3442                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3443
3444   /* initialize type of slippery elements */
3445   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3446   {
3447     if (!IS_CUSTOM_ELEMENT(i))
3448     {
3449       /* default: elements slip down either to the left or right randomly */
3450       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3451
3452       /* SP style elements prefer to slip down on the left side */
3453       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3454         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3455
3456       /* BD style elements prefer to slip down on the left side */
3457       if (game.emulation == EMU_BOULDERDASH)
3458         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3459     }
3460   }
3461
3462   /* initialize explosion and ignition delay */
3463   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3464   {
3465     if (!IS_CUSTOM_ELEMENT(i))
3466     {
3467       int num_phase = 8;
3468       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3469                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3470                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3471       int last_phase = (num_phase + 1) * delay;
3472       int half_phase = (num_phase / 2) * delay;
3473
3474       element_info[i].explosion_delay = last_phase - 1;
3475       element_info[i].ignition_delay = half_phase;
3476
3477       if (i == EL_BLACK_ORB)
3478         element_info[i].ignition_delay = 1;
3479     }
3480   }
3481
3482   /* correct non-moving belts to start moving left */
3483   for (i = 0; i < NUM_BELTS; i++)
3484     if (game.belt_dir[i] == MV_NONE)
3485       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3486
3487 #if USE_NEW_PLAYER_ASSIGNMENTS
3488   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3489   /* choose default local player */
3490   local_player = &stored_player[0];
3491
3492   for (i = 0; i < MAX_PLAYERS; i++)
3493     stored_player[i].connected = FALSE;
3494
3495   local_player->connected = TRUE;
3496   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3497
3498   if (tape.playing)
3499   {
3500     for (i = 0; i < MAX_PLAYERS; i++)
3501       stored_player[i].connected = tape.player_participates[i];
3502   }
3503   else if (game.team_mode && !options.network)
3504   {
3505     /* try to guess locally connected team mode players (needed for correct
3506        assignment of player figures from level to locally playing players) */
3507
3508     for (i = 0; i < MAX_PLAYERS; i++)
3509       if (setup.input[i].use_joystick ||
3510           setup.input[i].key.left != KSYM_UNDEFINED)
3511         stored_player[i].connected = TRUE;
3512   }
3513
3514 #if DEBUG_INIT_PLAYER
3515   if (options.debug)
3516   {
3517     printf("Player status after level initialization:\n");
3518
3519     for (i = 0; i < MAX_PLAYERS; i++)
3520     {
3521       struct PlayerInfo *player = &stored_player[i];
3522
3523       printf("- player %d: present == %d, connected == %d, active == %d",
3524              i + 1,
3525              player->present,
3526              player->connected,
3527              player->active);
3528
3529       if (local_player == player)
3530         printf(" (local player)");
3531
3532       printf("\n");
3533     }
3534   }
3535 #endif
3536
3537 #if DEBUG_INIT_PLAYER
3538   if (options.debug)
3539     printf("Reassigning players ...\n");
3540 #endif
3541
3542   /* check if any connected player was not found in playfield */
3543   for (i = 0; i < MAX_PLAYERS; i++)
3544   {
3545     struct PlayerInfo *player = &stored_player[i];
3546
3547     if (player->connected && !player->present)
3548     {
3549       struct PlayerInfo *field_player = NULL;
3550
3551 #if DEBUG_INIT_PLAYER
3552       if (options.debug)
3553         printf("- looking for field player for player %d ...\n", i + 1);
3554 #endif
3555
3556       /* assign first free player found that is present in the playfield */
3557
3558       /* first try: look for unmapped playfield player that is not connected */
3559       for (j = 0; j < MAX_PLAYERS; j++)
3560         if (field_player == NULL &&
3561             stored_player[j].present &&
3562             !stored_player[j].mapped &&
3563             !stored_player[j].connected)
3564           field_player = &stored_player[j];
3565
3566       /* second try: look for *any* unmapped playfield player */
3567       for (j = 0; j < MAX_PLAYERS; j++)
3568         if (field_player == NULL &&
3569             stored_player[j].present &&
3570             !stored_player[j].mapped)
3571           field_player = &stored_player[j];
3572
3573       if (field_player != NULL)
3574       {
3575         int jx = field_player->jx, jy = field_player->jy;
3576
3577 #if DEBUG_INIT_PLAYER
3578         if (options.debug)
3579           printf("- found player %d\n", field_player->index_nr + 1);
3580 #endif
3581
3582         player->present = FALSE;
3583         player->active = FALSE;
3584
3585         field_player->present = TRUE;
3586         field_player->active = TRUE;
3587
3588         /*
3589         player->initial_element = field_player->initial_element;
3590         player->artwork_element = field_player->artwork_element;
3591
3592         player->block_last_field       = field_player->block_last_field;
3593         player->block_delay_adjustment = field_player->block_delay_adjustment;
3594         */
3595
3596         StorePlayer[jx][jy] = field_player->element_nr;
3597
3598         field_player->jx = field_player->last_jx = jx;
3599         field_player->jy = field_player->last_jy = jy;
3600
3601         if (local_player == player)
3602           local_player = field_player;
3603
3604         map_player_action[field_player->index_nr] = i;
3605
3606         field_player->mapped = TRUE;
3607
3608 #if DEBUG_INIT_PLAYER
3609         if (options.debug)
3610           printf("- map_player_action[%d] == %d\n",
3611                  field_player->index_nr + 1, i + 1);
3612 #endif
3613       }
3614     }
3615
3616     if (player->connected && player->present)
3617       player->mapped = TRUE;
3618   }
3619
3620 #if DEBUG_INIT_PLAYER
3621   if (options.debug)
3622   {
3623     printf("Player status after player assignment (first stage):\n");
3624
3625     for (i = 0; i < MAX_PLAYERS; i++)
3626     {
3627       struct PlayerInfo *player = &stored_player[i];
3628
3629       printf("- player %d: present == %d, connected == %d, active == %d",
3630              i + 1,
3631              player->present,
3632              player->connected,
3633              player->active);
3634
3635       if (local_player == player)
3636         printf(" (local player)");
3637
3638       printf("\n");
3639     }
3640   }
3641 #endif
3642
3643 #else
3644
3645   /* check if any connected player was not found in playfield */
3646   for (i = 0; i < MAX_PLAYERS; i++)
3647   {
3648     struct PlayerInfo *player = &stored_player[i];
3649
3650     if (player->connected && !player->present)
3651     {
3652       for (j = 0; j < MAX_PLAYERS; j++)
3653       {
3654         struct PlayerInfo *field_player = &stored_player[j];
3655         int jx = field_player->jx, jy = field_player->jy;
3656
3657         /* assign first free player found that is present in the playfield */
3658         if (field_player->present && !field_player->connected)
3659         {
3660           player->present = TRUE;
3661           player->active = TRUE;
3662
3663           field_player->present = FALSE;
3664           field_player->active = FALSE;
3665
3666           player->initial_element = field_player->initial_element;
3667           player->artwork_element = field_player->artwork_element;
3668
3669           player->block_last_field       = field_player->block_last_field;
3670           player->block_delay_adjustment = field_player->block_delay_adjustment;
3671
3672           StorePlayer[jx][jy] = player->element_nr;
3673
3674           player->jx = player->last_jx = jx;
3675           player->jy = player->last_jy = jy;
3676
3677           break;
3678         }
3679       }
3680     }
3681   }
3682 #endif
3683
3684 #if 0
3685   printf("::: local_player->present == %d\n", local_player->present);
3686 #endif
3687
3688   if (tape.playing)
3689   {
3690     /* when playing a tape, eliminate all players who do not participate */
3691
3692 #if USE_NEW_PLAYER_ASSIGNMENTS
3693
3694     if (!game.team_mode)
3695     {
3696       for (i = 0; i < MAX_PLAYERS; i++)
3697       {
3698         if (stored_player[i].active &&
3699             !tape.player_participates[map_player_action[i]])
3700         {
3701           struct PlayerInfo *player = &stored_player[i];
3702           int jx = player->jx, jy = player->jy;
3703
3704 #if DEBUG_INIT_PLAYER
3705           if (options.debug)
3706             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3707 #endif
3708
3709           player->active = FALSE;
3710           StorePlayer[jx][jy] = 0;
3711           Feld[jx][jy] = EL_EMPTY;
3712         }
3713       }
3714     }
3715
3716 #else
3717
3718     for (i = 0; i < MAX_PLAYERS; i++)
3719     {
3720       if (stored_player[i].active &&
3721           !tape.player_participates[i])
3722       {
3723         struct PlayerInfo *player = &stored_player[i];
3724         int jx = player->jx, jy = player->jy;
3725
3726         player->active = FALSE;
3727         StorePlayer[jx][jy] = 0;
3728         Feld[jx][jy] = EL_EMPTY;
3729       }
3730     }
3731 #endif
3732   }
3733   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3734   {
3735     /* when in single player mode, eliminate all but the first active player */
3736
3737     for (i = 0; i < MAX_PLAYERS; i++)
3738     {
3739       if (stored_player[i].active)
3740       {
3741         for (j = i + 1; j < MAX_PLAYERS; j++)
3742         {
3743           if (stored_player[j].active)
3744           {
3745             struct PlayerInfo *player = &stored_player[j];
3746             int jx = player->jx, jy = player->jy;
3747
3748             player->active = FALSE;
3749             player->present = FALSE;
3750
3751             StorePlayer[jx][jy] = 0;
3752             Feld[jx][jy] = EL_EMPTY;
3753           }
3754         }
3755       }
3756     }
3757   }
3758
3759   /* when recording the game, store which players take part in the game */
3760   if (tape.recording)
3761   {
3762 #if USE_NEW_PLAYER_ASSIGNMENTS
3763     for (i = 0; i < MAX_PLAYERS; i++)
3764       if (stored_player[i].connected)
3765         tape.player_participates[i] = TRUE;
3766 #else
3767     for (i = 0; i < MAX_PLAYERS; i++)
3768       if (stored_player[i].active)
3769         tape.player_participates[i] = TRUE;
3770 #endif
3771   }
3772
3773 #if DEBUG_INIT_PLAYER
3774   if (options.debug)
3775   {
3776     printf("Player status after player assignment (final stage):\n");
3777
3778     for (i = 0; i < MAX_PLAYERS; i++)
3779     {
3780       struct PlayerInfo *player = &stored_player[i];
3781
3782       printf("- player %d: present == %d, connected == %d, active == %d",
3783              i + 1,
3784              player->present,
3785              player->connected,
3786              player->active);
3787
3788       if (local_player == player)
3789         printf(" (local player)");
3790
3791       printf("\n");
3792     }
3793   }
3794 #endif
3795
3796   if (BorderElement == EL_EMPTY)
3797   {
3798     SBX_Left = 0;
3799     SBX_Right = lev_fieldx - SCR_FIELDX;
3800     SBY_Upper = 0;
3801     SBY_Lower = lev_fieldy - SCR_FIELDY;
3802   }
3803   else
3804   {
3805     SBX_Left = -1;
3806     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3807     SBY_Upper = -1;
3808     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3809   }
3810
3811   if (full_lev_fieldx <= SCR_FIELDX)
3812     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3813   if (full_lev_fieldy <= SCR_FIELDY)
3814     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3815
3816   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3817     SBX_Left--;
3818   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3819     SBY_Upper--;
3820
3821   /* if local player not found, look for custom element that might create
3822      the player (make some assumptions about the right custom element) */
3823   if (!local_player->present)
3824   {
3825     int start_x = 0, start_y = 0;
3826     int found_rating = 0;
3827     int found_element = EL_UNDEFINED;
3828     int player_nr = local_player->index_nr;
3829
3830     SCAN_PLAYFIELD(x, y)
3831     {
3832       int element = Feld[x][y];
3833       int content;
3834       int xx, yy;
3835       boolean is_player;
3836
3837       if (level.use_start_element[player_nr] &&
3838           level.start_element[player_nr] == element &&
3839           found_rating < 4)
3840       {
3841         start_x = x;
3842         start_y = y;
3843
3844         found_rating = 4;
3845         found_element = element;
3846       }
3847
3848       if (!IS_CUSTOM_ELEMENT(element))
3849         continue;
3850
3851       if (CAN_CHANGE(element))
3852       {
3853         for (i = 0; i < element_info[element].num_change_pages; i++)
3854         {
3855           /* check for player created from custom element as single target */
3856           content = element_info[element].change_page[i].target_element;
3857           is_player = ELEM_IS_PLAYER(content);
3858
3859           if (is_player && (found_rating < 3 ||
3860                             (found_rating == 3 && element < found_element)))
3861           {
3862             start_x = x;
3863             start_y = y;
3864
3865             found_rating = 3;
3866             found_element = element;
3867           }
3868         }
3869       }
3870
3871       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3872       {
3873         /* check for player created from custom element as explosion content */
3874         content = element_info[element].content.e[xx][yy];
3875         is_player = ELEM_IS_PLAYER(content);
3876
3877         if (is_player && (found_rating < 2 ||
3878                           (found_rating == 2 && element < found_element)))
3879         {
3880           start_x = x + xx - 1;
3881           start_y = y + yy - 1;
3882
3883           found_rating = 2;
3884           found_element = element;
3885         }
3886
3887         if (!CAN_CHANGE(element))
3888           continue;
3889
3890         for (i = 0; i < element_info[element].num_change_pages; i++)
3891         {
3892           /* check for player created from custom element as extended target */
3893           content =
3894             element_info[element].change_page[i].target_content.e[xx][yy];
3895
3896           is_player = ELEM_IS_PLAYER(content);
3897
3898           if (is_player && (found_rating < 1 ||
3899                             (found_rating == 1 && element < found_element)))
3900           {
3901             start_x = x + xx - 1;
3902             start_y = y + yy - 1;
3903
3904             found_rating = 1;
3905             found_element = element;
3906           }
3907         }
3908       }
3909     }
3910
3911     scroll_x = SCROLL_POSITION_X(start_x);
3912     scroll_y = SCROLL_POSITION_Y(start_y);
3913   }
3914   else
3915   {
3916     scroll_x = SCROLL_POSITION_X(local_player->jx);
3917     scroll_y = SCROLL_POSITION_Y(local_player->jy);
3918   }
3919
3920   /* !!! FIX THIS (START) !!! */
3921   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3922   {
3923     InitGameEngine_EM();
3924   }
3925   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3926   {
3927     InitGameEngine_SP();
3928   }
3929   else
3930   {
3931     DrawLevel(REDRAW_FIELD);
3932     DrawAllPlayers();
3933
3934     /* after drawing the level, correct some elements */
3935     if (game.timegate_time_left == 0)
3936       CloseAllOpenTimegates();
3937   }
3938
3939   /* blit playfield from scroll buffer to normal back buffer for fading in */
3940   BlitScreenToBitmap(backbuffer);
3941   /* !!! FIX THIS (END) !!! */
3942
3943   DrawMaskedBorder(fade_mask);
3944
3945   FadeIn(fade_mask);
3946
3947 #if 1
3948   // full screen redraw is required at this point in the following cases:
3949   // - special editor door undrawn when game was started from level editor
3950   // - drawing area (playfield) was changed and has to be removed completely
3951   redraw_mask = REDRAW_ALL;
3952   BackToFront();
3953 #endif
3954
3955   if (!game.restart_level)
3956   {
3957     /* copy default game door content to main double buffer */
3958
3959     /* !!! CHECK AGAIN !!! */
3960     SetPanelBackground();
3961     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3962     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3963   }
3964
3965   SetPanelBackground();
3966   SetDrawBackgroundMask(REDRAW_DOOR_1);
3967
3968   UpdateAndDisplayGameControlValues();
3969
3970   if (!game.restart_level)
3971   {
3972     UnmapGameButtons();
3973     UnmapTapeButtons();
3974
3975     FreeGameButtons();
3976     CreateGameButtons();
3977
3978     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3979     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3980     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3981
3982     MapGameButtons();
3983     MapTapeButtons();
3984
3985     /* copy actual game door content to door double buffer for OpenDoor() */
3986     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3987
3988     OpenDoor(DOOR_OPEN_ALL);
3989
3990     PlaySound(SND_GAME_STARTING);
3991
3992     if (setup.sound_music)
3993       PlayLevelMusic();
3994
3995     KeyboardAutoRepeatOffUnlessAutoplay();
3996
3997 #if DEBUG_INIT_PLAYER
3998     if (options.debug)
3999     {
4000       printf("Player status (final):\n");
4001
4002       for (i = 0; i < MAX_PLAYERS; i++)
4003       {
4004         struct PlayerInfo *player = &stored_player[i];
4005
4006         printf("- player %d: present == %d, connected == %d, active == %d",
4007                i + 1,
4008                player->present,
4009                player->connected,
4010                player->active);
4011
4012         if (local_player == player)
4013           printf(" (local player)");
4014
4015         printf("\n");
4016       }
4017     }
4018 #endif
4019   }
4020
4021   UnmapAllGadgets();
4022
4023   MapGameButtons();
4024   MapTapeButtons();
4025
4026   if (!game.restart_level && !tape.playing)
4027   {
4028     LevelStats_incPlayed(level_nr);
4029
4030     SaveLevelSetup_SeriesInfo();
4031   }
4032
4033   game.restart_level = FALSE;
4034
4035   SaveEngineSnapshotToListInitial();
4036 }
4037
4038 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y,
4039                         int actual_player_x, int actual_player_y)
4040 {
4041   /* this is used for non-R'n'D game engines to update certain engine values */
4042
4043   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
4044   {
4045     actual_player_x = correctLevelPosX_EM(actual_player_x);
4046     actual_player_y = correctLevelPosY_EM(actual_player_y);
4047   }
4048
4049   /* needed to determine if sounds are played within the visible screen area */
4050   scroll_x = actual_scroll_x;
4051   scroll_y = actual_scroll_y;
4052
4053   /* needed to get player position for "follow finger" playing input method */
4054   local_player->jx = actual_player_x;
4055   local_player->jy = actual_player_y;
4056 }
4057
4058 void InitMovDir(int x, int y)
4059 {
4060   int i, element = Feld[x][y];
4061   static int xy[4][2] =
4062   {
4063     {  0, +1 },
4064     { +1,  0 },
4065     {  0, -1 },
4066     { -1,  0 }
4067   };
4068   static int direction[3][4] =
4069   {
4070     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4071     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4072     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4073   };
4074
4075   switch (element)
4076   {
4077     case EL_BUG_RIGHT:
4078     case EL_BUG_UP:
4079     case EL_BUG_LEFT:
4080     case EL_BUG_DOWN:
4081       Feld[x][y] = EL_BUG;
4082       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4083       break;
4084
4085     case EL_SPACESHIP_RIGHT:
4086     case EL_SPACESHIP_UP:
4087     case EL_SPACESHIP_LEFT:
4088     case EL_SPACESHIP_DOWN:
4089       Feld[x][y] = EL_SPACESHIP;
4090       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4091       break;
4092
4093     case EL_BD_BUTTERFLY_RIGHT:
4094     case EL_BD_BUTTERFLY_UP:
4095     case EL_BD_BUTTERFLY_LEFT:
4096     case EL_BD_BUTTERFLY_DOWN:
4097       Feld[x][y] = EL_BD_BUTTERFLY;
4098       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4099       break;
4100
4101     case EL_BD_FIREFLY_RIGHT:
4102     case EL_BD_FIREFLY_UP:
4103     case EL_BD_FIREFLY_LEFT:
4104     case EL_BD_FIREFLY_DOWN:
4105       Feld[x][y] = EL_BD_FIREFLY;
4106       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4107       break;
4108
4109     case EL_PACMAN_RIGHT:
4110     case EL_PACMAN_UP:
4111     case EL_PACMAN_LEFT:
4112     case EL_PACMAN_DOWN:
4113       Feld[x][y] = EL_PACMAN;
4114       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4115       break;
4116
4117     case EL_YAMYAM_LEFT:
4118     case EL_YAMYAM_RIGHT:
4119     case EL_YAMYAM_UP:
4120     case EL_YAMYAM_DOWN:
4121       Feld[x][y] = EL_YAMYAM;
4122       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4123       break;
4124
4125     case EL_SP_SNIKSNAK:
4126       MovDir[x][y] = MV_UP;
4127       break;
4128
4129     case EL_SP_ELECTRON:
4130       MovDir[x][y] = MV_LEFT;
4131       break;
4132
4133     case EL_MOLE_LEFT:
4134     case EL_MOLE_RIGHT:
4135     case EL_MOLE_UP:
4136     case EL_MOLE_DOWN:
4137       Feld[x][y] = EL_MOLE;
4138       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4139       break;
4140
4141     default:
4142       if (IS_CUSTOM_ELEMENT(element))
4143       {
4144         struct ElementInfo *ei = &element_info[element];
4145         int move_direction_initial = ei->move_direction_initial;
4146         int move_pattern = ei->move_pattern;
4147
4148         if (move_direction_initial == MV_START_PREVIOUS)
4149         {
4150           if (MovDir[x][y] != MV_NONE)
4151             return;
4152
4153           move_direction_initial = MV_START_AUTOMATIC;
4154         }
4155
4156         if (move_direction_initial == MV_START_RANDOM)
4157           MovDir[x][y] = 1 << RND(4);
4158         else if (move_direction_initial & MV_ANY_DIRECTION)
4159           MovDir[x][y] = move_direction_initial;
4160         else if (move_pattern == MV_ALL_DIRECTIONS ||
4161                  move_pattern == MV_TURNING_LEFT ||
4162                  move_pattern == MV_TURNING_RIGHT ||
4163                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4164                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4165                  move_pattern == MV_TURNING_RANDOM)
4166           MovDir[x][y] = 1 << RND(4);
4167         else if (move_pattern == MV_HORIZONTAL)
4168           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4169         else if (move_pattern == MV_VERTICAL)
4170           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4171         else if (move_pattern & MV_ANY_DIRECTION)
4172           MovDir[x][y] = element_info[element].move_pattern;
4173         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4174                  move_pattern == MV_ALONG_RIGHT_SIDE)
4175         {
4176           /* use random direction as default start direction */
4177           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4178             MovDir[x][y] = 1 << RND(4);
4179
4180           for (i = 0; i < NUM_DIRECTIONS; i++)
4181           {
4182             int x1 = x + xy[i][0];
4183             int y1 = y + xy[i][1];
4184
4185             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4186             {
4187               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4188                 MovDir[x][y] = direction[0][i];
4189               else
4190                 MovDir[x][y] = direction[1][i];
4191
4192               break;
4193             }
4194           }
4195         }                
4196       }
4197       else
4198       {
4199         MovDir[x][y] = 1 << RND(4);
4200
4201         if (element != EL_BUG &&
4202             element != EL_SPACESHIP &&
4203             element != EL_BD_BUTTERFLY &&
4204             element != EL_BD_FIREFLY)
4205           break;
4206
4207         for (i = 0; i < NUM_DIRECTIONS; i++)
4208         {
4209           int x1 = x + xy[i][0];
4210           int y1 = y + xy[i][1];
4211
4212           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4213           {
4214             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4215             {
4216               MovDir[x][y] = direction[0][i];
4217               break;
4218             }
4219             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4220                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4221             {
4222               MovDir[x][y] = direction[1][i];
4223               break;
4224             }
4225           }
4226         }
4227       }
4228       break;
4229   }
4230
4231   GfxDir[x][y] = MovDir[x][y];
4232 }
4233
4234 void InitAmoebaNr(int x, int y)
4235 {
4236   int i;
4237   int group_nr = AmoebeNachbarNr(x, y);
4238
4239   if (group_nr == 0)
4240   {
4241     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4242     {
4243       if (AmoebaCnt[i] == 0)
4244       {
4245         group_nr = i;
4246         break;
4247       }
4248     }
4249   }
4250
4251   AmoebaNr[x][y] = group_nr;
4252   AmoebaCnt[group_nr]++;
4253   AmoebaCnt2[group_nr]++;
4254 }
4255
4256 static void PlayerWins(struct PlayerInfo *player)
4257 {
4258   player->LevelSolved = TRUE;
4259   player->GameOver = TRUE;
4260
4261   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4262                          level.native_em_level->lev->score : player->score);
4263
4264   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4265                                       TimeLeft);
4266   player->LevelSolved_CountingScore = player->score_final;
4267 }
4268
4269 void GameWon()
4270 {
4271   static int time, time_final;
4272   static int score, score_final;
4273   static int game_over_delay_1 = 0;
4274   static int game_over_delay_2 = 0;
4275   int game_over_delay_value_1 = 50;
4276   int game_over_delay_value_2 = 50;
4277
4278   if (!local_player->LevelSolved_GameWon)
4279   {
4280     int i;
4281
4282     /* do not start end game actions before the player stops moving (to exit) */
4283     if (local_player->MovPos)
4284       return;
4285
4286     local_player->LevelSolved_GameWon = TRUE;
4287     local_player->LevelSolved_SaveTape = tape.recording;
4288     local_player->LevelSolved_SaveScore = !tape.playing;
4289
4290     if (!tape.playing)
4291     {
4292       LevelStats_incSolved(level_nr);
4293
4294       SaveLevelSetup_SeriesInfo();
4295     }
4296
4297     if (tape.auto_play)         /* tape might already be stopped here */
4298       tape.auto_play_level_solved = TRUE;
4299
4300     TapeStop();
4301
4302     game_over_delay_1 = game_over_delay_value_1;
4303     game_over_delay_2 = game_over_delay_value_2;
4304
4305     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4306     score = score_final = local_player->score_final;
4307
4308     if (TimeLeft > 0)
4309     {
4310       time_final = 0;
4311       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4312     }
4313     else if (game.no_time_limit && TimePlayed < 999)
4314     {
4315       time_final = 999;
4316       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4317     }
4318
4319     local_player->score_final = score_final;
4320
4321     if (level_editor_test_game)
4322     {
4323       time = time_final;
4324       score = score_final;
4325
4326       local_player->LevelSolved_CountingTime = time;
4327       local_player->LevelSolved_CountingScore = score;
4328
4329       game_panel_controls[GAME_PANEL_TIME].value = time;
4330       game_panel_controls[GAME_PANEL_SCORE].value = score;
4331
4332       DisplayGameControlValues();
4333     }
4334
4335     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4336     {
4337       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4338       {
4339         /* close exit door after last player */
4340         if ((AllPlayersGone &&
4341              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4342               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4343               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4344             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4345             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4346         {
4347           int element = Feld[ExitX][ExitY];
4348
4349           Feld[ExitX][ExitY] =
4350             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4351              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4352              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4353              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4354              EL_EM_STEEL_EXIT_CLOSING);
4355
4356           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4357         }
4358
4359         /* player disappears */
4360         DrawLevelField(ExitX, ExitY);
4361       }
4362
4363       for (i = 0; i < MAX_PLAYERS; i++)
4364       {
4365         struct PlayerInfo *player = &stored_player[i];
4366
4367         if (player->present)
4368         {
4369           RemovePlayer(player);
4370
4371           /* player disappears */
4372           DrawLevelField(player->jx, player->jy);
4373         }
4374       }
4375     }
4376
4377     PlaySound(SND_GAME_WINNING);
4378   }
4379
4380   if (game_over_delay_1 > 0)
4381   {
4382     game_over_delay_1--;
4383
4384     return;
4385   }
4386
4387   if (time != time_final)
4388   {
4389     int time_to_go = ABS(time_final - time);
4390     int time_count_dir = (time < time_final ? +1 : -1);
4391     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4392
4393     time  += time_count_steps * time_count_dir;
4394     score += time_count_steps * level.score[SC_TIME_BONUS];
4395
4396     local_player->LevelSolved_CountingTime = time;
4397     local_player->LevelSolved_CountingScore = score;
4398
4399     game_panel_controls[GAME_PANEL_TIME].value = time;
4400     game_panel_controls[GAME_PANEL_SCORE].value = score;
4401
4402     DisplayGameControlValues();
4403
4404     if (time == time_final)
4405       StopSound(SND_GAME_LEVELTIME_BONUS);
4406     else if (setup.sound_loops)
4407       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4408     else
4409       PlaySound(SND_GAME_LEVELTIME_BONUS);
4410
4411     return;
4412   }
4413
4414   local_player->LevelSolved_PanelOff = TRUE;
4415
4416   if (game_over_delay_2 > 0)
4417   {
4418     game_over_delay_2--;
4419
4420     return;
4421   }
4422
4423   GameEnd();
4424 }
4425
4426 void GameEnd()
4427 {
4428   int hi_pos;
4429   boolean raise_level = FALSE;
4430
4431   local_player->LevelSolved_GameEnd = TRUE;
4432
4433   if (!global.use_envelope_request)
4434     CloseDoor(DOOR_CLOSE_1);
4435
4436   if (local_player->LevelSolved_SaveTape)
4437   {
4438     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4439   }
4440
4441   CloseDoor(DOOR_CLOSE_ALL);
4442
4443   if (level_editor_test_game)
4444   {
4445     SetGameStatus(GAME_MODE_MAIN);
4446
4447     DrawMainMenu();
4448
4449     return;
4450   }
4451
4452   if (!local_player->LevelSolved_SaveScore)
4453   {
4454     SetGameStatus(GAME_MODE_MAIN);
4455
4456     DrawMainMenu();
4457
4458     return;
4459   }
4460
4461   if (level_nr == leveldir_current->handicap_level)
4462   {
4463     leveldir_current->handicap_level++;
4464
4465     SaveLevelSetup_SeriesInfo();
4466   }
4467
4468   if (setup.increment_levels &&
4469       level_nr < leveldir_current->last_level)
4470     raise_level = TRUE;                 /* advance to next level */
4471
4472   if ((hi_pos = NewHiScore()) >= 0) 
4473   {
4474     SetGameStatus(GAME_MODE_SCORES);
4475
4476     DrawHallOfFame(hi_pos);
4477
4478     if (raise_level)
4479     {
4480       level_nr++;
4481       TapeErase();
4482     }
4483   }
4484   else
4485   {
4486     SetGameStatus(GAME_MODE_MAIN);
4487
4488     if (raise_level)
4489     {
4490       level_nr++;
4491       TapeErase();
4492     }
4493
4494     DrawMainMenu();
4495   }
4496 }
4497
4498 int NewHiScore()
4499 {
4500   int k, l;
4501   int position = -1;
4502   boolean one_score_entry_per_name = !program.many_scores_per_name;
4503
4504   LoadScore(level_nr);
4505
4506   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4507       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4508     return -1;
4509
4510   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4511   {
4512     if (local_player->score_final > highscore[k].Score)
4513     {
4514       /* player has made it to the hall of fame */
4515
4516       if (k < MAX_SCORE_ENTRIES - 1)
4517       {
4518         int m = MAX_SCORE_ENTRIES - 1;
4519
4520         if (one_score_entry_per_name)
4521         {
4522           for (l = k; l < MAX_SCORE_ENTRIES; l++)
4523             if (strEqual(setup.player_name, highscore[l].Name))
4524               m = l;
4525
4526           if (m == k)   /* player's new highscore overwrites his old one */
4527             goto put_into_list;
4528         }
4529
4530         for (l = m; l > k; l--)
4531         {
4532           strcpy(highscore[l].Name, highscore[l - 1].Name);
4533           highscore[l].Score = highscore[l - 1].Score;
4534         }
4535       }
4536
4537       put_into_list:
4538
4539       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4540       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4541       highscore[k].Score = local_player->score_final; 
4542       position = k;
4543
4544       break;
4545     }
4546     else if (one_score_entry_per_name &&
4547              !strncmp(setup.player_name, highscore[k].Name,
4548                       MAX_PLAYER_NAME_LEN))
4549       break;    /* player already there with a higher score */
4550   }
4551
4552   if (position >= 0) 
4553     SaveScore(level_nr);
4554
4555   return position;
4556 }
4557
4558 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4559 {
4560   int element = Feld[x][y];
4561   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4562   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4563   int horiz_move = (dx != 0);
4564   int sign = (horiz_move ? dx : dy);
4565   int step = sign * element_info[element].move_stepsize;
4566
4567   /* special values for move stepsize for spring and things on conveyor belt */
4568   if (horiz_move)
4569   {
4570     if (CAN_FALL(element) &&
4571         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4572       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4573     else if (element == EL_SPRING)
4574       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4575   }
4576
4577   return step;
4578 }
4579
4580 inline static int getElementMoveStepsize(int x, int y)
4581 {
4582   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4583 }
4584
4585 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4586 {
4587   if (player->GfxAction != action || player->GfxDir != dir)
4588   {
4589     player->GfxAction = action;
4590     player->GfxDir = dir;
4591     player->Frame = 0;
4592     player->StepFrame = 0;
4593   }
4594 }
4595
4596 static void ResetGfxFrame(int x, int y)
4597 {
4598   int element = Feld[x][y];
4599   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4600
4601   if (graphic_info[graphic].anim_global_sync)
4602     GfxFrame[x][y] = FrameCounter;
4603   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4604     GfxFrame[x][y] = CustomValue[x][y];
4605   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4606     GfxFrame[x][y] = element_info[element].collect_score;
4607   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4608     GfxFrame[x][y] = ChangeDelay[x][y];
4609 }
4610
4611 static void ResetGfxAnimation(int x, int y)
4612 {
4613   GfxAction[x][y] = ACTION_DEFAULT;
4614   GfxDir[x][y] = MovDir[x][y];
4615   GfxFrame[x][y] = 0;
4616
4617   ResetGfxFrame(x, y);
4618 }
4619
4620 static void ResetRandomAnimationValue(int x, int y)
4621 {
4622   GfxRandom[x][y] = INIT_GFX_RANDOM();
4623 }
4624
4625 void InitMovingField(int x, int y, int direction)
4626 {
4627   int element = Feld[x][y];
4628   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4629   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4630   int newx = x + dx;
4631   int newy = y + dy;
4632   boolean is_moving_before, is_moving_after;
4633
4634   /* check if element was/is moving or being moved before/after mode change */
4635   is_moving_before = (WasJustMoving[x][y] != 0);
4636   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4637
4638   /* reset animation only for moving elements which change direction of moving
4639      or which just started or stopped moving
4640      (else CEs with property "can move" / "not moving" are reset each frame) */
4641   if (is_moving_before != is_moving_after ||
4642       direction != MovDir[x][y])
4643     ResetGfxAnimation(x, y);
4644
4645   MovDir[x][y] = direction;
4646   GfxDir[x][y] = direction;
4647
4648   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4649                      direction == MV_DOWN && CAN_FALL(element) ?
4650                      ACTION_FALLING : ACTION_MOVING);
4651
4652   /* this is needed for CEs with property "can move" / "not moving" */
4653
4654   if (is_moving_after)
4655   {
4656     if (Feld[newx][newy] == EL_EMPTY)
4657       Feld[newx][newy] = EL_BLOCKED;
4658
4659     MovDir[newx][newy] = MovDir[x][y];
4660
4661     CustomValue[newx][newy] = CustomValue[x][y];
4662
4663     GfxFrame[newx][newy] = GfxFrame[x][y];
4664     GfxRandom[newx][newy] = GfxRandom[x][y];
4665     GfxAction[newx][newy] = GfxAction[x][y];
4666     GfxDir[newx][newy] = GfxDir[x][y];
4667   }
4668 }
4669
4670 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4671 {
4672   int direction = MovDir[x][y];
4673   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4674   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4675
4676   *goes_to_x = newx;
4677   *goes_to_y = newy;
4678 }
4679
4680 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4681 {
4682   int oldx = x, oldy = y;
4683   int direction = MovDir[x][y];
4684
4685   if (direction == MV_LEFT)
4686     oldx++;
4687   else if (direction == MV_RIGHT)
4688     oldx--;
4689   else if (direction == MV_UP)
4690     oldy++;
4691   else if (direction == MV_DOWN)
4692     oldy--;
4693
4694   *comes_from_x = oldx;
4695   *comes_from_y = oldy;
4696 }
4697
4698 int MovingOrBlocked2Element(int x, int y)
4699 {
4700   int element = Feld[x][y];
4701
4702   if (element == EL_BLOCKED)
4703   {
4704     int oldx, oldy;
4705
4706     Blocked2Moving(x, y, &oldx, &oldy);
4707     return Feld[oldx][oldy];
4708   }
4709   else
4710     return element;
4711 }
4712
4713 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4714 {
4715   /* like MovingOrBlocked2Element(), but if element is moving
4716      and (x,y) is the field the moving element is just leaving,
4717      return EL_BLOCKED instead of the element value */
4718   int element = Feld[x][y];
4719
4720   if (IS_MOVING(x, y))
4721   {
4722     if (element == EL_BLOCKED)
4723     {
4724       int oldx, oldy;
4725
4726       Blocked2Moving(x, y, &oldx, &oldy);
4727       return Feld[oldx][oldy];
4728     }
4729     else
4730       return EL_BLOCKED;
4731   }
4732   else
4733     return element;
4734 }
4735
4736 static void RemoveField(int x, int y)
4737 {
4738   Feld[x][y] = EL_EMPTY;
4739
4740   MovPos[x][y] = 0;
4741   MovDir[x][y] = 0;
4742   MovDelay[x][y] = 0;
4743
4744   CustomValue[x][y] = 0;
4745
4746   AmoebaNr[x][y] = 0;
4747   ChangeDelay[x][y] = 0;
4748   ChangePage[x][y] = -1;
4749   Pushed[x][y] = FALSE;
4750
4751   GfxElement[x][y] = EL_UNDEFINED;
4752   GfxAction[x][y] = ACTION_DEFAULT;
4753   GfxDir[x][y] = MV_NONE;
4754 }
4755
4756 void RemoveMovingField(int x, int y)
4757 {
4758   int oldx = x, oldy = y, newx = x, newy = y;
4759   int element = Feld[x][y];
4760   int next_element = EL_UNDEFINED;
4761
4762   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4763     return;
4764
4765   if (IS_MOVING(x, y))
4766   {
4767     Moving2Blocked(x, y, &newx, &newy);
4768
4769     if (Feld[newx][newy] != EL_BLOCKED)
4770     {
4771       /* element is moving, but target field is not free (blocked), but
4772          already occupied by something different (example: acid pool);
4773          in this case, only remove the moving field, but not the target */
4774
4775       RemoveField(oldx, oldy);
4776
4777       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4778
4779       TEST_DrawLevelField(oldx, oldy);
4780
4781       return;
4782     }
4783   }
4784   else if (element == EL_BLOCKED)
4785   {
4786     Blocked2Moving(x, y, &oldx, &oldy);
4787     if (!IS_MOVING(oldx, oldy))
4788       return;
4789   }
4790
4791   if (element == EL_BLOCKED &&
4792       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4793        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4794        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4795        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4796        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4797        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4798     next_element = get_next_element(Feld[oldx][oldy]);
4799
4800   RemoveField(oldx, oldy);
4801   RemoveField(newx, newy);
4802
4803   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4804
4805   if (next_element != EL_UNDEFINED)
4806     Feld[oldx][oldy] = next_element;
4807
4808   TEST_DrawLevelField(oldx, oldy);
4809   TEST_DrawLevelField(newx, newy);
4810 }
4811
4812 void DrawDynamite(int x, int y)
4813 {
4814   int sx = SCREENX(x), sy = SCREENY(y);
4815   int graphic = el2img(Feld[x][y]);
4816   int frame;
4817
4818   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4819     return;
4820
4821   if (IS_WALKABLE_INSIDE(Back[x][y]))
4822     return;
4823
4824   if (Back[x][y])
4825     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4826   else if (Store[x][y])
4827     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4828
4829   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4830
4831   if (Back[x][y] || Store[x][y])
4832     DrawGraphicThruMask(sx, sy, graphic, frame);
4833   else
4834     DrawGraphic(sx, sy, graphic, frame);
4835 }
4836
4837 void CheckDynamite(int x, int y)
4838 {
4839   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4840   {
4841     MovDelay[x][y]--;
4842
4843     if (MovDelay[x][y] != 0)
4844     {
4845       DrawDynamite(x, y);
4846       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4847
4848       return;
4849     }
4850   }
4851
4852   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4853
4854   Bang(x, y);
4855 }
4856
4857 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4858 {
4859   boolean num_checked_players = 0;
4860   int i;
4861
4862   for (i = 0; i < MAX_PLAYERS; i++)
4863   {
4864     if (stored_player[i].active)
4865     {
4866       int sx = stored_player[i].jx;
4867       int sy = stored_player[i].jy;
4868
4869       if (num_checked_players == 0)
4870       {
4871         *sx1 = *sx2 = sx;
4872         *sy1 = *sy2 = sy;
4873       }
4874       else
4875       {
4876         *sx1 = MIN(*sx1, sx);
4877         *sy1 = MIN(*sy1, sy);
4878         *sx2 = MAX(*sx2, sx);
4879         *sy2 = MAX(*sy2, sy);
4880       }
4881
4882       num_checked_players++;
4883     }
4884   }
4885 }
4886
4887 static boolean checkIfAllPlayersFitToScreen_RND()
4888 {
4889   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4890
4891   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4892
4893   return (sx2 - sx1 < SCR_FIELDX &&
4894           sy2 - sy1 < SCR_FIELDY);
4895 }
4896
4897 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4898 {
4899   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4900
4901   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4902
4903   *sx = (sx1 + sx2) / 2;
4904   *sy = (sy1 + sy2) / 2;
4905 }
4906
4907 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4908                         boolean center_screen, boolean quick_relocation)
4909 {
4910   unsigned int frame_delay_value_old = GetVideoFrameDelay();
4911   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4912   boolean no_delay = (tape.warp_forward);
4913   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4914   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4915   int new_scroll_x, new_scroll_y;
4916
4917   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4918   {
4919     /* case 1: quick relocation inside visible screen (without scrolling) */
4920
4921     RedrawPlayfield();
4922
4923     return;
4924   }
4925
4926   if (!level.shifted_relocation || center_screen)
4927   {
4928     /* relocation _with_ centering of screen */
4929
4930     new_scroll_x = SCROLL_POSITION_X(x);
4931     new_scroll_y = SCROLL_POSITION_Y(y);
4932   }
4933   else
4934   {
4935     /* relocation _without_ centering of screen */
4936
4937     int center_scroll_x = SCROLL_POSITION_X(old_x);
4938     int center_scroll_y = SCROLL_POSITION_Y(old_y);
4939     int offset_x = x + (scroll_x - center_scroll_x);
4940     int offset_y = y + (scroll_y - center_scroll_y);
4941
4942     /* for new screen position, apply previous offset to center position */
4943     new_scroll_x = SCROLL_POSITION_X(offset_x);
4944     new_scroll_y = SCROLL_POSITION_Y(offset_y);
4945   }
4946
4947   if (quick_relocation)
4948   {
4949     /* case 2: quick relocation (redraw without visible scrolling) */
4950
4951     scroll_x = new_scroll_x;
4952     scroll_y = new_scroll_y;
4953
4954     RedrawPlayfield();
4955
4956     return;
4957   }
4958
4959   /* case 3: visible relocation (with scrolling to new position) */
4960
4961   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4962
4963   SetVideoFrameDelay(wait_delay_value);
4964
4965   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4966   {
4967     int dx = 0, dy = 0;
4968     int fx = FX, fy = FY;
4969
4970     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4971     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4972
4973     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4974       break;
4975
4976     scroll_x -= dx;
4977     scroll_y -= dy;
4978
4979     fx += dx * TILEX / 2;
4980     fy += dy * TILEY / 2;
4981
4982     ScrollLevel(dx, dy);
4983     DrawAllPlayers();
4984
4985     /* scroll in two steps of half tile size to make things smoother */
4986     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4987
4988     /* scroll second step to align at full tile size */
4989     BlitScreenToBitmap(window);
4990   }
4991
4992   DrawAllPlayers();
4993   BackToFront();
4994
4995   SetVideoFrameDelay(frame_delay_value_old);
4996 }
4997
4998 void RelocatePlayer(int jx, int jy, int el_player_raw)
4999 {
5000   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5001   int player_nr = GET_PLAYER_NR(el_player);
5002   struct PlayerInfo *player = &stored_player[player_nr];
5003   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5004   boolean no_delay = (tape.warp_forward);
5005   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5006   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5007   int old_jx = player->jx;
5008   int old_jy = player->jy;
5009   int old_element = Feld[old_jx][old_jy];
5010   int element = Feld[jx][jy];
5011   boolean player_relocated = (old_jx != jx || old_jy != jy);
5012
5013   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5014   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5015   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5016   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5017   int leave_side_horiz = move_dir_horiz;
5018   int leave_side_vert  = move_dir_vert;
5019   int enter_side = enter_side_horiz | enter_side_vert;
5020   int leave_side = leave_side_horiz | leave_side_vert;
5021
5022   if (player->GameOver)         /* do not reanimate dead player */
5023     return;
5024
5025   if (!player_relocated)        /* no need to relocate the player */
5026     return;
5027
5028   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5029   {
5030     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5031     DrawLevelField(jx, jy);
5032   }
5033
5034   if (player->present)
5035   {
5036     while (player->MovPos)
5037     {
5038       ScrollPlayer(player, SCROLL_GO_ON);
5039       ScrollScreen(NULL, SCROLL_GO_ON);
5040
5041       AdvanceFrameAndPlayerCounters(player->index_nr);
5042
5043       DrawPlayer(player);
5044
5045       BackToFront_WithFrameDelay(wait_delay_value);
5046     }
5047
5048     DrawPlayer(player);         /* needed here only to cleanup last field */
5049     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5050
5051     player->is_moving = FALSE;
5052   }
5053
5054   if (IS_CUSTOM_ELEMENT(old_element))
5055     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5056                                CE_LEFT_BY_PLAYER,
5057                                player->index_bit, leave_side);
5058
5059   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5060                                       CE_PLAYER_LEAVES_X,
5061                                       player->index_bit, leave_side);
5062
5063   Feld[jx][jy] = el_player;
5064   InitPlayerField(jx, jy, el_player, TRUE);
5065
5066   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5067      possible that the relocation target field did not contain a player element,
5068      but a walkable element, to which the new player was relocated -- in this
5069      case, restore that (already initialized!) element on the player field */
5070   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5071   {
5072     Feld[jx][jy] = element;     /* restore previously existing element */
5073   }
5074
5075   /* only visually relocate centered player */
5076   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5077                      FALSE, level.instant_relocation);
5078
5079   TestIfPlayerTouchesBadThing(jx, jy);
5080   TestIfPlayerTouchesCustomElement(jx, jy);
5081
5082   if (IS_CUSTOM_ELEMENT(element))
5083     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5084                                player->index_bit, enter_side);
5085
5086   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5087                                       player->index_bit, enter_side);
5088
5089   if (player->is_switching)
5090   {
5091     /* ensure that relocation while still switching an element does not cause
5092        a new element to be treated as also switched directly after relocation
5093        (this is important for teleporter switches that teleport the player to
5094        a place where another teleporter switch is in the same direction, which
5095        would then incorrectly be treated as immediately switched before the
5096        direction key that caused the switch was released) */
5097
5098     player->switch_x += jx - old_jx;
5099     player->switch_y += jy - old_jy;
5100   }
5101 }
5102
5103 void Explode(int ex, int ey, int phase, int mode)
5104 {
5105   int x, y;
5106   int last_phase;
5107   int border_element;
5108
5109   /* !!! eliminate this variable !!! */
5110   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5111
5112   if (game.explosions_delayed)
5113   {
5114     ExplodeField[ex][ey] = mode;
5115     return;
5116   }
5117
5118   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5119   {
5120     int center_element = Feld[ex][ey];
5121     int artwork_element, explosion_element;     /* set these values later */
5122
5123     /* remove things displayed in background while burning dynamite */
5124     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5125       Back[ex][ey] = 0;
5126
5127     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5128     {
5129       /* put moving element to center field (and let it explode there) */
5130       center_element = MovingOrBlocked2Element(ex, ey);
5131       RemoveMovingField(ex, ey);
5132       Feld[ex][ey] = center_element;
5133     }
5134
5135     /* now "center_element" is finally determined -- set related values now */
5136     artwork_element = center_element;           /* for custom player artwork */
5137     explosion_element = center_element;         /* for custom player artwork */
5138
5139     if (IS_PLAYER(ex, ey))
5140     {
5141       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5142
5143       artwork_element = stored_player[player_nr].artwork_element;
5144
5145       if (level.use_explosion_element[player_nr])
5146       {
5147         explosion_element = level.explosion_element[player_nr];
5148         artwork_element = explosion_element;
5149       }
5150     }
5151
5152     if (mode == EX_TYPE_NORMAL ||
5153         mode == EX_TYPE_CENTER ||
5154         mode == EX_TYPE_CROSS)
5155       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5156
5157     last_phase = element_info[explosion_element].explosion_delay + 1;
5158
5159     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5160     {
5161       int xx = x - ex + 1;
5162       int yy = y - ey + 1;
5163       int element;
5164
5165       if (!IN_LEV_FIELD(x, y) ||
5166           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5167           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5168         continue;
5169
5170       element = Feld[x][y];
5171
5172       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5173       {
5174         element = MovingOrBlocked2Element(x, y);
5175
5176         if (!IS_EXPLOSION_PROOF(element))
5177           RemoveMovingField(x, y);
5178       }
5179
5180       /* indestructible elements can only explode in center (but not flames) */
5181       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5182                                            mode == EX_TYPE_BORDER)) ||
5183           element == EL_FLAMES)
5184         continue;
5185
5186       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5187          behaviour, for example when touching a yamyam that explodes to rocks
5188          with active deadly shield, a rock is created under the player !!! */
5189       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5190 #if 0
5191       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5192           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5193            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5194 #else
5195       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5196 #endif
5197       {
5198         if (IS_ACTIVE_BOMB(element))
5199         {
5200           /* re-activate things under the bomb like gate or penguin */
5201           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5202           Back[x][y] = 0;
5203         }
5204
5205         continue;
5206       }
5207
5208       /* save walkable background elements while explosion on same tile */
5209       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5210           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5211         Back[x][y] = element;
5212
5213       /* ignite explodable elements reached by other explosion */
5214       if (element == EL_EXPLOSION)
5215         element = Store2[x][y];
5216
5217       if (AmoebaNr[x][y] &&
5218           (element == EL_AMOEBA_FULL ||
5219            element == EL_BD_AMOEBA ||
5220            element == EL_AMOEBA_GROWING))
5221       {
5222         AmoebaCnt[AmoebaNr[x][y]]--;
5223         AmoebaCnt2[AmoebaNr[x][y]]--;
5224       }
5225
5226       RemoveField(x, y);
5227
5228       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5229       {
5230         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5231
5232         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5233
5234         if (PLAYERINFO(ex, ey)->use_murphy)
5235           Store[x][y] = EL_EMPTY;
5236       }
5237
5238       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5239          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5240       else if (ELEM_IS_PLAYER(center_element))
5241         Store[x][y] = EL_EMPTY;
5242       else if (center_element == EL_YAMYAM)
5243         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5244       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5245         Store[x][y] = element_info[center_element].content.e[xx][yy];
5246 #if 1
5247       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5248          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5249          otherwise) -- FIX THIS !!! */
5250       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5251         Store[x][y] = element_info[element].content.e[1][1];
5252 #else
5253       else if (!CAN_EXPLODE(element))
5254         Store[x][y] = element_info[element].content.e[1][1];
5255 #endif
5256       else
5257         Store[x][y] = EL_EMPTY;
5258
5259       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5260           center_element == EL_AMOEBA_TO_DIAMOND)
5261         Store2[x][y] = element;
5262
5263       Feld[x][y] = EL_EXPLOSION;
5264       GfxElement[x][y] = artwork_element;
5265
5266       ExplodePhase[x][y] = 1;
5267       ExplodeDelay[x][y] = last_phase;
5268
5269       Stop[x][y] = TRUE;
5270     }
5271
5272     if (center_element == EL_YAMYAM)
5273       game.yamyam_content_nr =
5274         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5275
5276     return;
5277   }
5278
5279   if (Stop[ex][ey])
5280     return;
5281
5282   x = ex;
5283   y = ey;
5284
5285   if (phase == 1)
5286     GfxFrame[x][y] = 0;         /* restart explosion animation */
5287
5288   last_phase = ExplodeDelay[x][y];
5289
5290   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5291
5292   /* this can happen if the player leaves an explosion just in time */
5293   if (GfxElement[x][y] == EL_UNDEFINED)
5294     GfxElement[x][y] = EL_EMPTY;
5295
5296   border_element = Store2[x][y];
5297   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5298     border_element = StorePlayer[x][y];
5299
5300   if (phase == element_info[border_element].ignition_delay ||
5301       phase == last_phase)
5302   {
5303     boolean border_explosion = FALSE;
5304
5305     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5306         !PLAYER_EXPLOSION_PROTECTED(x, y))
5307     {
5308       KillPlayerUnlessExplosionProtected(x, y);
5309       border_explosion = TRUE;
5310     }
5311     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5312     {
5313       Feld[x][y] = Store2[x][y];
5314       Store2[x][y] = 0;
5315       Bang(x, y);
5316       border_explosion = TRUE;
5317     }
5318     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5319     {
5320       AmoebeUmwandeln(x, y);
5321       Store2[x][y] = 0;
5322       border_explosion = TRUE;
5323     }
5324
5325     /* if an element just explodes due to another explosion (chain-reaction),
5326        do not immediately end the new explosion when it was the last frame of
5327        the explosion (as it would be done in the following "if"-statement!) */
5328     if (border_explosion && phase == last_phase)
5329       return;
5330   }
5331
5332   if (phase == last_phase)
5333   {
5334     int element;
5335
5336     element = Feld[x][y] = Store[x][y];
5337     Store[x][y] = Store2[x][y] = 0;
5338     GfxElement[x][y] = EL_UNDEFINED;
5339
5340     /* player can escape from explosions and might therefore be still alive */
5341     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5342         element <= EL_PLAYER_IS_EXPLODING_4)
5343     {
5344       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5345       int explosion_element = EL_PLAYER_1 + player_nr;
5346       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5347       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5348
5349       if (level.use_explosion_element[player_nr])
5350         explosion_element = level.explosion_element[player_nr];
5351
5352       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5353                     element_info[explosion_element].content.e[xx][yy]);
5354     }
5355
5356     /* restore probably existing indestructible background element */
5357     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5358       element = Feld[x][y] = Back[x][y];
5359     Back[x][y] = 0;
5360
5361     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5362     GfxDir[x][y] = MV_NONE;
5363     ChangeDelay[x][y] = 0;
5364     ChangePage[x][y] = -1;
5365
5366     CustomValue[x][y] = 0;
5367
5368     InitField_WithBug2(x, y, FALSE);
5369
5370     TEST_DrawLevelField(x, y);
5371
5372     TestIfElementTouchesCustomElement(x, y);
5373
5374     if (GFX_CRUMBLED(element))
5375       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5376
5377     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5378       StorePlayer[x][y] = 0;
5379
5380     if (ELEM_IS_PLAYER(element))
5381       RelocatePlayer(x, y, element);
5382   }
5383   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5384   {
5385     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5386     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5387
5388     if (phase == delay)
5389       TEST_DrawLevelFieldCrumbled(x, y);
5390
5391     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5392     {
5393       DrawLevelElement(x, y, Back[x][y]);
5394       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5395     }
5396     else if (IS_WALKABLE_UNDER(Back[x][y]))
5397     {
5398       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5399       DrawLevelElementThruMask(x, y, Back[x][y]);
5400     }
5401     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5402       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5403   }
5404 }
5405
5406 void DynaExplode(int ex, int ey)
5407 {
5408   int i, j;
5409   int dynabomb_element = Feld[ex][ey];
5410   int dynabomb_size = 1;
5411   boolean dynabomb_xl = FALSE;
5412   struct PlayerInfo *player;
5413   static int xy[4][2] =
5414   {
5415     { 0, -1 },
5416     { -1, 0 },
5417     { +1, 0 },
5418     { 0, +1 }
5419   };
5420
5421   if (IS_ACTIVE_BOMB(dynabomb_element))
5422   {
5423     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5424     dynabomb_size = player->dynabomb_size;
5425     dynabomb_xl = player->dynabomb_xl;
5426     player->dynabombs_left++;
5427   }
5428
5429   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5430
5431   for (i = 0; i < NUM_DIRECTIONS; i++)
5432   {
5433     for (j = 1; j <= dynabomb_size; j++)
5434     {
5435       int x = ex + j * xy[i][0];
5436       int y = ey + j * xy[i][1];
5437       int element;
5438
5439       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5440         break;
5441
5442       element = Feld[x][y];
5443
5444       /* do not restart explosions of fields with active bombs */
5445       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5446         continue;
5447
5448       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5449
5450       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5451           !IS_DIGGABLE(element) && !dynabomb_xl)
5452         break;
5453     }
5454   }
5455 }
5456
5457 void Bang(int x, int y)
5458 {
5459   int element = MovingOrBlocked2Element(x, y);
5460   int explosion_type = EX_TYPE_NORMAL;
5461
5462   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5463   {
5464     struct PlayerInfo *player = PLAYERINFO(x, y);
5465
5466     element = Feld[x][y] = player->initial_element;
5467
5468     if (level.use_explosion_element[player->index_nr])
5469     {
5470       int explosion_element = level.explosion_element[player->index_nr];
5471
5472       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5473         explosion_type = EX_TYPE_CROSS;
5474       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5475         explosion_type = EX_TYPE_CENTER;
5476     }
5477   }
5478
5479   switch (element)
5480   {
5481     case EL_BUG:
5482     case EL_SPACESHIP:
5483     case EL_BD_BUTTERFLY:
5484     case EL_BD_FIREFLY:
5485     case EL_YAMYAM:
5486     case EL_DARK_YAMYAM:
5487     case EL_ROBOT:
5488     case EL_PACMAN:
5489     case EL_MOLE:
5490       RaiseScoreElement(element);
5491       break;
5492
5493     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5494     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5495     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5496     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5497     case EL_DYNABOMB_INCREASE_NUMBER:
5498     case EL_DYNABOMB_INCREASE_SIZE:
5499     case EL_DYNABOMB_INCREASE_POWER:
5500       explosion_type = EX_TYPE_DYNA;
5501       break;
5502
5503     case EL_DC_LANDMINE:
5504       explosion_type = EX_TYPE_CENTER;
5505       break;
5506
5507     case EL_PENGUIN:
5508     case EL_LAMP:
5509     case EL_LAMP_ACTIVE:
5510     case EL_AMOEBA_TO_DIAMOND:
5511       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5512         explosion_type = EX_TYPE_CENTER;
5513       break;
5514
5515     default:
5516       if (element_info[element].explosion_type == EXPLODES_CROSS)
5517         explosion_type = EX_TYPE_CROSS;
5518       else if (element_info[element].explosion_type == EXPLODES_1X1)
5519         explosion_type = EX_TYPE_CENTER;
5520       break;
5521   }
5522
5523   if (explosion_type == EX_TYPE_DYNA)
5524     DynaExplode(x, y);
5525   else
5526     Explode(x, y, EX_PHASE_START, explosion_type);
5527
5528   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5529 }
5530
5531 void SplashAcid(int x, int y)
5532 {
5533   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5534       (!IN_LEV_FIELD(x - 1, y - 2) ||
5535        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5536     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5537
5538   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5539       (!IN_LEV_FIELD(x + 1, y - 2) ||
5540        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5541     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5542
5543   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5544 }
5545
5546 static void InitBeltMovement()
5547 {
5548   static int belt_base_element[4] =
5549   {
5550     EL_CONVEYOR_BELT_1_LEFT,
5551     EL_CONVEYOR_BELT_2_LEFT,
5552     EL_CONVEYOR_BELT_3_LEFT,
5553     EL_CONVEYOR_BELT_4_LEFT
5554   };
5555   static int belt_base_active_element[4] =
5556   {
5557     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5558     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5559     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5560     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5561   };
5562
5563   int x, y, i, j;
5564
5565   /* set frame order for belt animation graphic according to belt direction */
5566   for (i = 0; i < NUM_BELTS; i++)
5567   {
5568     int belt_nr = i;
5569
5570     for (j = 0; j < NUM_BELT_PARTS; j++)
5571     {
5572       int element = belt_base_active_element[belt_nr] + j;
5573       int graphic_1 = el2img(element);
5574       int graphic_2 = el2panelimg(element);
5575
5576       if (game.belt_dir[i] == MV_LEFT)
5577       {
5578         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5579         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5580       }
5581       else
5582       {
5583         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5584         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5585       }
5586     }
5587   }
5588
5589   SCAN_PLAYFIELD(x, y)
5590   {
5591     int element = Feld[x][y];
5592
5593     for (i = 0; i < NUM_BELTS; i++)
5594     {
5595       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5596       {
5597         int e_belt_nr = getBeltNrFromBeltElement(element);
5598         int belt_nr = i;
5599
5600         if (e_belt_nr == belt_nr)
5601         {
5602           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5603
5604           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5605         }
5606       }
5607     }
5608   }
5609 }
5610
5611 static void ToggleBeltSwitch(int x, int y)
5612 {
5613   static int belt_base_element[4] =
5614   {
5615     EL_CONVEYOR_BELT_1_LEFT,
5616     EL_CONVEYOR_BELT_2_LEFT,
5617     EL_CONVEYOR_BELT_3_LEFT,
5618     EL_CONVEYOR_BELT_4_LEFT
5619   };
5620   static int belt_base_active_element[4] =
5621   {
5622     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5623     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5624     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5625     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5626   };
5627   static int belt_base_switch_element[4] =
5628   {
5629     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5630     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5631     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5632     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5633   };
5634   static int belt_move_dir[4] =
5635   {
5636     MV_LEFT,
5637     MV_NONE,
5638     MV_RIGHT,
5639     MV_NONE,
5640   };
5641
5642   int element = Feld[x][y];
5643   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5644   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5645   int belt_dir = belt_move_dir[belt_dir_nr];
5646   int xx, yy, i;
5647
5648   if (!IS_BELT_SWITCH(element))
5649     return;
5650
5651   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5652   game.belt_dir[belt_nr] = belt_dir;
5653
5654   if (belt_dir_nr == 3)
5655     belt_dir_nr = 1;
5656
5657   /* set frame order for belt animation graphic according to belt direction */
5658   for (i = 0; i < NUM_BELT_PARTS; i++)
5659   {
5660     int element = belt_base_active_element[belt_nr] + i;
5661     int graphic_1 = el2img(element);
5662     int graphic_2 = el2panelimg(element);
5663
5664     if (belt_dir == MV_LEFT)
5665     {
5666       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5667       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5668     }
5669     else
5670     {
5671       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5672       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5673     }
5674   }
5675
5676   SCAN_PLAYFIELD(xx, yy)
5677   {
5678     int element = Feld[xx][yy];
5679
5680     if (IS_BELT_SWITCH(element))
5681     {
5682       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5683
5684       if (e_belt_nr == belt_nr)
5685       {
5686         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5687         TEST_DrawLevelField(xx, yy);
5688       }
5689     }
5690     else if (IS_BELT(element) && belt_dir != MV_NONE)
5691     {
5692       int e_belt_nr = getBeltNrFromBeltElement(element);
5693
5694       if (e_belt_nr == belt_nr)
5695       {
5696         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5697
5698         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5699         TEST_DrawLevelField(xx, yy);
5700       }
5701     }
5702     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5703     {
5704       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5705
5706       if (e_belt_nr == belt_nr)
5707       {
5708         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5709
5710         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5711         TEST_DrawLevelField(xx, yy);
5712       }
5713     }
5714   }
5715 }
5716
5717 static void ToggleSwitchgateSwitch(int x, int y)
5718 {
5719   int xx, yy;
5720
5721   game.switchgate_pos = !game.switchgate_pos;
5722
5723   SCAN_PLAYFIELD(xx, yy)
5724   {
5725     int element = Feld[xx][yy];
5726
5727     if (element == EL_SWITCHGATE_SWITCH_UP)
5728     {
5729       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5730       TEST_DrawLevelField(xx, yy);
5731     }
5732     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5733     {
5734       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5735       TEST_DrawLevelField(xx, yy);
5736     }
5737     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5738     {
5739       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5740       TEST_DrawLevelField(xx, yy);
5741     }
5742     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5743     {
5744       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5745       TEST_DrawLevelField(xx, yy);
5746     }
5747     else if (element == EL_SWITCHGATE_OPEN ||
5748              element == EL_SWITCHGATE_OPENING)
5749     {
5750       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5751
5752       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5753     }
5754     else if (element == EL_SWITCHGATE_CLOSED ||
5755              element == EL_SWITCHGATE_CLOSING)
5756     {
5757       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5758
5759       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5760     }
5761   }
5762 }
5763
5764 static int getInvisibleActiveFromInvisibleElement(int element)
5765 {
5766   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5767           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5768           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5769           element);
5770 }
5771
5772 static int getInvisibleFromInvisibleActiveElement(int element)
5773 {
5774   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5775           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5776           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5777           element);
5778 }
5779
5780 static void RedrawAllLightSwitchesAndInvisibleElements()
5781 {
5782   int x, y;
5783
5784   SCAN_PLAYFIELD(x, y)
5785   {
5786     int element = Feld[x][y];
5787
5788     if (element == EL_LIGHT_SWITCH &&
5789         game.light_time_left > 0)
5790     {
5791       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5792       TEST_DrawLevelField(x, y);
5793     }
5794     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5795              game.light_time_left == 0)
5796     {
5797       Feld[x][y] = EL_LIGHT_SWITCH;
5798       TEST_DrawLevelField(x, y);
5799     }
5800     else if (element == EL_EMC_DRIPPER &&
5801              game.light_time_left > 0)
5802     {
5803       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5804       TEST_DrawLevelField(x, y);
5805     }
5806     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5807              game.light_time_left == 0)
5808     {
5809       Feld[x][y] = EL_EMC_DRIPPER;
5810       TEST_DrawLevelField(x, y);
5811     }
5812     else if (element == EL_INVISIBLE_STEELWALL ||
5813              element == EL_INVISIBLE_WALL ||
5814              element == EL_INVISIBLE_SAND)
5815     {
5816       if (game.light_time_left > 0)
5817         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5818
5819       TEST_DrawLevelField(x, y);
5820
5821       /* uncrumble neighbour fields, if needed */
5822       if (element == EL_INVISIBLE_SAND)
5823         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5824     }
5825     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5826              element == EL_INVISIBLE_WALL_ACTIVE ||
5827              element == EL_INVISIBLE_SAND_ACTIVE)
5828     {
5829       if (game.light_time_left == 0)
5830         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5831
5832       TEST_DrawLevelField(x, y);
5833
5834       /* re-crumble neighbour fields, if needed */
5835       if (element == EL_INVISIBLE_SAND)
5836         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5837     }
5838   }
5839 }
5840
5841 static void RedrawAllInvisibleElementsForLenses()
5842 {
5843   int x, y;
5844
5845   SCAN_PLAYFIELD(x, y)
5846   {
5847     int element = Feld[x][y];
5848
5849     if (element == EL_EMC_DRIPPER &&
5850         game.lenses_time_left > 0)
5851     {
5852       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5853       TEST_DrawLevelField(x, y);
5854     }
5855     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5856              game.lenses_time_left == 0)
5857     {
5858       Feld[x][y] = EL_EMC_DRIPPER;
5859       TEST_DrawLevelField(x, y);
5860     }
5861     else if (element == EL_INVISIBLE_STEELWALL ||
5862              element == EL_INVISIBLE_WALL ||
5863              element == EL_INVISIBLE_SAND)
5864     {
5865       if (game.lenses_time_left > 0)
5866         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5867
5868       TEST_DrawLevelField(x, y);
5869
5870       /* uncrumble neighbour fields, if needed */
5871       if (element == EL_INVISIBLE_SAND)
5872         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5873     }
5874     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5875              element == EL_INVISIBLE_WALL_ACTIVE ||
5876              element == EL_INVISIBLE_SAND_ACTIVE)
5877     {
5878       if (game.lenses_time_left == 0)
5879         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5880
5881       TEST_DrawLevelField(x, y);
5882
5883       /* re-crumble neighbour fields, if needed */
5884       if (element == EL_INVISIBLE_SAND)
5885         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5886     }
5887   }
5888 }
5889
5890 static void RedrawAllInvisibleElementsForMagnifier()
5891 {
5892   int x, y;
5893
5894   SCAN_PLAYFIELD(x, y)
5895   {
5896     int element = Feld[x][y];
5897
5898     if (element == EL_EMC_FAKE_GRASS &&
5899         game.magnify_time_left > 0)
5900     {
5901       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5902       TEST_DrawLevelField(x, y);
5903     }
5904     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5905              game.magnify_time_left == 0)
5906     {
5907       Feld[x][y] = EL_EMC_FAKE_GRASS;
5908       TEST_DrawLevelField(x, y);
5909     }
5910     else if (IS_GATE_GRAY(element) &&
5911              game.magnify_time_left > 0)
5912     {
5913       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5914                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5915                     IS_EM_GATE_GRAY(element) ?
5916                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5917                     IS_EMC_GATE_GRAY(element) ?
5918                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5919                     IS_DC_GATE_GRAY(element) ?
5920                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5921                     element);
5922       TEST_DrawLevelField(x, y);
5923     }
5924     else if (IS_GATE_GRAY_ACTIVE(element) &&
5925              game.magnify_time_left == 0)
5926     {
5927       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5928                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5929                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5930                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5931                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5932                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5933                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5934                     EL_DC_GATE_WHITE_GRAY :
5935                     element);
5936       TEST_DrawLevelField(x, y);
5937     }
5938   }
5939 }
5940
5941 static void ToggleLightSwitch(int x, int y)
5942 {
5943   int element = Feld[x][y];
5944
5945   game.light_time_left =
5946     (element == EL_LIGHT_SWITCH ?
5947      level.time_light * FRAMES_PER_SECOND : 0);
5948
5949   RedrawAllLightSwitchesAndInvisibleElements();
5950 }
5951
5952 static void ActivateTimegateSwitch(int x, int y)
5953 {
5954   int xx, yy;
5955
5956   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5957
5958   SCAN_PLAYFIELD(xx, yy)
5959   {
5960     int element = Feld[xx][yy];
5961
5962     if (element == EL_TIMEGATE_CLOSED ||
5963         element == EL_TIMEGATE_CLOSING)
5964     {
5965       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5966       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5967     }
5968
5969     /*
5970     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5971     {
5972       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5973       TEST_DrawLevelField(xx, yy);
5974     }
5975     */
5976
5977   }
5978
5979   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5980                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5981 }
5982
5983 void Impact(int x, int y)
5984 {
5985   boolean last_line = (y == lev_fieldy - 1);
5986   boolean object_hit = FALSE;
5987   boolean impact = (last_line || object_hit);
5988   int element = Feld[x][y];
5989   int smashed = EL_STEELWALL;
5990
5991   if (!last_line)       /* check if element below was hit */
5992   {
5993     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5994       return;
5995
5996     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5997                                          MovDir[x][y + 1] != MV_DOWN ||
5998                                          MovPos[x][y + 1] <= TILEY / 2));
5999
6000     /* do not smash moving elements that left the smashed field in time */
6001     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6002         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6003       object_hit = FALSE;
6004
6005 #if USE_QUICKSAND_IMPACT_BUGFIX
6006     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6007     {
6008       RemoveMovingField(x, y + 1);
6009       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6010       Feld[x][y + 2] = EL_ROCK;
6011       TEST_DrawLevelField(x, y + 2);
6012
6013       object_hit = TRUE;
6014     }
6015
6016     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6017     {
6018       RemoveMovingField(x, y + 1);
6019       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6020       Feld[x][y + 2] = EL_ROCK;
6021       TEST_DrawLevelField(x, y + 2);
6022
6023       object_hit = TRUE;
6024     }
6025 #endif
6026
6027     if (object_hit)
6028       smashed = MovingOrBlocked2Element(x, y + 1);
6029
6030     impact = (last_line || object_hit);
6031   }
6032
6033   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6034   {
6035     SplashAcid(x, y + 1);
6036     return;
6037   }
6038
6039   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6040   /* only reset graphic animation if graphic really changes after impact */
6041   if (impact &&
6042       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6043   {
6044     ResetGfxAnimation(x, y);
6045     TEST_DrawLevelField(x, y);
6046   }
6047
6048   if (impact && CAN_EXPLODE_IMPACT(element))
6049   {
6050     Bang(x, y);
6051     return;
6052   }
6053   else if (impact && element == EL_PEARL &&
6054            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6055   {
6056     ResetGfxAnimation(x, y);
6057
6058     Feld[x][y] = EL_PEARL_BREAKING;
6059     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6060     return;
6061   }
6062   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6063   {
6064     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6065
6066     return;
6067   }
6068
6069   if (impact && element == EL_AMOEBA_DROP)
6070   {
6071     if (object_hit && IS_PLAYER(x, y + 1))
6072       KillPlayerUnlessEnemyProtected(x, y + 1);
6073     else if (object_hit && smashed == EL_PENGUIN)
6074       Bang(x, y + 1);
6075     else
6076     {
6077       Feld[x][y] = EL_AMOEBA_GROWING;
6078       Store[x][y] = EL_AMOEBA_WET;
6079
6080       ResetRandomAnimationValue(x, y);
6081     }
6082     return;
6083   }
6084
6085   if (object_hit)               /* check which object was hit */
6086   {
6087     if ((CAN_PASS_MAGIC_WALL(element) && 
6088          (smashed == EL_MAGIC_WALL ||
6089           smashed == EL_BD_MAGIC_WALL)) ||
6090         (CAN_PASS_DC_MAGIC_WALL(element) &&
6091          smashed == EL_DC_MAGIC_WALL))
6092     {
6093       int xx, yy;
6094       int activated_magic_wall =
6095         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6096          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6097          EL_DC_MAGIC_WALL_ACTIVE);
6098
6099       /* activate magic wall / mill */
6100       SCAN_PLAYFIELD(xx, yy)
6101       {
6102         if (Feld[xx][yy] == smashed)
6103           Feld[xx][yy] = activated_magic_wall;
6104       }
6105
6106       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6107       game.magic_wall_active = TRUE;
6108
6109       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6110                             SND_MAGIC_WALL_ACTIVATING :
6111                             smashed == EL_BD_MAGIC_WALL ?
6112                             SND_BD_MAGIC_WALL_ACTIVATING :
6113                             SND_DC_MAGIC_WALL_ACTIVATING));
6114     }
6115
6116     if (IS_PLAYER(x, y + 1))
6117     {
6118       if (CAN_SMASH_PLAYER(element))
6119       {
6120         KillPlayerUnlessEnemyProtected(x, y + 1);
6121         return;
6122       }
6123     }
6124     else if (smashed == EL_PENGUIN)
6125     {
6126       if (CAN_SMASH_PLAYER(element))
6127       {
6128         Bang(x, y + 1);
6129         return;
6130       }
6131     }
6132     else if (element == EL_BD_DIAMOND)
6133     {
6134       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6135       {
6136         Bang(x, y + 1);
6137         return;
6138       }
6139     }
6140     else if (((element == EL_SP_INFOTRON ||
6141                element == EL_SP_ZONK) &&
6142               (smashed == EL_SP_SNIKSNAK ||
6143                smashed == EL_SP_ELECTRON ||
6144                smashed == EL_SP_DISK_ORANGE)) ||
6145              (element == EL_SP_INFOTRON &&
6146               smashed == EL_SP_DISK_YELLOW))
6147     {
6148       Bang(x, y + 1);
6149       return;
6150     }
6151     else if (CAN_SMASH_EVERYTHING(element))
6152     {
6153       if (IS_CLASSIC_ENEMY(smashed) ||
6154           CAN_EXPLODE_SMASHED(smashed))
6155       {
6156         Bang(x, y + 1);
6157         return;
6158       }
6159       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6160       {
6161         if (smashed == EL_LAMP ||
6162             smashed == EL_LAMP_ACTIVE)
6163         {
6164           Bang(x, y + 1);
6165           return;
6166         }
6167         else if (smashed == EL_NUT)
6168         {
6169           Feld[x][y + 1] = EL_NUT_BREAKING;
6170           PlayLevelSound(x, y, SND_NUT_BREAKING);
6171           RaiseScoreElement(EL_NUT);
6172           return;
6173         }
6174         else if (smashed == EL_PEARL)
6175         {
6176           ResetGfxAnimation(x, y);
6177
6178           Feld[x][y + 1] = EL_PEARL_BREAKING;
6179           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6180           return;
6181         }
6182         else if (smashed == EL_DIAMOND)
6183         {
6184           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6185           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6186           return;
6187         }
6188         else if (IS_BELT_SWITCH(smashed))
6189         {
6190           ToggleBeltSwitch(x, y + 1);
6191         }
6192         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6193                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6194                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6195                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6196         {
6197           ToggleSwitchgateSwitch(x, y + 1);
6198         }
6199         else if (smashed == EL_LIGHT_SWITCH ||
6200                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6201         {
6202           ToggleLightSwitch(x, y + 1);
6203         }
6204         else
6205         {
6206           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6207
6208           CheckElementChangeBySide(x, y + 1, smashed, element,
6209                                    CE_SWITCHED, CH_SIDE_TOP);
6210           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6211                                             CH_SIDE_TOP);
6212         }
6213       }
6214       else
6215       {
6216         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6217       }
6218     }
6219   }
6220
6221   /* play sound of magic wall / mill */
6222   if (!last_line &&
6223       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6224        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6225        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6226   {
6227     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6228       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6229     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6230       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6231     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6232       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6233
6234     return;
6235   }
6236
6237   /* play sound of object that hits the ground */
6238   if (last_line || object_hit)
6239     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6240 }
6241
6242 inline static void TurnRoundExt(int x, int y)
6243 {
6244   static struct
6245   {
6246     int dx, dy;
6247   } move_xy[] =
6248   {
6249     {  0,  0 },
6250     { -1,  0 },
6251     { +1,  0 },
6252     {  0,  0 },
6253     {  0, -1 },
6254     {  0,  0 }, { 0, 0 }, { 0, 0 },
6255     {  0, +1 }
6256   };
6257   static struct
6258   {
6259     int left, right, back;
6260   } turn[] =
6261   {
6262     { 0,        0,              0        },
6263     { MV_DOWN,  MV_UP,          MV_RIGHT },
6264     { MV_UP,    MV_DOWN,        MV_LEFT  },
6265     { 0,        0,              0        },
6266     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6267     { 0,        0,              0        },
6268     { 0,        0,              0        },
6269     { 0,        0,              0        },
6270     { MV_RIGHT, MV_LEFT,        MV_UP    }
6271   };
6272
6273   int element = Feld[x][y];
6274   int move_pattern = element_info[element].move_pattern;
6275
6276   int old_move_dir = MovDir[x][y];
6277   int left_dir  = turn[old_move_dir].left;
6278   int right_dir = turn[old_move_dir].right;
6279   int back_dir  = turn[old_move_dir].back;
6280
6281   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6282   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6283   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6284   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6285
6286   int left_x  = x + left_dx,  left_y  = y + left_dy;
6287   int right_x = x + right_dx, right_y = y + right_dy;
6288   int move_x  = x + move_dx,  move_y  = y + move_dy;
6289
6290   int xx, yy;
6291
6292   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6293   {
6294     TestIfBadThingTouchesOtherBadThing(x, y);
6295
6296     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6297       MovDir[x][y] = right_dir;
6298     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6299       MovDir[x][y] = left_dir;
6300
6301     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6302       MovDelay[x][y] = 9;
6303     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6304       MovDelay[x][y] = 1;
6305   }
6306   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6307   {
6308     TestIfBadThingTouchesOtherBadThing(x, y);
6309
6310     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6311       MovDir[x][y] = left_dir;
6312     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6313       MovDir[x][y] = right_dir;
6314
6315     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6316       MovDelay[x][y] = 9;
6317     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6318       MovDelay[x][y] = 1;
6319   }
6320   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6321   {
6322     TestIfBadThingTouchesOtherBadThing(x, y);
6323
6324     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6325       MovDir[x][y] = left_dir;
6326     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6327       MovDir[x][y] = right_dir;
6328
6329     if (MovDir[x][y] != old_move_dir)
6330       MovDelay[x][y] = 9;
6331   }
6332   else if (element == EL_YAMYAM)
6333   {
6334     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6335     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6336
6337     if (can_turn_left && can_turn_right)
6338       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6339     else if (can_turn_left)
6340       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6341     else if (can_turn_right)
6342       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6343     else
6344       MovDir[x][y] = back_dir;
6345
6346     MovDelay[x][y] = 16 + 16 * RND(3);
6347   }
6348   else if (element == EL_DARK_YAMYAM)
6349   {
6350     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6351                                                          left_x, left_y);
6352     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6353                                                          right_x, right_y);
6354
6355     if (can_turn_left && can_turn_right)
6356       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6357     else if (can_turn_left)
6358       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6359     else if (can_turn_right)
6360       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6361     else
6362       MovDir[x][y] = back_dir;
6363
6364     MovDelay[x][y] = 16 + 16 * RND(3);
6365   }
6366   else if (element == EL_PACMAN)
6367   {
6368     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6369     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6370
6371     if (can_turn_left && can_turn_right)
6372       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6373     else if (can_turn_left)
6374       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6375     else if (can_turn_right)
6376       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6377     else
6378       MovDir[x][y] = back_dir;
6379
6380     MovDelay[x][y] = 6 + RND(40);
6381   }
6382   else if (element == EL_PIG)
6383   {
6384     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6385     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6386     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6387     boolean should_turn_left, should_turn_right, should_move_on;
6388     int rnd_value = 24;
6389     int rnd = RND(rnd_value);
6390
6391     should_turn_left = (can_turn_left &&
6392                         (!can_move_on ||
6393                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6394                                                    y + back_dy + left_dy)));
6395     should_turn_right = (can_turn_right &&
6396                          (!can_move_on ||
6397                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6398                                                     y + back_dy + right_dy)));
6399     should_move_on = (can_move_on &&
6400                       (!can_turn_left ||
6401                        !can_turn_right ||
6402                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6403                                                  y + move_dy + left_dy) ||
6404                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6405                                                  y + move_dy + right_dy)));
6406
6407     if (should_turn_left || should_turn_right || should_move_on)
6408     {
6409       if (should_turn_left && should_turn_right && should_move_on)
6410         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6411                         rnd < 2 * rnd_value / 3 ? right_dir :
6412                         old_move_dir);
6413       else if (should_turn_left && should_turn_right)
6414         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6415       else if (should_turn_left && should_move_on)
6416         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6417       else if (should_turn_right && should_move_on)
6418         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6419       else if (should_turn_left)
6420         MovDir[x][y] = left_dir;
6421       else if (should_turn_right)
6422         MovDir[x][y] = right_dir;
6423       else if (should_move_on)
6424         MovDir[x][y] = old_move_dir;
6425     }
6426     else if (can_move_on && rnd > rnd_value / 8)
6427       MovDir[x][y] = old_move_dir;
6428     else if (can_turn_left && can_turn_right)
6429       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6430     else if (can_turn_left && rnd > rnd_value / 8)
6431       MovDir[x][y] = left_dir;
6432     else if (can_turn_right && rnd > rnd_value/8)
6433       MovDir[x][y] = right_dir;
6434     else
6435       MovDir[x][y] = back_dir;
6436
6437     xx = x + move_xy[MovDir[x][y]].dx;
6438     yy = y + move_xy[MovDir[x][y]].dy;
6439
6440     if (!IN_LEV_FIELD(xx, yy) ||
6441         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6442       MovDir[x][y] = old_move_dir;
6443
6444     MovDelay[x][y] = 0;
6445   }
6446   else if (element == EL_DRAGON)
6447   {
6448     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6449     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6450     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6451     int rnd_value = 24;
6452     int rnd = RND(rnd_value);
6453
6454     if (can_move_on && rnd > rnd_value / 8)
6455       MovDir[x][y] = old_move_dir;
6456     else if (can_turn_left && can_turn_right)
6457       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6458     else if (can_turn_left && rnd > rnd_value / 8)
6459       MovDir[x][y] = left_dir;
6460     else if (can_turn_right && rnd > rnd_value / 8)
6461       MovDir[x][y] = right_dir;
6462     else
6463       MovDir[x][y] = back_dir;
6464
6465     xx = x + move_xy[MovDir[x][y]].dx;
6466     yy = y + move_xy[MovDir[x][y]].dy;
6467
6468     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6469       MovDir[x][y] = old_move_dir;
6470
6471     MovDelay[x][y] = 0;
6472   }
6473   else if (element == EL_MOLE)
6474   {
6475     boolean can_move_on =
6476       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6477                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6478                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6479     if (!can_move_on)
6480     {
6481       boolean can_turn_left =
6482         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6483                               IS_AMOEBOID(Feld[left_x][left_y])));
6484
6485       boolean can_turn_right =
6486         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6487                               IS_AMOEBOID(Feld[right_x][right_y])));
6488
6489       if (can_turn_left && can_turn_right)
6490         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6491       else if (can_turn_left)
6492         MovDir[x][y] = left_dir;
6493       else
6494         MovDir[x][y] = right_dir;
6495     }
6496
6497     if (MovDir[x][y] != old_move_dir)
6498       MovDelay[x][y] = 9;
6499   }
6500   else if (element == EL_BALLOON)
6501   {
6502     MovDir[x][y] = game.wind_direction;
6503     MovDelay[x][y] = 0;
6504   }
6505   else if (element == EL_SPRING)
6506   {
6507     if (MovDir[x][y] & MV_HORIZONTAL)
6508     {
6509       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6510           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6511       {
6512         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6513         ResetGfxAnimation(move_x, move_y);
6514         TEST_DrawLevelField(move_x, move_y);
6515
6516         MovDir[x][y] = back_dir;
6517       }
6518       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6519                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6520         MovDir[x][y] = MV_NONE;
6521     }
6522
6523     MovDelay[x][y] = 0;
6524   }
6525   else if (element == EL_ROBOT ||
6526            element == EL_SATELLITE ||
6527            element == EL_PENGUIN ||
6528            element == EL_EMC_ANDROID)
6529   {
6530     int attr_x = -1, attr_y = -1;
6531
6532     if (AllPlayersGone)
6533     {
6534       attr_x = ExitX;
6535       attr_y = ExitY;
6536     }
6537     else
6538     {
6539       int i;
6540
6541       for (i = 0; i < MAX_PLAYERS; i++)
6542       {
6543         struct PlayerInfo *player = &stored_player[i];
6544         int jx = player->jx, jy = player->jy;
6545
6546         if (!player->active)
6547           continue;
6548
6549         if (attr_x == -1 ||
6550             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6551         {
6552           attr_x = jx;
6553           attr_y = jy;
6554         }
6555       }
6556     }
6557
6558     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6559         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6560          game.engine_version < VERSION_IDENT(3,1,0,0)))
6561     {
6562       attr_x = ZX;
6563       attr_y = ZY;
6564     }
6565
6566     if (element == EL_PENGUIN)
6567     {
6568       int i;
6569       static int xy[4][2] =
6570       {
6571         { 0, -1 },
6572         { -1, 0 },
6573         { +1, 0 },
6574         { 0, +1 }
6575       };
6576
6577       for (i = 0; i < NUM_DIRECTIONS; i++)
6578       {
6579         int ex = x + xy[i][0];
6580         int ey = y + xy[i][1];
6581
6582         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6583                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6584                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6585                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6586         {
6587           attr_x = ex;
6588           attr_y = ey;
6589           break;
6590         }
6591       }
6592     }
6593
6594     MovDir[x][y] = MV_NONE;
6595     if (attr_x < x)
6596       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6597     else if (attr_x > x)
6598       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6599     if (attr_y < y)
6600       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6601     else if (attr_y > y)
6602       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6603
6604     if (element == EL_ROBOT)
6605     {
6606       int newx, newy;
6607
6608       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6609         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6610       Moving2Blocked(x, y, &newx, &newy);
6611
6612       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6613         MovDelay[x][y] = 8 + 8 * !RND(3);
6614       else
6615         MovDelay[x][y] = 16;
6616     }
6617     else if (element == EL_PENGUIN)
6618     {
6619       int newx, newy;
6620
6621       MovDelay[x][y] = 1;
6622
6623       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6624       {
6625         boolean first_horiz = RND(2);
6626         int new_move_dir = MovDir[x][y];
6627
6628         MovDir[x][y] =
6629           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6630         Moving2Blocked(x, y, &newx, &newy);
6631
6632         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6633           return;
6634
6635         MovDir[x][y] =
6636           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6637         Moving2Blocked(x, y, &newx, &newy);
6638
6639         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6640           return;
6641
6642         MovDir[x][y] = old_move_dir;
6643         return;
6644       }
6645     }
6646     else if (element == EL_SATELLITE)
6647     {
6648       int newx, newy;
6649
6650       MovDelay[x][y] = 1;
6651
6652       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6653       {
6654         boolean first_horiz = RND(2);
6655         int new_move_dir = MovDir[x][y];
6656
6657         MovDir[x][y] =
6658           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6659         Moving2Blocked(x, y, &newx, &newy);
6660
6661         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6662           return;
6663
6664         MovDir[x][y] =
6665           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6666         Moving2Blocked(x, y, &newx, &newy);
6667
6668         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6669           return;
6670
6671         MovDir[x][y] = old_move_dir;
6672         return;
6673       }
6674     }
6675     else if (element == EL_EMC_ANDROID)
6676     {
6677       static int check_pos[16] =
6678       {
6679         -1,             /*  0 => (invalid)          */
6680         7,              /*  1 => MV_LEFT            */
6681         3,              /*  2 => MV_RIGHT           */
6682         -1,             /*  3 => (invalid)          */
6683         1,              /*  4 =>            MV_UP   */
6684         0,              /*  5 => MV_LEFT  | MV_UP   */
6685         2,              /*  6 => MV_RIGHT | MV_UP   */
6686         -1,             /*  7 => (invalid)          */
6687         5,              /*  8 =>            MV_DOWN */
6688         6,              /*  9 => MV_LEFT  | MV_DOWN */
6689         4,              /* 10 => MV_RIGHT | MV_DOWN */
6690         -1,             /* 11 => (invalid)          */
6691         -1,             /* 12 => (invalid)          */
6692         -1,             /* 13 => (invalid)          */
6693         -1,             /* 14 => (invalid)          */
6694         -1,             /* 15 => (invalid)          */
6695       };
6696       static struct
6697       {
6698         int dx, dy;
6699         int dir;
6700       } check_xy[8] =
6701       {
6702         { -1, -1,       MV_LEFT  | MV_UP   },
6703         {  0, -1,                  MV_UP   },
6704         { +1, -1,       MV_RIGHT | MV_UP   },
6705         { +1,  0,       MV_RIGHT           },
6706         { +1, +1,       MV_RIGHT | MV_DOWN },
6707         {  0, +1,                  MV_DOWN },
6708         { -1, +1,       MV_LEFT  | MV_DOWN },
6709         { -1,  0,       MV_LEFT            },
6710       };
6711       int start_pos, check_order;
6712       boolean can_clone = FALSE;
6713       int i;
6714
6715       /* check if there is any free field around current position */
6716       for (i = 0; i < 8; i++)
6717       {
6718         int newx = x + check_xy[i].dx;
6719         int newy = y + check_xy[i].dy;
6720
6721         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6722         {
6723           can_clone = TRUE;
6724
6725           break;
6726         }
6727       }
6728
6729       if (can_clone)            /* randomly find an element to clone */
6730       {
6731         can_clone = FALSE;
6732
6733         start_pos = check_pos[RND(8)];
6734         check_order = (RND(2) ? -1 : +1);
6735
6736         for (i = 0; i < 8; i++)
6737         {
6738           int pos_raw = start_pos + i * check_order;
6739           int pos = (pos_raw + 8) % 8;
6740           int newx = x + check_xy[pos].dx;
6741           int newy = y + check_xy[pos].dy;
6742
6743           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6744           {
6745             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6746             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6747
6748             Store[x][y] = Feld[newx][newy];
6749
6750             can_clone = TRUE;
6751
6752             break;
6753           }
6754         }
6755       }
6756
6757       if (can_clone)            /* randomly find a direction to move */
6758       {
6759         can_clone = FALSE;
6760
6761         start_pos = check_pos[RND(8)];
6762         check_order = (RND(2) ? -1 : +1);
6763
6764         for (i = 0; i < 8; i++)
6765         {
6766           int pos_raw = start_pos + i * check_order;
6767           int pos = (pos_raw + 8) % 8;
6768           int newx = x + check_xy[pos].dx;
6769           int newy = y + check_xy[pos].dy;
6770           int new_move_dir = check_xy[pos].dir;
6771
6772           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6773           {
6774             MovDir[x][y] = new_move_dir;
6775             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6776
6777             can_clone = TRUE;
6778
6779             break;
6780           }
6781         }
6782       }
6783
6784       if (can_clone)            /* cloning and moving successful */
6785         return;
6786
6787       /* cannot clone -- try to move towards player */
6788
6789       start_pos = check_pos[MovDir[x][y] & 0x0f];
6790       check_order = (RND(2) ? -1 : +1);
6791
6792       for (i = 0; i < 3; i++)
6793       {
6794         /* first check start_pos, then previous/next or (next/previous) pos */
6795         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6796         int pos = (pos_raw + 8) % 8;
6797         int newx = x + check_xy[pos].dx;
6798         int newy = y + check_xy[pos].dy;
6799         int new_move_dir = check_xy[pos].dir;
6800
6801         if (IS_PLAYER(newx, newy))
6802           break;
6803
6804         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6805         {
6806           MovDir[x][y] = new_move_dir;
6807           MovDelay[x][y] = level.android_move_time * 8 + 1;
6808
6809           break;
6810         }
6811       }
6812     }
6813   }
6814   else if (move_pattern == MV_TURNING_LEFT ||
6815            move_pattern == MV_TURNING_RIGHT ||
6816            move_pattern == MV_TURNING_LEFT_RIGHT ||
6817            move_pattern == MV_TURNING_RIGHT_LEFT ||
6818            move_pattern == MV_TURNING_RANDOM ||
6819            move_pattern == MV_ALL_DIRECTIONS)
6820   {
6821     boolean can_turn_left =
6822       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6823     boolean can_turn_right =
6824       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6825
6826     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6827       return;
6828
6829     if (move_pattern == MV_TURNING_LEFT)
6830       MovDir[x][y] = left_dir;
6831     else if (move_pattern == MV_TURNING_RIGHT)
6832       MovDir[x][y] = right_dir;
6833     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6834       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6835     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6836       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6837     else if (move_pattern == MV_TURNING_RANDOM)
6838       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6839                       can_turn_right && !can_turn_left ? right_dir :
6840                       RND(2) ? left_dir : right_dir);
6841     else if (can_turn_left && can_turn_right)
6842       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6843     else if (can_turn_left)
6844       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6845     else if (can_turn_right)
6846       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6847     else
6848       MovDir[x][y] = back_dir;
6849
6850     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6851   }
6852   else if (move_pattern == MV_HORIZONTAL ||
6853            move_pattern == MV_VERTICAL)
6854   {
6855     if (move_pattern & old_move_dir)
6856       MovDir[x][y] = back_dir;
6857     else if (move_pattern == MV_HORIZONTAL)
6858       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6859     else if (move_pattern == MV_VERTICAL)
6860       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6861
6862     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6863   }
6864   else if (move_pattern & MV_ANY_DIRECTION)
6865   {
6866     MovDir[x][y] = move_pattern;
6867     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6868   }
6869   else if (move_pattern & MV_WIND_DIRECTION)
6870   {
6871     MovDir[x][y] = game.wind_direction;
6872     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6873   }
6874   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6875   {
6876     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6877       MovDir[x][y] = left_dir;
6878     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6879       MovDir[x][y] = right_dir;
6880
6881     if (MovDir[x][y] != old_move_dir)
6882       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6883   }
6884   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6885   {
6886     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6887       MovDir[x][y] = right_dir;
6888     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6889       MovDir[x][y] = left_dir;
6890
6891     if (MovDir[x][y] != old_move_dir)
6892       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6893   }
6894   else if (move_pattern == MV_TOWARDS_PLAYER ||
6895            move_pattern == MV_AWAY_FROM_PLAYER)
6896   {
6897     int attr_x = -1, attr_y = -1;
6898     int newx, newy;
6899     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6900
6901     if (AllPlayersGone)
6902     {
6903       attr_x = ExitX;
6904       attr_y = ExitY;
6905     }
6906     else
6907     {
6908       int i;
6909
6910       for (i = 0; i < MAX_PLAYERS; i++)
6911       {
6912         struct PlayerInfo *player = &stored_player[i];
6913         int jx = player->jx, jy = player->jy;
6914
6915         if (!player->active)
6916           continue;
6917
6918         if (attr_x == -1 ||
6919             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6920         {
6921           attr_x = jx;
6922           attr_y = jy;
6923         }
6924       }
6925     }
6926
6927     MovDir[x][y] = MV_NONE;
6928     if (attr_x < x)
6929       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6930     else if (attr_x > x)
6931       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6932     if (attr_y < y)
6933       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6934     else if (attr_y > y)
6935       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6936
6937     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6938
6939     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6940     {
6941       boolean first_horiz = RND(2);
6942       int new_move_dir = MovDir[x][y];
6943
6944       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6945       {
6946         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6947         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6948
6949         return;
6950       }
6951
6952       MovDir[x][y] =
6953         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6954       Moving2Blocked(x, y, &newx, &newy);
6955
6956       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6957         return;
6958
6959       MovDir[x][y] =
6960         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6961       Moving2Blocked(x, y, &newx, &newy);
6962
6963       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6964         return;
6965
6966       MovDir[x][y] = old_move_dir;
6967     }
6968   }
6969   else if (move_pattern == MV_WHEN_PUSHED ||
6970            move_pattern == MV_WHEN_DROPPED)
6971   {
6972     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6973       MovDir[x][y] = MV_NONE;
6974
6975     MovDelay[x][y] = 0;
6976   }
6977   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6978   {
6979     static int test_xy[7][2] =
6980     {
6981       { 0, -1 },
6982       { -1, 0 },
6983       { +1, 0 },
6984       { 0, +1 },
6985       { 0, -1 },
6986       { -1, 0 },
6987       { +1, 0 },
6988     };
6989     static int test_dir[7] =
6990     {
6991       MV_UP,
6992       MV_LEFT,
6993       MV_RIGHT,
6994       MV_DOWN,
6995       MV_UP,
6996       MV_LEFT,
6997       MV_RIGHT,
6998     };
6999     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7000     int move_preference = -1000000;     /* start with very low preference */
7001     int new_move_dir = MV_NONE;
7002     int start_test = RND(4);
7003     int i;
7004
7005     for (i = 0; i < NUM_DIRECTIONS; i++)
7006     {
7007       int move_dir = test_dir[start_test + i];
7008       int move_dir_preference;
7009
7010       xx = x + test_xy[start_test + i][0];
7011       yy = y + test_xy[start_test + i][1];
7012
7013       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7014           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7015       {
7016         new_move_dir = move_dir;
7017
7018         break;
7019       }
7020
7021       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7022         continue;
7023
7024       move_dir_preference = -1 * RunnerVisit[xx][yy];
7025       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7026         move_dir_preference = PlayerVisit[xx][yy];
7027
7028       if (move_dir_preference > move_preference)
7029       {
7030         /* prefer field that has not been visited for the longest time */
7031         move_preference = move_dir_preference;
7032         new_move_dir = move_dir;
7033       }
7034       else if (move_dir_preference == move_preference &&
7035                move_dir == old_move_dir)
7036       {
7037         /* prefer last direction when all directions are preferred equally */
7038         move_preference = move_dir_preference;
7039         new_move_dir = move_dir;
7040       }
7041     }
7042
7043     MovDir[x][y] = new_move_dir;
7044     if (old_move_dir != new_move_dir)
7045       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7046   }
7047 }
7048
7049 static void TurnRound(int x, int y)
7050 {
7051   int direction = MovDir[x][y];
7052
7053   TurnRoundExt(x, y);
7054
7055   GfxDir[x][y] = MovDir[x][y];
7056
7057   if (direction != MovDir[x][y])
7058     GfxFrame[x][y] = 0;
7059
7060   if (MovDelay[x][y])
7061     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7062
7063   ResetGfxFrame(x, y);
7064 }
7065
7066 static boolean JustBeingPushed(int x, int y)
7067 {
7068   int i;
7069
7070   for (i = 0; i < MAX_PLAYERS; i++)
7071   {
7072     struct PlayerInfo *player = &stored_player[i];
7073
7074     if (player->active && player->is_pushing && player->MovPos)
7075     {
7076       int next_jx = player->jx + (player->jx - player->last_jx);
7077       int next_jy = player->jy + (player->jy - player->last_jy);
7078
7079       if (x == next_jx && y == next_jy)
7080         return TRUE;
7081     }
7082   }
7083
7084   return FALSE;
7085 }
7086
7087 void StartMoving(int x, int y)
7088 {
7089   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7090   int element = Feld[x][y];
7091
7092   if (Stop[x][y])
7093     return;
7094
7095   if (MovDelay[x][y] == 0)
7096     GfxAction[x][y] = ACTION_DEFAULT;
7097
7098   if (CAN_FALL(element) && y < lev_fieldy - 1)
7099   {
7100     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7101         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7102       if (JustBeingPushed(x, y))
7103         return;
7104
7105     if (element == EL_QUICKSAND_FULL)
7106     {
7107       if (IS_FREE(x, y + 1))
7108       {
7109         InitMovingField(x, y, MV_DOWN);
7110         started_moving = TRUE;
7111
7112         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7113 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7114         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7115           Store[x][y] = EL_ROCK;
7116 #else
7117         Store[x][y] = EL_ROCK;
7118 #endif
7119
7120         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7121       }
7122       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7123       {
7124         if (!MovDelay[x][y])
7125         {
7126           MovDelay[x][y] = TILEY + 1;
7127
7128           ResetGfxAnimation(x, y);
7129           ResetGfxAnimation(x, y + 1);
7130         }
7131
7132         if (MovDelay[x][y])
7133         {
7134           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7135           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7136
7137           MovDelay[x][y]--;
7138           if (MovDelay[x][y])
7139             return;
7140         }
7141
7142         Feld[x][y] = EL_QUICKSAND_EMPTY;
7143         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7144         Store[x][y + 1] = Store[x][y];
7145         Store[x][y] = 0;
7146
7147         PlayLevelSoundAction(x, y, ACTION_FILLING);
7148       }
7149       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7150       {
7151         if (!MovDelay[x][y])
7152         {
7153           MovDelay[x][y] = TILEY + 1;
7154
7155           ResetGfxAnimation(x, y);
7156           ResetGfxAnimation(x, y + 1);
7157         }
7158
7159         if (MovDelay[x][y])
7160         {
7161           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7162           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7163
7164           MovDelay[x][y]--;
7165           if (MovDelay[x][y])
7166             return;
7167         }
7168
7169         Feld[x][y] = EL_QUICKSAND_EMPTY;
7170         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7171         Store[x][y + 1] = Store[x][y];
7172         Store[x][y] = 0;
7173
7174         PlayLevelSoundAction(x, y, ACTION_FILLING);
7175       }
7176     }
7177     else if (element == EL_QUICKSAND_FAST_FULL)
7178     {
7179       if (IS_FREE(x, y + 1))
7180       {
7181         InitMovingField(x, y, MV_DOWN);
7182         started_moving = TRUE;
7183
7184         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7185 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7186         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7187           Store[x][y] = EL_ROCK;
7188 #else
7189         Store[x][y] = EL_ROCK;
7190 #endif
7191
7192         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7193       }
7194       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7195       {
7196         if (!MovDelay[x][y])
7197         {
7198           MovDelay[x][y] = TILEY + 1;
7199
7200           ResetGfxAnimation(x, y);
7201           ResetGfxAnimation(x, y + 1);
7202         }
7203
7204         if (MovDelay[x][y])
7205         {
7206           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7207           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7208
7209           MovDelay[x][y]--;
7210           if (MovDelay[x][y])
7211             return;
7212         }
7213
7214         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7215         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7216         Store[x][y + 1] = Store[x][y];
7217         Store[x][y] = 0;
7218
7219         PlayLevelSoundAction(x, y, ACTION_FILLING);
7220       }
7221       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7222       {
7223         if (!MovDelay[x][y])
7224         {
7225           MovDelay[x][y] = TILEY + 1;
7226
7227           ResetGfxAnimation(x, y);
7228           ResetGfxAnimation(x, y + 1);
7229         }
7230
7231         if (MovDelay[x][y])
7232         {
7233           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7234           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7235
7236           MovDelay[x][y]--;
7237           if (MovDelay[x][y])
7238             return;
7239         }
7240
7241         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7242         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7243         Store[x][y + 1] = Store[x][y];
7244         Store[x][y] = 0;
7245
7246         PlayLevelSoundAction(x, y, ACTION_FILLING);
7247       }
7248     }
7249     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7250              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7251     {
7252       InitMovingField(x, y, MV_DOWN);
7253       started_moving = TRUE;
7254
7255       Feld[x][y] = EL_QUICKSAND_FILLING;
7256       Store[x][y] = element;
7257
7258       PlayLevelSoundAction(x, y, ACTION_FILLING);
7259     }
7260     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7261              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7262     {
7263       InitMovingField(x, y, MV_DOWN);
7264       started_moving = TRUE;
7265
7266       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7267       Store[x][y] = element;
7268
7269       PlayLevelSoundAction(x, y, ACTION_FILLING);
7270     }
7271     else if (element == EL_MAGIC_WALL_FULL)
7272     {
7273       if (IS_FREE(x, y + 1))
7274       {
7275         InitMovingField(x, y, MV_DOWN);
7276         started_moving = TRUE;
7277
7278         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7279         Store[x][y] = EL_CHANGED(Store[x][y]);
7280       }
7281       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7282       {
7283         if (!MovDelay[x][y])
7284           MovDelay[x][y] = TILEY / 4 + 1;
7285
7286         if (MovDelay[x][y])
7287         {
7288           MovDelay[x][y]--;
7289           if (MovDelay[x][y])
7290             return;
7291         }
7292
7293         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7294         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7295         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7296         Store[x][y] = 0;
7297       }
7298     }
7299     else if (element == EL_BD_MAGIC_WALL_FULL)
7300     {
7301       if (IS_FREE(x, y + 1))
7302       {
7303         InitMovingField(x, y, MV_DOWN);
7304         started_moving = TRUE;
7305
7306         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7307         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7308       }
7309       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7310       {
7311         if (!MovDelay[x][y])
7312           MovDelay[x][y] = TILEY / 4 + 1;
7313
7314         if (MovDelay[x][y])
7315         {
7316           MovDelay[x][y]--;
7317           if (MovDelay[x][y])
7318             return;
7319         }
7320
7321         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7322         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7323         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7324         Store[x][y] = 0;
7325       }
7326     }
7327     else if (element == EL_DC_MAGIC_WALL_FULL)
7328     {
7329       if (IS_FREE(x, y + 1))
7330       {
7331         InitMovingField(x, y, MV_DOWN);
7332         started_moving = TRUE;
7333
7334         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7335         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7336       }
7337       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7338       {
7339         if (!MovDelay[x][y])
7340           MovDelay[x][y] = TILEY / 4 + 1;
7341
7342         if (MovDelay[x][y])
7343         {
7344           MovDelay[x][y]--;
7345           if (MovDelay[x][y])
7346             return;
7347         }
7348
7349         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7350         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7351         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7352         Store[x][y] = 0;
7353       }
7354     }
7355     else if ((CAN_PASS_MAGIC_WALL(element) &&
7356               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7357                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7358              (CAN_PASS_DC_MAGIC_WALL(element) &&
7359               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7360
7361     {
7362       InitMovingField(x, y, MV_DOWN);
7363       started_moving = TRUE;
7364
7365       Feld[x][y] =
7366         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7367          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7368          EL_DC_MAGIC_WALL_FILLING);
7369       Store[x][y] = element;
7370     }
7371     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7372     {
7373       SplashAcid(x, y + 1);
7374
7375       InitMovingField(x, y, MV_DOWN);
7376       started_moving = TRUE;
7377
7378       Store[x][y] = EL_ACID;
7379     }
7380     else if (
7381              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7382               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7383              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7384               CAN_FALL(element) && WasJustFalling[x][y] &&
7385               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7386
7387              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7388               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7389               (Feld[x][y + 1] == EL_BLOCKED)))
7390     {
7391       /* this is needed for a special case not covered by calling "Impact()"
7392          from "ContinueMoving()": if an element moves to a tile directly below
7393          another element which was just falling on that tile (which was empty
7394          in the previous frame), the falling element above would just stop
7395          instead of smashing the element below (in previous version, the above
7396          element was just checked for "moving" instead of "falling", resulting
7397          in incorrect smashes caused by horizontal movement of the above
7398          element; also, the case of the player being the element to smash was
7399          simply not covered here... :-/ ) */
7400
7401       CheckCollision[x][y] = 0;
7402       CheckImpact[x][y] = 0;
7403
7404       Impact(x, y);
7405     }
7406     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7407     {
7408       if (MovDir[x][y] == MV_NONE)
7409       {
7410         InitMovingField(x, y, MV_DOWN);
7411         started_moving = TRUE;
7412       }
7413     }
7414     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7415     {
7416       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7417         MovDir[x][y] = MV_DOWN;
7418
7419       InitMovingField(x, y, MV_DOWN);
7420       started_moving = TRUE;
7421     }
7422     else if (element == EL_AMOEBA_DROP)
7423     {
7424       Feld[x][y] = EL_AMOEBA_GROWING;
7425       Store[x][y] = EL_AMOEBA_WET;
7426     }
7427     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7428               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7429              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7430              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7431     {
7432       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7433                                 (IS_FREE(x - 1, y + 1) ||
7434                                  Feld[x - 1][y + 1] == EL_ACID));
7435       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7436                                 (IS_FREE(x + 1, y + 1) ||
7437                                  Feld[x + 1][y + 1] == EL_ACID));
7438       boolean can_fall_any  = (can_fall_left || can_fall_right);
7439       boolean can_fall_both = (can_fall_left && can_fall_right);
7440       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7441
7442       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7443       {
7444         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7445           can_fall_right = FALSE;
7446         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7447           can_fall_left = FALSE;
7448         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7449           can_fall_right = FALSE;
7450         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7451           can_fall_left = FALSE;
7452
7453         can_fall_any  = (can_fall_left || can_fall_right);
7454         can_fall_both = FALSE;
7455       }
7456
7457       if (can_fall_both)
7458       {
7459         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7460           can_fall_right = FALSE;       /* slip down on left side */
7461         else
7462           can_fall_left = !(can_fall_right = RND(2));
7463
7464         can_fall_both = FALSE;
7465       }
7466
7467       if (can_fall_any)
7468       {
7469         /* if not determined otherwise, prefer left side for slipping down */
7470         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7471         started_moving = TRUE;
7472       }
7473     }
7474     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7475     {
7476       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7477       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7478       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7479       int belt_dir = game.belt_dir[belt_nr];
7480
7481       if ((belt_dir == MV_LEFT  && left_is_free) ||
7482           (belt_dir == MV_RIGHT && right_is_free))
7483       {
7484         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7485
7486         InitMovingField(x, y, belt_dir);
7487         started_moving = TRUE;
7488
7489         Pushed[x][y] = TRUE;
7490         Pushed[nextx][y] = TRUE;
7491
7492         GfxAction[x][y] = ACTION_DEFAULT;
7493       }
7494       else
7495       {
7496         MovDir[x][y] = 0;       /* if element was moving, stop it */
7497       }
7498     }
7499   }
7500
7501   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7502   if (CAN_MOVE(element) && !started_moving)
7503   {
7504     int move_pattern = element_info[element].move_pattern;
7505     int newx, newy;
7506
7507     Moving2Blocked(x, y, &newx, &newy);
7508
7509     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7510       return;
7511
7512     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7513         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7514     {
7515       WasJustMoving[x][y] = 0;
7516       CheckCollision[x][y] = 0;
7517
7518       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7519
7520       if (Feld[x][y] != element)        /* element has changed */
7521         return;
7522     }
7523
7524     if (!MovDelay[x][y])        /* start new movement phase */
7525     {
7526       /* all objects that can change their move direction after each step
7527          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7528
7529       if (element != EL_YAMYAM &&
7530           element != EL_DARK_YAMYAM &&
7531           element != EL_PACMAN &&
7532           !(move_pattern & MV_ANY_DIRECTION) &&
7533           move_pattern != MV_TURNING_LEFT &&
7534           move_pattern != MV_TURNING_RIGHT &&
7535           move_pattern != MV_TURNING_LEFT_RIGHT &&
7536           move_pattern != MV_TURNING_RIGHT_LEFT &&
7537           move_pattern != MV_TURNING_RANDOM)
7538       {
7539         TurnRound(x, y);
7540
7541         if (MovDelay[x][y] && (element == EL_BUG ||
7542                                element == EL_SPACESHIP ||
7543                                element == EL_SP_SNIKSNAK ||
7544                                element == EL_SP_ELECTRON ||
7545                                element == EL_MOLE))
7546           TEST_DrawLevelField(x, y);
7547       }
7548     }
7549
7550     if (MovDelay[x][y])         /* wait some time before next movement */
7551     {
7552       MovDelay[x][y]--;
7553
7554       if (element == EL_ROBOT ||
7555           element == EL_YAMYAM ||
7556           element == EL_DARK_YAMYAM)
7557       {
7558         DrawLevelElementAnimationIfNeeded(x, y, element);
7559         PlayLevelSoundAction(x, y, ACTION_WAITING);
7560       }
7561       else if (element == EL_SP_ELECTRON)
7562         DrawLevelElementAnimationIfNeeded(x, y, element);
7563       else if (element == EL_DRAGON)
7564       {
7565         int i;
7566         int dir = MovDir[x][y];
7567         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7568         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7569         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7570                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7571                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7572                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7573         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7574
7575         GfxAction[x][y] = ACTION_ATTACKING;
7576
7577         if (IS_PLAYER(x, y))
7578           DrawPlayerField(x, y);
7579         else
7580           TEST_DrawLevelField(x, y);
7581
7582         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7583
7584         for (i = 1; i <= 3; i++)
7585         {
7586           int xx = x + i * dx;
7587           int yy = y + i * dy;
7588           int sx = SCREENX(xx);
7589           int sy = SCREENY(yy);
7590           int flame_graphic = graphic + (i - 1);
7591
7592           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7593             break;
7594
7595           if (MovDelay[x][y])
7596           {
7597             int flamed = MovingOrBlocked2Element(xx, yy);
7598
7599             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7600               Bang(xx, yy);
7601             else
7602               RemoveMovingField(xx, yy);
7603
7604             ChangeDelay[xx][yy] = 0;
7605
7606             Feld[xx][yy] = EL_FLAMES;
7607
7608             if (IN_SCR_FIELD(sx, sy))
7609             {
7610               TEST_DrawLevelFieldCrumbled(xx, yy);
7611               DrawGraphic(sx, sy, flame_graphic, frame);
7612             }
7613           }
7614           else
7615           {
7616             if (Feld[xx][yy] == EL_FLAMES)
7617               Feld[xx][yy] = EL_EMPTY;
7618             TEST_DrawLevelField(xx, yy);
7619           }
7620         }
7621       }
7622
7623       if (MovDelay[x][y])       /* element still has to wait some time */
7624       {
7625         PlayLevelSoundAction(x, y, ACTION_WAITING);
7626
7627         return;
7628       }
7629     }
7630
7631     /* now make next step */
7632
7633     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7634
7635     if (DONT_COLLIDE_WITH(element) &&
7636         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7637         !PLAYER_ENEMY_PROTECTED(newx, newy))
7638     {
7639       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7640
7641       return;
7642     }
7643
7644     else if (CAN_MOVE_INTO_ACID(element) &&
7645              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7646              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7647              (MovDir[x][y] == MV_DOWN ||
7648               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7649     {
7650       SplashAcid(newx, newy);
7651       Store[x][y] = EL_ACID;
7652     }
7653     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7654     {
7655       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7656           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7657           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7658           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7659       {
7660         RemoveField(x, y);
7661         TEST_DrawLevelField(x, y);
7662
7663         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7664         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7665           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7666
7667         local_player->friends_still_needed--;
7668         if (!local_player->friends_still_needed &&
7669             !local_player->GameOver && AllPlayersGone)
7670           PlayerWins(local_player);
7671
7672         return;
7673       }
7674       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7675       {
7676         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7677           TEST_DrawLevelField(newx, newy);
7678         else
7679           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7680       }
7681       else if (!IS_FREE(newx, newy))
7682       {
7683         GfxAction[x][y] = ACTION_WAITING;
7684
7685         if (IS_PLAYER(x, y))
7686           DrawPlayerField(x, y);
7687         else
7688           TEST_DrawLevelField(x, y);
7689
7690         return;
7691       }
7692     }
7693     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7694     {
7695       if (IS_FOOD_PIG(Feld[newx][newy]))
7696       {
7697         if (IS_MOVING(newx, newy))
7698           RemoveMovingField(newx, newy);
7699         else
7700         {
7701           Feld[newx][newy] = EL_EMPTY;
7702           TEST_DrawLevelField(newx, newy);
7703         }
7704
7705         PlayLevelSound(x, y, SND_PIG_DIGGING);
7706       }
7707       else if (!IS_FREE(newx, newy))
7708       {
7709         if (IS_PLAYER(x, y))
7710           DrawPlayerField(x, y);
7711         else
7712           TEST_DrawLevelField(x, y);
7713
7714         return;
7715       }
7716     }
7717     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7718     {
7719       if (Store[x][y] != EL_EMPTY)
7720       {
7721         boolean can_clone = FALSE;
7722         int xx, yy;
7723
7724         /* check if element to clone is still there */
7725         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7726         {
7727           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7728           {
7729             can_clone = TRUE;
7730
7731             break;
7732           }
7733         }
7734
7735         /* cannot clone or target field not free anymore -- do not clone */
7736         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7737           Store[x][y] = EL_EMPTY;
7738       }
7739
7740       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7741       {
7742         if (IS_MV_DIAGONAL(MovDir[x][y]))
7743         {
7744           int diagonal_move_dir = MovDir[x][y];
7745           int stored = Store[x][y];
7746           int change_delay = 8;
7747           int graphic;
7748
7749           /* android is moving diagonally */
7750
7751           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7752
7753           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7754           GfxElement[x][y] = EL_EMC_ANDROID;
7755           GfxAction[x][y] = ACTION_SHRINKING;
7756           GfxDir[x][y] = diagonal_move_dir;
7757           ChangeDelay[x][y] = change_delay;
7758
7759           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7760                                    GfxDir[x][y]);
7761
7762           DrawLevelGraphicAnimation(x, y, graphic);
7763           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7764
7765           if (Feld[newx][newy] == EL_ACID)
7766           {
7767             SplashAcid(newx, newy);
7768
7769             return;
7770           }
7771
7772           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7773
7774           Store[newx][newy] = EL_EMC_ANDROID;
7775           GfxElement[newx][newy] = EL_EMC_ANDROID;
7776           GfxAction[newx][newy] = ACTION_GROWING;
7777           GfxDir[newx][newy] = diagonal_move_dir;
7778           ChangeDelay[newx][newy] = change_delay;
7779
7780           graphic = el_act_dir2img(GfxElement[newx][newy],
7781                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7782
7783           DrawLevelGraphicAnimation(newx, newy, graphic);
7784           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7785
7786           return;
7787         }
7788         else
7789         {
7790           Feld[newx][newy] = EL_EMPTY;
7791           TEST_DrawLevelField(newx, newy);
7792
7793           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7794         }
7795       }
7796       else if (!IS_FREE(newx, newy))
7797       {
7798         return;
7799       }
7800     }
7801     else if (IS_CUSTOM_ELEMENT(element) &&
7802              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7803     {
7804       if (!DigFieldByCE(newx, newy, element))
7805         return;
7806
7807       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7808       {
7809         RunnerVisit[x][y] = FrameCounter;
7810         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7811       }
7812     }
7813     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7814     {
7815       if (!IS_FREE(newx, newy))
7816       {
7817         if (IS_PLAYER(x, y))
7818           DrawPlayerField(x, y);
7819         else
7820           TEST_DrawLevelField(x, y);
7821
7822         return;
7823       }
7824       else
7825       {
7826         boolean wanna_flame = !RND(10);
7827         int dx = newx - x, dy = newy - y;
7828         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7829         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7830         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7831                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7832         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7833                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7834
7835         if ((wanna_flame ||
7836              IS_CLASSIC_ENEMY(element1) ||
7837              IS_CLASSIC_ENEMY(element2)) &&
7838             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7839             element1 != EL_FLAMES && element2 != EL_FLAMES)
7840         {
7841           ResetGfxAnimation(x, y);
7842           GfxAction[x][y] = ACTION_ATTACKING;
7843
7844           if (IS_PLAYER(x, y))
7845             DrawPlayerField(x, y);
7846           else
7847             TEST_DrawLevelField(x, y);
7848
7849           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7850
7851           MovDelay[x][y] = 50;
7852
7853           Feld[newx][newy] = EL_FLAMES;
7854           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7855             Feld[newx1][newy1] = EL_FLAMES;
7856           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7857             Feld[newx2][newy2] = EL_FLAMES;
7858
7859           return;
7860         }
7861       }
7862     }
7863     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7864              Feld[newx][newy] == EL_DIAMOND)
7865     {
7866       if (IS_MOVING(newx, newy))
7867         RemoveMovingField(newx, newy);
7868       else
7869       {
7870         Feld[newx][newy] = EL_EMPTY;
7871         TEST_DrawLevelField(newx, newy);
7872       }
7873
7874       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7875     }
7876     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7877              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7878     {
7879       if (AmoebaNr[newx][newy])
7880       {
7881         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7882         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7883             Feld[newx][newy] == EL_BD_AMOEBA)
7884           AmoebaCnt[AmoebaNr[newx][newy]]--;
7885       }
7886
7887       if (IS_MOVING(newx, newy))
7888       {
7889         RemoveMovingField(newx, newy);
7890       }
7891       else
7892       {
7893         Feld[newx][newy] = EL_EMPTY;
7894         TEST_DrawLevelField(newx, newy);
7895       }
7896
7897       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7898     }
7899     else if ((element == EL_PACMAN || element == EL_MOLE)
7900              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7901     {
7902       if (AmoebaNr[newx][newy])
7903       {
7904         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7905         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7906             Feld[newx][newy] == EL_BD_AMOEBA)
7907           AmoebaCnt[AmoebaNr[newx][newy]]--;
7908       }
7909
7910       if (element == EL_MOLE)
7911       {
7912         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7913         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7914
7915         ResetGfxAnimation(x, y);
7916         GfxAction[x][y] = ACTION_DIGGING;
7917         TEST_DrawLevelField(x, y);
7918
7919         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7920
7921         return;                         /* wait for shrinking amoeba */
7922       }
7923       else      /* element == EL_PACMAN */
7924       {
7925         Feld[newx][newy] = EL_EMPTY;
7926         TEST_DrawLevelField(newx, newy);
7927         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7928       }
7929     }
7930     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7931              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7932               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7933     {
7934       /* wait for shrinking amoeba to completely disappear */
7935       return;
7936     }
7937     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7938     {
7939       /* object was running against a wall */
7940
7941       TurnRound(x, y);
7942
7943       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7944         DrawLevelElementAnimation(x, y, element);
7945
7946       if (DONT_TOUCH(element))
7947         TestIfBadThingTouchesPlayer(x, y);
7948
7949       return;
7950     }
7951
7952     InitMovingField(x, y, MovDir[x][y]);
7953
7954     PlayLevelSoundAction(x, y, ACTION_MOVING);
7955   }
7956
7957   if (MovDir[x][y])
7958     ContinueMoving(x, y);
7959 }
7960
7961 void ContinueMoving(int x, int y)
7962 {
7963   int element = Feld[x][y];
7964   struct ElementInfo *ei = &element_info[element];
7965   int direction = MovDir[x][y];
7966   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7967   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7968   int newx = x + dx, newy = y + dy;
7969   int stored = Store[x][y];
7970   int stored_new = Store[newx][newy];
7971   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7972   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7973   boolean last_line = (newy == lev_fieldy - 1);
7974
7975   MovPos[x][y] += getElementMoveStepsize(x, y);
7976
7977   if (pushed_by_player) /* special case: moving object pushed by player */
7978     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7979
7980   if (ABS(MovPos[x][y]) < TILEX)
7981   {
7982     TEST_DrawLevelField(x, y);
7983
7984     return;     /* element is still moving */
7985   }
7986
7987   /* element reached destination field */
7988
7989   Feld[x][y] = EL_EMPTY;
7990   Feld[newx][newy] = element;
7991   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7992
7993   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7994   {
7995     element = Feld[newx][newy] = EL_ACID;
7996   }
7997   else if (element == EL_MOLE)
7998   {
7999     Feld[x][y] = EL_SAND;
8000
8001     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8002   }
8003   else if (element == EL_QUICKSAND_FILLING)
8004   {
8005     element = Feld[newx][newy] = get_next_element(element);
8006     Store[newx][newy] = Store[x][y];
8007   }
8008   else if (element == EL_QUICKSAND_EMPTYING)
8009   {
8010     Feld[x][y] = get_next_element(element);
8011     element = Feld[newx][newy] = Store[x][y];
8012   }
8013   else if (element == EL_QUICKSAND_FAST_FILLING)
8014   {
8015     element = Feld[newx][newy] = get_next_element(element);
8016     Store[newx][newy] = Store[x][y];
8017   }
8018   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8019   {
8020     Feld[x][y] = get_next_element(element);
8021     element = Feld[newx][newy] = Store[x][y];
8022   }
8023   else if (element == EL_MAGIC_WALL_FILLING)
8024   {
8025     element = Feld[newx][newy] = get_next_element(element);
8026     if (!game.magic_wall_active)
8027       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8028     Store[newx][newy] = Store[x][y];
8029   }
8030   else if (element == EL_MAGIC_WALL_EMPTYING)
8031   {
8032     Feld[x][y] = get_next_element(element);
8033     if (!game.magic_wall_active)
8034       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8035     element = Feld[newx][newy] = Store[x][y];
8036
8037     InitField(newx, newy, FALSE);
8038   }
8039   else if (element == EL_BD_MAGIC_WALL_FILLING)
8040   {
8041     element = Feld[newx][newy] = get_next_element(element);
8042     if (!game.magic_wall_active)
8043       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8044     Store[newx][newy] = Store[x][y];
8045   }
8046   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8047   {
8048     Feld[x][y] = get_next_element(element);
8049     if (!game.magic_wall_active)
8050       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8051     element = Feld[newx][newy] = Store[x][y];
8052
8053     InitField(newx, newy, FALSE);
8054   }
8055   else if (element == EL_DC_MAGIC_WALL_FILLING)
8056   {
8057     element = Feld[newx][newy] = get_next_element(element);
8058     if (!game.magic_wall_active)
8059       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8060     Store[newx][newy] = Store[x][y];
8061   }
8062   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8063   {
8064     Feld[x][y] = get_next_element(element);
8065     if (!game.magic_wall_active)
8066       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8067     element = Feld[newx][newy] = Store[x][y];
8068
8069     InitField(newx, newy, FALSE);
8070   }
8071   else if (element == EL_AMOEBA_DROPPING)
8072   {
8073     Feld[x][y] = get_next_element(element);
8074     element = Feld[newx][newy] = Store[x][y];
8075   }
8076   else if (element == EL_SOKOBAN_OBJECT)
8077   {
8078     if (Back[x][y])
8079       Feld[x][y] = Back[x][y];
8080
8081     if (Back[newx][newy])
8082       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8083
8084     Back[x][y] = Back[newx][newy] = 0;
8085   }
8086
8087   Store[x][y] = EL_EMPTY;
8088   MovPos[x][y] = 0;
8089   MovDir[x][y] = 0;
8090   MovDelay[x][y] = 0;
8091
8092   MovDelay[newx][newy] = 0;
8093
8094   if (CAN_CHANGE_OR_HAS_ACTION(element))
8095   {
8096     /* copy element change control values to new field */
8097     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8098     ChangePage[newx][newy]  = ChangePage[x][y];
8099     ChangeCount[newx][newy] = ChangeCount[x][y];
8100     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8101   }
8102
8103   CustomValue[newx][newy] = CustomValue[x][y];
8104
8105   ChangeDelay[x][y] = 0;
8106   ChangePage[x][y] = -1;
8107   ChangeCount[x][y] = 0;
8108   ChangeEvent[x][y] = -1;
8109
8110   CustomValue[x][y] = 0;
8111
8112   /* copy animation control values to new field */
8113   GfxFrame[newx][newy]  = GfxFrame[x][y];
8114   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8115   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8116   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8117
8118   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8119
8120   /* some elements can leave other elements behind after moving */
8121   if (ei->move_leave_element != EL_EMPTY &&
8122       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8123       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8124   {
8125     int move_leave_element = ei->move_leave_element;
8126
8127     /* this makes it possible to leave the removed element again */
8128     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8129       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8130
8131     Feld[x][y] = move_leave_element;
8132
8133     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8134       MovDir[x][y] = direction;
8135
8136     InitField(x, y, FALSE);
8137
8138     if (GFX_CRUMBLED(Feld[x][y]))
8139       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8140
8141     if (ELEM_IS_PLAYER(move_leave_element))
8142       RelocatePlayer(x, y, move_leave_element);
8143   }
8144
8145   /* do this after checking for left-behind element */
8146   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8147
8148   if (!CAN_MOVE(element) ||
8149       (CAN_FALL(element) && direction == MV_DOWN &&
8150        (element == EL_SPRING ||
8151         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8152         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8153     GfxDir[x][y] = MovDir[newx][newy] = 0;
8154
8155   TEST_DrawLevelField(x, y);
8156   TEST_DrawLevelField(newx, newy);
8157
8158   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8159
8160   /* prevent pushed element from moving on in pushed direction */
8161   if (pushed_by_player && CAN_MOVE(element) &&
8162       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8163       !(element_info[element].move_pattern & direction))
8164     TurnRound(newx, newy);
8165
8166   /* prevent elements on conveyor belt from moving on in last direction */
8167   if (pushed_by_conveyor && CAN_FALL(element) &&
8168       direction & MV_HORIZONTAL)
8169     MovDir[newx][newy] = 0;
8170
8171   if (!pushed_by_player)
8172   {
8173     int nextx = newx + dx, nexty = newy + dy;
8174     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8175
8176     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8177
8178     if (CAN_FALL(element) && direction == MV_DOWN)
8179       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8180
8181     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8182       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8183
8184     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8185       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8186   }
8187
8188   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8189   {
8190     TestIfBadThingTouchesPlayer(newx, newy);
8191     TestIfBadThingTouchesFriend(newx, newy);
8192
8193     if (!IS_CUSTOM_ELEMENT(element))
8194       TestIfBadThingTouchesOtherBadThing(newx, newy);
8195   }
8196   else if (element == EL_PENGUIN)
8197     TestIfFriendTouchesBadThing(newx, newy);
8198
8199   if (DONT_GET_HIT_BY(element))
8200   {
8201     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8202   }
8203
8204   /* give the player one last chance (one more frame) to move away */
8205   if (CAN_FALL(element) && direction == MV_DOWN &&
8206       (last_line || (!IS_FREE(x, newy + 1) &&
8207                      (!IS_PLAYER(x, newy + 1) ||
8208                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8209     Impact(x, newy);
8210
8211   if (pushed_by_player && !game.use_change_when_pushing_bug)
8212   {
8213     int push_side = MV_DIR_OPPOSITE(direction);
8214     struct PlayerInfo *player = PLAYERINFO(x, y);
8215
8216     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8217                                player->index_bit, push_side);
8218     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8219                                         player->index_bit, push_side);
8220   }
8221
8222   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8223     MovDelay[newx][newy] = 1;
8224
8225   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8226
8227   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8228   TestIfElementHitsCustomElement(newx, newy, direction);
8229   TestIfPlayerTouchesCustomElement(newx, newy);
8230   TestIfElementTouchesCustomElement(newx, newy);
8231
8232   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8233       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8234     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8235                              MV_DIR_OPPOSITE(direction));
8236 }
8237
8238 int AmoebeNachbarNr(int ax, int ay)
8239 {
8240   int i;
8241   int element = Feld[ax][ay];
8242   int group_nr = 0;
8243   static int xy[4][2] =
8244   {
8245     { 0, -1 },
8246     { -1, 0 },
8247     { +1, 0 },
8248     { 0, +1 }
8249   };
8250
8251   for (i = 0; i < NUM_DIRECTIONS; i++)
8252   {
8253     int x = ax + xy[i][0];
8254     int y = ay + xy[i][1];
8255
8256     if (!IN_LEV_FIELD(x, y))
8257       continue;
8258
8259     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8260       group_nr = AmoebaNr[x][y];
8261   }
8262
8263   return group_nr;
8264 }
8265
8266 void AmoebenVereinigen(int ax, int ay)
8267 {
8268   int i, x, y, xx, yy;
8269   int new_group_nr = AmoebaNr[ax][ay];
8270   static int xy[4][2] =
8271   {
8272     { 0, -1 },
8273     { -1, 0 },
8274     { +1, 0 },
8275     { 0, +1 }
8276   };
8277
8278   if (new_group_nr == 0)
8279     return;
8280
8281   for (i = 0; i < NUM_DIRECTIONS; i++)
8282   {
8283     x = ax + xy[i][0];
8284     y = ay + xy[i][1];
8285
8286     if (!IN_LEV_FIELD(x, y))
8287       continue;
8288
8289     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8290          Feld[x][y] == EL_BD_AMOEBA ||
8291          Feld[x][y] == EL_AMOEBA_DEAD) &&
8292         AmoebaNr[x][y] != new_group_nr)
8293     {
8294       int old_group_nr = AmoebaNr[x][y];
8295
8296       if (old_group_nr == 0)
8297         return;
8298
8299       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8300       AmoebaCnt[old_group_nr] = 0;
8301       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8302       AmoebaCnt2[old_group_nr] = 0;
8303
8304       SCAN_PLAYFIELD(xx, yy)
8305       {
8306         if (AmoebaNr[xx][yy] == old_group_nr)
8307           AmoebaNr[xx][yy] = new_group_nr;
8308       }
8309     }
8310   }
8311 }
8312
8313 void AmoebeUmwandeln(int ax, int ay)
8314 {
8315   int i, x, y;
8316
8317   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8318   {
8319     int group_nr = AmoebaNr[ax][ay];
8320
8321 #ifdef DEBUG
8322     if (group_nr == 0)
8323     {
8324       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8325       printf("AmoebeUmwandeln(): This should never happen!\n");
8326       return;
8327     }
8328 #endif
8329
8330     SCAN_PLAYFIELD(x, y)
8331     {
8332       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8333       {
8334         AmoebaNr[x][y] = 0;
8335         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8336       }
8337     }
8338
8339     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8340                             SND_AMOEBA_TURNING_TO_GEM :
8341                             SND_AMOEBA_TURNING_TO_ROCK));
8342     Bang(ax, ay);
8343   }
8344   else
8345   {
8346     static int xy[4][2] =
8347     {
8348       { 0, -1 },
8349       { -1, 0 },
8350       { +1, 0 },
8351       { 0, +1 }
8352     };
8353
8354     for (i = 0; i < NUM_DIRECTIONS; i++)
8355     {
8356       x = ax + xy[i][0];
8357       y = ay + xy[i][1];
8358
8359       if (!IN_LEV_FIELD(x, y))
8360         continue;
8361
8362       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8363       {
8364         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8365                               SND_AMOEBA_TURNING_TO_GEM :
8366                               SND_AMOEBA_TURNING_TO_ROCK));
8367         Bang(x, y);
8368       }
8369     }
8370   }
8371 }
8372
8373 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8374 {
8375   int x, y;
8376   int group_nr = AmoebaNr[ax][ay];
8377   boolean done = FALSE;
8378
8379 #ifdef DEBUG
8380   if (group_nr == 0)
8381   {
8382     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8383     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8384     return;
8385   }
8386 #endif
8387
8388   SCAN_PLAYFIELD(x, y)
8389   {
8390     if (AmoebaNr[x][y] == group_nr &&
8391         (Feld[x][y] == EL_AMOEBA_DEAD ||
8392          Feld[x][y] == EL_BD_AMOEBA ||
8393          Feld[x][y] == EL_AMOEBA_GROWING))
8394     {
8395       AmoebaNr[x][y] = 0;
8396       Feld[x][y] = new_element;
8397       InitField(x, y, FALSE);
8398       TEST_DrawLevelField(x, y);
8399       done = TRUE;
8400     }
8401   }
8402
8403   if (done)
8404     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8405                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8406                             SND_BD_AMOEBA_TURNING_TO_GEM));
8407 }
8408
8409 void AmoebeWaechst(int x, int y)
8410 {
8411   static unsigned int sound_delay = 0;
8412   static unsigned int sound_delay_value = 0;
8413
8414   if (!MovDelay[x][y])          /* start new growing cycle */
8415   {
8416     MovDelay[x][y] = 7;
8417
8418     if (DelayReached(&sound_delay, sound_delay_value))
8419     {
8420       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8421       sound_delay_value = 30;
8422     }
8423   }
8424
8425   if (MovDelay[x][y])           /* wait some time before growing bigger */
8426   {
8427     MovDelay[x][y]--;
8428     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8429     {
8430       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8431                                            6 - MovDelay[x][y]);
8432
8433       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8434     }
8435
8436     if (!MovDelay[x][y])
8437     {
8438       Feld[x][y] = Store[x][y];
8439       Store[x][y] = 0;
8440       TEST_DrawLevelField(x, y);
8441     }
8442   }
8443 }
8444
8445 void AmoebaDisappearing(int x, int y)
8446 {
8447   static unsigned int sound_delay = 0;
8448   static unsigned int sound_delay_value = 0;
8449
8450   if (!MovDelay[x][y])          /* start new shrinking cycle */
8451   {
8452     MovDelay[x][y] = 7;
8453
8454     if (DelayReached(&sound_delay, sound_delay_value))
8455       sound_delay_value = 30;
8456   }
8457
8458   if (MovDelay[x][y])           /* wait some time before shrinking */
8459   {
8460     MovDelay[x][y]--;
8461     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8462     {
8463       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8464                                            6 - MovDelay[x][y]);
8465
8466       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8467     }
8468
8469     if (!MovDelay[x][y])
8470     {
8471       Feld[x][y] = EL_EMPTY;
8472       TEST_DrawLevelField(x, y);
8473
8474       /* don't let mole enter this field in this cycle;
8475          (give priority to objects falling to this field from above) */
8476       Stop[x][y] = TRUE;
8477     }
8478   }
8479 }
8480
8481 void AmoebeAbleger(int ax, int ay)
8482 {
8483   int i;
8484   int element = Feld[ax][ay];
8485   int graphic = el2img(element);
8486   int newax = ax, neway = ay;
8487   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8488   static int xy[4][2] =
8489   {
8490     { 0, -1 },
8491     { -1, 0 },
8492     { +1, 0 },
8493     { 0, +1 }
8494   };
8495
8496   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8497   {
8498     Feld[ax][ay] = EL_AMOEBA_DEAD;
8499     TEST_DrawLevelField(ax, ay);
8500     return;
8501   }
8502
8503   if (IS_ANIMATED(graphic))
8504     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8505
8506   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8507     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8508
8509   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8510   {
8511     MovDelay[ax][ay]--;
8512     if (MovDelay[ax][ay])
8513       return;
8514   }
8515
8516   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8517   {
8518     int start = RND(4);
8519     int x = ax + xy[start][0];
8520     int y = ay + xy[start][1];
8521
8522     if (!IN_LEV_FIELD(x, y))
8523       return;
8524
8525     if (IS_FREE(x, y) ||
8526         CAN_GROW_INTO(Feld[x][y]) ||
8527         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8528         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8529     {
8530       newax = x;
8531       neway = y;
8532     }
8533
8534     if (newax == ax && neway == ay)
8535       return;
8536   }
8537   else                          /* normal or "filled" (BD style) amoeba */
8538   {
8539     int start = RND(4);
8540     boolean waiting_for_player = FALSE;
8541
8542     for (i = 0; i < NUM_DIRECTIONS; i++)
8543     {
8544       int j = (start + i) % 4;
8545       int x = ax + xy[j][0];
8546       int y = ay + xy[j][1];
8547
8548       if (!IN_LEV_FIELD(x, y))
8549         continue;
8550
8551       if (IS_FREE(x, y) ||
8552           CAN_GROW_INTO(Feld[x][y]) ||
8553           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8554           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8555       {
8556         newax = x;
8557         neway = y;
8558         break;
8559       }
8560       else if (IS_PLAYER(x, y))
8561         waiting_for_player = TRUE;
8562     }
8563
8564     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8565     {
8566       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8567       {
8568         Feld[ax][ay] = EL_AMOEBA_DEAD;
8569         TEST_DrawLevelField(ax, ay);
8570         AmoebaCnt[AmoebaNr[ax][ay]]--;
8571
8572         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8573         {
8574           if (element == EL_AMOEBA_FULL)
8575             AmoebeUmwandeln(ax, ay);
8576           else if (element == EL_BD_AMOEBA)
8577             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8578         }
8579       }
8580       return;
8581     }
8582     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8583     {
8584       /* amoeba gets larger by growing in some direction */
8585
8586       int new_group_nr = AmoebaNr[ax][ay];
8587
8588 #ifdef DEBUG
8589   if (new_group_nr == 0)
8590   {
8591     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8592     printf("AmoebeAbleger(): This should never happen!\n");
8593     return;
8594   }
8595 #endif
8596
8597       AmoebaNr[newax][neway] = new_group_nr;
8598       AmoebaCnt[new_group_nr]++;
8599       AmoebaCnt2[new_group_nr]++;
8600
8601       /* if amoeba touches other amoeba(s) after growing, unify them */
8602       AmoebenVereinigen(newax, neway);
8603
8604       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8605       {
8606         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8607         return;
8608       }
8609     }
8610   }
8611
8612   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8613       (neway == lev_fieldy - 1 && newax != ax))
8614   {
8615     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8616     Store[newax][neway] = element;
8617   }
8618   else if (neway == ay || element == EL_EMC_DRIPPER)
8619   {
8620     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8621
8622     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8623   }
8624   else
8625   {
8626     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8627     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8628     Store[ax][ay] = EL_AMOEBA_DROP;
8629     ContinueMoving(ax, ay);
8630     return;
8631   }
8632
8633   TEST_DrawLevelField(newax, neway);
8634 }
8635
8636 void Life(int ax, int ay)
8637 {
8638   int x1, y1, x2, y2;
8639   int life_time = 40;
8640   int element = Feld[ax][ay];
8641   int graphic = el2img(element);
8642   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8643                          level.biomaze);
8644   boolean changed = FALSE;
8645
8646   if (IS_ANIMATED(graphic))
8647     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8648
8649   if (Stop[ax][ay])
8650     return;
8651
8652   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8653     MovDelay[ax][ay] = life_time;
8654
8655   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8656   {
8657     MovDelay[ax][ay]--;
8658     if (MovDelay[ax][ay])
8659       return;
8660   }
8661
8662   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8663   {
8664     int xx = ax+x1, yy = ay+y1;
8665     int nachbarn = 0;
8666
8667     if (!IN_LEV_FIELD(xx, yy))
8668       continue;
8669
8670     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8671     {
8672       int x = xx+x2, y = yy+y2;
8673
8674       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8675         continue;
8676
8677       if (((Feld[x][y] == element ||
8678             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8679            !Stop[x][y]) ||
8680           (IS_FREE(x, y) && Stop[x][y]))
8681         nachbarn++;
8682     }
8683
8684     if (xx == ax && yy == ay)           /* field in the middle */
8685     {
8686       if (nachbarn < life_parameter[0] ||
8687           nachbarn > life_parameter[1])
8688       {
8689         Feld[xx][yy] = EL_EMPTY;
8690         if (!Stop[xx][yy])
8691           TEST_DrawLevelField(xx, yy);
8692         Stop[xx][yy] = TRUE;
8693         changed = TRUE;
8694       }
8695     }
8696     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8697     {                                   /* free border field */
8698       if (nachbarn >= life_parameter[2] &&
8699           nachbarn <= life_parameter[3])
8700       {
8701         Feld[xx][yy] = element;
8702         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8703         if (!Stop[xx][yy])
8704           TEST_DrawLevelField(xx, yy);
8705         Stop[xx][yy] = TRUE;
8706         changed = TRUE;
8707       }
8708     }
8709   }
8710
8711   if (changed)
8712     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8713                    SND_GAME_OF_LIFE_GROWING);
8714 }
8715
8716 static void InitRobotWheel(int x, int y)
8717 {
8718   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8719 }
8720
8721 static void RunRobotWheel(int x, int y)
8722 {
8723   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8724 }
8725
8726 static void StopRobotWheel(int x, int y)
8727 {
8728   if (ZX == x && ZY == y)
8729   {
8730     ZX = ZY = -1;
8731
8732     game.robot_wheel_active = FALSE;
8733   }
8734 }
8735
8736 static void InitTimegateWheel(int x, int y)
8737 {
8738   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8739 }
8740
8741 static void RunTimegateWheel(int x, int y)
8742 {
8743   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8744 }
8745
8746 static void InitMagicBallDelay(int x, int y)
8747 {
8748   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8749 }
8750
8751 static void ActivateMagicBall(int bx, int by)
8752 {
8753   int x, y;
8754
8755   if (level.ball_random)
8756   {
8757     int pos_border = RND(8);    /* select one of the eight border elements */
8758     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8759     int xx = pos_content % 3;
8760     int yy = pos_content / 3;
8761
8762     x = bx - 1 + xx;
8763     y = by - 1 + yy;
8764
8765     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8766       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8767   }
8768   else
8769   {
8770     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8771     {
8772       int xx = x - bx + 1;
8773       int yy = y - by + 1;
8774
8775       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8776         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8777     }
8778   }
8779
8780   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8781 }
8782
8783 void CheckExit(int x, int y)
8784 {
8785   if (local_player->gems_still_needed > 0 ||
8786       local_player->sokobanfields_still_needed > 0 ||
8787       local_player->lights_still_needed > 0)
8788   {
8789     int element = Feld[x][y];
8790     int graphic = el2img(element);
8791
8792     if (IS_ANIMATED(graphic))
8793       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8794
8795     return;
8796   }
8797
8798   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8799     return;
8800
8801   Feld[x][y] = EL_EXIT_OPENING;
8802
8803   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8804 }
8805
8806 void CheckExitEM(int x, int y)
8807 {
8808   if (local_player->gems_still_needed > 0 ||
8809       local_player->sokobanfields_still_needed > 0 ||
8810       local_player->lights_still_needed > 0)
8811   {
8812     int element = Feld[x][y];
8813     int graphic = el2img(element);
8814
8815     if (IS_ANIMATED(graphic))
8816       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8817
8818     return;
8819   }
8820
8821   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8822     return;
8823
8824   Feld[x][y] = EL_EM_EXIT_OPENING;
8825
8826   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8827 }
8828
8829 void CheckExitSteel(int x, int y)
8830 {
8831   if (local_player->gems_still_needed > 0 ||
8832       local_player->sokobanfields_still_needed > 0 ||
8833       local_player->lights_still_needed > 0)
8834   {
8835     int element = Feld[x][y];
8836     int graphic = el2img(element);
8837
8838     if (IS_ANIMATED(graphic))
8839       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8840
8841     return;
8842   }
8843
8844   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8845     return;
8846
8847   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8848
8849   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8850 }
8851
8852 void CheckExitSteelEM(int x, int y)
8853 {
8854   if (local_player->gems_still_needed > 0 ||
8855       local_player->sokobanfields_still_needed > 0 ||
8856       local_player->lights_still_needed > 0)
8857   {
8858     int element = Feld[x][y];
8859     int graphic = el2img(element);
8860
8861     if (IS_ANIMATED(graphic))
8862       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8863
8864     return;
8865   }
8866
8867   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8868     return;
8869
8870   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8871
8872   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8873 }
8874
8875 void CheckExitSP(int x, int y)
8876 {
8877   if (local_player->gems_still_needed > 0)
8878   {
8879     int element = Feld[x][y];
8880     int graphic = el2img(element);
8881
8882     if (IS_ANIMATED(graphic))
8883       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8884
8885     return;
8886   }
8887
8888   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8889     return;
8890
8891   Feld[x][y] = EL_SP_EXIT_OPENING;
8892
8893   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8894 }
8895
8896 static void CloseAllOpenTimegates()
8897 {
8898   int x, y;
8899
8900   SCAN_PLAYFIELD(x, y)
8901   {
8902     int element = Feld[x][y];
8903
8904     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8905     {
8906       Feld[x][y] = EL_TIMEGATE_CLOSING;
8907
8908       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8909     }
8910   }
8911 }
8912
8913 void DrawTwinkleOnField(int x, int y)
8914 {
8915   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8916     return;
8917
8918   if (Feld[x][y] == EL_BD_DIAMOND)
8919     return;
8920
8921   if (MovDelay[x][y] == 0)      /* next animation frame */
8922     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8923
8924   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8925   {
8926     MovDelay[x][y]--;
8927
8928     DrawLevelElementAnimation(x, y, Feld[x][y]);
8929
8930     if (MovDelay[x][y] != 0)
8931     {
8932       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8933                                            10 - MovDelay[x][y]);
8934
8935       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8936     }
8937   }
8938 }
8939
8940 void MauerWaechst(int x, int y)
8941 {
8942   int delay = 6;
8943
8944   if (!MovDelay[x][y])          /* next animation frame */
8945     MovDelay[x][y] = 3 * delay;
8946
8947   if (MovDelay[x][y])           /* wait some time before next frame */
8948   {
8949     MovDelay[x][y]--;
8950
8951     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8952     {
8953       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8954       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8955
8956       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8957     }
8958
8959     if (!MovDelay[x][y])
8960     {
8961       if (MovDir[x][y] == MV_LEFT)
8962       {
8963         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8964           TEST_DrawLevelField(x - 1, y);
8965       }
8966       else if (MovDir[x][y] == MV_RIGHT)
8967       {
8968         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8969           TEST_DrawLevelField(x + 1, y);
8970       }
8971       else if (MovDir[x][y] == MV_UP)
8972       {
8973         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8974           TEST_DrawLevelField(x, y - 1);
8975       }
8976       else
8977       {
8978         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8979           TEST_DrawLevelField(x, y + 1);
8980       }
8981
8982       Feld[x][y] = Store[x][y];
8983       Store[x][y] = 0;
8984       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8985       TEST_DrawLevelField(x, y);
8986     }
8987   }
8988 }
8989
8990 void MauerAbleger(int ax, int ay)
8991 {
8992   int element = Feld[ax][ay];
8993   int graphic = el2img(element);
8994   boolean oben_frei = FALSE, unten_frei = FALSE;
8995   boolean links_frei = FALSE, rechts_frei = FALSE;
8996   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8997   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8998   boolean new_wall = FALSE;
8999
9000   if (IS_ANIMATED(graphic))
9001     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9002
9003   if (!MovDelay[ax][ay])        /* start building new wall */
9004     MovDelay[ax][ay] = 6;
9005
9006   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9007   {
9008     MovDelay[ax][ay]--;
9009     if (MovDelay[ax][ay])
9010       return;
9011   }
9012
9013   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9014     oben_frei = TRUE;
9015   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9016     unten_frei = TRUE;
9017   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9018     links_frei = TRUE;
9019   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9020     rechts_frei = TRUE;
9021
9022   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9023       element == EL_EXPANDABLE_WALL_ANY)
9024   {
9025     if (oben_frei)
9026     {
9027       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9028       Store[ax][ay-1] = element;
9029       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9030       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9031         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9032                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9033       new_wall = TRUE;
9034     }
9035     if (unten_frei)
9036     {
9037       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9038       Store[ax][ay+1] = element;
9039       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9040       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9041         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9042                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9043       new_wall = TRUE;
9044     }
9045   }
9046
9047   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9048       element == EL_EXPANDABLE_WALL_ANY ||
9049       element == EL_EXPANDABLE_WALL ||
9050       element == EL_BD_EXPANDABLE_WALL)
9051   {
9052     if (links_frei)
9053     {
9054       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9055       Store[ax-1][ay] = element;
9056       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9057       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9058         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9059                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9060       new_wall = TRUE;
9061     }
9062
9063     if (rechts_frei)
9064     {
9065       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9066       Store[ax+1][ay] = element;
9067       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9068       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9069         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9070                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9071       new_wall = TRUE;
9072     }
9073   }
9074
9075   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9076     TEST_DrawLevelField(ax, ay);
9077
9078   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9079     oben_massiv = TRUE;
9080   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9081     unten_massiv = TRUE;
9082   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9083     links_massiv = TRUE;
9084   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9085     rechts_massiv = TRUE;
9086
9087   if (((oben_massiv && unten_massiv) ||
9088        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9089        element == EL_EXPANDABLE_WALL) &&
9090       ((links_massiv && rechts_massiv) ||
9091        element == EL_EXPANDABLE_WALL_VERTICAL))
9092     Feld[ax][ay] = EL_WALL;
9093
9094   if (new_wall)
9095     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9096 }
9097
9098 void MauerAblegerStahl(int ax, int ay)
9099 {
9100   int element = Feld[ax][ay];
9101   int graphic = el2img(element);
9102   boolean oben_frei = FALSE, unten_frei = FALSE;
9103   boolean links_frei = FALSE, rechts_frei = FALSE;
9104   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9105   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9106   boolean new_wall = FALSE;
9107
9108   if (IS_ANIMATED(graphic))
9109     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9110
9111   if (!MovDelay[ax][ay])        /* start building new wall */
9112     MovDelay[ax][ay] = 6;
9113
9114   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9115   {
9116     MovDelay[ax][ay]--;
9117     if (MovDelay[ax][ay])
9118       return;
9119   }
9120
9121   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9122     oben_frei = TRUE;
9123   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9124     unten_frei = TRUE;
9125   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9126     links_frei = TRUE;
9127   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9128     rechts_frei = TRUE;
9129
9130   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9131       element == EL_EXPANDABLE_STEELWALL_ANY)
9132   {
9133     if (oben_frei)
9134     {
9135       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9136       Store[ax][ay-1] = element;
9137       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9138       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9139         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9140                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9141       new_wall = TRUE;
9142     }
9143     if (unten_frei)
9144     {
9145       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9146       Store[ax][ay+1] = element;
9147       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9148       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9149         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9150                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9151       new_wall = TRUE;
9152     }
9153   }
9154
9155   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9156       element == EL_EXPANDABLE_STEELWALL_ANY)
9157   {
9158     if (links_frei)
9159     {
9160       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9161       Store[ax-1][ay] = element;
9162       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9163       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9164         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9165                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9166       new_wall = TRUE;
9167     }
9168
9169     if (rechts_frei)
9170     {
9171       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9172       Store[ax+1][ay] = element;
9173       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9174       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9175         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9176                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9177       new_wall = TRUE;
9178     }
9179   }
9180
9181   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9182     oben_massiv = TRUE;
9183   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9184     unten_massiv = TRUE;
9185   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9186     links_massiv = TRUE;
9187   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9188     rechts_massiv = TRUE;
9189
9190   if (((oben_massiv && unten_massiv) ||
9191        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9192       ((links_massiv && rechts_massiv) ||
9193        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9194     Feld[ax][ay] = EL_STEELWALL;
9195
9196   if (new_wall)
9197     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9198 }
9199
9200 void CheckForDragon(int x, int y)
9201 {
9202   int i, j;
9203   boolean dragon_found = FALSE;
9204   static int xy[4][2] =
9205   {
9206     { 0, -1 },
9207     { -1, 0 },
9208     { +1, 0 },
9209     { 0, +1 }
9210   };
9211
9212   for (i = 0; i < NUM_DIRECTIONS; i++)
9213   {
9214     for (j = 0; j < 4; j++)
9215     {
9216       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9217
9218       if (IN_LEV_FIELD(xx, yy) &&
9219           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9220       {
9221         if (Feld[xx][yy] == EL_DRAGON)
9222           dragon_found = TRUE;
9223       }
9224       else
9225         break;
9226     }
9227   }
9228
9229   if (!dragon_found)
9230   {
9231     for (i = 0; i < NUM_DIRECTIONS; i++)
9232     {
9233       for (j = 0; j < 3; j++)
9234       {
9235         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9236   
9237         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9238         {
9239           Feld[xx][yy] = EL_EMPTY;
9240           TEST_DrawLevelField(xx, yy);
9241         }
9242         else
9243           break;
9244       }
9245     }
9246   }
9247 }
9248
9249 static void InitBuggyBase(int x, int y)
9250 {
9251   int element = Feld[x][y];
9252   int activating_delay = FRAMES_PER_SECOND / 4;
9253
9254   ChangeDelay[x][y] =
9255     (element == EL_SP_BUGGY_BASE ?
9256      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9257      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9258      activating_delay :
9259      element == EL_SP_BUGGY_BASE_ACTIVE ?
9260      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9261 }
9262
9263 static void WarnBuggyBase(int x, int y)
9264 {
9265   int i;
9266   static int xy[4][2] =
9267   {
9268     { 0, -1 },
9269     { -1, 0 },
9270     { +1, 0 },
9271     { 0, +1 }
9272   };
9273
9274   for (i = 0; i < NUM_DIRECTIONS; i++)
9275   {
9276     int xx = x + xy[i][0];
9277     int yy = y + xy[i][1];
9278
9279     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9280     {
9281       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9282
9283       break;
9284     }
9285   }
9286 }
9287
9288 static void InitTrap(int x, int y)
9289 {
9290   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9291 }
9292
9293 static void ActivateTrap(int x, int y)
9294 {
9295   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9296 }
9297
9298 static void ChangeActiveTrap(int x, int y)
9299 {
9300   int graphic = IMG_TRAP_ACTIVE;
9301
9302   /* if new animation frame was drawn, correct crumbled sand border */
9303   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9304     TEST_DrawLevelFieldCrumbled(x, y);
9305 }
9306
9307 static int getSpecialActionElement(int element, int number, int base_element)
9308 {
9309   return (element != EL_EMPTY ? element :
9310           number != -1 ? base_element + number - 1 :
9311           EL_EMPTY);
9312 }
9313
9314 static int getModifiedActionNumber(int value_old, int operator, int operand,
9315                                    int value_min, int value_max)
9316 {
9317   int value_new = (operator == CA_MODE_SET      ? operand :
9318                    operator == CA_MODE_ADD      ? value_old + operand :
9319                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9320                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9321                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9322                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9323                    value_old);
9324
9325   return (value_new < value_min ? value_min :
9326           value_new > value_max ? value_max :
9327           value_new);
9328 }
9329
9330 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9331 {
9332   struct ElementInfo *ei = &element_info[element];
9333   struct ElementChangeInfo *change = &ei->change_page[page];
9334   int target_element = change->target_element;
9335   int action_type = change->action_type;
9336   int action_mode = change->action_mode;
9337   int action_arg = change->action_arg;
9338   int action_element = change->action_element;
9339   int i;
9340
9341   if (!change->has_action)
9342     return;
9343
9344   /* ---------- determine action paramater values -------------------------- */
9345
9346   int level_time_value =
9347     (level.time > 0 ? TimeLeft :
9348      TimePlayed);
9349
9350   int action_arg_element_raw =
9351     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9352      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9353      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9354      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9355      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9356      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9357      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9358      EL_EMPTY);
9359   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9360
9361   int action_arg_direction =
9362     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9363      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9364      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9365      change->actual_trigger_side :
9366      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9367      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9368      MV_NONE);
9369
9370   int action_arg_number_min =
9371     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9372      CA_ARG_MIN);
9373
9374   int action_arg_number_max =
9375     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9376      action_type == CA_SET_LEVEL_GEMS ? 999 :
9377      action_type == CA_SET_LEVEL_TIME ? 9999 :
9378      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9379      action_type == CA_SET_CE_VALUE ? 9999 :
9380      action_type == CA_SET_CE_SCORE ? 9999 :
9381      CA_ARG_MAX);
9382
9383   int action_arg_number_reset =
9384     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9385      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9386      action_type == CA_SET_LEVEL_TIME ? level.time :
9387      action_type == CA_SET_LEVEL_SCORE ? 0 :
9388      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9389      action_type == CA_SET_CE_SCORE ? 0 :
9390      0);
9391
9392   int action_arg_number =
9393     (action_arg <= CA_ARG_MAX ? action_arg :
9394      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9395      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9396      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9397      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9398      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9399      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9400      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9401      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9402      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9403      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9404      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9405      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9406      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9407      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9408      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9409      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9410      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9411      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9412      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9413      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9414      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9415      -1);
9416
9417   int action_arg_number_old =
9418     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9419      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9420      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9421      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9422      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9423      0);
9424
9425   int action_arg_number_new =
9426     getModifiedActionNumber(action_arg_number_old,
9427                             action_mode, action_arg_number,
9428                             action_arg_number_min, action_arg_number_max);
9429
9430   int trigger_player_bits =
9431     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9432      change->actual_trigger_player_bits : change->trigger_player);
9433
9434   int action_arg_player_bits =
9435     (action_arg >= CA_ARG_PLAYER_1 &&
9436      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9437      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9438      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9439      PLAYER_BITS_ANY);
9440
9441   /* ---------- execute action  -------------------------------------------- */
9442
9443   switch (action_type)
9444   {
9445     case CA_NO_ACTION:
9446     {
9447       return;
9448     }
9449
9450     /* ---------- level actions  ------------------------------------------- */
9451
9452     case CA_RESTART_LEVEL:
9453     {
9454       game.restart_level = TRUE;
9455
9456       break;
9457     }
9458
9459     case CA_SHOW_ENVELOPE:
9460     {
9461       int element = getSpecialActionElement(action_arg_element,
9462                                             action_arg_number, EL_ENVELOPE_1);
9463
9464       if (IS_ENVELOPE(element))
9465         local_player->show_envelope = element;
9466
9467       break;
9468     }
9469
9470     case CA_SET_LEVEL_TIME:
9471     {
9472       if (level.time > 0)       /* only modify limited time value */
9473       {
9474         TimeLeft = action_arg_number_new;
9475
9476         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9477
9478         DisplayGameControlValues();
9479
9480         if (!TimeLeft && setup.time_limit)
9481           for (i = 0; i < MAX_PLAYERS; i++)
9482             KillPlayer(&stored_player[i]);
9483       }
9484
9485       break;
9486     }
9487
9488     case CA_SET_LEVEL_SCORE:
9489     {
9490       local_player->score = action_arg_number_new;
9491
9492       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9493
9494       DisplayGameControlValues();
9495
9496       break;
9497     }
9498
9499     case CA_SET_LEVEL_GEMS:
9500     {
9501       local_player->gems_still_needed = action_arg_number_new;
9502
9503       game.snapshot.collected_item = TRUE;
9504
9505       game_panel_controls[GAME_PANEL_GEMS].value =
9506         local_player->gems_still_needed;
9507
9508       DisplayGameControlValues();
9509
9510       break;
9511     }
9512
9513     case CA_SET_LEVEL_WIND:
9514     {
9515       game.wind_direction = action_arg_direction;
9516
9517       break;
9518     }
9519
9520     case CA_SET_LEVEL_RANDOM_SEED:
9521     {
9522       /* ensure that setting a new random seed while playing is predictable */
9523       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9524
9525       break;
9526     }
9527
9528     /* ---------- player actions  ------------------------------------------ */
9529
9530     case CA_MOVE_PLAYER:
9531     {
9532       /* automatically move to the next field in specified direction */
9533       for (i = 0; i < MAX_PLAYERS; i++)
9534         if (trigger_player_bits & (1 << i))
9535           stored_player[i].programmed_action = action_arg_direction;
9536
9537       break;
9538     }
9539
9540     case CA_EXIT_PLAYER:
9541     {
9542       for (i = 0; i < MAX_PLAYERS; i++)
9543         if (action_arg_player_bits & (1 << i))
9544           PlayerWins(&stored_player[i]);
9545
9546       break;
9547     }
9548
9549     case CA_KILL_PLAYER:
9550     {
9551       for (i = 0; i < MAX_PLAYERS; i++)
9552         if (action_arg_player_bits & (1 << i))
9553           KillPlayer(&stored_player[i]);
9554
9555       break;
9556     }
9557
9558     case CA_SET_PLAYER_KEYS:
9559     {
9560       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9561       int element = getSpecialActionElement(action_arg_element,
9562                                             action_arg_number, EL_KEY_1);
9563
9564       if (IS_KEY(element))
9565       {
9566         for (i = 0; i < MAX_PLAYERS; i++)
9567         {
9568           if (trigger_player_bits & (1 << i))
9569           {
9570             stored_player[i].key[KEY_NR(element)] = key_state;
9571
9572             DrawGameDoorValues();
9573           }
9574         }
9575       }
9576
9577       break;
9578     }
9579
9580     case CA_SET_PLAYER_SPEED:
9581     {
9582       for (i = 0; i < MAX_PLAYERS; i++)
9583       {
9584         if (trigger_player_bits & (1 << i))
9585         {
9586           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9587
9588           if (action_arg == CA_ARG_SPEED_FASTER &&
9589               stored_player[i].cannot_move)
9590           {
9591             action_arg_number = STEPSIZE_VERY_SLOW;
9592           }
9593           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9594                    action_arg == CA_ARG_SPEED_FASTER)
9595           {
9596             action_arg_number = 2;
9597             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9598                            CA_MODE_MULTIPLY);
9599           }
9600           else if (action_arg == CA_ARG_NUMBER_RESET)
9601           {
9602             action_arg_number = level.initial_player_stepsize[i];
9603           }
9604
9605           move_stepsize =
9606             getModifiedActionNumber(move_stepsize,
9607                                     action_mode,
9608                                     action_arg_number,
9609                                     action_arg_number_min,
9610                                     action_arg_number_max);
9611
9612           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9613         }
9614       }
9615
9616       break;
9617     }
9618
9619     case CA_SET_PLAYER_SHIELD:
9620     {
9621       for (i = 0; i < MAX_PLAYERS; i++)
9622       {
9623         if (trigger_player_bits & (1 << i))
9624         {
9625           if (action_arg == CA_ARG_SHIELD_OFF)
9626           {
9627             stored_player[i].shield_normal_time_left = 0;
9628             stored_player[i].shield_deadly_time_left = 0;
9629           }
9630           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9631           {
9632             stored_player[i].shield_normal_time_left = 999999;
9633           }
9634           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9635           {
9636             stored_player[i].shield_normal_time_left = 999999;
9637             stored_player[i].shield_deadly_time_left = 999999;
9638           }
9639         }
9640       }
9641
9642       break;
9643     }
9644
9645     case CA_SET_PLAYER_GRAVITY:
9646     {
9647       for (i = 0; i < MAX_PLAYERS; i++)
9648       {
9649         if (trigger_player_bits & (1 << i))
9650         {
9651           stored_player[i].gravity =
9652             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9653              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9654              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9655              stored_player[i].gravity);
9656         }
9657       }
9658
9659       break;
9660     }
9661
9662     case CA_SET_PLAYER_ARTWORK:
9663     {
9664       for (i = 0; i < MAX_PLAYERS; i++)
9665       {
9666         if (trigger_player_bits & (1 << i))
9667         {
9668           int artwork_element = action_arg_element;
9669
9670           if (action_arg == CA_ARG_ELEMENT_RESET)
9671             artwork_element =
9672               (level.use_artwork_element[i] ? level.artwork_element[i] :
9673                stored_player[i].element_nr);
9674
9675           if (stored_player[i].artwork_element != artwork_element)
9676             stored_player[i].Frame = 0;
9677
9678           stored_player[i].artwork_element = artwork_element;
9679
9680           SetPlayerWaiting(&stored_player[i], FALSE);
9681
9682           /* set number of special actions for bored and sleeping animation */
9683           stored_player[i].num_special_action_bored =
9684             get_num_special_action(artwork_element,
9685                                    ACTION_BORING_1, ACTION_BORING_LAST);
9686           stored_player[i].num_special_action_sleeping =
9687             get_num_special_action(artwork_element,
9688                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9689         }
9690       }
9691
9692       break;
9693     }
9694
9695     case CA_SET_PLAYER_INVENTORY:
9696     {
9697       for (i = 0; i < MAX_PLAYERS; i++)
9698       {
9699         struct PlayerInfo *player = &stored_player[i];
9700         int j, k;
9701
9702         if (trigger_player_bits & (1 << i))
9703         {
9704           int inventory_element = action_arg_element;
9705
9706           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9707               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9708               action_arg == CA_ARG_ELEMENT_ACTION)
9709           {
9710             int element = inventory_element;
9711             int collect_count = element_info[element].collect_count_initial;
9712
9713             if (!IS_CUSTOM_ELEMENT(element))
9714               collect_count = 1;
9715
9716             if (collect_count == 0)
9717               player->inventory_infinite_element = element;
9718             else
9719               for (k = 0; k < collect_count; k++)
9720                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9721                   player->inventory_element[player->inventory_size++] =
9722                     element;
9723           }
9724           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9725                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9726                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9727           {
9728             if (player->inventory_infinite_element != EL_UNDEFINED &&
9729                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9730                                      action_arg_element_raw))
9731               player->inventory_infinite_element = EL_UNDEFINED;
9732
9733             for (k = 0, j = 0; j < player->inventory_size; j++)
9734             {
9735               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9736                                         action_arg_element_raw))
9737                 player->inventory_element[k++] = player->inventory_element[j];
9738             }
9739
9740             player->inventory_size = k;
9741           }
9742           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9743           {
9744             if (player->inventory_size > 0)
9745             {
9746               for (j = 0; j < player->inventory_size - 1; j++)
9747                 player->inventory_element[j] = player->inventory_element[j + 1];
9748
9749               player->inventory_size--;
9750             }
9751           }
9752           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9753           {
9754             if (player->inventory_size > 0)
9755               player->inventory_size--;
9756           }
9757           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9758           {
9759             player->inventory_infinite_element = EL_UNDEFINED;
9760             player->inventory_size = 0;
9761           }
9762           else if (action_arg == CA_ARG_INVENTORY_RESET)
9763           {
9764             player->inventory_infinite_element = EL_UNDEFINED;
9765             player->inventory_size = 0;
9766
9767             if (level.use_initial_inventory[i])
9768             {
9769               for (j = 0; j < level.initial_inventory_size[i]; j++)
9770               {
9771                 int element = level.initial_inventory_content[i][j];
9772                 int collect_count = element_info[element].collect_count_initial;
9773
9774                 if (!IS_CUSTOM_ELEMENT(element))
9775                   collect_count = 1;
9776
9777                 if (collect_count == 0)
9778                   player->inventory_infinite_element = element;
9779                 else
9780                   for (k = 0; k < collect_count; k++)
9781                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9782                       player->inventory_element[player->inventory_size++] =
9783                         element;
9784               }
9785             }
9786           }
9787         }
9788       }
9789
9790       break;
9791     }
9792
9793     /* ---------- CE actions  ---------------------------------------------- */
9794
9795     case CA_SET_CE_VALUE:
9796     {
9797       int last_ce_value = CustomValue[x][y];
9798
9799       CustomValue[x][y] = action_arg_number_new;
9800
9801       if (CustomValue[x][y] != last_ce_value)
9802       {
9803         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9804         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9805
9806         if (CustomValue[x][y] == 0)
9807         {
9808           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9809           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9810         }
9811       }
9812
9813       break;
9814     }
9815
9816     case CA_SET_CE_SCORE:
9817     {
9818       int last_ce_score = ei->collect_score;
9819
9820       ei->collect_score = action_arg_number_new;
9821
9822       if (ei->collect_score != last_ce_score)
9823       {
9824         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9825         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9826
9827         if (ei->collect_score == 0)
9828         {
9829           int xx, yy;
9830
9831           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9832           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9833
9834           /*
9835             This is a very special case that seems to be a mixture between
9836             CheckElementChange() and CheckTriggeredElementChange(): while
9837             the first one only affects single elements that are triggered
9838             directly, the second one affects multiple elements in the playfield
9839             that are triggered indirectly by another element. This is a third
9840             case: Changing the CE score always affects multiple identical CEs,
9841             so every affected CE must be checked, not only the single CE for
9842             which the CE score was changed in the first place (as every instance
9843             of that CE shares the same CE score, and therefore also can change)!
9844           */
9845           SCAN_PLAYFIELD(xx, yy)
9846           {
9847             if (Feld[xx][yy] == element)
9848               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9849                                  CE_SCORE_GETS_ZERO);
9850           }
9851         }
9852       }
9853
9854       break;
9855     }
9856
9857     case CA_SET_CE_ARTWORK:
9858     {
9859       int artwork_element = action_arg_element;
9860       boolean reset_frame = FALSE;
9861       int xx, yy;
9862
9863       if (action_arg == CA_ARG_ELEMENT_RESET)
9864         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9865                            element);
9866
9867       if (ei->gfx_element != artwork_element)
9868         reset_frame = TRUE;
9869
9870       ei->gfx_element = artwork_element;
9871
9872       SCAN_PLAYFIELD(xx, yy)
9873       {
9874         if (Feld[xx][yy] == element)
9875         {
9876           if (reset_frame)
9877           {
9878             ResetGfxAnimation(xx, yy);
9879             ResetRandomAnimationValue(xx, yy);
9880           }
9881
9882           TEST_DrawLevelField(xx, yy);
9883         }
9884       }
9885
9886       break;
9887     }
9888
9889     /* ---------- engine actions  ------------------------------------------ */
9890
9891     case CA_SET_ENGINE_SCAN_MODE:
9892     {
9893       InitPlayfieldScanMode(action_arg);
9894
9895       break;
9896     }
9897
9898     default:
9899       break;
9900   }
9901 }
9902
9903 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9904 {
9905   int old_element = Feld[x][y];
9906   int new_element = GetElementFromGroupElement(element);
9907   int previous_move_direction = MovDir[x][y];
9908   int last_ce_value = CustomValue[x][y];
9909   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9910   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9911   boolean add_player_onto_element = (new_element_is_player &&
9912                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9913                                      IS_WALKABLE(old_element));
9914
9915   if (!add_player_onto_element)
9916   {
9917     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9918       RemoveMovingField(x, y);
9919     else
9920       RemoveField(x, y);
9921
9922     Feld[x][y] = new_element;
9923
9924     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9925       MovDir[x][y] = previous_move_direction;
9926
9927     if (element_info[new_element].use_last_ce_value)
9928       CustomValue[x][y] = last_ce_value;
9929
9930     InitField_WithBug1(x, y, FALSE);
9931
9932     new_element = Feld[x][y];   /* element may have changed */
9933
9934     ResetGfxAnimation(x, y);
9935     ResetRandomAnimationValue(x, y);
9936
9937     TEST_DrawLevelField(x, y);
9938
9939     if (GFX_CRUMBLED(new_element))
9940       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9941   }
9942
9943   /* check if element under the player changes from accessible to unaccessible
9944      (needed for special case of dropping element which then changes) */
9945   /* (must be checked after creating new element for walkable group elements) */
9946   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9947       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9948   {
9949     Bang(x, y);
9950
9951     return;
9952   }
9953
9954   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9955   if (new_element_is_player)
9956     RelocatePlayer(x, y, new_element);
9957
9958   if (is_change)
9959     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9960
9961   TestIfBadThingTouchesPlayer(x, y);
9962   TestIfPlayerTouchesCustomElement(x, y);
9963   TestIfElementTouchesCustomElement(x, y);
9964 }
9965
9966 static void CreateField(int x, int y, int element)
9967 {
9968   CreateFieldExt(x, y, element, FALSE);
9969 }
9970
9971 static void CreateElementFromChange(int x, int y, int element)
9972 {
9973   element = GET_VALID_RUNTIME_ELEMENT(element);
9974
9975   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9976   {
9977     int old_element = Feld[x][y];
9978
9979     /* prevent changed element from moving in same engine frame
9980        unless both old and new element can either fall or move */
9981     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9982         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9983       Stop[x][y] = TRUE;
9984   }
9985
9986   CreateFieldExt(x, y, element, TRUE);
9987 }
9988
9989 static boolean ChangeElement(int x, int y, int element, int page)
9990 {
9991   struct ElementInfo *ei = &element_info[element];
9992   struct ElementChangeInfo *change = &ei->change_page[page];
9993   int ce_value = CustomValue[x][y];
9994   int ce_score = ei->collect_score;
9995   int target_element;
9996   int old_element = Feld[x][y];
9997
9998   /* always use default change event to prevent running into a loop */
9999   if (ChangeEvent[x][y] == -1)
10000     ChangeEvent[x][y] = CE_DELAY;
10001
10002   if (ChangeEvent[x][y] == CE_DELAY)
10003   {
10004     /* reset actual trigger element, trigger player and action element */
10005     change->actual_trigger_element = EL_EMPTY;
10006     change->actual_trigger_player = EL_EMPTY;
10007     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10008     change->actual_trigger_side = CH_SIDE_NONE;
10009     change->actual_trigger_ce_value = 0;
10010     change->actual_trigger_ce_score = 0;
10011   }
10012
10013   /* do not change elements more than a specified maximum number of changes */
10014   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10015     return FALSE;
10016
10017   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10018
10019   if (change->explode)
10020   {
10021     Bang(x, y);
10022
10023     return TRUE;
10024   }
10025
10026   if (change->use_target_content)
10027   {
10028     boolean complete_replace = TRUE;
10029     boolean can_replace[3][3];
10030     int xx, yy;
10031
10032     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10033     {
10034       boolean is_empty;
10035       boolean is_walkable;
10036       boolean is_diggable;
10037       boolean is_collectible;
10038       boolean is_removable;
10039       boolean is_destructible;
10040       int ex = x + xx - 1;
10041       int ey = y + yy - 1;
10042       int content_element = change->target_content.e[xx][yy];
10043       int e;
10044
10045       can_replace[xx][yy] = TRUE;
10046
10047       if (ex == x && ey == y)   /* do not check changing element itself */
10048         continue;
10049
10050       if (content_element == EL_EMPTY_SPACE)
10051       {
10052         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10053
10054         continue;
10055       }
10056
10057       if (!IN_LEV_FIELD(ex, ey))
10058       {
10059         can_replace[xx][yy] = FALSE;
10060         complete_replace = FALSE;
10061
10062         continue;
10063       }
10064
10065       e = Feld[ex][ey];
10066
10067       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10068         e = MovingOrBlocked2Element(ex, ey);
10069
10070       is_empty = (IS_FREE(ex, ey) ||
10071                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10072
10073       is_walkable     = (is_empty || IS_WALKABLE(e));
10074       is_diggable     = (is_empty || IS_DIGGABLE(e));
10075       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10076       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10077       is_removable    = (is_diggable || is_collectible);
10078
10079       can_replace[xx][yy] =
10080         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10081           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10082           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10083           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10084           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10085           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10086          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10087
10088       if (!can_replace[xx][yy])
10089         complete_replace = FALSE;
10090     }
10091
10092     if (!change->only_if_complete || complete_replace)
10093     {
10094       boolean something_has_changed = FALSE;
10095
10096       if (change->only_if_complete && change->use_random_replace &&
10097           RND(100) < change->random_percentage)
10098         return FALSE;
10099
10100       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10101       {
10102         int ex = x + xx - 1;
10103         int ey = y + yy - 1;
10104         int content_element;
10105
10106         if (can_replace[xx][yy] && (!change->use_random_replace ||
10107                                     RND(100) < change->random_percentage))
10108         {
10109           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10110             RemoveMovingField(ex, ey);
10111
10112           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10113
10114           content_element = change->target_content.e[xx][yy];
10115           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10116                                               ce_value, ce_score);
10117
10118           CreateElementFromChange(ex, ey, target_element);
10119
10120           something_has_changed = TRUE;
10121
10122           /* for symmetry reasons, freeze newly created border elements */
10123           if (ex != x || ey != y)
10124             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10125         }
10126       }
10127
10128       if (something_has_changed)
10129       {
10130         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10131         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10132       }
10133     }
10134   }
10135   else
10136   {
10137     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10138                                         ce_value, ce_score);
10139
10140     if (element == EL_DIAGONAL_GROWING ||
10141         element == EL_DIAGONAL_SHRINKING)
10142     {
10143       target_element = Store[x][y];
10144
10145       Store[x][y] = EL_EMPTY;
10146     }
10147
10148     CreateElementFromChange(x, y, target_element);
10149
10150     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10151     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10152   }
10153
10154   /* this uses direct change before indirect change */
10155   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10156
10157   return TRUE;
10158 }
10159
10160 static void HandleElementChange(int x, int y, int page)
10161 {
10162   int element = MovingOrBlocked2Element(x, y);
10163   struct ElementInfo *ei = &element_info[element];
10164   struct ElementChangeInfo *change = &ei->change_page[page];
10165   boolean handle_action_before_change = FALSE;
10166
10167 #ifdef DEBUG
10168   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10169       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10170   {
10171     printf("\n\n");
10172     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10173            x, y, element, element_info[element].token_name);
10174     printf("HandleElementChange(): This should never happen!\n");
10175     printf("\n\n");
10176   }
10177 #endif
10178
10179   /* this can happen with classic bombs on walkable, changing elements */
10180   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10181   {
10182     return;
10183   }
10184
10185   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10186   {
10187     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10188
10189     if (change->can_change)
10190     {
10191       /* !!! not clear why graphic animation should be reset at all here !!! */
10192       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10193       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10194
10195       /*
10196         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10197
10198         When using an animation frame delay of 1 (this only happens with
10199         "sp_zonk.moving.left/right" in the classic graphics), the default
10200         (non-moving) animation shows wrong animation frames (while the
10201         moving animation, like "sp_zonk.moving.left/right", is correct,
10202         so this graphical bug never shows up with the classic graphics).
10203         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10204         be drawn instead of the correct frames 0,1,2,3. This is caused by
10205         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10206         an element change: First when the change delay ("ChangeDelay[][]")
10207         counter has reached zero after decrementing, then a second time in
10208         the next frame (after "GfxFrame[][]" was already incremented) when
10209         "ChangeDelay[][]" is reset to the initial delay value again.
10210
10211         This causes frame 0 to be drawn twice, while the last frame won't
10212         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10213
10214         As some animations may already be cleverly designed around this bug
10215         (at least the "Snake Bite" snake tail animation does this), it cannot
10216         simply be fixed here without breaking such existing animations.
10217         Unfortunately, it cannot easily be detected if a graphics set was
10218         designed "before" or "after" the bug was fixed. As a workaround,
10219         a new graphics set option "game.graphics_engine_version" was added
10220         to be able to specify the game's major release version for which the
10221         graphics set was designed, which can then be used to decide if the
10222         bugfix should be used (version 4 and above) or not (version 3 or
10223         below, or if no version was specified at all, as with old sets).
10224
10225         (The wrong/fixed animation frames can be tested with the test level set
10226         "test_gfxframe" and level "000", which contains a specially prepared
10227         custom element at level position (x/y) == (11/9) which uses the zonk
10228         animation mentioned above. Using "game.graphics_engine_version: 4"
10229         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10230         This can also be seen from the debug output for this test element.)
10231       */
10232
10233       /* when a custom element is about to change (for example by change delay),
10234          do not reset graphic animation when the custom element is moving */
10235       if (game.graphics_engine_version < 4 &&
10236           !IS_MOVING(x, y))
10237       {
10238         ResetGfxAnimation(x, y);
10239         ResetRandomAnimationValue(x, y);
10240       }
10241
10242       if (change->pre_change_function)
10243         change->pre_change_function(x, y);
10244     }
10245   }
10246
10247   ChangeDelay[x][y]--;
10248
10249   if (ChangeDelay[x][y] != 0)           /* continue element change */
10250   {
10251     if (change->can_change)
10252     {
10253       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10254
10255       if (IS_ANIMATED(graphic))
10256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10257
10258       if (change->change_function)
10259         change->change_function(x, y);
10260     }
10261   }
10262   else                                  /* finish element change */
10263   {
10264     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10265     {
10266       page = ChangePage[x][y];
10267       ChangePage[x][y] = -1;
10268
10269       change = &ei->change_page[page];
10270     }
10271
10272     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10273     {
10274       ChangeDelay[x][y] = 1;            /* try change after next move step */
10275       ChangePage[x][y] = page;          /* remember page to use for change */
10276
10277       return;
10278     }
10279
10280     /* special case: set new level random seed before changing element */
10281     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10282       handle_action_before_change = TRUE;
10283
10284     if (change->has_action && handle_action_before_change)
10285       ExecuteCustomElementAction(x, y, element, page);
10286
10287     if (change->can_change)
10288     {
10289       if (ChangeElement(x, y, element, page))
10290       {
10291         if (change->post_change_function)
10292           change->post_change_function(x, y);
10293       }
10294     }
10295
10296     if (change->has_action && !handle_action_before_change)
10297       ExecuteCustomElementAction(x, y, element, page);
10298   }
10299 }
10300
10301 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10302                                               int trigger_element,
10303                                               int trigger_event,
10304                                               int trigger_player,
10305                                               int trigger_side,
10306                                               int trigger_page)
10307 {
10308   boolean change_done_any = FALSE;
10309   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10310   int i;
10311
10312   if (!(trigger_events[trigger_element][trigger_event]))
10313     return FALSE;
10314
10315   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10316
10317   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10318   {
10319     int element = EL_CUSTOM_START + i;
10320     boolean change_done = FALSE;
10321     int p;
10322
10323     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10325       continue;
10326
10327     for (p = 0; p < element_info[element].num_change_pages; p++)
10328     {
10329       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10330
10331       if (change->can_change_or_has_action &&
10332           change->has_event[trigger_event] &&
10333           change->trigger_side & trigger_side &&
10334           change->trigger_player & trigger_player &&
10335           change->trigger_page & trigger_page_bits &&
10336           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10337       {
10338         change->actual_trigger_element = trigger_element;
10339         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10340         change->actual_trigger_player_bits = trigger_player;
10341         change->actual_trigger_side = trigger_side;
10342         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10343         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10344
10345         if ((change->can_change && !change_done) || change->has_action)
10346         {
10347           int x, y;
10348
10349           SCAN_PLAYFIELD(x, y)
10350           {
10351             if (Feld[x][y] == element)
10352             {
10353               if (change->can_change && !change_done)
10354               {
10355                 /* if element already changed in this frame, not only prevent
10356                    another element change (checked in ChangeElement()), but
10357                    also prevent additional element actions for this element */
10358
10359                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10360                     !level.use_action_after_change_bug)
10361                   continue;
10362
10363                 ChangeDelay[x][y] = 1;
10364                 ChangeEvent[x][y] = trigger_event;
10365
10366                 HandleElementChange(x, y, p);
10367               }
10368               else if (change->has_action)
10369               {
10370                 /* if element already changed in this frame, not only prevent
10371                    another element change (checked in ChangeElement()), but
10372                    also prevent additional element actions for this element */
10373
10374                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10375                     !level.use_action_after_change_bug)
10376                   continue;
10377
10378                 ExecuteCustomElementAction(x, y, element, p);
10379                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10380               }
10381             }
10382           }
10383
10384           if (change->can_change)
10385           {
10386             change_done = TRUE;
10387             change_done_any = TRUE;
10388           }
10389         }
10390       }
10391     }
10392   }
10393
10394   RECURSION_LOOP_DETECTION_END();
10395
10396   return change_done_any;
10397 }
10398
10399 static boolean CheckElementChangeExt(int x, int y,
10400                                      int element,
10401                                      int trigger_element,
10402                                      int trigger_event,
10403                                      int trigger_player,
10404                                      int trigger_side)
10405 {
10406   boolean change_done = FALSE;
10407   int p;
10408
10409   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10411     return FALSE;
10412
10413   if (Feld[x][y] == EL_BLOCKED)
10414   {
10415     Blocked2Moving(x, y, &x, &y);
10416     element = Feld[x][y];
10417   }
10418
10419   /* check if element has already changed or is about to change after moving */
10420   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421        Feld[x][y] != element) ||
10422
10423       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425         ChangePage[x][y] != -1)))
10426     return FALSE;
10427
10428   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10429
10430   for (p = 0; p < element_info[element].num_change_pages; p++)
10431   {
10432     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10433
10434     /* check trigger element for all events where the element that is checked
10435        for changing interacts with a directly adjacent element -- this is
10436        different to element changes that affect other elements to change on the
10437        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10438     boolean check_trigger_element =
10439       (trigger_event == CE_TOUCHING_X ||
10440        trigger_event == CE_HITTING_X ||
10441        trigger_event == CE_HIT_BY_X ||
10442        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10443
10444     if (change->can_change_or_has_action &&
10445         change->has_event[trigger_event] &&
10446         change->trigger_side & trigger_side &&
10447         change->trigger_player & trigger_player &&
10448         (!check_trigger_element ||
10449          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10450     {
10451       change->actual_trigger_element = trigger_element;
10452       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10453       change->actual_trigger_player_bits = trigger_player;
10454       change->actual_trigger_side = trigger_side;
10455       change->actual_trigger_ce_value = CustomValue[x][y];
10456       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10457
10458       /* special case: trigger element not at (x,y) position for some events */
10459       if (check_trigger_element)
10460       {
10461         static struct
10462         {
10463           int dx, dy;
10464         } move_xy[] =
10465           {
10466             {  0,  0 },
10467             { -1,  0 },
10468             { +1,  0 },
10469             {  0,  0 },
10470             {  0, -1 },
10471             {  0,  0 }, { 0, 0 }, { 0, 0 },
10472             {  0, +1 }
10473           };
10474
10475         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10476         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10477
10478         change->actual_trigger_ce_value = CustomValue[xx][yy];
10479         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10480       }
10481
10482       if (change->can_change && !change_done)
10483       {
10484         ChangeDelay[x][y] = 1;
10485         ChangeEvent[x][y] = trigger_event;
10486
10487         HandleElementChange(x, y, p);
10488
10489         change_done = TRUE;
10490       }
10491       else if (change->has_action)
10492       {
10493         ExecuteCustomElementAction(x, y, element, p);
10494         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10495       }
10496     }
10497   }
10498
10499   RECURSION_LOOP_DETECTION_END();
10500
10501   return change_done;
10502 }
10503
10504 static void PlayPlayerSound(struct PlayerInfo *player)
10505 {
10506   int jx = player->jx, jy = player->jy;
10507   int sound_element = player->artwork_element;
10508   int last_action = player->last_action_waiting;
10509   int action = player->action_waiting;
10510
10511   if (player->is_waiting)
10512   {
10513     if (action != last_action)
10514       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10515     else
10516       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10517   }
10518   else
10519   {
10520     if (action != last_action)
10521       StopSound(element_info[sound_element].sound[last_action]);
10522
10523     if (last_action == ACTION_SLEEPING)
10524       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10525   }
10526 }
10527
10528 static void PlayAllPlayersSound()
10529 {
10530   int i;
10531
10532   for (i = 0; i < MAX_PLAYERS; i++)
10533     if (stored_player[i].active)
10534       PlayPlayerSound(&stored_player[i]);
10535 }
10536
10537 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10538 {
10539   boolean last_waiting = player->is_waiting;
10540   int move_dir = player->MovDir;
10541
10542   player->dir_waiting = move_dir;
10543   player->last_action_waiting = player->action_waiting;
10544
10545   if (is_waiting)
10546   {
10547     if (!last_waiting)          /* not waiting -> waiting */
10548     {
10549       player->is_waiting = TRUE;
10550
10551       player->frame_counter_bored =
10552         FrameCounter +
10553         game.player_boring_delay_fixed +
10554         GetSimpleRandom(game.player_boring_delay_random);
10555       player->frame_counter_sleeping =
10556         FrameCounter +
10557         game.player_sleeping_delay_fixed +
10558         GetSimpleRandom(game.player_sleeping_delay_random);
10559
10560       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10561     }
10562
10563     if (game.player_sleeping_delay_fixed +
10564         game.player_sleeping_delay_random > 0 &&
10565         player->anim_delay_counter == 0 &&
10566         player->post_delay_counter == 0 &&
10567         FrameCounter >= player->frame_counter_sleeping)
10568       player->is_sleeping = TRUE;
10569     else if (game.player_boring_delay_fixed +
10570              game.player_boring_delay_random > 0 &&
10571              FrameCounter >= player->frame_counter_bored)
10572       player->is_bored = TRUE;
10573
10574     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10575                               player->is_bored ? ACTION_BORING :
10576                               ACTION_WAITING);
10577
10578     if (player->is_sleeping && player->use_murphy)
10579     {
10580       /* special case for sleeping Murphy when leaning against non-free tile */
10581
10582       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10583           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10584            !IS_MOVING(player->jx - 1, player->jy)))
10585         move_dir = MV_LEFT;
10586       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10587                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10588                 !IS_MOVING(player->jx + 1, player->jy)))
10589         move_dir = MV_RIGHT;
10590       else
10591         player->is_sleeping = FALSE;
10592
10593       player->dir_waiting = move_dir;
10594     }
10595
10596     if (player->is_sleeping)
10597     {
10598       if (player->num_special_action_sleeping > 0)
10599       {
10600         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10601         {
10602           int last_special_action = player->special_action_sleeping;
10603           int num_special_action = player->num_special_action_sleeping;
10604           int special_action =
10605             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10606              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10607              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10608              last_special_action + 1 : ACTION_SLEEPING);
10609           int special_graphic =
10610             el_act_dir2img(player->artwork_element, special_action, move_dir);
10611
10612           player->anim_delay_counter =
10613             graphic_info[special_graphic].anim_delay_fixed +
10614             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10615           player->post_delay_counter =
10616             graphic_info[special_graphic].post_delay_fixed +
10617             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10618
10619           player->special_action_sleeping = special_action;
10620         }
10621
10622         if (player->anim_delay_counter > 0)
10623         {
10624           player->action_waiting = player->special_action_sleeping;
10625           player->anim_delay_counter--;
10626         }
10627         else if (player->post_delay_counter > 0)
10628         {
10629           player->post_delay_counter--;
10630         }
10631       }
10632     }
10633     else if (player->is_bored)
10634     {
10635       if (player->num_special_action_bored > 0)
10636       {
10637         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10638         {
10639           int special_action =
10640             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10641           int special_graphic =
10642             el_act_dir2img(player->artwork_element, special_action, move_dir);
10643
10644           player->anim_delay_counter =
10645             graphic_info[special_graphic].anim_delay_fixed +
10646             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10647           player->post_delay_counter =
10648             graphic_info[special_graphic].post_delay_fixed +
10649             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10650
10651           player->special_action_bored = special_action;
10652         }
10653
10654         if (player->anim_delay_counter > 0)
10655         {
10656           player->action_waiting = player->special_action_bored;
10657           player->anim_delay_counter--;
10658         }
10659         else if (player->post_delay_counter > 0)
10660         {
10661           player->post_delay_counter--;
10662         }
10663       }
10664     }
10665   }
10666   else if (last_waiting)        /* waiting -> not waiting */
10667   {
10668     player->is_waiting = FALSE;
10669     player->is_bored = FALSE;
10670     player->is_sleeping = FALSE;
10671
10672     player->frame_counter_bored = -1;
10673     player->frame_counter_sleeping = -1;
10674
10675     player->anim_delay_counter = 0;
10676     player->post_delay_counter = 0;
10677
10678     player->dir_waiting = player->MovDir;
10679     player->action_waiting = ACTION_DEFAULT;
10680
10681     player->special_action_bored = ACTION_DEFAULT;
10682     player->special_action_sleeping = ACTION_DEFAULT;
10683   }
10684 }
10685
10686 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10687 {
10688   if ((!player->is_moving  && player->was_moving) ||
10689       (player->MovPos == 0 && player->was_moving) ||
10690       (player->is_snapping && !player->was_snapping) ||
10691       (player->is_dropping && !player->was_dropping))
10692   {
10693     if (!CheckSaveEngineSnapshotToList())
10694       return;
10695
10696     player->was_moving = FALSE;
10697     player->was_snapping = TRUE;
10698     player->was_dropping = TRUE;
10699   }
10700   else
10701   {
10702     if (player->is_moving)
10703       player->was_moving = TRUE;
10704
10705     if (!player->is_snapping)
10706       player->was_snapping = FALSE;
10707
10708     if (!player->is_dropping)
10709       player->was_dropping = FALSE;
10710   }
10711 }
10712
10713 static void CheckSingleStepMode(struct PlayerInfo *player)
10714 {
10715   if (tape.single_step && tape.recording && !tape.pausing)
10716   {
10717     /* as it is called "single step mode", just return to pause mode when the
10718        player stopped moving after one tile (or never starts moving at all) */
10719     if (!player->is_moving &&
10720         !player->is_pushing &&
10721         !player->is_dropping_pressed)
10722     {
10723       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10724       SnapField(player, 0, 0);                  /* stop snapping */
10725     }
10726   }
10727
10728   CheckSaveEngineSnapshot(player);
10729 }
10730
10731 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10732 {
10733   int left      = player_action & JOY_LEFT;
10734   int right     = player_action & JOY_RIGHT;
10735   int up        = player_action & JOY_UP;
10736   int down      = player_action & JOY_DOWN;
10737   int button1   = player_action & JOY_BUTTON_1;
10738   int button2   = player_action & JOY_BUTTON_2;
10739   int dx        = (left ? -1 : right ? 1 : 0);
10740   int dy        = (up   ? -1 : down  ? 1 : 0);
10741
10742   if (!player->active || tape.pausing)
10743     return 0;
10744
10745   if (player_action)
10746   {
10747     if (button1)
10748       SnapField(player, dx, dy);
10749     else
10750     {
10751       if (button2)
10752         DropElement(player);
10753
10754       MovePlayer(player, dx, dy);
10755     }
10756
10757     CheckSingleStepMode(player);
10758
10759     SetPlayerWaiting(player, FALSE);
10760
10761     return player_action;
10762   }
10763   else
10764   {
10765     /* no actions for this player (no input at player's configured device) */
10766
10767     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10768     SnapField(player, 0, 0);
10769     CheckGravityMovementWhenNotMoving(player);
10770
10771     if (player->MovPos == 0)
10772       SetPlayerWaiting(player, TRUE);
10773
10774     if (player->MovPos == 0)    /* needed for tape.playing */
10775       player->is_moving = FALSE;
10776
10777     player->is_dropping = FALSE;
10778     player->is_dropping_pressed = FALSE;
10779     player->drop_pressed_delay = 0;
10780
10781     CheckSingleStepMode(player);
10782
10783     return 0;
10784   }
10785 }
10786
10787 static void CheckLevelTime()
10788 {
10789   int i;
10790
10791   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10792   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10793   {
10794     if (level.native_em_level->lev->home == 0)  /* all players at home */
10795     {
10796       PlayerWins(local_player);
10797
10798       AllPlayersGone = TRUE;
10799
10800       level.native_em_level->lev->home = -1;
10801     }
10802
10803     if (level.native_em_level->ply[0]->alive == 0 &&
10804         level.native_em_level->ply[1]->alive == 0 &&
10805         level.native_em_level->ply[2]->alive == 0 &&
10806         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10807       AllPlayersGone = TRUE;
10808   }
10809   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10810   {
10811     if (game_sp.LevelSolved &&
10812         !game_sp.GameOver)                              /* game won */
10813     {
10814       PlayerWins(local_player);
10815
10816       game_sp.GameOver = TRUE;
10817
10818       AllPlayersGone = TRUE;
10819     }
10820
10821     if (game_sp.GameOver)                               /* game lost */
10822       AllPlayersGone = TRUE;
10823   }
10824
10825   if (TimeFrames >= FRAMES_PER_SECOND)
10826   {
10827     TimeFrames = 0;
10828     TapeTime++;
10829
10830     for (i = 0; i < MAX_PLAYERS; i++)
10831     {
10832       struct PlayerInfo *player = &stored_player[i];
10833
10834       if (SHIELD_ON(player))
10835       {
10836         player->shield_normal_time_left--;
10837
10838         if (player->shield_deadly_time_left > 0)
10839           player->shield_deadly_time_left--;
10840       }
10841     }
10842
10843     if (!local_player->LevelSolved && !level.use_step_counter)
10844     {
10845       TimePlayed++;
10846
10847       if (TimeLeft > 0)
10848       {
10849         TimeLeft--;
10850
10851         if (TimeLeft <= 10 && setup.time_limit)
10852           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10853
10854         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10855            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10856
10857         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10858
10859         if (!TimeLeft && setup.time_limit)
10860         {
10861           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10862             level.native_em_level->lev->killed_out_of_time = TRUE;
10863           else
10864             for (i = 0; i < MAX_PLAYERS; i++)
10865               KillPlayer(&stored_player[i]);
10866         }
10867       }
10868       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10869       {
10870         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10871       }
10872
10873       level.native_em_level->lev->time =
10874         (game.no_time_limit ? TimePlayed : TimeLeft);
10875     }
10876
10877     if (tape.recording || tape.playing)
10878       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10879   }
10880
10881   if (tape.recording || tape.playing)
10882     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10883
10884   UpdateAndDisplayGameControlValues();
10885 }
10886
10887 void AdvanceFrameAndPlayerCounters(int player_nr)
10888 {
10889   int i;
10890
10891   /* advance frame counters (global frame counter and time frame counter) */
10892   FrameCounter++;
10893   TimeFrames++;
10894
10895   /* advance player counters (counters for move delay, move animation etc.) */
10896   for (i = 0; i < MAX_PLAYERS; i++)
10897   {
10898     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10899     int move_delay_value = stored_player[i].move_delay_value;
10900     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10901
10902     if (!advance_player_counters)       /* not all players may be affected */
10903       continue;
10904
10905     if (move_frames == 0)       /* less than one move per game frame */
10906     {
10907       int stepsize = TILEX / move_delay_value;
10908       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10909       int count = (stored_player[i].is_moving ?
10910                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10911
10912       if (count % delay == 0)
10913         move_frames = 1;
10914     }
10915
10916     stored_player[i].Frame += move_frames;
10917
10918     if (stored_player[i].MovPos != 0)
10919       stored_player[i].StepFrame += move_frames;
10920
10921     if (stored_player[i].move_delay > 0)
10922       stored_player[i].move_delay--;
10923
10924     /* due to bugs in previous versions, counter must count up, not down */
10925     if (stored_player[i].push_delay != -1)
10926       stored_player[i].push_delay++;
10927
10928     if (stored_player[i].drop_delay > 0)
10929       stored_player[i].drop_delay--;
10930
10931     if (stored_player[i].is_dropping_pressed)
10932       stored_player[i].drop_pressed_delay++;
10933   }
10934 }
10935
10936 void StartGameActions(boolean init_network_game, boolean record_tape,
10937                       int random_seed)
10938 {
10939   unsigned int new_random_seed = InitRND(random_seed);
10940
10941   if (record_tape)
10942     TapeStartRecording(new_random_seed);
10943
10944 #if defined(NETWORK_AVALIABLE)
10945   if (init_network_game)
10946   {
10947     SendToServer_StartPlaying();
10948
10949     return;
10950   }
10951 #endif
10952
10953   InitGame();
10954 }
10955
10956 void GameActionsExt()
10957 {
10958 #if 0
10959   static unsigned int game_frame_delay = 0;
10960 #endif
10961   unsigned int game_frame_delay_value;
10962   byte *recorded_player_action;
10963   byte summarized_player_action = 0;
10964   byte tape_action[MAX_PLAYERS];
10965   int i;
10966
10967   /* detect endless loops, caused by custom element programming */
10968   if (recursion_loop_detected && recursion_loop_depth == 0)
10969   {
10970     char *message = getStringCat3("Internal Error! Element ",
10971                                   EL_NAME(recursion_loop_element),
10972                                   " caused endless loop! Quit the game?");
10973
10974     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10975           EL_NAME(recursion_loop_element));
10976
10977     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10978
10979     recursion_loop_detected = FALSE;    /* if game should be continued */
10980
10981     free(message);
10982
10983     return;
10984   }
10985
10986   if (game.restart_level)
10987     StartGameActions(options.network, setup.autorecord, level.random_seed);
10988
10989   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10990   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10991   {
10992     if (level.native_em_level->lev->home == 0)  /* all players at home */
10993     {
10994       PlayerWins(local_player);
10995
10996       AllPlayersGone = TRUE;
10997
10998       level.native_em_level->lev->home = -1;
10999     }
11000
11001     if (level.native_em_level->ply[0]->alive == 0 &&
11002         level.native_em_level->ply[1]->alive == 0 &&
11003         level.native_em_level->ply[2]->alive == 0 &&
11004         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11005       AllPlayersGone = TRUE;
11006   }
11007   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11008   {
11009     if (game_sp.LevelSolved &&
11010         !game_sp.GameOver)                              /* game won */
11011     {
11012       PlayerWins(local_player);
11013
11014       game_sp.GameOver = TRUE;
11015
11016       AllPlayersGone = TRUE;
11017     }
11018
11019     if (game_sp.GameOver)                               /* game lost */
11020       AllPlayersGone = TRUE;
11021   }
11022
11023   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11024     GameWon();
11025
11026   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11027     TapeStop();
11028
11029   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11030     return;
11031
11032   game_frame_delay_value =
11033     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11034
11035   if (tape.playing && tape.warp_forward && !tape.pausing)
11036     game_frame_delay_value = 0;
11037
11038   SetVideoFrameDelay(game_frame_delay_value);
11039
11040 #if 0
11041 #if 0
11042   /* ---------- main game synchronization point ---------- */
11043
11044   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11045
11046   printf("::: skip == %d\n", skip);
11047
11048 #else
11049   /* ---------- main game synchronization point ---------- */
11050
11051   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11052 #endif
11053 #endif
11054
11055   if (network_playing && !network_player_action_received)
11056   {
11057     /* try to get network player actions in time */
11058
11059 #if defined(NETWORK_AVALIABLE)
11060     /* last chance to get network player actions without main loop delay */
11061     HandleNetworking();
11062 #endif
11063
11064     /* game was quit by network peer */
11065     if (game_status != GAME_MODE_PLAYING)
11066       return;
11067
11068     if (!network_player_action_received)
11069       return;           /* failed to get network player actions in time */
11070
11071     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11072   }
11073
11074   if (tape.pausing)
11075     return;
11076
11077   /* at this point we know that we really continue executing the game */
11078
11079   network_player_action_received = FALSE;
11080
11081   /* when playing tape, read previously recorded player input from tape data */
11082   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11083
11084   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11085   if (tape.pausing)
11086     return;
11087
11088   if (tape.set_centered_player)
11089   {
11090     game.centered_player_nr_next = tape.centered_player_nr_next;
11091     game.set_centered_player = TRUE;
11092   }
11093
11094   for (i = 0; i < MAX_PLAYERS; i++)
11095   {
11096     summarized_player_action |= stored_player[i].action;
11097
11098     if (!network_playing && (game.team_mode || tape.playing))
11099       stored_player[i].effective_action = stored_player[i].action;
11100   }
11101
11102 #if defined(NETWORK_AVALIABLE)
11103   if (network_playing)
11104     SendToServer_MovePlayer(summarized_player_action);
11105 #endif
11106
11107   // summarize all actions at local players mapped input device position
11108   // (this allows using different input devices in single player mode)
11109   if (!options.network && !game.team_mode)
11110     stored_player[map_player_action[local_player->index_nr]].effective_action =
11111       summarized_player_action;
11112
11113   if (tape.recording &&
11114       setup.team_mode &&
11115       setup.input_on_focus &&
11116       game.centered_player_nr != -1)
11117   {
11118     for (i = 0; i < MAX_PLAYERS; i++)
11119       stored_player[i].effective_action =
11120         (i == game.centered_player_nr ? summarized_player_action : 0);
11121   }
11122
11123   if (recorded_player_action != NULL)
11124     for (i = 0; i < MAX_PLAYERS; i++)
11125       stored_player[i].effective_action = recorded_player_action[i];
11126
11127   for (i = 0; i < MAX_PLAYERS; i++)
11128   {
11129     tape_action[i] = stored_player[i].effective_action;
11130
11131     /* (this may happen in the RND game engine if a player was not present on
11132        the playfield on level start, but appeared later from a custom element */
11133     if (setup.team_mode &&
11134         tape.recording &&
11135         tape_action[i] &&
11136         !tape.player_participates[i])
11137       tape.player_participates[i] = TRUE;
11138   }
11139
11140   /* only record actions from input devices, but not programmed actions */
11141   if (tape.recording)
11142     TapeRecordAction(tape_action);
11143
11144 #if USE_NEW_PLAYER_ASSIGNMENTS
11145   // !!! also map player actions in single player mode !!!
11146   // if (game.team_mode)
11147   if (1)
11148   {
11149     byte mapped_action[MAX_PLAYERS];
11150
11151 #if DEBUG_PLAYER_ACTIONS
11152     printf(":::");
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154       printf(" %d, ", stored_player[i].effective_action);
11155 #endif
11156
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11159
11160     for (i = 0; i < MAX_PLAYERS; i++)
11161       stored_player[i].effective_action = mapped_action[i];
11162
11163 #if DEBUG_PLAYER_ACTIONS
11164     printf(" =>");
11165     for (i = 0; i < MAX_PLAYERS; i++)
11166       printf(" %d, ", stored_player[i].effective_action);
11167     printf("\n");
11168 #endif
11169   }
11170 #if DEBUG_PLAYER_ACTIONS
11171   else
11172   {
11173     printf(":::");
11174     for (i = 0; i < MAX_PLAYERS; i++)
11175       printf(" %d, ", stored_player[i].effective_action);
11176     printf("\n");
11177   }
11178 #endif
11179 #endif
11180
11181   for (i = 0; i < MAX_PLAYERS; i++)
11182   {
11183     // allow engine snapshot in case of changed movement attempt
11184     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11185         (stored_player[i].effective_action & KEY_MOTION))
11186       game.snapshot.changed_action = TRUE;
11187
11188     // allow engine snapshot in case of snapping/dropping attempt
11189     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11190         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11191       game.snapshot.changed_action = TRUE;
11192
11193     game.snapshot.last_action[i] = stored_player[i].effective_action;
11194   }
11195
11196   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11197   {
11198     GameActions_EM_Main();
11199   }
11200   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11201   {
11202     GameActions_SP_Main();
11203   }
11204   else
11205   {
11206     GameActions_RND_Main();
11207   }
11208
11209   BlitScreenToBitmap(backbuffer);
11210
11211   CheckLevelTime();
11212
11213   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11214
11215   if (options.debug)                    /* calculate frames per second */
11216   {
11217     static unsigned int fps_counter = 0;
11218     static int fps_frames = 0;
11219     unsigned int fps_delay_ms = Counter() - fps_counter;
11220
11221     fps_frames++;
11222
11223     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11224     {
11225       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11226
11227       fps_frames = 0;
11228       fps_counter = Counter();
11229
11230       /* always draw FPS to screen after FPS value was updated */
11231       redraw_mask |= REDRAW_FPS;
11232     }
11233
11234     /* only draw FPS if no screen areas are deactivated (invisible warp mode) */
11235     if (GetDrawDeactivationMask() == REDRAW_NONE)
11236       redraw_mask |= REDRAW_FPS;
11237   }
11238 }
11239
11240 static void GameActions_CheckSaveEngineSnapshot()
11241 {
11242   if (!game.snapshot.save_snapshot)
11243     return;
11244
11245   // clear flag for saving snapshot _before_ saving snapshot
11246   game.snapshot.save_snapshot = FALSE;
11247
11248   SaveEngineSnapshotToList();
11249 }
11250
11251 void GameActions()
11252 {
11253   GameActionsExt();
11254
11255   GameActions_CheckSaveEngineSnapshot();
11256 }
11257
11258 void GameActions_EM_Main()
11259 {
11260   byte effective_action[MAX_PLAYERS];
11261   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11262   int i;
11263
11264   for (i = 0; i < MAX_PLAYERS; i++)
11265     effective_action[i] = stored_player[i].effective_action;
11266
11267   GameActions_EM(effective_action, warp_mode);
11268 }
11269
11270 void GameActions_SP_Main()
11271 {
11272   byte effective_action[MAX_PLAYERS];
11273   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11274   int i;
11275
11276   for (i = 0; i < MAX_PLAYERS; i++)
11277     effective_action[i] = stored_player[i].effective_action;
11278
11279   GameActions_SP(effective_action, warp_mode);
11280
11281   for (i = 0; i < MAX_PLAYERS; i++)
11282   {
11283     if (stored_player[i].force_dropping)
11284       stored_player[i].action |= KEY_BUTTON_DROP;
11285
11286     stored_player[i].force_dropping = FALSE;
11287   }
11288 }
11289
11290 void GameActions_RND_Main()
11291 {
11292   GameActions_RND();
11293 }
11294
11295 void GameActions_RND()
11296 {
11297   int magic_wall_x = 0, magic_wall_y = 0;
11298   int i, x, y, element, graphic, last_gfx_frame;
11299
11300   InitPlayfieldScanModeVars();
11301
11302   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11303   {
11304     SCAN_PLAYFIELD(x, y)
11305     {
11306       ChangeCount[x][y] = 0;
11307       ChangeEvent[x][y] = -1;
11308     }
11309   }
11310
11311   if (game.set_centered_player)
11312   {
11313     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11314
11315     /* switching to "all players" only possible if all players fit to screen */
11316     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11317     {
11318       game.centered_player_nr_next = game.centered_player_nr;
11319       game.set_centered_player = FALSE;
11320     }
11321
11322     /* do not switch focus to non-existing (or non-active) player */
11323     if (game.centered_player_nr_next >= 0 &&
11324         !stored_player[game.centered_player_nr_next].active)
11325     {
11326       game.centered_player_nr_next = game.centered_player_nr;
11327       game.set_centered_player = FALSE;
11328     }
11329   }
11330
11331   if (game.set_centered_player &&
11332       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11333   {
11334     int sx, sy;
11335
11336     if (game.centered_player_nr_next == -1)
11337     {
11338       setScreenCenteredToAllPlayers(&sx, &sy);
11339     }
11340     else
11341     {
11342       sx = stored_player[game.centered_player_nr_next].jx;
11343       sy = stored_player[game.centered_player_nr_next].jy;
11344     }
11345
11346     game.centered_player_nr = game.centered_player_nr_next;
11347     game.set_centered_player = FALSE;
11348
11349     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11350     DrawGameDoorValues();
11351   }
11352
11353   for (i = 0; i < MAX_PLAYERS; i++)
11354   {
11355     int actual_player_action = stored_player[i].effective_action;
11356
11357 #if 1
11358     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11359        - rnd_equinox_tetrachloride 048
11360        - rnd_equinox_tetrachloride_ii 096
11361        - rnd_emanuel_schmieg 002
11362        - doctor_sloan_ww 001, 020
11363     */
11364     if (stored_player[i].MovPos == 0)
11365       CheckGravityMovement(&stored_player[i]);
11366 #endif
11367
11368     /* overwrite programmed action with tape action */
11369     if (stored_player[i].programmed_action)
11370       actual_player_action = stored_player[i].programmed_action;
11371
11372     PlayerActions(&stored_player[i], actual_player_action);
11373
11374     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11375   }
11376
11377   ScrollScreen(NULL, SCROLL_GO_ON);
11378
11379   /* for backwards compatibility, the following code emulates a fixed bug that
11380      occured when pushing elements (causing elements that just made their last
11381      pushing step to already (if possible) make their first falling step in the
11382      same game frame, which is bad); this code is also needed to use the famous
11383      "spring push bug" which is used in older levels and might be wanted to be
11384      used also in newer levels, but in this case the buggy pushing code is only
11385      affecting the "spring" element and no other elements */
11386
11387   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11388   {
11389     for (i = 0; i < MAX_PLAYERS; i++)
11390     {
11391       struct PlayerInfo *player = &stored_player[i];
11392       int x = player->jx;
11393       int y = player->jy;
11394
11395       if (player->active && player->is_pushing && player->is_moving &&
11396           IS_MOVING(x, y) &&
11397           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11398            Feld[x][y] == EL_SPRING))
11399       {
11400         ContinueMoving(x, y);
11401
11402         /* continue moving after pushing (this is actually a bug) */
11403         if (!IS_MOVING(x, y))
11404           Stop[x][y] = FALSE;
11405       }
11406     }
11407   }
11408
11409   SCAN_PLAYFIELD(x, y)
11410   {
11411     ChangeCount[x][y] = 0;
11412     ChangeEvent[x][y] = -1;
11413
11414     /* this must be handled before main playfield loop */
11415     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11416     {
11417       MovDelay[x][y]--;
11418       if (MovDelay[x][y] <= 0)
11419         RemoveField(x, y);
11420     }
11421
11422     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11423     {
11424       MovDelay[x][y]--;
11425       if (MovDelay[x][y] <= 0)
11426       {
11427         RemoveField(x, y);
11428         TEST_DrawLevelField(x, y);
11429
11430         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11431       }
11432     }
11433
11434 #if DEBUG
11435     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11436     {
11437       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11438       printf("GameActions(): This should never happen!\n");
11439
11440       ChangePage[x][y] = -1;
11441     }
11442 #endif
11443
11444     Stop[x][y] = FALSE;
11445     if (WasJustMoving[x][y] > 0)
11446       WasJustMoving[x][y]--;
11447     if (WasJustFalling[x][y] > 0)
11448       WasJustFalling[x][y]--;
11449     if (CheckCollision[x][y] > 0)
11450       CheckCollision[x][y]--;
11451     if (CheckImpact[x][y] > 0)
11452       CheckImpact[x][y]--;
11453
11454     GfxFrame[x][y]++;
11455
11456     /* reset finished pushing action (not done in ContinueMoving() to allow
11457        continuous pushing animation for elements with zero push delay) */
11458     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11459     {
11460       ResetGfxAnimation(x, y);
11461       TEST_DrawLevelField(x, y);
11462     }
11463
11464 #if DEBUG
11465     if (IS_BLOCKED(x, y))
11466     {
11467       int oldx, oldy;
11468
11469       Blocked2Moving(x, y, &oldx, &oldy);
11470       if (!IS_MOVING(oldx, oldy))
11471       {
11472         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11473         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11474         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11475         printf("GameActions(): This should never happen!\n");
11476       }
11477     }
11478 #endif
11479   }
11480
11481   SCAN_PLAYFIELD(x, y)
11482   {
11483     element = Feld[x][y];
11484     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11485     last_gfx_frame = GfxFrame[x][y];
11486
11487     ResetGfxFrame(x, y);
11488
11489     if (GfxFrame[x][y] != last_gfx_frame && !Stop[x][y])
11490       DrawLevelGraphicAnimation(x, y, graphic);
11491
11492     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11493         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11494       ResetRandomAnimationValue(x, y);
11495
11496     SetRandomAnimationValue(x, y);
11497
11498     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11499
11500     if (IS_INACTIVE(element))
11501     {
11502       if (IS_ANIMATED(graphic))
11503         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11504
11505       continue;
11506     }
11507
11508     /* this may take place after moving, so 'element' may have changed */
11509     if (IS_CHANGING(x, y) &&
11510         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11511     {
11512       int page = element_info[element].event_page_nr[CE_DELAY];
11513
11514       HandleElementChange(x, y, page);
11515
11516       element = Feld[x][y];
11517       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11518     }
11519
11520     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11521     {
11522       StartMoving(x, y);
11523
11524       element = Feld[x][y];
11525       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11526
11527       if (IS_ANIMATED(graphic) &&
11528           !IS_MOVING(x, y) &&
11529           !Stop[x][y])
11530         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11531
11532       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11533         TEST_DrawTwinkleOnField(x, y);
11534     }
11535     else if (element == EL_ACID)
11536     {
11537       if (!Stop[x][y])
11538         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11539     }
11540     else if ((element == EL_EXIT_OPEN ||
11541               element == EL_EM_EXIT_OPEN ||
11542               element == EL_SP_EXIT_OPEN ||
11543               element == EL_STEEL_EXIT_OPEN ||
11544               element == EL_EM_STEEL_EXIT_OPEN ||
11545               element == EL_SP_TERMINAL ||
11546               element == EL_SP_TERMINAL_ACTIVE ||
11547               element == EL_EXTRA_TIME ||
11548               element == EL_SHIELD_NORMAL ||
11549               element == EL_SHIELD_DEADLY) &&
11550              IS_ANIMATED(graphic))
11551       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11552     else if (IS_MOVING(x, y))
11553       ContinueMoving(x, y);
11554     else if (IS_ACTIVE_BOMB(element))
11555       CheckDynamite(x, y);
11556     else if (element == EL_AMOEBA_GROWING)
11557       AmoebeWaechst(x, y);
11558     else if (element == EL_AMOEBA_SHRINKING)
11559       AmoebaDisappearing(x, y);
11560
11561 #if !USE_NEW_AMOEBA_CODE
11562     else if (IS_AMOEBALIVE(element))
11563       AmoebeAbleger(x, y);
11564 #endif
11565
11566     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11567       Life(x, y);
11568     else if (element == EL_EXIT_CLOSED)
11569       CheckExit(x, y);
11570     else if (element == EL_EM_EXIT_CLOSED)
11571       CheckExitEM(x, y);
11572     else if (element == EL_STEEL_EXIT_CLOSED)
11573       CheckExitSteel(x, y);
11574     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11575       CheckExitSteelEM(x, y);
11576     else if (element == EL_SP_EXIT_CLOSED)
11577       CheckExitSP(x, y);
11578     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11579              element == EL_EXPANDABLE_STEELWALL_GROWING)
11580       MauerWaechst(x, y);
11581     else if (element == EL_EXPANDABLE_WALL ||
11582              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11583              element == EL_EXPANDABLE_WALL_VERTICAL ||
11584              element == EL_EXPANDABLE_WALL_ANY ||
11585              element == EL_BD_EXPANDABLE_WALL)
11586       MauerAbleger(x, y);
11587     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11588              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11589              element == EL_EXPANDABLE_STEELWALL_ANY)
11590       MauerAblegerStahl(x, y);
11591     else if (element == EL_FLAMES)
11592       CheckForDragon(x, y);
11593     else if (element == EL_EXPLOSION)
11594       ; /* drawing of correct explosion animation is handled separately */
11595     else if (element == EL_ELEMENT_SNAPPING ||
11596              element == EL_DIAGONAL_SHRINKING ||
11597              element == EL_DIAGONAL_GROWING)
11598     {
11599       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11600
11601       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11602     }
11603     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11604       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11605
11606     if (IS_BELT_ACTIVE(element))
11607       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11608
11609     if (game.magic_wall_active)
11610     {
11611       int jx = local_player->jx, jy = local_player->jy;
11612
11613       /* play the element sound at the position nearest to the player */
11614       if ((element == EL_MAGIC_WALL_FULL ||
11615            element == EL_MAGIC_WALL_ACTIVE ||
11616            element == EL_MAGIC_WALL_EMPTYING ||
11617            element == EL_BD_MAGIC_WALL_FULL ||
11618            element == EL_BD_MAGIC_WALL_ACTIVE ||
11619            element == EL_BD_MAGIC_WALL_EMPTYING ||
11620            element == EL_DC_MAGIC_WALL_FULL ||
11621            element == EL_DC_MAGIC_WALL_ACTIVE ||
11622            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11623           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11624       {
11625         magic_wall_x = x;
11626         magic_wall_y = y;
11627       }
11628     }
11629   }
11630
11631 #if USE_NEW_AMOEBA_CODE
11632   /* new experimental amoeba growth stuff */
11633   if (!(FrameCounter % 8))
11634   {
11635     static unsigned int random = 1684108901;
11636
11637     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11638     {
11639       x = RND(lev_fieldx);
11640       y = RND(lev_fieldy);
11641       element = Feld[x][y];
11642
11643       if (!IS_PLAYER(x,y) &&
11644           (element == EL_EMPTY ||
11645            CAN_GROW_INTO(element) ||
11646            element == EL_QUICKSAND_EMPTY ||
11647            element == EL_QUICKSAND_FAST_EMPTY ||
11648            element == EL_ACID_SPLASH_LEFT ||
11649            element == EL_ACID_SPLASH_RIGHT))
11650       {
11651         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11652             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11653             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11654             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11655           Feld[x][y] = EL_AMOEBA_DROP;
11656       }
11657
11658       random = random * 129 + 1;
11659     }
11660   }
11661 #endif
11662
11663   game.explosions_delayed = FALSE;
11664
11665   SCAN_PLAYFIELD(x, y)
11666   {
11667     element = Feld[x][y];
11668
11669     if (ExplodeField[x][y])
11670       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11671     else if (element == EL_EXPLOSION)
11672       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11673
11674     ExplodeField[x][y] = EX_TYPE_NONE;
11675   }
11676
11677   game.explosions_delayed = TRUE;
11678
11679   if (game.magic_wall_active)
11680   {
11681     if (!(game.magic_wall_time_left % 4))
11682     {
11683       int element = Feld[magic_wall_x][magic_wall_y];
11684
11685       if (element == EL_BD_MAGIC_WALL_FULL ||
11686           element == EL_BD_MAGIC_WALL_ACTIVE ||
11687           element == EL_BD_MAGIC_WALL_EMPTYING)
11688         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11689       else if (element == EL_DC_MAGIC_WALL_FULL ||
11690                element == EL_DC_MAGIC_WALL_ACTIVE ||
11691                element == EL_DC_MAGIC_WALL_EMPTYING)
11692         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11693       else
11694         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11695     }
11696
11697     if (game.magic_wall_time_left > 0)
11698     {
11699       game.magic_wall_time_left--;
11700
11701       if (!game.magic_wall_time_left)
11702       {
11703         SCAN_PLAYFIELD(x, y)
11704         {
11705           element = Feld[x][y];
11706
11707           if (element == EL_MAGIC_WALL_ACTIVE ||
11708               element == EL_MAGIC_WALL_FULL)
11709           {
11710             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11711             TEST_DrawLevelField(x, y);
11712           }
11713           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11714                    element == EL_BD_MAGIC_WALL_FULL)
11715           {
11716             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11717             TEST_DrawLevelField(x, y);
11718           }
11719           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11720                    element == EL_DC_MAGIC_WALL_FULL)
11721           {
11722             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11723             TEST_DrawLevelField(x, y);
11724           }
11725         }
11726
11727         game.magic_wall_active = FALSE;
11728       }
11729     }
11730   }
11731
11732   if (game.light_time_left > 0)
11733   {
11734     game.light_time_left--;
11735
11736     if (game.light_time_left == 0)
11737       RedrawAllLightSwitchesAndInvisibleElements();
11738   }
11739
11740   if (game.timegate_time_left > 0)
11741   {
11742     game.timegate_time_left--;
11743
11744     if (game.timegate_time_left == 0)
11745       CloseAllOpenTimegates();
11746   }
11747
11748   if (game.lenses_time_left > 0)
11749   {
11750     game.lenses_time_left--;
11751
11752     if (game.lenses_time_left == 0)
11753       RedrawAllInvisibleElementsForLenses();
11754   }
11755
11756   if (game.magnify_time_left > 0)
11757   {
11758     game.magnify_time_left--;
11759
11760     if (game.magnify_time_left == 0)
11761       RedrawAllInvisibleElementsForMagnifier();
11762   }
11763
11764   for (i = 0; i < MAX_PLAYERS; i++)
11765   {
11766     struct PlayerInfo *player = &stored_player[i];
11767
11768     if (SHIELD_ON(player))
11769     {
11770       if (player->shield_deadly_time_left)
11771         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11772       else if (player->shield_normal_time_left)
11773         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11774     }
11775   }
11776
11777 #if USE_DELAYED_GFX_REDRAW
11778   SCAN_PLAYFIELD(x, y)
11779   {
11780     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11781     {
11782       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11783          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11784
11785       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11786         DrawLevelField(x, y);
11787
11788       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11789         DrawLevelFieldCrumbled(x, y);
11790
11791       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11792         DrawLevelFieldCrumbledNeighbours(x, y);
11793
11794       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11795         DrawTwinkleOnField(x, y);
11796     }
11797
11798     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11799   }
11800 #endif
11801
11802   DrawAllPlayers();
11803   PlayAllPlayersSound();
11804
11805   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11806   {
11807     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11808
11809     local_player->show_envelope = 0;
11810   }
11811
11812   /* use random number generator in every frame to make it less predictable */
11813   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11814     RND(1);
11815 }
11816
11817 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11818 {
11819   int min_x = x, min_y = y, max_x = x, max_y = y;
11820   int i;
11821
11822   for (i = 0; i < MAX_PLAYERS; i++)
11823   {
11824     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11825
11826     if (!stored_player[i].active || &stored_player[i] == player)
11827       continue;
11828
11829     min_x = MIN(min_x, jx);
11830     min_y = MIN(min_y, jy);
11831     max_x = MAX(max_x, jx);
11832     max_y = MAX(max_y, jy);
11833   }
11834
11835   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11836 }
11837
11838 static boolean AllPlayersInVisibleScreen()
11839 {
11840   int i;
11841
11842   for (i = 0; i < MAX_PLAYERS; i++)
11843   {
11844     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11845
11846     if (!stored_player[i].active)
11847       continue;
11848
11849     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11850       return FALSE;
11851   }
11852
11853   return TRUE;
11854 }
11855
11856 void ScrollLevel(int dx, int dy)
11857 {
11858   int scroll_offset = 2 * TILEX_VAR;
11859   int x, y;
11860
11861   BlitBitmap(drawto_field, drawto_field,
11862              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11863              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11864              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11865              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11866              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11867              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11868
11869   if (dx != 0)
11870   {
11871     x = (dx == 1 ? BX1 : BX2);
11872     for (y = BY1; y <= BY2; y++)
11873       DrawScreenField(x, y);
11874   }
11875
11876   if (dy != 0)
11877   {
11878     y = (dy == 1 ? BY1 : BY2);
11879     for (x = BX1; x <= BX2; x++)
11880       DrawScreenField(x, y);
11881   }
11882
11883   redraw_mask |= REDRAW_FIELD;
11884 }
11885
11886 static boolean canFallDown(struct PlayerInfo *player)
11887 {
11888   int jx = player->jx, jy = player->jy;
11889
11890   return (IN_LEV_FIELD(jx, jy + 1) &&
11891           (IS_FREE(jx, jy + 1) ||
11892            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11893           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11894           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11895 }
11896
11897 static boolean canPassField(int x, int y, int move_dir)
11898 {
11899   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11900   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11901   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11902   int nextx = x + dx;
11903   int nexty = y + dy;
11904   int element = Feld[x][y];
11905
11906   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11907           !CAN_MOVE(element) &&
11908           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11909           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11910           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11911 }
11912
11913 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11914 {
11915   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11916   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11917   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11918   int newx = x + dx;
11919   int newy = y + dy;
11920
11921   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11922           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11923           (IS_DIGGABLE(Feld[newx][newy]) ||
11924            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11925            canPassField(newx, newy, move_dir)));
11926 }
11927
11928 static void CheckGravityMovement(struct PlayerInfo *player)
11929 {
11930   if (player->gravity && !player->programmed_action)
11931   {
11932     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11933     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11934     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11935     int jx = player->jx, jy = player->jy;
11936     boolean player_is_moving_to_valid_field =
11937       (!player_is_snapping &&
11938        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11939         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11940     boolean player_can_fall_down = canFallDown(player);
11941
11942     if (player_can_fall_down &&
11943         !player_is_moving_to_valid_field)
11944       player->programmed_action = MV_DOWN;
11945   }
11946 }
11947
11948 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11949 {
11950   return CheckGravityMovement(player);
11951
11952   if (player->gravity && !player->programmed_action)
11953   {
11954     int jx = player->jx, jy = player->jy;
11955     boolean field_under_player_is_free =
11956       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11957     boolean player_is_standing_on_valid_field =
11958       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11959        (IS_WALKABLE(Feld[jx][jy]) &&
11960         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11961
11962     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11963       player->programmed_action = MV_DOWN;
11964   }
11965 }
11966
11967 /*
11968   MovePlayerOneStep()
11969   -----------------------------------------------------------------------------
11970   dx, dy:               direction (non-diagonal) to try to move the player to
11971   real_dx, real_dy:     direction as read from input device (can be diagonal)
11972 */
11973
11974 boolean MovePlayerOneStep(struct PlayerInfo *player,
11975                           int dx, int dy, int real_dx, int real_dy)
11976 {
11977   int jx = player->jx, jy = player->jy;
11978   int new_jx = jx + dx, new_jy = jy + dy;
11979   int can_move;
11980   boolean player_can_move = !player->cannot_move;
11981
11982   if (!player->active || (!dx && !dy))
11983     return MP_NO_ACTION;
11984
11985   player->MovDir = (dx < 0 ? MV_LEFT :
11986                     dx > 0 ? MV_RIGHT :
11987                     dy < 0 ? MV_UP :
11988                     dy > 0 ? MV_DOWN :  MV_NONE);
11989
11990   if (!IN_LEV_FIELD(new_jx, new_jy))
11991     return MP_NO_ACTION;
11992
11993   if (!player_can_move)
11994   {
11995     if (player->MovPos == 0)
11996     {
11997       player->is_moving = FALSE;
11998       player->is_digging = FALSE;
11999       player->is_collecting = FALSE;
12000       player->is_snapping = FALSE;
12001       player->is_pushing = FALSE;
12002     }
12003   }
12004
12005   if (!options.network && game.centered_player_nr == -1 &&
12006       !AllPlayersInSight(player, new_jx, new_jy))
12007     return MP_NO_ACTION;
12008
12009   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12010   if (can_move != MP_MOVING)
12011     return can_move;
12012
12013   /* check if DigField() has caused relocation of the player */
12014   if (player->jx != jx || player->jy != jy)
12015     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12016
12017   StorePlayer[jx][jy] = 0;
12018   player->last_jx = jx;
12019   player->last_jy = jy;
12020   player->jx = new_jx;
12021   player->jy = new_jy;
12022   StorePlayer[new_jx][new_jy] = player->element_nr;
12023
12024   if (player->move_delay_value_next != -1)
12025   {
12026     player->move_delay_value = player->move_delay_value_next;
12027     player->move_delay_value_next = -1;
12028   }
12029
12030   player->MovPos =
12031     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12032
12033   player->step_counter++;
12034
12035   PlayerVisit[jx][jy] = FrameCounter;
12036
12037   player->is_moving = TRUE;
12038
12039 #if 1
12040   /* should better be called in MovePlayer(), but this breaks some tapes */
12041   ScrollPlayer(player, SCROLL_INIT);
12042 #endif
12043
12044   return MP_MOVING;
12045 }
12046
12047 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12048 {
12049   int jx = player->jx, jy = player->jy;
12050   int old_jx = jx, old_jy = jy;
12051   int moved = MP_NO_ACTION;
12052
12053   if (!player->active)
12054     return FALSE;
12055
12056   if (!dx && !dy)
12057   {
12058     if (player->MovPos == 0)
12059     {
12060       player->is_moving = FALSE;
12061       player->is_digging = FALSE;
12062       player->is_collecting = FALSE;
12063       player->is_snapping = FALSE;
12064       player->is_pushing = FALSE;
12065     }
12066
12067     return FALSE;
12068   }
12069
12070   if (player->move_delay > 0)
12071     return FALSE;
12072
12073   player->move_delay = -1;              /* set to "uninitialized" value */
12074
12075   /* store if player is automatically moved to next field */
12076   player->is_auto_moving = (player->programmed_action != MV_NONE);
12077
12078   /* remove the last programmed player action */
12079   player->programmed_action = 0;
12080
12081   if (player->MovPos)
12082   {
12083     /* should only happen if pre-1.2 tape recordings are played */
12084     /* this is only for backward compatibility */
12085
12086     int original_move_delay_value = player->move_delay_value;
12087
12088 #if DEBUG
12089     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12090            tape.counter);
12091 #endif
12092
12093     /* scroll remaining steps with finest movement resolution */
12094     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12095
12096     while (player->MovPos)
12097     {
12098       ScrollPlayer(player, SCROLL_GO_ON);
12099       ScrollScreen(NULL, SCROLL_GO_ON);
12100
12101       AdvanceFrameAndPlayerCounters(player->index_nr);
12102
12103       DrawAllPlayers();
12104       BackToFront_WithFrameDelay(0);
12105     }
12106
12107     player->move_delay_value = original_move_delay_value;
12108   }
12109
12110   player->is_active = FALSE;
12111
12112   if (player->last_move_dir & MV_HORIZONTAL)
12113   {
12114     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12115       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12116   }
12117   else
12118   {
12119     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12120       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12121   }
12122
12123   if (!moved && !player->is_active)
12124   {
12125     player->is_moving = FALSE;
12126     player->is_digging = FALSE;
12127     player->is_collecting = FALSE;
12128     player->is_snapping = FALSE;
12129     player->is_pushing = FALSE;
12130   }
12131
12132   jx = player->jx;
12133   jy = player->jy;
12134
12135   if (moved & MP_MOVING && !ScreenMovPos &&
12136       (player->index_nr == game.centered_player_nr ||
12137        game.centered_player_nr == -1))
12138   {
12139     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12140     int offset = game.scroll_delay_value;
12141
12142     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12143     {
12144       /* actual player has left the screen -- scroll in that direction */
12145       if (jx != old_jx)         /* player has moved horizontally */
12146         scroll_x += (jx - old_jx);
12147       else                      /* player has moved vertically */
12148         scroll_y += (jy - old_jy);
12149     }
12150     else
12151     {
12152       if (jx != old_jx)         /* player has moved horizontally */
12153       {
12154         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12155             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12156           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12157
12158         /* don't scroll over playfield boundaries */
12159         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12160           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12161
12162         /* don't scroll more than one field at a time */
12163         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12164
12165         /* don't scroll against the player's moving direction */
12166         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12167             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12168           scroll_x = old_scroll_x;
12169       }
12170       else                      /* player has moved vertically */
12171       {
12172         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12173             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12174           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12175
12176         /* don't scroll over playfield boundaries */
12177         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12178           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12179
12180         /* don't scroll more than one field at a time */
12181         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12182
12183         /* don't scroll against the player's moving direction */
12184         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12185             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12186           scroll_y = old_scroll_y;
12187       }
12188     }
12189
12190     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12191     {
12192       if (!options.network && game.centered_player_nr == -1 &&
12193           !AllPlayersInVisibleScreen())
12194       {
12195         scroll_x = old_scroll_x;
12196         scroll_y = old_scroll_y;
12197       }
12198       else
12199       {
12200         ScrollScreen(player, SCROLL_INIT);
12201         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12202       }
12203     }
12204   }
12205
12206   player->StepFrame = 0;
12207
12208   if (moved & MP_MOVING)
12209   {
12210     if (old_jx != jx && old_jy == jy)
12211       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12212     else if (old_jx == jx && old_jy != jy)
12213       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12214
12215     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12216
12217     player->last_move_dir = player->MovDir;
12218     player->is_moving = TRUE;
12219     player->is_snapping = FALSE;
12220     player->is_switching = FALSE;
12221     player->is_dropping = FALSE;
12222     player->is_dropping_pressed = FALSE;
12223     player->drop_pressed_delay = 0;
12224
12225 #if 0
12226     /* should better be called here than above, but this breaks some tapes */
12227     ScrollPlayer(player, SCROLL_INIT);
12228 #endif
12229   }
12230   else
12231   {
12232     CheckGravityMovementWhenNotMoving(player);
12233
12234     player->is_moving = FALSE;
12235
12236     /* at this point, the player is allowed to move, but cannot move right now
12237        (e.g. because of something blocking the way) -- ensure that the player
12238        is also allowed to move in the next frame (in old versions before 3.1.1,
12239        the player was forced to wait again for eight frames before next try) */
12240
12241     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12242       player->move_delay = 0;   /* allow direct movement in the next frame */
12243   }
12244
12245   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12246     player->move_delay = player->move_delay_value;
12247
12248   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12249   {
12250     TestIfPlayerTouchesBadThing(jx, jy);
12251     TestIfPlayerTouchesCustomElement(jx, jy);
12252   }
12253
12254   if (!player->active)
12255     RemovePlayer(player);
12256
12257   return moved;
12258 }
12259
12260 void ScrollPlayer(struct PlayerInfo *player, int mode)
12261 {
12262   int jx = player->jx, jy = player->jy;
12263   int last_jx = player->last_jx, last_jy = player->last_jy;
12264   int move_stepsize = TILEX / player->move_delay_value;
12265
12266   if (!player->active)
12267     return;
12268
12269   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12270     return;
12271
12272   if (mode == SCROLL_INIT)
12273   {
12274     player->actual_frame_counter = FrameCounter;
12275     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12276
12277     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12278         Feld[last_jx][last_jy] == EL_EMPTY)
12279     {
12280       int last_field_block_delay = 0;   /* start with no blocking at all */
12281       int block_delay_adjustment = player->block_delay_adjustment;
12282
12283       /* if player blocks last field, add delay for exactly one move */
12284       if (player->block_last_field)
12285       {
12286         last_field_block_delay += player->move_delay_value;
12287
12288         /* when blocking enabled, prevent moving up despite gravity */
12289         if (player->gravity && player->MovDir == MV_UP)
12290           block_delay_adjustment = -1;
12291       }
12292
12293       /* add block delay adjustment (also possible when not blocking) */
12294       last_field_block_delay += block_delay_adjustment;
12295
12296       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12297       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12298     }
12299
12300     if (player->MovPos != 0)    /* player has not yet reached destination */
12301       return;
12302   }
12303   else if (!FrameReached(&player->actual_frame_counter, 1))
12304     return;
12305
12306   if (player->MovPos != 0)
12307   {
12308     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12309     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12310
12311     /* before DrawPlayer() to draw correct player graphic for this case */
12312     if (player->MovPos == 0)
12313       CheckGravityMovement(player);
12314   }
12315
12316   if (player->MovPos == 0)      /* player reached destination field */
12317   {
12318     if (player->move_delay_reset_counter > 0)
12319     {
12320       player->move_delay_reset_counter--;
12321
12322       if (player->move_delay_reset_counter == 0)
12323       {
12324         /* continue with normal speed after quickly moving through gate */
12325         HALVE_PLAYER_SPEED(player);
12326
12327         /* be able to make the next move without delay */
12328         player->move_delay = 0;
12329       }
12330     }
12331
12332     player->last_jx = jx;
12333     player->last_jy = jy;
12334
12335     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12336         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12337         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12338         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12339         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12340         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12341         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12342         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12343     {
12344       DrawPlayer(player);       /* needed here only to cleanup last field */
12345       RemovePlayer(player);
12346
12347       if (local_player->friends_still_needed == 0 ||
12348           IS_SP_ELEMENT(Feld[jx][jy]))
12349         PlayerWins(player);
12350     }
12351
12352     /* this breaks one level: "machine", level 000 */
12353     {
12354       int move_direction = player->MovDir;
12355       int enter_side = MV_DIR_OPPOSITE(move_direction);
12356       int leave_side = move_direction;
12357       int old_jx = last_jx;
12358       int old_jy = last_jy;
12359       int old_element = Feld[old_jx][old_jy];
12360       int new_element = Feld[jx][jy];
12361
12362       if (IS_CUSTOM_ELEMENT(old_element))
12363         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12364                                    CE_LEFT_BY_PLAYER,
12365                                    player->index_bit, leave_side);
12366
12367       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12368                                           CE_PLAYER_LEAVES_X,
12369                                           player->index_bit, leave_side);
12370
12371       if (IS_CUSTOM_ELEMENT(new_element))
12372         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12373                                    player->index_bit, enter_side);
12374
12375       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12376                                           CE_PLAYER_ENTERS_X,
12377                                           player->index_bit, enter_side);
12378
12379       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12380                                         CE_MOVE_OF_X, move_direction);
12381     }
12382
12383     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12384     {
12385       TestIfPlayerTouchesBadThing(jx, jy);
12386       TestIfPlayerTouchesCustomElement(jx, jy);
12387
12388       /* needed because pushed element has not yet reached its destination,
12389          so it would trigger a change event at its previous field location */
12390       if (!player->is_pushing)
12391         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12392
12393       if (!player->active)
12394         RemovePlayer(player);
12395     }
12396
12397     if (!local_player->LevelSolved && level.use_step_counter)
12398     {
12399       int i;
12400
12401       TimePlayed++;
12402
12403       if (TimeLeft > 0)
12404       {
12405         TimeLeft--;
12406
12407         if (TimeLeft <= 10 && setup.time_limit)
12408           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12409
12410         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12411
12412         DisplayGameControlValues();
12413
12414         if (!TimeLeft && setup.time_limit)
12415           for (i = 0; i < MAX_PLAYERS; i++)
12416             KillPlayer(&stored_player[i]);
12417       }
12418       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12419       {
12420         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12421
12422         DisplayGameControlValues();
12423       }
12424     }
12425
12426     if (tape.single_step && tape.recording && !tape.pausing &&
12427         !player->programmed_action)
12428       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12429
12430     if (!player->programmed_action)
12431       CheckSaveEngineSnapshot(player);
12432   }
12433 }
12434
12435 void ScrollScreen(struct PlayerInfo *player, int mode)
12436 {
12437   static unsigned int screen_frame_counter = 0;
12438
12439   if (mode == SCROLL_INIT)
12440   {
12441     /* set scrolling step size according to actual player's moving speed */
12442     ScrollStepSize = TILEX / player->move_delay_value;
12443
12444     screen_frame_counter = FrameCounter;
12445     ScreenMovDir = player->MovDir;
12446     ScreenMovPos = player->MovPos;
12447     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12448     return;
12449   }
12450   else if (!FrameReached(&screen_frame_counter, 1))
12451     return;
12452
12453   if (ScreenMovPos)
12454   {
12455     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12456     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12457     redraw_mask |= REDRAW_FIELD;
12458   }
12459   else
12460     ScreenMovDir = MV_NONE;
12461 }
12462
12463 void TestIfPlayerTouchesCustomElement(int x, int y)
12464 {
12465   static int xy[4][2] =
12466   {
12467     { 0, -1 },
12468     { -1, 0 },
12469     { +1, 0 },
12470     { 0, +1 }
12471   };
12472   static int trigger_sides[4][2] =
12473   {
12474     /* center side       border side */
12475     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12476     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12477     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12478     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12479   };
12480   static int touch_dir[4] =
12481   {
12482     MV_LEFT | MV_RIGHT,
12483     MV_UP   | MV_DOWN,
12484     MV_UP   | MV_DOWN,
12485     MV_LEFT | MV_RIGHT
12486   };
12487   int center_element = Feld[x][y];      /* should always be non-moving! */
12488   int i;
12489
12490   for (i = 0; i < NUM_DIRECTIONS; i++)
12491   {
12492     int xx = x + xy[i][0];
12493     int yy = y + xy[i][1];
12494     int center_side = trigger_sides[i][0];
12495     int border_side = trigger_sides[i][1];
12496     int border_element;
12497
12498     if (!IN_LEV_FIELD(xx, yy))
12499       continue;
12500
12501     if (IS_PLAYER(x, y))                /* player found at center element */
12502     {
12503       struct PlayerInfo *player = PLAYERINFO(x, y);
12504
12505       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12506         border_element = Feld[xx][yy];          /* may be moving! */
12507       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12508         border_element = Feld[xx][yy];
12509       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12510         border_element = MovingOrBlocked2Element(xx, yy);
12511       else
12512         continue;               /* center and border element do not touch */
12513
12514       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12515                                  player->index_bit, border_side);
12516       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12517                                           CE_PLAYER_TOUCHES_X,
12518                                           player->index_bit, border_side);
12519
12520       {
12521         /* use player element that is initially defined in the level playfield,
12522            not the player element that corresponds to the runtime player number
12523            (example: a level that contains EL_PLAYER_3 as the only player would
12524            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12525         int player_element = PLAYERINFO(x, y)->initial_element;
12526
12527         CheckElementChangeBySide(xx, yy, border_element, player_element,
12528                                  CE_TOUCHING_X, border_side);
12529       }
12530     }
12531     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12532     {
12533       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12534
12535       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12536       {
12537         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12538           continue;             /* center and border element do not touch */
12539       }
12540
12541       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12542                                  player->index_bit, center_side);
12543       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12544                                           CE_PLAYER_TOUCHES_X,
12545                                           player->index_bit, center_side);
12546
12547       {
12548         /* use player element that is initially defined in the level playfield,
12549            not the player element that corresponds to the runtime player number
12550            (example: a level that contains EL_PLAYER_3 as the only player would
12551            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12552         int player_element = PLAYERINFO(xx, yy)->initial_element;
12553
12554         CheckElementChangeBySide(x, y, center_element, player_element,
12555                                  CE_TOUCHING_X, center_side);
12556       }
12557
12558       break;
12559     }
12560   }
12561 }
12562
12563 void TestIfElementTouchesCustomElement(int x, int y)
12564 {
12565   static int xy[4][2] =
12566   {
12567     { 0, -1 },
12568     { -1, 0 },
12569     { +1, 0 },
12570     { 0, +1 }
12571   };
12572   static int trigger_sides[4][2] =
12573   {
12574     /* center side      border side */
12575     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12576     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12577     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12578     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12579   };
12580   static int touch_dir[4] =
12581   {
12582     MV_LEFT | MV_RIGHT,
12583     MV_UP   | MV_DOWN,
12584     MV_UP   | MV_DOWN,
12585     MV_LEFT | MV_RIGHT
12586   };
12587   boolean change_center_element = FALSE;
12588   int center_element = Feld[x][y];      /* should always be non-moving! */
12589   int border_element_old[NUM_DIRECTIONS];
12590   int i;
12591
12592   for (i = 0; i < NUM_DIRECTIONS; i++)
12593   {
12594     int xx = x + xy[i][0];
12595     int yy = y + xy[i][1];
12596     int border_element;
12597
12598     border_element_old[i] = -1;
12599
12600     if (!IN_LEV_FIELD(xx, yy))
12601       continue;
12602
12603     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12604       border_element = Feld[xx][yy];    /* may be moving! */
12605     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12606       border_element = Feld[xx][yy];
12607     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12608       border_element = MovingOrBlocked2Element(xx, yy);
12609     else
12610       continue;                 /* center and border element do not touch */
12611
12612     border_element_old[i] = border_element;
12613   }
12614
12615   for (i = 0; i < NUM_DIRECTIONS; i++)
12616   {
12617     int xx = x + xy[i][0];
12618     int yy = y + xy[i][1];
12619     int center_side = trigger_sides[i][0];
12620     int border_element = border_element_old[i];
12621
12622     if (border_element == -1)
12623       continue;
12624
12625     /* check for change of border element */
12626     CheckElementChangeBySide(xx, yy, border_element, center_element,
12627                              CE_TOUCHING_X, center_side);
12628
12629     /* (center element cannot be player, so we dont have to check this here) */
12630   }
12631
12632   for (i = 0; i < NUM_DIRECTIONS; i++)
12633   {
12634     int xx = x + xy[i][0];
12635     int yy = y + xy[i][1];
12636     int border_side = trigger_sides[i][1];
12637     int border_element = border_element_old[i];
12638
12639     if (border_element == -1)
12640       continue;
12641
12642     /* check for change of center element (but change it only once) */
12643     if (!change_center_element)
12644       change_center_element =
12645         CheckElementChangeBySide(x, y, center_element, border_element,
12646                                  CE_TOUCHING_X, border_side);
12647
12648     if (IS_PLAYER(xx, yy))
12649     {
12650       /* use player element that is initially defined in the level playfield,
12651          not the player element that corresponds to the runtime player number
12652          (example: a level that contains EL_PLAYER_3 as the only player would
12653          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12654       int player_element = PLAYERINFO(xx, yy)->initial_element;
12655
12656       CheckElementChangeBySide(x, y, center_element, player_element,
12657                                CE_TOUCHING_X, border_side);
12658     }
12659   }
12660 }
12661
12662 void TestIfElementHitsCustomElement(int x, int y, int direction)
12663 {
12664   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12665   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12666   int hitx = x + dx, hity = y + dy;
12667   int hitting_element = Feld[x][y];
12668   int touched_element;
12669
12670   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12671     return;
12672
12673   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12674                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12675
12676   if (IN_LEV_FIELD(hitx, hity))
12677   {
12678     int opposite_direction = MV_DIR_OPPOSITE(direction);
12679     int hitting_side = direction;
12680     int touched_side = opposite_direction;
12681     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12682                           MovDir[hitx][hity] != direction ||
12683                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12684
12685     object_hit = TRUE;
12686
12687     if (object_hit)
12688     {
12689       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12690                                CE_HITTING_X, touched_side);
12691
12692       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12693                                CE_HIT_BY_X, hitting_side);
12694
12695       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12696                                CE_HIT_BY_SOMETHING, opposite_direction);
12697
12698       if (IS_PLAYER(hitx, hity))
12699       {
12700         /* use player element that is initially defined in the level playfield,
12701            not the player element that corresponds to the runtime player number
12702            (example: a level that contains EL_PLAYER_3 as the only player would
12703            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12704         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12705
12706         CheckElementChangeBySide(x, y, hitting_element, player_element,
12707                                  CE_HITTING_X, touched_side);
12708       }
12709     }
12710   }
12711
12712   /* "hitting something" is also true when hitting the playfield border */
12713   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12714                            CE_HITTING_SOMETHING, direction);
12715 }
12716
12717 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12718 {
12719   int i, kill_x = -1, kill_y = -1;
12720
12721   int bad_element = -1;
12722   static int test_xy[4][2] =
12723   {
12724     { 0, -1 },
12725     { -1, 0 },
12726     { +1, 0 },
12727     { 0, +1 }
12728   };
12729   static int test_dir[4] =
12730   {
12731     MV_UP,
12732     MV_LEFT,
12733     MV_RIGHT,
12734     MV_DOWN
12735   };
12736
12737   for (i = 0; i < NUM_DIRECTIONS; i++)
12738   {
12739     int test_x, test_y, test_move_dir, test_element;
12740
12741     test_x = good_x + test_xy[i][0];
12742     test_y = good_y + test_xy[i][1];
12743
12744     if (!IN_LEV_FIELD(test_x, test_y))
12745       continue;
12746
12747     test_move_dir =
12748       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12749
12750     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12751
12752     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12753        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12754     */
12755     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12756         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12757     {
12758       kill_x = test_x;
12759       kill_y = test_y;
12760       bad_element = test_element;
12761
12762       break;
12763     }
12764   }
12765
12766   if (kill_x != -1 || kill_y != -1)
12767   {
12768     if (IS_PLAYER(good_x, good_y))
12769     {
12770       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12771
12772       if (player->shield_deadly_time_left > 0 &&
12773           !IS_INDESTRUCTIBLE(bad_element))
12774         Bang(kill_x, kill_y);
12775       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12776         KillPlayer(player);
12777     }
12778     else
12779       Bang(good_x, good_y);
12780   }
12781 }
12782
12783 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12784 {
12785   int i, kill_x = -1, kill_y = -1;
12786   int bad_element = Feld[bad_x][bad_y];
12787   static int test_xy[4][2] =
12788   {
12789     { 0, -1 },
12790     { -1, 0 },
12791     { +1, 0 },
12792     { 0, +1 }
12793   };
12794   static int touch_dir[4] =
12795   {
12796     MV_LEFT | MV_RIGHT,
12797     MV_UP   | MV_DOWN,
12798     MV_UP   | MV_DOWN,
12799     MV_LEFT | MV_RIGHT
12800   };
12801   static int test_dir[4] =
12802   {
12803     MV_UP,
12804     MV_LEFT,
12805     MV_RIGHT,
12806     MV_DOWN
12807   };
12808
12809   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12810     return;
12811
12812   for (i = 0; i < NUM_DIRECTIONS; i++)
12813   {
12814     int test_x, test_y, test_move_dir, test_element;
12815
12816     test_x = bad_x + test_xy[i][0];
12817     test_y = bad_y + test_xy[i][1];
12818
12819     if (!IN_LEV_FIELD(test_x, test_y))
12820       continue;
12821
12822     test_move_dir =
12823       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12824
12825     test_element = Feld[test_x][test_y];
12826
12827     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12828        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12829     */
12830     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12831         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12832     {
12833       /* good thing is player or penguin that does not move away */
12834       if (IS_PLAYER(test_x, test_y))
12835       {
12836         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12837
12838         if (bad_element == EL_ROBOT && player->is_moving)
12839           continue;     /* robot does not kill player if he is moving */
12840
12841         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12842         {
12843           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12844             continue;           /* center and border element do not touch */
12845         }
12846
12847         kill_x = test_x;
12848         kill_y = test_y;
12849
12850         break;
12851       }
12852       else if (test_element == EL_PENGUIN)
12853       {
12854         kill_x = test_x;
12855         kill_y = test_y;
12856
12857         break;
12858       }
12859     }
12860   }
12861
12862   if (kill_x != -1 || kill_y != -1)
12863   {
12864     if (IS_PLAYER(kill_x, kill_y))
12865     {
12866       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12867
12868       if (player->shield_deadly_time_left > 0 &&
12869           !IS_INDESTRUCTIBLE(bad_element))
12870         Bang(bad_x, bad_y);
12871       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12872         KillPlayer(player);
12873     }
12874     else
12875       Bang(kill_x, kill_y);
12876   }
12877 }
12878
12879 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12880 {
12881   int bad_element = Feld[bad_x][bad_y];
12882   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12883   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12884   int test_x = bad_x + dx, test_y = bad_y + dy;
12885   int test_move_dir, test_element;
12886   int kill_x = -1, kill_y = -1;
12887
12888   if (!IN_LEV_FIELD(test_x, test_y))
12889     return;
12890
12891   test_move_dir =
12892     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12893
12894   test_element = Feld[test_x][test_y];
12895
12896   if (test_move_dir != bad_move_dir)
12897   {
12898     /* good thing can be player or penguin that does not move away */
12899     if (IS_PLAYER(test_x, test_y))
12900     {
12901       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12902
12903       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12904          player as being hit when he is moving towards the bad thing, because
12905          the "get hit by" condition would be lost after the player stops) */
12906       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12907         return;         /* player moves away from bad thing */
12908
12909       kill_x = test_x;
12910       kill_y = test_y;
12911     }
12912     else if (test_element == EL_PENGUIN)
12913     {
12914       kill_x = test_x;
12915       kill_y = test_y;
12916     }
12917   }
12918
12919   if (kill_x != -1 || kill_y != -1)
12920   {
12921     if (IS_PLAYER(kill_x, kill_y))
12922     {
12923       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12924
12925       if (player->shield_deadly_time_left > 0 &&
12926           !IS_INDESTRUCTIBLE(bad_element))
12927         Bang(bad_x, bad_y);
12928       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12929         KillPlayer(player);
12930     }
12931     else
12932       Bang(kill_x, kill_y);
12933   }
12934 }
12935
12936 void TestIfPlayerTouchesBadThing(int x, int y)
12937 {
12938   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12939 }
12940
12941 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12942 {
12943   TestIfGoodThingHitsBadThing(x, y, move_dir);
12944 }
12945
12946 void TestIfBadThingTouchesPlayer(int x, int y)
12947 {
12948   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12949 }
12950
12951 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12952 {
12953   TestIfBadThingHitsGoodThing(x, y, move_dir);
12954 }
12955
12956 void TestIfFriendTouchesBadThing(int x, int y)
12957 {
12958   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12959 }
12960
12961 void TestIfBadThingTouchesFriend(int x, int y)
12962 {
12963   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12964 }
12965
12966 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12967 {
12968   int i, kill_x = bad_x, kill_y = bad_y;
12969   static int xy[4][2] =
12970   {
12971     { 0, -1 },
12972     { -1, 0 },
12973     { +1, 0 },
12974     { 0, +1 }
12975   };
12976
12977   for (i = 0; i < NUM_DIRECTIONS; i++)
12978   {
12979     int x, y, element;
12980
12981     x = bad_x + xy[i][0];
12982     y = bad_y + xy[i][1];
12983     if (!IN_LEV_FIELD(x, y))
12984       continue;
12985
12986     element = Feld[x][y];
12987     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12988         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12989     {
12990       kill_x = x;
12991       kill_y = y;
12992       break;
12993     }
12994   }
12995
12996   if (kill_x != bad_x || kill_y != bad_y)
12997     Bang(bad_x, bad_y);
12998 }
12999
13000 void KillPlayer(struct PlayerInfo *player)
13001 {
13002   int jx = player->jx, jy = player->jy;
13003
13004   if (!player->active)
13005     return;
13006
13007 #if 0
13008   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13009          player->killed, player->active, player->reanimated);
13010 #endif
13011
13012   /* the following code was introduced to prevent an infinite loop when calling
13013      -> Bang()
13014      -> CheckTriggeredElementChangeExt()
13015      -> ExecuteCustomElementAction()
13016      -> KillPlayer()
13017      -> (infinitely repeating the above sequence of function calls)
13018      which occurs when killing the player while having a CE with the setting
13019      "kill player X when explosion of <player X>"; the solution using a new
13020      field "player->killed" was chosen for backwards compatibility, although
13021      clever use of the fields "player->active" etc. would probably also work */
13022 #if 1
13023   if (player->killed)
13024     return;
13025 #endif
13026
13027   player->killed = TRUE;
13028
13029   /* remove accessible field at the player's position */
13030   Feld[jx][jy] = EL_EMPTY;
13031
13032   /* deactivate shield (else Bang()/Explode() would not work right) */
13033   player->shield_normal_time_left = 0;
13034   player->shield_deadly_time_left = 0;
13035
13036 #if 0
13037   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13038          player->killed, player->active, player->reanimated);
13039 #endif
13040
13041   Bang(jx, jy);
13042
13043 #if 0
13044   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13045          player->killed, player->active, player->reanimated);
13046 #endif
13047
13048   if (player->reanimated)       /* killed player may have been reanimated */
13049     player->killed = player->reanimated = FALSE;
13050   else
13051     BuryPlayer(player);
13052 }
13053
13054 static void KillPlayerUnlessEnemyProtected(int x, int y)
13055 {
13056   if (!PLAYER_ENEMY_PROTECTED(x, y))
13057     KillPlayer(PLAYERINFO(x, y));
13058 }
13059
13060 static void KillPlayerUnlessExplosionProtected(int x, int y)
13061 {
13062   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13063     KillPlayer(PLAYERINFO(x, y));
13064 }
13065
13066 void BuryPlayer(struct PlayerInfo *player)
13067 {
13068   int jx = player->jx, jy = player->jy;
13069
13070   if (!player->active)
13071     return;
13072
13073   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13074   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13075
13076   player->GameOver = TRUE;
13077   RemovePlayer(player);
13078 }
13079
13080 void RemovePlayer(struct PlayerInfo *player)
13081 {
13082   int jx = player->jx, jy = player->jy;
13083   int i, found = FALSE;
13084
13085   player->present = FALSE;
13086   player->active = FALSE;
13087
13088   if (!ExplodeField[jx][jy])
13089     StorePlayer[jx][jy] = 0;
13090
13091   if (player->is_moving)
13092     TEST_DrawLevelField(player->last_jx, player->last_jy);
13093
13094   for (i = 0; i < MAX_PLAYERS; i++)
13095     if (stored_player[i].active)
13096       found = TRUE;
13097
13098   if (!found)
13099     AllPlayersGone = TRUE;
13100
13101   ExitX = ZX = jx;
13102   ExitY = ZY = jy;
13103 }
13104
13105 static void setFieldForSnapping(int x, int y, int element, int direction)
13106 {
13107   struct ElementInfo *ei = &element_info[element];
13108   int direction_bit = MV_DIR_TO_BIT(direction);
13109   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13110   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13111                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13112
13113   Feld[x][y] = EL_ELEMENT_SNAPPING;
13114   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13115
13116   ResetGfxAnimation(x, y);
13117
13118   GfxElement[x][y] = element;
13119   GfxAction[x][y] = action;
13120   GfxDir[x][y] = direction;
13121   GfxFrame[x][y] = -1;
13122 }
13123
13124 /*
13125   =============================================================================
13126   checkDiagonalPushing()
13127   -----------------------------------------------------------------------------
13128   check if diagonal input device direction results in pushing of object
13129   (by checking if the alternative direction is walkable, diggable, ...)
13130   =============================================================================
13131 */
13132
13133 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13134                                     int x, int y, int real_dx, int real_dy)
13135 {
13136   int jx, jy, dx, dy, xx, yy;
13137
13138   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13139     return TRUE;
13140
13141   /* diagonal direction: check alternative direction */
13142   jx = player->jx;
13143   jy = player->jy;
13144   dx = x - jx;
13145   dy = y - jy;
13146   xx = jx + (dx == 0 ? real_dx : 0);
13147   yy = jy + (dy == 0 ? real_dy : 0);
13148
13149   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13150 }
13151
13152 /*
13153   =============================================================================
13154   DigField()
13155   -----------------------------------------------------------------------------
13156   x, y:                 field next to player (non-diagonal) to try to dig to
13157   real_dx, real_dy:     direction as read from input device (can be diagonal)
13158   =============================================================================
13159 */
13160
13161 static int DigField(struct PlayerInfo *player,
13162                     int oldx, int oldy, int x, int y,
13163                     int real_dx, int real_dy, int mode)
13164 {
13165   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13166   boolean player_was_pushing = player->is_pushing;
13167   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13168   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13169   int jx = oldx, jy = oldy;
13170   int dx = x - jx, dy = y - jy;
13171   int nextx = x + dx, nexty = y + dy;
13172   int move_direction = (dx == -1 ? MV_LEFT  :
13173                         dx == +1 ? MV_RIGHT :
13174                         dy == -1 ? MV_UP    :
13175                         dy == +1 ? MV_DOWN  : MV_NONE);
13176   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13177   int dig_side = MV_DIR_OPPOSITE(move_direction);
13178   int old_element = Feld[jx][jy];
13179   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13180   int collect_count;
13181
13182   if (is_player)                /* function can also be called by EL_PENGUIN */
13183   {
13184     if (player->MovPos == 0)
13185     {
13186       player->is_digging = FALSE;
13187       player->is_collecting = FALSE;
13188     }
13189
13190     if (player->MovPos == 0)    /* last pushing move finished */
13191       player->is_pushing = FALSE;
13192
13193     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13194     {
13195       player->is_switching = FALSE;
13196       player->push_delay = -1;
13197
13198       return MP_NO_ACTION;
13199     }
13200   }
13201
13202   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13203     old_element = Back[jx][jy];
13204
13205   /* in case of element dropped at player position, check background */
13206   else if (Back[jx][jy] != EL_EMPTY &&
13207            game.engine_version >= VERSION_IDENT(2,2,0,0))
13208     old_element = Back[jx][jy];
13209
13210   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13211     return MP_NO_ACTION;        /* field has no opening in this direction */
13212
13213   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13214     return MP_NO_ACTION;        /* field has no opening in this direction */
13215
13216   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13217   {
13218     SplashAcid(x, y);
13219
13220     Feld[jx][jy] = player->artwork_element;
13221     InitMovingField(jx, jy, MV_DOWN);
13222     Store[jx][jy] = EL_ACID;
13223     ContinueMoving(jx, jy);
13224     BuryPlayer(player);
13225
13226     return MP_DONT_RUN_INTO;
13227   }
13228
13229   if (player_can_move && DONT_RUN_INTO(element))
13230   {
13231     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13232
13233     return MP_DONT_RUN_INTO;
13234   }
13235
13236   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13237     return MP_NO_ACTION;
13238
13239   collect_count = element_info[element].collect_count_initial;
13240
13241   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13242     return MP_NO_ACTION;
13243
13244   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13245     player_can_move = player_can_move_or_snap;
13246
13247   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13248       game.engine_version >= VERSION_IDENT(2,2,0,0))
13249   {
13250     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13251                                player->index_bit, dig_side);
13252     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13253                                         player->index_bit, dig_side);
13254
13255     if (element == EL_DC_LANDMINE)
13256       Bang(x, y);
13257
13258     if (Feld[x][y] != element)          /* field changed by snapping */
13259       return MP_ACTION;
13260
13261     return MP_NO_ACTION;
13262   }
13263
13264   if (player->gravity && is_player && !player->is_auto_moving &&
13265       canFallDown(player) && move_direction != MV_DOWN &&
13266       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13267     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13268
13269   if (player_can_move &&
13270       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13271   {
13272     int sound_element = SND_ELEMENT(element);
13273     int sound_action = ACTION_WALKING;
13274
13275     if (IS_RND_GATE(element))
13276     {
13277       if (!player->key[RND_GATE_NR(element)])
13278         return MP_NO_ACTION;
13279     }
13280     else if (IS_RND_GATE_GRAY(element))
13281     {
13282       if (!player->key[RND_GATE_GRAY_NR(element)])
13283         return MP_NO_ACTION;
13284     }
13285     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13286     {
13287       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13288         return MP_NO_ACTION;
13289     }
13290     else if (element == EL_EXIT_OPEN ||
13291              element == EL_EM_EXIT_OPEN ||
13292              element == EL_EM_EXIT_OPENING ||
13293              element == EL_STEEL_EXIT_OPEN ||
13294              element == EL_EM_STEEL_EXIT_OPEN ||
13295              element == EL_EM_STEEL_EXIT_OPENING ||
13296              element == EL_SP_EXIT_OPEN ||
13297              element == EL_SP_EXIT_OPENING)
13298     {
13299       sound_action = ACTION_PASSING;    /* player is passing exit */
13300     }
13301     else if (element == EL_EMPTY)
13302     {
13303       sound_action = ACTION_MOVING;             /* nothing to walk on */
13304     }
13305
13306     /* play sound from background or player, whatever is available */
13307     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13308       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13309     else
13310       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13311   }
13312   else if (player_can_move &&
13313            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13314   {
13315     if (!ACCESS_FROM(element, opposite_direction))
13316       return MP_NO_ACTION;      /* field not accessible from this direction */
13317
13318     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13319       return MP_NO_ACTION;
13320
13321     if (IS_EM_GATE(element))
13322     {
13323       if (!player->key[EM_GATE_NR(element)])
13324         return MP_NO_ACTION;
13325     }
13326     else if (IS_EM_GATE_GRAY(element))
13327     {
13328       if (!player->key[EM_GATE_GRAY_NR(element)])
13329         return MP_NO_ACTION;
13330     }
13331     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13332     {
13333       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13334         return MP_NO_ACTION;
13335     }
13336     else if (IS_EMC_GATE(element))
13337     {
13338       if (!player->key[EMC_GATE_NR(element)])
13339         return MP_NO_ACTION;
13340     }
13341     else if (IS_EMC_GATE_GRAY(element))
13342     {
13343       if (!player->key[EMC_GATE_GRAY_NR(element)])
13344         return MP_NO_ACTION;
13345     }
13346     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13347     {
13348       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13349         return MP_NO_ACTION;
13350     }
13351     else if (element == EL_DC_GATE_WHITE ||
13352              element == EL_DC_GATE_WHITE_GRAY ||
13353              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13354     {
13355       if (player->num_white_keys == 0)
13356         return MP_NO_ACTION;
13357
13358       player->num_white_keys--;
13359     }
13360     else if (IS_SP_PORT(element))
13361     {
13362       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13363           element == EL_SP_GRAVITY_PORT_RIGHT ||
13364           element == EL_SP_GRAVITY_PORT_UP ||
13365           element == EL_SP_GRAVITY_PORT_DOWN)
13366         player->gravity = !player->gravity;
13367       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13368                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13369                element == EL_SP_GRAVITY_ON_PORT_UP ||
13370                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13371         player->gravity = TRUE;
13372       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13373                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13374                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13375                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13376         player->gravity = FALSE;
13377     }
13378
13379     /* automatically move to the next field with double speed */
13380     player->programmed_action = move_direction;
13381
13382     if (player->move_delay_reset_counter == 0)
13383     {
13384       player->move_delay_reset_counter = 2;     /* two double speed steps */
13385
13386       DOUBLE_PLAYER_SPEED(player);
13387     }
13388
13389     PlayLevelSoundAction(x, y, ACTION_PASSING);
13390   }
13391   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13392   {
13393     RemoveField(x, y);
13394
13395     if (mode != DF_SNAP)
13396     {
13397       GfxElement[x][y] = GFX_ELEMENT(element);
13398       player->is_digging = TRUE;
13399     }
13400
13401     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13402
13403     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13404                                         player->index_bit, dig_side);
13405
13406     if (mode == DF_SNAP)
13407     {
13408       if (level.block_snap_field)
13409         setFieldForSnapping(x, y, element, move_direction);
13410       else
13411         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13412
13413       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13414                                           player->index_bit, dig_side);
13415     }
13416   }
13417   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13418   {
13419     RemoveField(x, y);
13420
13421     if (is_player && mode != DF_SNAP)
13422     {
13423       GfxElement[x][y] = element;
13424       player->is_collecting = TRUE;
13425     }
13426
13427     if (element == EL_SPEED_PILL)
13428     {
13429       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13430     }
13431     else if (element == EL_EXTRA_TIME && level.time > 0)
13432     {
13433       TimeLeft += level.extra_time;
13434
13435       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13436
13437       DisplayGameControlValues();
13438     }
13439     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13440     {
13441       player->shield_normal_time_left += level.shield_normal_time;
13442       if (element == EL_SHIELD_DEADLY)
13443         player->shield_deadly_time_left += level.shield_deadly_time;
13444     }
13445     else if (element == EL_DYNAMITE ||
13446              element == EL_EM_DYNAMITE ||
13447              element == EL_SP_DISK_RED)
13448     {
13449       if (player->inventory_size < MAX_INVENTORY_SIZE)
13450         player->inventory_element[player->inventory_size++] = element;
13451
13452       DrawGameDoorValues();
13453     }
13454     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13455     {
13456       player->dynabomb_count++;
13457       player->dynabombs_left++;
13458     }
13459     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13460     {
13461       player->dynabomb_size++;
13462     }
13463     else if (element == EL_DYNABOMB_INCREASE_POWER)
13464     {
13465       player->dynabomb_xl = TRUE;
13466     }
13467     else if (IS_KEY(element))
13468     {
13469       player->key[KEY_NR(element)] = TRUE;
13470
13471       DrawGameDoorValues();
13472     }
13473     else if (element == EL_DC_KEY_WHITE)
13474     {
13475       player->num_white_keys++;
13476
13477       /* display white keys? */
13478       /* DrawGameDoorValues(); */
13479     }
13480     else if (IS_ENVELOPE(element))
13481     {
13482       player->show_envelope = element;
13483     }
13484     else if (element == EL_EMC_LENSES)
13485     {
13486       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13487
13488       RedrawAllInvisibleElementsForLenses();
13489     }
13490     else if (element == EL_EMC_MAGNIFIER)
13491     {
13492       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13493
13494       RedrawAllInvisibleElementsForMagnifier();
13495     }
13496     else if (IS_DROPPABLE(element) ||
13497              IS_THROWABLE(element))     /* can be collected and dropped */
13498     {
13499       int i;
13500
13501       if (collect_count == 0)
13502         player->inventory_infinite_element = element;
13503       else
13504         for (i = 0; i < collect_count; i++)
13505           if (player->inventory_size < MAX_INVENTORY_SIZE)
13506             player->inventory_element[player->inventory_size++] = element;
13507
13508       DrawGameDoorValues();
13509     }
13510     else if (collect_count > 0)
13511     {
13512       local_player->gems_still_needed -= collect_count;
13513       if (local_player->gems_still_needed < 0)
13514         local_player->gems_still_needed = 0;
13515
13516       game.snapshot.collected_item = TRUE;
13517
13518       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13519
13520       DisplayGameControlValues();
13521     }
13522
13523     RaiseScoreElement(element);
13524     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13525
13526     if (is_player)
13527       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13528                                           player->index_bit, dig_side);
13529
13530     if (mode == DF_SNAP)
13531     {
13532       if (level.block_snap_field)
13533         setFieldForSnapping(x, y, element, move_direction);
13534       else
13535         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13536
13537       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13538                                           player->index_bit, dig_side);
13539     }
13540   }
13541   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13542   {
13543     if (mode == DF_SNAP && element != EL_BD_ROCK)
13544       return MP_NO_ACTION;
13545
13546     if (CAN_FALL(element) && dy)
13547       return MP_NO_ACTION;
13548
13549     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13550         !(element == EL_SPRING && level.use_spring_bug))
13551       return MP_NO_ACTION;
13552
13553     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13554         ((move_direction & MV_VERTICAL &&
13555           ((element_info[element].move_pattern & MV_LEFT &&
13556             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13557            (element_info[element].move_pattern & MV_RIGHT &&
13558             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13559          (move_direction & MV_HORIZONTAL &&
13560           ((element_info[element].move_pattern & MV_UP &&
13561             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13562            (element_info[element].move_pattern & MV_DOWN &&
13563             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13564       return MP_NO_ACTION;
13565
13566     /* do not push elements already moving away faster than player */
13567     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13568         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13569       return MP_NO_ACTION;
13570
13571     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13572     {
13573       if (player->push_delay_value == -1 || !player_was_pushing)
13574         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13575     }
13576     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13577     {
13578       if (player->push_delay_value == -1)
13579         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13580     }
13581     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13582     {
13583       if (!player->is_pushing)
13584         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13585     }
13586
13587     player->is_pushing = TRUE;
13588     player->is_active = TRUE;
13589
13590     if (!(IN_LEV_FIELD(nextx, nexty) &&
13591           (IS_FREE(nextx, nexty) ||
13592            (IS_SB_ELEMENT(element) &&
13593             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13594            (IS_CUSTOM_ELEMENT(element) &&
13595             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13596       return MP_NO_ACTION;
13597
13598     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13599       return MP_NO_ACTION;
13600
13601     if (player->push_delay == -1)       /* new pushing; restart delay */
13602       player->push_delay = 0;
13603
13604     if (player->push_delay < player->push_delay_value &&
13605         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13606         element != EL_SPRING && element != EL_BALLOON)
13607     {
13608       /* make sure that there is no move delay before next try to push */
13609       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13610         player->move_delay = 0;
13611
13612       return MP_NO_ACTION;
13613     }
13614
13615     if (IS_CUSTOM_ELEMENT(element) &&
13616         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13617     {
13618       if (!DigFieldByCE(nextx, nexty, element))
13619         return MP_NO_ACTION;
13620     }
13621
13622     if (IS_SB_ELEMENT(element))
13623     {
13624       if (element == EL_SOKOBAN_FIELD_FULL)
13625       {
13626         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13627         local_player->sokobanfields_still_needed++;
13628       }
13629
13630       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13631       {
13632         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13633         local_player->sokobanfields_still_needed--;
13634       }
13635
13636       Feld[x][y] = EL_SOKOBAN_OBJECT;
13637
13638       if (Back[x][y] == Back[nextx][nexty])
13639         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13640       else if (Back[x][y] != 0)
13641         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13642                                     ACTION_EMPTYING);
13643       else
13644         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13645                                     ACTION_FILLING);
13646
13647       if (local_player->sokobanfields_still_needed == 0 &&
13648           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13649       {
13650         PlayerWins(player);
13651
13652         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13653       }
13654     }
13655     else
13656       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13657
13658     InitMovingField(x, y, move_direction);
13659     GfxAction[x][y] = ACTION_PUSHING;
13660
13661     if (mode == DF_SNAP)
13662       ContinueMoving(x, y);
13663     else
13664       MovPos[x][y] = (dx != 0 ? dx : dy);
13665
13666     Pushed[x][y] = TRUE;
13667     Pushed[nextx][nexty] = TRUE;
13668
13669     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13670       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13671     else
13672       player->push_delay_value = -1;    /* get new value later */
13673
13674     /* check for element change _after_ element has been pushed */
13675     if (game.use_change_when_pushing_bug)
13676     {
13677       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13678                                  player->index_bit, dig_side);
13679       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13680                                           player->index_bit, dig_side);
13681     }
13682   }
13683   else if (IS_SWITCHABLE(element))
13684   {
13685     if (PLAYER_SWITCHING(player, x, y))
13686     {
13687       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13688                                           player->index_bit, dig_side);
13689
13690       return MP_ACTION;
13691     }
13692
13693     player->is_switching = TRUE;
13694     player->switch_x = x;
13695     player->switch_y = y;
13696
13697     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13698
13699     if (element == EL_ROBOT_WHEEL)
13700     {
13701       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13702       ZX = x;
13703       ZY = y;
13704
13705       game.robot_wheel_active = TRUE;
13706
13707       TEST_DrawLevelField(x, y);
13708     }
13709     else if (element == EL_SP_TERMINAL)
13710     {
13711       int xx, yy;
13712
13713       SCAN_PLAYFIELD(xx, yy)
13714       {
13715         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13716         {
13717           Bang(xx, yy);
13718         }
13719         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13720         {
13721           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13722
13723           ResetGfxAnimation(xx, yy);
13724           TEST_DrawLevelField(xx, yy);
13725         }
13726       }
13727     }
13728     else if (IS_BELT_SWITCH(element))
13729     {
13730       ToggleBeltSwitch(x, y);
13731     }
13732     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13733              element == EL_SWITCHGATE_SWITCH_DOWN ||
13734              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13735              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13736     {
13737       ToggleSwitchgateSwitch(x, y);
13738     }
13739     else if (element == EL_LIGHT_SWITCH ||
13740              element == EL_LIGHT_SWITCH_ACTIVE)
13741     {
13742       ToggleLightSwitch(x, y);
13743     }
13744     else if (element == EL_TIMEGATE_SWITCH ||
13745              element == EL_DC_TIMEGATE_SWITCH)
13746     {
13747       ActivateTimegateSwitch(x, y);
13748     }
13749     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13750              element == EL_BALLOON_SWITCH_RIGHT ||
13751              element == EL_BALLOON_SWITCH_UP    ||
13752              element == EL_BALLOON_SWITCH_DOWN  ||
13753              element == EL_BALLOON_SWITCH_NONE  ||
13754              element == EL_BALLOON_SWITCH_ANY)
13755     {
13756       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13757                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13758                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13759                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13760                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13761                              move_direction);
13762     }
13763     else if (element == EL_LAMP)
13764     {
13765       Feld[x][y] = EL_LAMP_ACTIVE;
13766       local_player->lights_still_needed--;
13767
13768       ResetGfxAnimation(x, y);
13769       TEST_DrawLevelField(x, y);
13770     }
13771     else if (element == EL_TIME_ORB_FULL)
13772     {
13773       Feld[x][y] = EL_TIME_ORB_EMPTY;
13774
13775       if (level.time > 0 || level.use_time_orb_bug)
13776       {
13777         TimeLeft += level.time_orb_time;
13778         game.no_time_limit = FALSE;
13779
13780         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13781
13782         DisplayGameControlValues();
13783       }
13784
13785       ResetGfxAnimation(x, y);
13786       TEST_DrawLevelField(x, y);
13787     }
13788     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13789              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13790     {
13791       int xx, yy;
13792
13793       game.ball_state = !game.ball_state;
13794
13795       SCAN_PLAYFIELD(xx, yy)
13796       {
13797         int e = Feld[xx][yy];
13798
13799         if (game.ball_state)
13800         {
13801           if (e == EL_EMC_MAGIC_BALL)
13802             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13803           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13804             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13805         }
13806         else
13807         {
13808           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13809             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13810           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13811             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13812         }
13813       }
13814     }
13815
13816     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13817                                         player->index_bit, dig_side);
13818
13819     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13820                                         player->index_bit, dig_side);
13821
13822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13823                                         player->index_bit, dig_side);
13824
13825     return MP_ACTION;
13826   }
13827   else
13828   {
13829     if (!PLAYER_SWITCHING(player, x, y))
13830     {
13831       player->is_switching = TRUE;
13832       player->switch_x = x;
13833       player->switch_y = y;
13834
13835       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13836                                  player->index_bit, dig_side);
13837       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13838                                           player->index_bit, dig_side);
13839
13840       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13841                                  player->index_bit, dig_side);
13842       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13843                                           player->index_bit, dig_side);
13844     }
13845
13846     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13847                                player->index_bit, dig_side);
13848     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13849                                         player->index_bit, dig_side);
13850
13851     return MP_NO_ACTION;
13852   }
13853
13854   player->push_delay = -1;
13855
13856   if (is_player)                /* function can also be called by EL_PENGUIN */
13857   {
13858     if (Feld[x][y] != element)          /* really digged/collected something */
13859     {
13860       player->is_collecting = !player->is_digging;
13861       player->is_active = TRUE;
13862     }
13863   }
13864
13865   return MP_MOVING;
13866 }
13867
13868 static boolean DigFieldByCE(int x, int y, int digging_element)
13869 {
13870   int element = Feld[x][y];
13871
13872   if (!IS_FREE(x, y))
13873   {
13874     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13875                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13876                   ACTION_BREAKING);
13877
13878     /* no element can dig solid indestructible elements */
13879     if (IS_INDESTRUCTIBLE(element) &&
13880         !IS_DIGGABLE(element) &&
13881         !IS_COLLECTIBLE(element))
13882       return FALSE;
13883
13884     if (AmoebaNr[x][y] &&
13885         (element == EL_AMOEBA_FULL ||
13886          element == EL_BD_AMOEBA ||
13887          element == EL_AMOEBA_GROWING))
13888     {
13889       AmoebaCnt[AmoebaNr[x][y]]--;
13890       AmoebaCnt2[AmoebaNr[x][y]]--;
13891     }
13892
13893     if (IS_MOVING(x, y))
13894       RemoveMovingField(x, y);
13895     else
13896     {
13897       RemoveField(x, y);
13898       TEST_DrawLevelField(x, y);
13899     }
13900
13901     /* if digged element was about to explode, prevent the explosion */
13902     ExplodeField[x][y] = EX_TYPE_NONE;
13903
13904     PlayLevelSoundAction(x, y, action);
13905   }
13906
13907   Store[x][y] = EL_EMPTY;
13908
13909   /* this makes it possible to leave the removed element again */
13910   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13911     Store[x][y] = element;
13912
13913   return TRUE;
13914 }
13915
13916 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13917 {
13918   int jx = player->jx, jy = player->jy;
13919   int x = jx + dx, y = jy + dy;
13920   int snap_direction = (dx == -1 ? MV_LEFT  :
13921                         dx == +1 ? MV_RIGHT :
13922                         dy == -1 ? MV_UP    :
13923                         dy == +1 ? MV_DOWN  : MV_NONE);
13924   boolean can_continue_snapping = (level.continuous_snapping &&
13925                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13926
13927   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13928     return FALSE;
13929
13930   if (!player->active || !IN_LEV_FIELD(x, y))
13931     return FALSE;
13932
13933   if (dx && dy)
13934     return FALSE;
13935
13936   if (!dx && !dy)
13937   {
13938     if (player->MovPos == 0)
13939       player->is_pushing = FALSE;
13940
13941     player->is_snapping = FALSE;
13942
13943     if (player->MovPos == 0)
13944     {
13945       player->is_moving = FALSE;
13946       player->is_digging = FALSE;
13947       player->is_collecting = FALSE;
13948     }
13949
13950     return FALSE;
13951   }
13952
13953   /* prevent snapping with already pressed snap key when not allowed */
13954   if (player->is_snapping && !can_continue_snapping)
13955     return FALSE;
13956
13957   player->MovDir = snap_direction;
13958
13959   if (player->MovPos == 0)
13960   {
13961     player->is_moving = FALSE;
13962     player->is_digging = FALSE;
13963     player->is_collecting = FALSE;
13964   }
13965
13966   player->is_dropping = FALSE;
13967   player->is_dropping_pressed = FALSE;
13968   player->drop_pressed_delay = 0;
13969
13970   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13971     return FALSE;
13972
13973   player->is_snapping = TRUE;
13974   player->is_active = TRUE;
13975
13976   if (player->MovPos == 0)
13977   {
13978     player->is_moving = FALSE;
13979     player->is_digging = FALSE;
13980     player->is_collecting = FALSE;
13981   }
13982
13983   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13984     TEST_DrawLevelField(player->last_jx, player->last_jy);
13985
13986   TEST_DrawLevelField(x, y);
13987
13988   return TRUE;
13989 }
13990
13991 static boolean DropElement(struct PlayerInfo *player)
13992 {
13993   int old_element, new_element;
13994   int dropx = player->jx, dropy = player->jy;
13995   int drop_direction = player->MovDir;
13996   int drop_side = drop_direction;
13997   int drop_element = get_next_dropped_element(player);
13998
13999   /* do not drop an element on top of another element; when holding drop key
14000      pressed without moving, dropped element must move away before the next
14001      element can be dropped (this is especially important if the next element
14002      is dynamite, which can be placed on background for historical reasons) */
14003   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14004     return MP_ACTION;
14005
14006   if (IS_THROWABLE(drop_element))
14007   {
14008     dropx += GET_DX_FROM_DIR(drop_direction);
14009     dropy += GET_DY_FROM_DIR(drop_direction);
14010
14011     if (!IN_LEV_FIELD(dropx, dropy))
14012       return FALSE;
14013   }
14014
14015   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14016   new_element = drop_element;           /* default: no change when dropping */
14017
14018   /* check if player is active, not moving and ready to drop */
14019   if (!player->active || player->MovPos || player->drop_delay > 0)
14020     return FALSE;
14021
14022   /* check if player has anything that can be dropped */
14023   if (new_element == EL_UNDEFINED)
14024     return FALSE;
14025
14026   /* only set if player has anything that can be dropped */
14027   player->is_dropping_pressed = TRUE;
14028
14029   /* check if drop key was pressed long enough for EM style dynamite */
14030   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14031     return FALSE;
14032
14033   /* check if anything can be dropped at the current position */
14034   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14035     return FALSE;
14036
14037   /* collected custom elements can only be dropped on empty fields */
14038   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14039     return FALSE;
14040
14041   if (old_element != EL_EMPTY)
14042     Back[dropx][dropy] = old_element;   /* store old element on this field */
14043
14044   ResetGfxAnimation(dropx, dropy);
14045   ResetRandomAnimationValue(dropx, dropy);
14046
14047   if (player->inventory_size > 0 ||
14048       player->inventory_infinite_element != EL_UNDEFINED)
14049   {
14050     if (player->inventory_size > 0)
14051     {
14052       player->inventory_size--;
14053
14054       DrawGameDoorValues();
14055
14056       if (new_element == EL_DYNAMITE)
14057         new_element = EL_DYNAMITE_ACTIVE;
14058       else if (new_element == EL_EM_DYNAMITE)
14059         new_element = EL_EM_DYNAMITE_ACTIVE;
14060       else if (new_element == EL_SP_DISK_RED)
14061         new_element = EL_SP_DISK_RED_ACTIVE;
14062     }
14063
14064     Feld[dropx][dropy] = new_element;
14065
14066     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14067       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14068                           el2img(Feld[dropx][dropy]), 0);
14069
14070     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14071
14072     /* needed if previous element just changed to "empty" in the last frame */
14073     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14074
14075     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14076                                player->index_bit, drop_side);
14077     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14078                                         CE_PLAYER_DROPS_X,
14079                                         player->index_bit, drop_side);
14080
14081     TestIfElementTouchesCustomElement(dropx, dropy);
14082   }
14083   else          /* player is dropping a dyna bomb */
14084   {
14085     player->dynabombs_left--;
14086
14087     Feld[dropx][dropy] = new_element;
14088
14089     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14090       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14091                           el2img(Feld[dropx][dropy]), 0);
14092
14093     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14094   }
14095
14096   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14097     InitField_WithBug1(dropx, dropy, FALSE);
14098
14099   new_element = Feld[dropx][dropy];     /* element might have changed */
14100
14101   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14102       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14103   {
14104     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14105       MovDir[dropx][dropy] = drop_direction;
14106
14107     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14108
14109     /* do not cause impact style collision by dropping elements that can fall */
14110     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14111   }
14112
14113   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14114   player->is_dropping = TRUE;
14115
14116   player->drop_pressed_delay = 0;
14117   player->is_dropping_pressed = FALSE;
14118
14119   player->drop_x = dropx;
14120   player->drop_y = dropy;
14121
14122   return TRUE;
14123 }
14124
14125 /* ------------------------------------------------------------------------- */
14126 /* game sound playing functions                                              */
14127 /* ------------------------------------------------------------------------- */
14128
14129 static int *loop_sound_frame = NULL;
14130 static int *loop_sound_volume = NULL;
14131
14132 void InitPlayLevelSound()
14133 {
14134   int num_sounds = getSoundListSize();
14135
14136   checked_free(loop_sound_frame);
14137   checked_free(loop_sound_volume);
14138
14139   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14140   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14141 }
14142
14143 static void PlayLevelSound(int x, int y, int nr)
14144 {
14145   int sx = SCREENX(x), sy = SCREENY(y);
14146   int volume, stereo_position;
14147   int max_distance = 8;
14148   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14149
14150   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14151       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14152     return;
14153
14154   if (!IN_LEV_FIELD(x, y) ||
14155       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14156       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14157     return;
14158
14159   volume = SOUND_MAX_VOLUME;
14160
14161   if (!IN_SCR_FIELD(sx, sy))
14162   {
14163     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14164     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14165
14166     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14167   }
14168
14169   stereo_position = (SOUND_MAX_LEFT +
14170                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14171                      (SCR_FIELDX + 2 * max_distance));
14172
14173   if (IS_LOOP_SOUND(nr))
14174   {
14175     /* This assures that quieter loop sounds do not overwrite louder ones,
14176        while restarting sound volume comparison with each new game frame. */
14177
14178     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14179       return;
14180
14181     loop_sound_volume[nr] = volume;
14182     loop_sound_frame[nr] = FrameCounter;
14183   }
14184
14185   PlaySoundExt(nr, volume, stereo_position, type);
14186 }
14187
14188 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14189 {
14190   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14191                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14192                  y < LEVELY(BY1) ? LEVELY(BY1) :
14193                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14194                  sound_action);
14195 }
14196
14197 static void PlayLevelSoundAction(int x, int y, int action)
14198 {
14199   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14200 }
14201
14202 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14203 {
14204   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14205
14206   if (sound_effect != SND_UNDEFINED)
14207     PlayLevelSound(x, y, sound_effect);
14208 }
14209
14210 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14211                                               int action)
14212 {
14213   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14214
14215   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14216     PlayLevelSound(x, y, sound_effect);
14217 }
14218
14219 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14220 {
14221   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14222
14223   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14224     PlayLevelSound(x, y, sound_effect);
14225 }
14226
14227 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14228 {
14229   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14230
14231   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14232     StopSound(sound_effect);
14233 }
14234
14235 static void PlayLevelMusic()
14236 {
14237   if (levelset.music[level_nr] != MUS_UNDEFINED)
14238     PlayMusic(levelset.music[level_nr]);        /* from config file */
14239   else
14240     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14241 }
14242
14243 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14244 {
14245   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14246   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14247   int x = xx - 1 - offset;
14248   int y = yy - 1 - offset;
14249
14250   switch (sample)
14251   {
14252     case SAMPLE_blank:
14253       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14254       break;
14255
14256     case SAMPLE_roll:
14257       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14258       break;
14259
14260     case SAMPLE_stone:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14262       break;
14263
14264     case SAMPLE_nut:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14266       break;
14267
14268     case SAMPLE_crack:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14270       break;
14271
14272     case SAMPLE_bug:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14274       break;
14275
14276     case SAMPLE_tank:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14278       break;
14279
14280     case SAMPLE_android_clone:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14282       break;
14283
14284     case SAMPLE_android_move:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14286       break;
14287
14288     case SAMPLE_spring:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14290       break;
14291
14292     case SAMPLE_slurp:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14294       break;
14295
14296     case SAMPLE_eater:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14298       break;
14299
14300     case SAMPLE_eater_eat:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14302       break;
14303
14304     case SAMPLE_alien:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14306       break;
14307
14308     case SAMPLE_collect:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14310       break;
14311
14312     case SAMPLE_diamond:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14314       break;
14315
14316     case SAMPLE_squash:
14317       /* !!! CHECK THIS !!! */
14318 #if 1
14319       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14320 #else
14321       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14322 #endif
14323       break;
14324
14325     case SAMPLE_wonderfall:
14326       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14327       break;
14328
14329     case SAMPLE_drip:
14330       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14331       break;
14332
14333     case SAMPLE_push:
14334       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14335       break;
14336
14337     case SAMPLE_dirt:
14338       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14339       break;
14340
14341     case SAMPLE_acid:
14342       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14343       break;
14344
14345     case SAMPLE_ball:
14346       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14347       break;
14348
14349     case SAMPLE_grow:
14350       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14351       break;
14352
14353     case SAMPLE_wonder:
14354       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14355       break;
14356
14357     case SAMPLE_door:
14358       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14359       break;
14360
14361     case SAMPLE_exit_open:
14362       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14363       break;
14364
14365     case SAMPLE_exit_leave:
14366       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14367       break;
14368
14369     case SAMPLE_dynamite:
14370       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14371       break;
14372
14373     case SAMPLE_tick:
14374       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14375       break;
14376
14377     case SAMPLE_press:
14378       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14379       break;
14380
14381     case SAMPLE_wheel:
14382       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14383       break;
14384
14385     case SAMPLE_boom:
14386       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14387       break;
14388
14389     case SAMPLE_die:
14390       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14391       break;
14392
14393     case SAMPLE_time:
14394       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14395       break;
14396
14397     default:
14398       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14399       break;
14400   }
14401 }
14402
14403 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14404 {
14405   int element = map_element_SP_to_RND(element_sp);
14406   int action = map_action_SP_to_RND(action_sp);
14407   int offset = (setup.sp_show_border_elements ? 0 : 1);
14408   int x = xx - offset;
14409   int y = yy - offset;
14410
14411   PlayLevelSoundElementAction(x, y, element, action);
14412 }
14413
14414 void RaiseScore(int value)
14415 {
14416   local_player->score += value;
14417
14418   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14419
14420   DisplayGameControlValues();
14421 }
14422
14423 void RaiseScoreElement(int element)
14424 {
14425   switch (element)
14426   {
14427     case EL_EMERALD:
14428     case EL_BD_DIAMOND:
14429     case EL_EMERALD_YELLOW:
14430     case EL_EMERALD_RED:
14431     case EL_EMERALD_PURPLE:
14432     case EL_SP_INFOTRON:
14433       RaiseScore(level.score[SC_EMERALD]);
14434       break;
14435     case EL_DIAMOND:
14436       RaiseScore(level.score[SC_DIAMOND]);
14437       break;
14438     case EL_CRYSTAL:
14439       RaiseScore(level.score[SC_CRYSTAL]);
14440       break;
14441     case EL_PEARL:
14442       RaiseScore(level.score[SC_PEARL]);
14443       break;
14444     case EL_BUG:
14445     case EL_BD_BUTTERFLY:
14446     case EL_SP_ELECTRON:
14447       RaiseScore(level.score[SC_BUG]);
14448       break;
14449     case EL_SPACESHIP:
14450     case EL_BD_FIREFLY:
14451     case EL_SP_SNIKSNAK:
14452       RaiseScore(level.score[SC_SPACESHIP]);
14453       break;
14454     case EL_YAMYAM:
14455     case EL_DARK_YAMYAM:
14456       RaiseScore(level.score[SC_YAMYAM]);
14457       break;
14458     case EL_ROBOT:
14459       RaiseScore(level.score[SC_ROBOT]);
14460       break;
14461     case EL_PACMAN:
14462       RaiseScore(level.score[SC_PACMAN]);
14463       break;
14464     case EL_NUT:
14465       RaiseScore(level.score[SC_NUT]);
14466       break;
14467     case EL_DYNAMITE:
14468     case EL_EM_DYNAMITE:
14469     case EL_SP_DISK_RED:
14470     case EL_DYNABOMB_INCREASE_NUMBER:
14471     case EL_DYNABOMB_INCREASE_SIZE:
14472     case EL_DYNABOMB_INCREASE_POWER:
14473       RaiseScore(level.score[SC_DYNAMITE]);
14474       break;
14475     case EL_SHIELD_NORMAL:
14476     case EL_SHIELD_DEADLY:
14477       RaiseScore(level.score[SC_SHIELD]);
14478       break;
14479     case EL_EXTRA_TIME:
14480       RaiseScore(level.extra_time_score);
14481       break;
14482     case EL_KEY_1:
14483     case EL_KEY_2:
14484     case EL_KEY_3:
14485     case EL_KEY_4:
14486     case EL_EM_KEY_1:
14487     case EL_EM_KEY_2:
14488     case EL_EM_KEY_3:
14489     case EL_EM_KEY_4:
14490     case EL_EMC_KEY_5:
14491     case EL_EMC_KEY_6:
14492     case EL_EMC_KEY_7:
14493     case EL_EMC_KEY_8:
14494     case EL_DC_KEY_WHITE:
14495       RaiseScore(level.score[SC_KEY]);
14496       break;
14497     default:
14498       RaiseScore(element_info[element].collect_score);
14499       break;
14500   }
14501 }
14502
14503 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14504 {
14505   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14506   {
14507     /* closing door required in case of envelope style request dialogs */
14508     if (!skip_request)
14509       CloseDoor(DOOR_CLOSE_1);
14510
14511 #if defined(NETWORK_AVALIABLE)
14512     if (options.network)
14513       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14514     else
14515 #endif
14516     {
14517       if (quick_quit)
14518         FadeSkipNextFadeIn();
14519
14520       SetGameStatus(GAME_MODE_MAIN);
14521
14522       DrawMainMenu();
14523     }
14524   }
14525   else          /* continue playing the game */
14526   {
14527     if (tape.playing && tape.deactivate_display)
14528       TapeDeactivateDisplayOff(TRUE);
14529
14530     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14531
14532     if (tape.playing && tape.deactivate_display)
14533       TapeDeactivateDisplayOn();
14534   }
14535 }
14536
14537 void RequestQuitGame(boolean ask_if_really_quit)
14538 {
14539   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14540   boolean skip_request = AllPlayersGone || quick_quit;
14541
14542   RequestQuitGameExt(skip_request, quick_quit,
14543                      "Do you really want to quit the game?");
14544 }
14545
14546
14547 /* ------------------------------------------------------------------------- */
14548 /* random generator functions                                                */
14549 /* ------------------------------------------------------------------------- */
14550
14551 unsigned int InitEngineRandom_RND(int seed)
14552 {
14553   game.num_random_calls = 0;
14554
14555   return InitEngineRandom(seed);
14556 }
14557
14558 unsigned int RND(int max)
14559 {
14560   if (max > 0)
14561   {
14562     game.num_random_calls++;
14563
14564     return GetEngineRandom(max);
14565   }
14566
14567   return 0;
14568 }
14569
14570
14571 /* ------------------------------------------------------------------------- */
14572 /* game engine snapshot handling functions                                   */
14573 /* ------------------------------------------------------------------------- */
14574
14575 struct EngineSnapshotInfo
14576 {
14577   /* runtime values for custom element collect score */
14578   int collect_score[NUM_CUSTOM_ELEMENTS];
14579
14580   /* runtime values for group element choice position */
14581   int choice_pos[NUM_GROUP_ELEMENTS];
14582
14583   /* runtime values for belt position animations */
14584   int belt_graphic[4][NUM_BELT_PARTS];
14585   int belt_anim_mode[4][NUM_BELT_PARTS];
14586 };
14587
14588 static struct EngineSnapshotInfo engine_snapshot_rnd;
14589 static char *snapshot_level_identifier = NULL;
14590 static int snapshot_level_nr = -1;
14591
14592 static void SaveEngineSnapshotValues_RND()
14593 {
14594   static int belt_base_active_element[4] =
14595   {
14596     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14597     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14598     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14599     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14600   };
14601   int i, j;
14602
14603   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14604   {
14605     int element = EL_CUSTOM_START + i;
14606
14607     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14608   }
14609
14610   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14611   {
14612     int element = EL_GROUP_START + i;
14613
14614     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14615   }
14616
14617   for (i = 0; i < 4; i++)
14618   {
14619     for (j = 0; j < NUM_BELT_PARTS; j++)
14620     {
14621       int element = belt_base_active_element[i] + j;
14622       int graphic = el2img(element);
14623       int anim_mode = graphic_info[graphic].anim_mode;
14624
14625       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14626       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14627     }
14628   }
14629 }
14630
14631 static void LoadEngineSnapshotValues_RND()
14632 {
14633   unsigned int num_random_calls = game.num_random_calls;
14634   int i, j;
14635
14636   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14637   {
14638     int element = EL_CUSTOM_START + i;
14639
14640     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14641   }
14642
14643   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14644   {
14645     int element = EL_GROUP_START + i;
14646
14647     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14648   }
14649
14650   for (i = 0; i < 4; i++)
14651   {
14652     for (j = 0; j < NUM_BELT_PARTS; j++)
14653     {
14654       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14655       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14656
14657       graphic_info[graphic].anim_mode = anim_mode;
14658     }
14659   }
14660
14661   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14662   {
14663     InitRND(tape.random_seed);
14664     for (i = 0; i < num_random_calls; i++)
14665       RND(1);
14666   }
14667
14668   if (game.num_random_calls != num_random_calls)
14669   {
14670     Error(ERR_INFO, "number of random calls out of sync");
14671     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14672     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14673     Error(ERR_EXIT, "this should not happen -- please debug");
14674   }
14675 }
14676
14677 void FreeEngineSnapshotSingle()
14678 {
14679   FreeSnapshotSingle();
14680
14681   setString(&snapshot_level_identifier, NULL);
14682   snapshot_level_nr = -1;
14683 }
14684
14685 void FreeEngineSnapshotList()
14686 {
14687   FreeSnapshotList();
14688 }
14689
14690 ListNode *SaveEngineSnapshotBuffers()
14691 {
14692   ListNode *buffers = NULL;
14693
14694   /* copy some special values to a structure better suited for the snapshot */
14695
14696   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14697     SaveEngineSnapshotValues_RND();
14698   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14699     SaveEngineSnapshotValues_EM();
14700   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14701     SaveEngineSnapshotValues_SP(&buffers);
14702
14703   /* save values stored in special snapshot structure */
14704
14705   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14706     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14707   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14708     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14709   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14710     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14711
14712   /* save further RND engine values */
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14717
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14722
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14728
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14732
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14734
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14736
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14739
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14747   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14749   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14752   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14758
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14761
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14765
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14768
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14771   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14774
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14777
14778 #if 0
14779   ListNode *node = engine_snapshot_list_rnd;
14780   int num_bytes = 0;
14781
14782   while (node != NULL)
14783   {
14784     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14785
14786     node = node->next;
14787   }
14788
14789   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14790 #endif
14791
14792   return buffers;
14793 }
14794
14795 void SaveEngineSnapshotSingle()
14796 {
14797   ListNode *buffers = SaveEngineSnapshotBuffers();
14798
14799   /* finally save all snapshot buffers to single snapshot */
14800   SaveSnapshotSingle(buffers);
14801
14802   /* save level identification information */
14803   setString(&snapshot_level_identifier, leveldir_current->identifier);
14804   snapshot_level_nr = level_nr;
14805 }
14806
14807 boolean CheckSaveEngineSnapshotToList()
14808 {
14809   boolean save_snapshot =
14810     ((game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14811      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14812       game.snapshot.changed_action) ||
14813      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
14814       game.snapshot.collected_item));
14815
14816   game.snapshot.changed_action = FALSE;
14817   game.snapshot.collected_item = FALSE;
14818   game.snapshot.save_snapshot = save_snapshot;
14819
14820   return save_snapshot;
14821 }
14822
14823 void SaveEngineSnapshotToList()
14824 {
14825   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14826       tape.quick_resume)
14827     return;
14828
14829   ListNode *buffers = SaveEngineSnapshotBuffers();
14830
14831   /* finally save all snapshot buffers to snapshot list */
14832   SaveSnapshotToList(buffers);
14833 }
14834
14835 void SaveEngineSnapshotToListInitial()
14836 {
14837   FreeEngineSnapshotList();
14838
14839   SaveEngineSnapshotToList();
14840 }
14841
14842 void LoadEngineSnapshotValues()
14843 {
14844   /* restore special values from snapshot structure */
14845
14846   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14847     LoadEngineSnapshotValues_RND();
14848   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14849     LoadEngineSnapshotValues_EM();
14850   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14851     LoadEngineSnapshotValues_SP();
14852 }
14853
14854 void LoadEngineSnapshotSingle()
14855 {
14856   LoadSnapshotSingle();
14857
14858   LoadEngineSnapshotValues();
14859 }
14860
14861 void LoadEngineSnapshot_Undo(int steps)
14862 {
14863   LoadSnapshotFromList_Older(steps);
14864
14865   LoadEngineSnapshotValues();
14866 }
14867
14868 void LoadEngineSnapshot_Redo(int steps)
14869 {
14870   LoadSnapshotFromList_Newer(steps);
14871
14872   LoadEngineSnapshotValues();
14873 }
14874
14875 boolean CheckEngineSnapshotSingle()
14876 {
14877   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14878           snapshot_level_nr == level_nr);
14879 }
14880
14881 boolean CheckEngineSnapshotList()
14882 {
14883   return CheckSnapshotList();
14884 }
14885
14886
14887 /* ---------- new game button stuff ---------------------------------------- */
14888
14889 static struct
14890 {
14891   int graphic;
14892   struct XY *pos;
14893   int gadget_id;
14894   char *infotext;
14895 } gamebutton_info[NUM_GAME_BUTTONS] =
14896 {
14897   {
14898     IMG_GFX_GAME_BUTTON_STOP,           &game.button.stop,
14899     GAME_CTRL_ID_STOP,                  "stop game"
14900   },
14901   {
14902     IMG_GFX_GAME_BUTTON_PAUSE,          &game.button.pause,
14903     GAME_CTRL_ID_PAUSE,                 "pause game"
14904   },
14905   {
14906     IMG_GFX_GAME_BUTTON_PLAY,           &game.button.play,
14907     GAME_CTRL_ID_PLAY,                  "play game"
14908   },
14909   {
14910     IMG_GFX_GAME_BUTTON_UNDO,           &game.button.undo,
14911     GAME_CTRL_ID_UNDO,                  "undo step"
14912   },
14913   {
14914     IMG_GFX_GAME_BUTTON_REDO,           &game.button.redo,
14915     GAME_CTRL_ID_REDO,                  "redo step"
14916   },
14917   {
14918     IMG_GFX_GAME_BUTTON_SAVE,           &game.button.save,
14919     GAME_CTRL_ID_SAVE,                  "save game"
14920   },
14921   {
14922     IMG_GFX_GAME_BUTTON_PAUSE2,         &game.button.pause2,
14923     GAME_CTRL_ID_PAUSE2,                "pause game"
14924   },
14925   {
14926     IMG_GFX_GAME_BUTTON_LOAD,           &game.button.load,
14927     GAME_CTRL_ID_LOAD,                  "load game"
14928   },
14929   {
14930     IMG_GFX_GAME_BUTTON_SOUND_MUSIC,    &game.button.sound_music,
14931     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14932   },
14933   {
14934     IMG_GFX_GAME_BUTTON_SOUND_LOOPS,    &game.button.sound_loops,
14935     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14936   },
14937   {
14938     IMG_GFX_GAME_BUTTON_SOUND_SIMPLE,   &game.button.sound_simple,
14939     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14940   }
14941 };
14942
14943 void CreateGameButtons()
14944 {
14945   int i;
14946
14947   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14948   {
14949     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14950     struct XY *pos = gamebutton_info[i].pos;
14951     struct GadgetInfo *gi;
14952     int button_type;
14953     boolean checked;
14954     unsigned int event_mask;
14955     int base_x = (tape.show_game_buttons ? VX : DX);
14956     int base_y = (tape.show_game_buttons ? VY : DY);
14957     int gd_x   = gfx->src_x;
14958     int gd_y   = gfx->src_y;
14959     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14960     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14961     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14962     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14963     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14964     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14965     int id = i;
14966
14967     if (gfx->bitmap == NULL)
14968     {
14969       game_gadget[id] = NULL;
14970
14971       continue;
14972     }
14973
14974     if (id == GAME_CTRL_ID_STOP ||
14975         id == GAME_CTRL_ID_PLAY ||
14976         id == GAME_CTRL_ID_SAVE ||
14977         id == GAME_CTRL_ID_LOAD)
14978     {
14979       button_type = GD_TYPE_NORMAL_BUTTON;
14980       checked = FALSE;
14981       event_mask = GD_EVENT_RELEASED;
14982     }
14983     else if (id == GAME_CTRL_ID_UNDO ||
14984              id == GAME_CTRL_ID_REDO)
14985     {
14986       button_type = GD_TYPE_NORMAL_BUTTON;
14987       checked = FALSE;
14988       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14989     }
14990     else
14991     {
14992       button_type = GD_TYPE_CHECK_BUTTON;
14993       checked =
14994         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14995          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14996          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14997       event_mask = GD_EVENT_PRESSED;
14998     }
14999
15000     gi = CreateGadget(GDI_CUSTOM_ID, id,
15001                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15002                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15003                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15004                       GDI_WIDTH, gfx->width,
15005                       GDI_HEIGHT, gfx->height,
15006                       GDI_TYPE, button_type,
15007                       GDI_STATE, GD_BUTTON_UNPRESSED,
15008                       GDI_CHECKED, checked,
15009                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15010                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15011                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15012                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15013                       GDI_DIRECT_DRAW, FALSE,
15014                       GDI_EVENT_MASK, event_mask,
15015                       GDI_CALLBACK_ACTION, HandleGameButtons,
15016                       GDI_END);
15017
15018     if (gi == NULL)
15019       Error(ERR_EXIT, "cannot create gadget");
15020
15021     game_gadget[id] = gi;
15022   }
15023 }
15024
15025 void FreeGameButtons()
15026 {
15027   int i;
15028
15029   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15030     FreeGadget(game_gadget[i]);
15031 }
15032
15033 static void UnmapGameButtonsAtSamePosition(int id)
15034 {
15035   int i;
15036
15037   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15038     if (i != id &&
15039         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15040         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15041       UnmapGadget(game_gadget[i]);
15042 }
15043
15044 static void UnmapGameButtonsAtSamePosition_All()
15045 {
15046   if (setup.show_snapshot_buttons)
15047   {
15048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15049     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15051   }
15052   else
15053   {
15054     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15055     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15056     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15057   }
15058 }
15059
15060 static void MapGameButtonsAtSamePosition(int id)
15061 {
15062   int i;
15063
15064   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15065     if (i != id &&
15066         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15067         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15068       MapGadget(game_gadget[i]);
15069
15070   UnmapGameButtonsAtSamePosition_All();
15071 }
15072
15073 void MapUndoRedoButtons()
15074 {
15075   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15076   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15077
15078   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15079   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15080
15081   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, TRUE, GDI_END);
15082 }
15083
15084 void UnmapUndoRedoButtons()
15085 {
15086   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15087   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15088
15089   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15090   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15091
15092   ModifyGadget(game_gadget[GAME_CTRL_ID_PAUSE2], GDI_CHECKED, FALSE, GDI_END);
15093 }
15094
15095 void MapGameButtons()
15096 {
15097   int i;
15098
15099   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15100     if (i != GAME_CTRL_ID_UNDO &&
15101         i != GAME_CTRL_ID_REDO)
15102       MapGadget(game_gadget[i]);
15103
15104   UnmapGameButtonsAtSamePosition_All();
15105
15106   RedrawGameButtons();
15107 }
15108
15109 void UnmapGameButtons()
15110 {
15111   int i;
15112
15113   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15114     UnmapGadget(game_gadget[i]);
15115 }
15116
15117 void RedrawGameButtons()
15118 {
15119   int i;
15120
15121   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15122     RedrawGadget(game_gadget[i]);
15123
15124   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15125   redraw_mask &= ~REDRAW_ALL;
15126 }
15127
15128 void GameUndoRedoExt()
15129 {
15130   ClearPlayerAction();
15131
15132   tape.pausing = TRUE;
15133
15134   RedrawPlayfield();
15135   UpdateAndDisplayGameControlValues();
15136
15137   DrawCompleteVideoDisplay();
15138   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15139   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15140   DrawVideoDisplay(VIDEO_STATE_1STEP(tape.single_step), 0);
15141
15142   BackToFront();
15143 }
15144
15145 void GameUndo(int steps)
15146 {
15147   if (!CheckEngineSnapshotList())
15148     return;
15149
15150   LoadEngineSnapshot_Undo(steps);
15151
15152   GameUndoRedoExt();
15153 }
15154
15155 void GameRedo(int steps)
15156 {
15157   if (!CheckEngineSnapshotList())
15158     return;
15159
15160   LoadEngineSnapshot_Redo(steps);
15161
15162   GameUndoRedoExt();
15163 }
15164
15165 static void HandleGameButtonsExt(int id, int button)
15166 {
15167   static boolean game_undo_executed = FALSE;
15168   int steps = BUTTON_STEPSIZE(button);
15169   boolean handle_game_buttons =
15170     (game_status == GAME_MODE_PLAYING ||
15171      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15172
15173   if (!handle_game_buttons)
15174     return;
15175
15176   switch (id)
15177   {
15178     case GAME_CTRL_ID_STOP:
15179       if (game_status == GAME_MODE_MAIN)
15180         break;
15181
15182       if (tape.playing)
15183         TapeStop();
15184       else
15185         RequestQuitGame(TRUE);
15186
15187       break;
15188
15189     case GAME_CTRL_ID_PAUSE:
15190     case GAME_CTRL_ID_PAUSE2:
15191       if (options.network && game_status == GAME_MODE_PLAYING)
15192       {
15193 #if defined(NETWORK_AVALIABLE)
15194         if (tape.pausing)
15195           SendToServer_ContinuePlaying();
15196         else
15197           SendToServer_PausePlaying();
15198 #endif
15199       }
15200       else
15201         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15202
15203       game_undo_executed = FALSE;
15204
15205       break;
15206
15207     case GAME_CTRL_ID_PLAY:
15208       if (game_status == GAME_MODE_MAIN)
15209       {
15210         StartGameActions(options.network, setup.autorecord, level.random_seed);
15211       }
15212       else if (tape.pausing)
15213       {
15214 #if defined(NETWORK_AVALIABLE)
15215         if (options.network)
15216           SendToServer_ContinuePlaying();
15217         else
15218 #endif
15219           TapeTogglePause(TAPE_TOGGLE_MANUAL | TAPE_TOGGLE_PLAY_PAUSE);
15220       }
15221       break;
15222
15223     case GAME_CTRL_ID_UNDO:
15224       // Important: When using "save snapshot when collecting an item" mode,
15225       // load last (current) snapshot for first "undo" after pressing "pause"
15226       // (else the last-but-one snapshot would be loaded, because the snapshot
15227       // pointer already points to the last snapshot when pressing "pause",
15228       // which is fine for "every step/move" mode, but not for "every collect")
15229       if (game.snapshot.mode == SNAPSHOT_MODE_EVERY_COLLECT &&
15230           !game_undo_executed)
15231         steps--;
15232
15233       game_undo_executed = TRUE;
15234
15235       GameUndo(steps);
15236       break;
15237
15238     case GAME_CTRL_ID_REDO:
15239       GameRedo(steps);
15240       break;
15241
15242     case GAME_CTRL_ID_SAVE:
15243       TapeQuickSave();
15244       break;
15245
15246     case GAME_CTRL_ID_LOAD:
15247       TapeQuickLoad();
15248       break;
15249
15250     case SOUND_CTRL_ID_MUSIC:
15251       if (setup.sound_music)
15252       { 
15253         setup.sound_music = FALSE;
15254
15255         FadeMusic();
15256       }
15257       else if (audio.music_available)
15258       { 
15259         setup.sound = setup.sound_music = TRUE;
15260
15261         SetAudioMode(setup.sound);
15262
15263         PlayLevelMusic();
15264       }
15265       break;
15266
15267     case SOUND_CTRL_ID_LOOPS:
15268       if (setup.sound_loops)
15269         setup.sound_loops = FALSE;
15270       else if (audio.loops_available)
15271       {
15272         setup.sound = setup.sound_loops = TRUE;
15273
15274         SetAudioMode(setup.sound);
15275       }
15276       break;
15277
15278     case SOUND_CTRL_ID_SIMPLE:
15279       if (setup.sound_simple)
15280         setup.sound_simple = FALSE;
15281       else if (audio.sound_available)
15282       {
15283         setup.sound = setup.sound_simple = TRUE;
15284
15285         SetAudioMode(setup.sound);
15286       }
15287       break;
15288
15289     default:
15290       break;
15291   }
15292 }
15293
15294 static void HandleGameButtons(struct GadgetInfo *gi)
15295 {
15296   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15297 }
15298
15299 void HandleSoundButtonKeys(Key key)
15300 {
15301
15302   if (key == setup.shortcut.sound_simple)
15303     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15304   else if (key == setup.shortcut.sound_loops)
15305     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15306   else if (key == setup.shortcut.sound_music)
15307     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15308 }