added setting engine snapshot mode to setup screen and file
[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
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode =
3043     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044      SNAPSHOT_MODE_EVERY_STEP :
3045      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3047
3048   FreeEngineSnapshotList();
3049 }
3050
3051 int get_num_special_action(int element, int action_first, int action_last)
3052 {
3053   int num_special_action = 0;
3054   int i, j;
3055
3056   for (i = action_first; i <= action_last; i++)
3057   {
3058     boolean found = FALSE;
3059
3060     for (j = 0; j < NUM_DIRECTIONS; j++)
3061       if (el_act_dir2img(element, i, j) !=
3062           el_act_dir2img(element, ACTION_DEFAULT, j))
3063         found = TRUE;
3064
3065     if (found)
3066       num_special_action++;
3067     else
3068       break;
3069   }
3070
3071   return num_special_action;
3072 }
3073
3074
3075 /*
3076   =============================================================================
3077   InitGame()
3078   -----------------------------------------------------------------------------
3079   initialize and start new game
3080   =============================================================================
3081 */
3082
3083 void InitGame()
3084 {
3085   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   game_status = GAME_MODE_PLAYING;
3095
3096   StopAnimation();
3097
3098   if (!game.restart_level)
3099     CloseDoor(DOOR_CLOSE_1);
3100
3101   if (level_editor_test_game)
3102     FadeSkipNextFadeIn();
3103   else
3104     FadeSetEnterScreen();
3105
3106   FadeOut(REDRAW_FIELD);
3107
3108   /* needed if different viewport properties defined for playing */
3109   ChangeViewportPropertiesIfNeeded();
3110
3111   DrawCompleteVideoDisplay();
3112
3113   InitGameEngine();
3114   InitGameControlValues();
3115
3116   /* don't play tapes over network */
3117   network_playing = (options.network && !tape.playing);
3118
3119   for (i = 0; i < MAX_PLAYERS; i++)
3120   {
3121     struct PlayerInfo *player = &stored_player[i];
3122
3123     player->index_nr = i;
3124     player->index_bit = (1 << i);
3125     player->element_nr = EL_PLAYER_1 + i;
3126
3127     player->present = FALSE;
3128     player->active = FALSE;
3129     player->mapped = FALSE;
3130
3131     player->killed = FALSE;
3132     player->reanimated = FALSE;
3133
3134     player->action = 0;
3135     player->effective_action = 0;
3136     player->programmed_action = 0;
3137
3138     player->score = 0;
3139     player->score_final = 0;
3140
3141     player->gems_still_needed = level.gems_needed;
3142     player->sokobanfields_still_needed = 0;
3143     player->lights_still_needed = 0;
3144     player->friends_still_needed = 0;
3145
3146     for (j = 0; j < MAX_NUM_KEYS; j++)
3147       player->key[j] = FALSE;
3148
3149     player->num_white_keys = 0;
3150
3151     player->dynabomb_count = 0;
3152     player->dynabomb_size = 1;
3153     player->dynabombs_left = 0;
3154     player->dynabomb_xl = FALSE;
3155
3156     player->MovDir = initial_move_dir;
3157     player->MovPos = 0;
3158     player->GfxPos = 0;
3159     player->GfxDir = initial_move_dir;
3160     player->GfxAction = ACTION_DEFAULT;
3161     player->Frame = 0;
3162     player->StepFrame = 0;
3163
3164     player->initial_element = player->element_nr;
3165     player->artwork_element =
3166       (level.use_artwork_element[i] ? level.artwork_element[i] :
3167        player->element_nr);
3168     player->use_murphy = FALSE;
3169
3170     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3171     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3172
3173     player->gravity = level.initial_player_gravity[i];
3174
3175     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3176
3177     player->actual_frame_counter = 0;
3178
3179     player->step_counter = 0;
3180
3181     player->last_move_dir = initial_move_dir;
3182
3183     player->is_active = FALSE;
3184
3185     player->is_waiting = FALSE;
3186     player->is_moving = FALSE;
3187     player->is_auto_moving = FALSE;
3188     player->is_digging = FALSE;
3189     player->is_snapping = FALSE;
3190     player->is_collecting = FALSE;
3191     player->is_pushing = FALSE;
3192     player->is_switching = FALSE;
3193     player->is_dropping = FALSE;
3194     player->is_dropping_pressed = FALSE;
3195
3196     player->is_bored = FALSE;
3197     player->is_sleeping = FALSE;
3198
3199     player->frame_counter_bored = -1;
3200     player->frame_counter_sleeping = -1;
3201
3202     player->anim_delay_counter = 0;
3203     player->post_delay_counter = 0;
3204
3205     player->dir_waiting = initial_move_dir;
3206     player->action_waiting = ACTION_DEFAULT;
3207     player->last_action_waiting = ACTION_DEFAULT;
3208     player->special_action_bored = ACTION_DEFAULT;
3209     player->special_action_sleeping = ACTION_DEFAULT;
3210
3211     player->switch_x = -1;
3212     player->switch_y = -1;
3213
3214     player->drop_x = -1;
3215     player->drop_y = -1;
3216
3217     player->show_envelope = 0;
3218
3219     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3220
3221     player->push_delay       = -1;      /* initialized when pushing starts */
3222     player->push_delay_value = game.initial_push_delay_value;
3223
3224     player->drop_delay = 0;
3225     player->drop_pressed_delay = 0;
3226
3227     player->last_jx = -1;
3228     player->last_jy = -1;
3229     player->jx = -1;
3230     player->jy = -1;
3231
3232     player->shield_normal_time_left = 0;
3233     player->shield_deadly_time_left = 0;
3234
3235     player->inventory_infinite_element = EL_UNDEFINED;
3236     player->inventory_size = 0;
3237
3238     if (level.use_initial_inventory[i])
3239     {
3240       for (j = 0; j < level.initial_inventory_size[i]; j++)
3241       {
3242         int element = level.initial_inventory_content[i][j];
3243         int collect_count = element_info[element].collect_count_initial;
3244         int k;
3245
3246         if (!IS_CUSTOM_ELEMENT(element))
3247           collect_count = 1;
3248
3249         if (collect_count == 0)
3250           player->inventory_infinite_element = element;
3251         else
3252           for (k = 0; k < collect_count; k++)
3253             if (player->inventory_size < MAX_INVENTORY_SIZE)
3254               player->inventory_element[player->inventory_size++] = element;
3255       }
3256     }
3257
3258     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3259     SnapField(player, 0, 0);
3260
3261     player->LevelSolved = FALSE;
3262     player->GameOver = FALSE;
3263
3264     player->LevelSolved_GameWon = FALSE;
3265     player->LevelSolved_GameEnd = FALSE;
3266     player->LevelSolved_PanelOff = FALSE;
3267     player->LevelSolved_SaveTape = FALSE;
3268     player->LevelSolved_SaveScore = FALSE;
3269     player->LevelSolved_CountingTime = 0;
3270     player->LevelSolved_CountingScore = 0;
3271
3272     map_player_action[i] = i;
3273   }
3274
3275   network_player_action_received = FALSE;
3276
3277 #if defined(NETWORK_AVALIABLE)
3278   /* initial null action */
3279   if (network_playing)
3280     SendToServer_MovePlayer(MV_NONE);
3281 #endif
3282
3283   ZX = ZY = -1;
3284   ExitX = ExitY = -1;
3285
3286   FrameCounter = 0;
3287   TimeFrames = 0;
3288   TimePlayed = 0;
3289   TimeLeft = level.time;
3290   TapeTime = 0;
3291
3292   ScreenMovDir = MV_NONE;
3293   ScreenMovPos = 0;
3294   ScreenGfxPos = 0;
3295
3296   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3297
3298   AllPlayersGone = FALSE;
3299
3300   game.no_time_limit = (level.time == 0);
3301
3302   game.yamyam_content_nr = 0;
3303   game.robot_wheel_active = FALSE;
3304   game.magic_wall_active = FALSE;
3305   game.magic_wall_time_left = 0;
3306   game.light_time_left = 0;
3307   game.timegate_time_left = 0;
3308   game.switchgate_pos = 0;
3309   game.wind_direction = level.wind_direction_initial;
3310
3311   game.lenses_time_left = 0;
3312   game.magnify_time_left = 0;
3313
3314   game.ball_state = level.ball_state_initial;
3315   game.ball_content_nr = 0;
3316
3317   game.envelope_active = FALSE;
3318
3319   /* set focus to local player for network games, else to all players */
3320   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3321   game.centered_player_nr_next = game.centered_player_nr;
3322   game.set_centered_player = FALSE;
3323
3324   if (network_playing && tape.recording)
3325   {
3326     /* store client dependent player focus when recording network games */
3327     tape.centered_player_nr_next = game.centered_player_nr_next;
3328     tape.set_centered_player = TRUE;
3329   }
3330
3331   for (i = 0; i < NUM_BELTS; i++)
3332   {
3333     game.belt_dir[i] = MV_NONE;
3334     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3335   }
3336
3337   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3338     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3339
3340 #if DEBUG_INIT_PLAYER
3341   if (options.debug)
3342   {
3343     printf("Player status at level initialization:\n");
3344   }
3345 #endif
3346
3347   SCAN_PLAYFIELD(x, y)
3348   {
3349     Feld[x][y] = level.field[x][y];
3350     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3351     ChangeDelay[x][y] = 0;
3352     ChangePage[x][y] = -1;
3353     CustomValue[x][y] = 0;              /* initialized in InitField() */
3354     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3355     AmoebaNr[x][y] = 0;
3356     WasJustMoving[x][y] = 0;
3357     WasJustFalling[x][y] = 0;
3358     CheckCollision[x][y] = 0;
3359     CheckImpact[x][y] = 0;
3360     Stop[x][y] = FALSE;
3361     Pushed[x][y] = FALSE;
3362
3363     ChangeCount[x][y] = 0;
3364     ChangeEvent[x][y] = -1;
3365
3366     ExplodePhase[x][y] = 0;
3367     ExplodeDelay[x][y] = 0;
3368     ExplodeField[x][y] = EX_TYPE_NONE;
3369
3370     RunnerVisit[x][y] = 0;
3371     PlayerVisit[x][y] = 0;
3372
3373     GfxFrame[x][y] = 0;
3374     GfxRandom[x][y] = INIT_GFX_RANDOM();
3375     GfxElement[x][y] = EL_UNDEFINED;
3376     GfxAction[x][y] = ACTION_DEFAULT;
3377     GfxDir[x][y] = MV_NONE;
3378     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3379   }
3380
3381   SCAN_PLAYFIELD(x, y)
3382   {
3383     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3384       emulate_bd = FALSE;
3385     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3386       emulate_sb = FALSE;
3387     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3388       emulate_sp = FALSE;
3389
3390     InitField(x, y, TRUE);
3391
3392     ResetGfxAnimation(x, y);
3393   }
3394
3395   InitBeltMovement();
3396
3397   for (i = 0; i < MAX_PLAYERS; i++)
3398   {
3399     struct PlayerInfo *player = &stored_player[i];
3400
3401     /* set number of special actions for bored and sleeping animation */
3402     player->num_special_action_bored =
3403       get_num_special_action(player->artwork_element,
3404                              ACTION_BORING_1, ACTION_BORING_LAST);
3405     player->num_special_action_sleeping =
3406       get_num_special_action(player->artwork_element,
3407                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3408   }
3409
3410   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3411                     emulate_sb ? EMU_SOKOBAN :
3412                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3413
3414   /* initialize type of slippery elements */
3415   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3416   {
3417     if (!IS_CUSTOM_ELEMENT(i))
3418     {
3419       /* default: elements slip down either to the left or right randomly */
3420       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3421
3422       /* SP style elements prefer to slip down on the left side */
3423       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3424         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3425
3426       /* BD style elements prefer to slip down on the left side */
3427       if (game.emulation == EMU_BOULDERDASH)
3428         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3429     }
3430   }
3431
3432   /* initialize explosion and ignition delay */
3433   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3434   {
3435     if (!IS_CUSTOM_ELEMENT(i))
3436     {
3437       int num_phase = 8;
3438       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3439                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3440                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3441       int last_phase = (num_phase + 1) * delay;
3442       int half_phase = (num_phase / 2) * delay;
3443
3444       element_info[i].explosion_delay = last_phase - 1;
3445       element_info[i].ignition_delay = half_phase;
3446
3447       if (i == EL_BLACK_ORB)
3448         element_info[i].ignition_delay = 1;
3449     }
3450   }
3451
3452   /* correct non-moving belts to start moving left */
3453   for (i = 0; i < NUM_BELTS; i++)
3454     if (game.belt_dir[i] == MV_NONE)
3455       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3456
3457 #if USE_NEW_PLAYER_ASSIGNMENTS
3458   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3459   /* choose default local player */
3460   local_player = &stored_player[0];
3461
3462   for (i = 0; i < MAX_PLAYERS; i++)
3463     stored_player[i].connected = FALSE;
3464
3465   local_player->connected = TRUE;
3466   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3467
3468   if (tape.playing)
3469   {
3470     for (i = 0; i < MAX_PLAYERS; i++)
3471       stored_player[i].connected = tape.player_participates[i];
3472   }
3473   else if (game.team_mode && !options.network)
3474   {
3475     /* try to guess locally connected team mode players (needed for correct
3476        assignment of player figures from level to locally playing players) */
3477
3478     for (i = 0; i < MAX_PLAYERS; i++)
3479       if (setup.input[i].use_joystick ||
3480           setup.input[i].key.left != KSYM_UNDEFINED)
3481         stored_player[i].connected = TRUE;
3482   }
3483
3484 #if DEBUG_INIT_PLAYER
3485   if (options.debug)
3486   {
3487     printf("Player status after level initialization:\n");
3488
3489     for (i = 0; i < MAX_PLAYERS; i++)
3490     {
3491       struct PlayerInfo *player = &stored_player[i];
3492
3493       printf("- player %d: present == %d, connected == %d, active == %d",
3494              i + 1,
3495              player->present,
3496              player->connected,
3497              player->active);
3498
3499       if (local_player == player)
3500         printf(" (local player)");
3501
3502       printf("\n");
3503     }
3504   }
3505 #endif
3506
3507 #if DEBUG_INIT_PLAYER
3508   if (options.debug)
3509     printf("Reassigning players ...\n");
3510 #endif
3511
3512   /* check if any connected player was not found in playfield */
3513   for (i = 0; i < MAX_PLAYERS; i++)
3514   {
3515     struct PlayerInfo *player = &stored_player[i];
3516
3517     if (player->connected && !player->present)
3518     {
3519       struct PlayerInfo *field_player = NULL;
3520
3521 #if DEBUG_INIT_PLAYER
3522       if (options.debug)
3523         printf("- looking for field player for player %d ...\n", i + 1);
3524 #endif
3525
3526       /* assign first free player found that is present in the playfield */
3527
3528       /* first try: look for unmapped playfield player that is not connected */
3529       for (j = 0; j < MAX_PLAYERS; j++)
3530         if (field_player == NULL &&
3531             stored_player[j].present &&
3532             !stored_player[j].mapped &&
3533             !stored_player[j].connected)
3534           field_player = &stored_player[j];
3535
3536       /* second try: look for *any* unmapped playfield player */
3537       for (j = 0; j < MAX_PLAYERS; j++)
3538         if (field_player == NULL &&
3539             stored_player[j].present &&
3540             !stored_player[j].mapped)
3541           field_player = &stored_player[j];
3542
3543       if (field_player != NULL)
3544       {
3545         int jx = field_player->jx, jy = field_player->jy;
3546
3547 #if DEBUG_INIT_PLAYER
3548         if (options.debug)
3549           printf("- found player %d\n", field_player->index_nr + 1);
3550 #endif
3551
3552         player->present = FALSE;
3553         player->active = FALSE;
3554
3555         field_player->present = TRUE;
3556         field_player->active = TRUE;
3557
3558         /*
3559         player->initial_element = field_player->initial_element;
3560         player->artwork_element = field_player->artwork_element;
3561
3562         player->block_last_field       = field_player->block_last_field;
3563         player->block_delay_adjustment = field_player->block_delay_adjustment;
3564         */
3565
3566         StorePlayer[jx][jy] = field_player->element_nr;
3567
3568         field_player->jx = field_player->last_jx = jx;
3569         field_player->jy = field_player->last_jy = jy;
3570
3571         if (local_player == player)
3572           local_player = field_player;
3573
3574         map_player_action[field_player->index_nr] = i;
3575
3576         field_player->mapped = TRUE;
3577
3578 #if DEBUG_INIT_PLAYER
3579         if (options.debug)
3580           printf("- map_player_action[%d] == %d\n",
3581                  field_player->index_nr + 1, i + 1);
3582 #endif
3583       }
3584     }
3585
3586     if (player->connected && player->present)
3587       player->mapped = TRUE;
3588   }
3589
3590 #if DEBUG_INIT_PLAYER
3591   if (options.debug)
3592   {
3593     printf("Player status after player assignment (first stage):\n");
3594
3595     for (i = 0; i < MAX_PLAYERS; i++)
3596     {
3597       struct PlayerInfo *player = &stored_player[i];
3598
3599       printf("- player %d: present == %d, connected == %d, active == %d",
3600              i + 1,
3601              player->present,
3602              player->connected,
3603              player->active);
3604
3605       if (local_player == player)
3606         printf(" (local player)");
3607
3608       printf("\n");
3609     }
3610   }
3611 #endif
3612
3613 #else
3614
3615   /* check if any connected player was not found in playfield */
3616   for (i = 0; i < MAX_PLAYERS; i++)
3617   {
3618     struct PlayerInfo *player = &stored_player[i];
3619
3620     if (player->connected && !player->present)
3621     {
3622       for (j = 0; j < MAX_PLAYERS; j++)
3623       {
3624         struct PlayerInfo *field_player = &stored_player[j];
3625         int jx = field_player->jx, jy = field_player->jy;
3626
3627         /* assign first free player found that is present in the playfield */
3628         if (field_player->present && !field_player->connected)
3629         {
3630           player->present = TRUE;
3631           player->active = TRUE;
3632
3633           field_player->present = FALSE;
3634           field_player->active = FALSE;
3635
3636           player->initial_element = field_player->initial_element;
3637           player->artwork_element = field_player->artwork_element;
3638
3639           player->block_last_field       = field_player->block_last_field;
3640           player->block_delay_adjustment = field_player->block_delay_adjustment;
3641
3642           StorePlayer[jx][jy] = player->element_nr;
3643
3644           player->jx = player->last_jx = jx;
3645           player->jy = player->last_jy = jy;
3646
3647           break;
3648         }
3649       }
3650     }
3651   }
3652 #endif
3653
3654 #if 0
3655   printf("::: local_player->present == %d\n", local_player->present);
3656 #endif
3657
3658   if (tape.playing)
3659   {
3660     /* when playing a tape, eliminate all players who do not participate */
3661
3662 #if USE_NEW_PLAYER_ASSIGNMENTS
3663
3664     if (!game.team_mode)
3665     {
3666       for (i = 0; i < MAX_PLAYERS; i++)
3667       {
3668         if (stored_player[i].active &&
3669             !tape.player_participates[map_player_action[i]])
3670         {
3671           struct PlayerInfo *player = &stored_player[i];
3672           int jx = player->jx, jy = player->jy;
3673
3674 #if DEBUG_INIT_PLAYER
3675           if (options.debug)
3676             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3677 #endif
3678
3679           player->active = FALSE;
3680           StorePlayer[jx][jy] = 0;
3681           Feld[jx][jy] = EL_EMPTY;
3682         }
3683       }
3684     }
3685
3686 #else
3687
3688     for (i = 0; i < MAX_PLAYERS; i++)
3689     {
3690       if (stored_player[i].active &&
3691           !tape.player_participates[i])
3692       {
3693         struct PlayerInfo *player = &stored_player[i];
3694         int jx = player->jx, jy = player->jy;
3695
3696         player->active = FALSE;
3697         StorePlayer[jx][jy] = 0;
3698         Feld[jx][jy] = EL_EMPTY;
3699       }
3700     }
3701 #endif
3702   }
3703   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3704   {
3705     /* when in single player mode, eliminate all but the first active player */
3706
3707     for (i = 0; i < MAX_PLAYERS; i++)
3708     {
3709       if (stored_player[i].active)
3710       {
3711         for (j = i + 1; j < MAX_PLAYERS; j++)
3712         {
3713           if (stored_player[j].active)
3714           {
3715             struct PlayerInfo *player = &stored_player[j];
3716             int jx = player->jx, jy = player->jy;
3717
3718             player->active = FALSE;
3719             player->present = FALSE;
3720
3721             StorePlayer[jx][jy] = 0;
3722             Feld[jx][jy] = EL_EMPTY;
3723           }
3724         }
3725       }
3726     }
3727   }
3728
3729   /* when recording the game, store which players take part in the game */
3730   if (tape.recording)
3731   {
3732 #if USE_NEW_PLAYER_ASSIGNMENTS
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734       if (stored_player[i].connected)
3735         tape.player_participates[i] = TRUE;
3736 #else
3737     for (i = 0; i < MAX_PLAYERS; i++)
3738       if (stored_player[i].active)
3739         tape.player_participates[i] = TRUE;
3740 #endif
3741   }
3742
3743 #if DEBUG_INIT_PLAYER
3744   if (options.debug)
3745   {
3746     printf("Player status after player assignment (final stage):\n");
3747
3748     for (i = 0; i < MAX_PLAYERS; i++)
3749     {
3750       struct PlayerInfo *player = &stored_player[i];
3751
3752       printf("- player %d: present == %d, connected == %d, active == %d",
3753              i + 1,
3754              player->present,
3755              player->connected,
3756              player->active);
3757
3758       if (local_player == player)
3759         printf(" (local player)");
3760
3761       printf("\n");
3762     }
3763   }
3764 #endif
3765
3766   if (BorderElement == EL_EMPTY)
3767   {
3768     SBX_Left = 0;
3769     SBX_Right = lev_fieldx - SCR_FIELDX;
3770     SBY_Upper = 0;
3771     SBY_Lower = lev_fieldy - SCR_FIELDY;
3772   }
3773   else
3774   {
3775     SBX_Left = -1;
3776     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3777     SBY_Upper = -1;
3778     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3779   }
3780
3781   if (full_lev_fieldx <= SCR_FIELDX)
3782     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3783   if (full_lev_fieldy <= SCR_FIELDY)
3784     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3785
3786   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3787     SBX_Left--;
3788   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3789     SBY_Upper--;
3790
3791   /* if local player not found, look for custom element that might create
3792      the player (make some assumptions about the right custom element) */
3793   if (!local_player->present)
3794   {
3795     int start_x = 0, start_y = 0;
3796     int found_rating = 0;
3797     int found_element = EL_UNDEFINED;
3798     int player_nr = local_player->index_nr;
3799
3800     SCAN_PLAYFIELD(x, y)
3801     {
3802       int element = Feld[x][y];
3803       int content;
3804       int xx, yy;
3805       boolean is_player;
3806
3807       if (level.use_start_element[player_nr] &&
3808           level.start_element[player_nr] == element &&
3809           found_rating < 4)
3810       {
3811         start_x = x;
3812         start_y = y;
3813
3814         found_rating = 4;
3815         found_element = element;
3816       }
3817
3818       if (!IS_CUSTOM_ELEMENT(element))
3819         continue;
3820
3821       if (CAN_CHANGE(element))
3822       {
3823         for (i = 0; i < element_info[element].num_change_pages; i++)
3824         {
3825           /* check for player created from custom element as single target */
3826           content = element_info[element].change_page[i].target_element;
3827           is_player = ELEM_IS_PLAYER(content);
3828
3829           if (is_player && (found_rating < 3 ||
3830                             (found_rating == 3 && element < found_element)))
3831           {
3832             start_x = x;
3833             start_y = y;
3834
3835             found_rating = 3;
3836             found_element = element;
3837           }
3838         }
3839       }
3840
3841       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3842       {
3843         /* check for player created from custom element as explosion content */
3844         content = element_info[element].content.e[xx][yy];
3845         is_player = ELEM_IS_PLAYER(content);
3846
3847         if (is_player && (found_rating < 2 ||
3848                           (found_rating == 2 && element < found_element)))
3849         {
3850           start_x = x + xx - 1;
3851           start_y = y + yy - 1;
3852
3853           found_rating = 2;
3854           found_element = element;
3855         }
3856
3857         if (!CAN_CHANGE(element))
3858           continue;
3859
3860         for (i = 0; i < element_info[element].num_change_pages; i++)
3861         {
3862           /* check for player created from custom element as extended target */
3863           content =
3864             element_info[element].change_page[i].target_content.e[xx][yy];
3865
3866           is_player = ELEM_IS_PLAYER(content);
3867
3868           if (is_player && (found_rating < 1 ||
3869                             (found_rating == 1 && element < found_element)))
3870           {
3871             start_x = x + xx - 1;
3872             start_y = y + yy - 1;
3873
3874             found_rating = 1;
3875             found_element = element;
3876           }
3877         }
3878       }
3879     }
3880
3881     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3882                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3883                 start_x - MIDPOSX);
3884
3885     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3886                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3887                 start_y - MIDPOSY);
3888   }
3889   else
3890   {
3891     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3892                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3893                 local_player->jx - MIDPOSX);
3894
3895     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3896                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3897                 local_player->jy - MIDPOSY);
3898   }
3899
3900   /* !!! FIX THIS (START) !!! */
3901   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3902   {
3903     InitGameEngine_EM();
3904   }
3905   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3906   {
3907     InitGameEngine_SP();
3908   }
3909   else
3910   {
3911     DrawLevel(REDRAW_FIELD);
3912     DrawAllPlayers();
3913
3914     /* after drawing the level, correct some elements */
3915     if (game.timegate_time_left == 0)
3916       CloseAllOpenTimegates();
3917   }
3918
3919   /* blit playfield from scroll buffer to normal back buffer for fading in */
3920   BlitScreenToBitmap(backbuffer);
3921
3922   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3923   /* !!! FIX THIS (END) !!! */
3924
3925   FadeIn(REDRAW_FIELD);
3926
3927 #if 1
3928   // full screen redraw is required at this point in the following cases:
3929   // - special editor door undrawn when game was started from level editor
3930   // - drawing area (playfield) was changed and has to be removed completely
3931   redraw_mask = REDRAW_ALL;
3932   BackToFront();
3933 #endif
3934
3935   if (!game.restart_level)
3936   {
3937     /* copy default game door content to main double buffer */
3938
3939     /* !!! CHECK AGAIN !!! */
3940     SetPanelBackground();
3941     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3942     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3943   }
3944
3945   SetPanelBackground();
3946   SetDrawBackgroundMask(REDRAW_DOOR_1);
3947
3948   UpdateAndDisplayGameControlValues();
3949
3950   if (!game.restart_level)
3951   {
3952     UnmapGameButtons();
3953     UnmapTapeButtons();
3954     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3955     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3956     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3957     MapGameButtons();
3958     MapTapeButtons();
3959
3960     /* copy actual game door content to door double buffer for OpenDoor() */
3961     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3962
3963     OpenDoor(DOOR_OPEN_ALL);
3964
3965     PlaySound(SND_GAME_STARTING);
3966
3967     if (setup.sound_music)
3968       PlayLevelMusic();
3969
3970     KeyboardAutoRepeatOffUnlessAutoplay();
3971
3972 #if DEBUG_INIT_PLAYER
3973     if (options.debug)
3974     {
3975       printf("Player status (final):\n");
3976
3977       for (i = 0; i < MAX_PLAYERS; i++)
3978       {
3979         struct PlayerInfo *player = &stored_player[i];
3980
3981         printf("- player %d: present == %d, connected == %d, active == %d",
3982                i + 1,
3983                player->present,
3984                player->connected,
3985                player->active);
3986
3987         if (local_player == player)
3988           printf(" (local player)");
3989
3990         printf("\n");
3991       }
3992     }
3993 #endif
3994   }
3995
3996   UnmapAllGadgets();
3997
3998   MapGameButtons();
3999   MapTapeButtons();
4000
4001   if (!game.restart_level && !tape.playing)
4002   {
4003     LevelStats_incPlayed(level_nr);
4004
4005     SaveLevelSetup_SeriesInfo();
4006   }
4007
4008   game.restart_level = FALSE;
4009
4010   SaveEngineSnapshotToList();
4011 }
4012
4013 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4014 {
4015   /* this is used for non-R'n'D game engines to update certain engine values */
4016
4017   /* needed to determine if sounds are played within the visible screen area */
4018   scroll_x = actual_scroll_x;
4019   scroll_y = actual_scroll_y;
4020 }
4021
4022 void InitMovDir(int x, int y)
4023 {
4024   int i, element = Feld[x][y];
4025   static int xy[4][2] =
4026   {
4027     {  0, +1 },
4028     { +1,  0 },
4029     {  0, -1 },
4030     { -1,  0 }
4031   };
4032   static int direction[3][4] =
4033   {
4034     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4035     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4036     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4037   };
4038
4039   switch (element)
4040   {
4041     case EL_BUG_RIGHT:
4042     case EL_BUG_UP:
4043     case EL_BUG_LEFT:
4044     case EL_BUG_DOWN:
4045       Feld[x][y] = EL_BUG;
4046       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4047       break;
4048
4049     case EL_SPACESHIP_RIGHT:
4050     case EL_SPACESHIP_UP:
4051     case EL_SPACESHIP_LEFT:
4052     case EL_SPACESHIP_DOWN:
4053       Feld[x][y] = EL_SPACESHIP;
4054       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4055       break;
4056
4057     case EL_BD_BUTTERFLY_RIGHT:
4058     case EL_BD_BUTTERFLY_UP:
4059     case EL_BD_BUTTERFLY_LEFT:
4060     case EL_BD_BUTTERFLY_DOWN:
4061       Feld[x][y] = EL_BD_BUTTERFLY;
4062       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4063       break;
4064
4065     case EL_BD_FIREFLY_RIGHT:
4066     case EL_BD_FIREFLY_UP:
4067     case EL_BD_FIREFLY_LEFT:
4068     case EL_BD_FIREFLY_DOWN:
4069       Feld[x][y] = EL_BD_FIREFLY;
4070       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4071       break;
4072
4073     case EL_PACMAN_RIGHT:
4074     case EL_PACMAN_UP:
4075     case EL_PACMAN_LEFT:
4076     case EL_PACMAN_DOWN:
4077       Feld[x][y] = EL_PACMAN;
4078       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4079       break;
4080
4081     case EL_YAMYAM_LEFT:
4082     case EL_YAMYAM_RIGHT:
4083     case EL_YAMYAM_UP:
4084     case EL_YAMYAM_DOWN:
4085       Feld[x][y] = EL_YAMYAM;
4086       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4087       break;
4088
4089     case EL_SP_SNIKSNAK:
4090       MovDir[x][y] = MV_UP;
4091       break;
4092
4093     case EL_SP_ELECTRON:
4094       MovDir[x][y] = MV_LEFT;
4095       break;
4096
4097     case EL_MOLE_LEFT:
4098     case EL_MOLE_RIGHT:
4099     case EL_MOLE_UP:
4100     case EL_MOLE_DOWN:
4101       Feld[x][y] = EL_MOLE;
4102       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4103       break;
4104
4105     default:
4106       if (IS_CUSTOM_ELEMENT(element))
4107       {
4108         struct ElementInfo *ei = &element_info[element];
4109         int move_direction_initial = ei->move_direction_initial;
4110         int move_pattern = ei->move_pattern;
4111
4112         if (move_direction_initial == MV_START_PREVIOUS)
4113         {
4114           if (MovDir[x][y] != MV_NONE)
4115             return;
4116
4117           move_direction_initial = MV_START_AUTOMATIC;
4118         }
4119
4120         if (move_direction_initial == MV_START_RANDOM)
4121           MovDir[x][y] = 1 << RND(4);
4122         else if (move_direction_initial & MV_ANY_DIRECTION)
4123           MovDir[x][y] = move_direction_initial;
4124         else if (move_pattern == MV_ALL_DIRECTIONS ||
4125                  move_pattern == MV_TURNING_LEFT ||
4126                  move_pattern == MV_TURNING_RIGHT ||
4127                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4128                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4129                  move_pattern == MV_TURNING_RANDOM)
4130           MovDir[x][y] = 1 << RND(4);
4131         else if (move_pattern == MV_HORIZONTAL)
4132           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4133         else if (move_pattern == MV_VERTICAL)
4134           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4135         else if (move_pattern & MV_ANY_DIRECTION)
4136           MovDir[x][y] = element_info[element].move_pattern;
4137         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4138                  move_pattern == MV_ALONG_RIGHT_SIDE)
4139         {
4140           /* use random direction as default start direction */
4141           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4142             MovDir[x][y] = 1 << RND(4);
4143
4144           for (i = 0; i < NUM_DIRECTIONS; i++)
4145           {
4146             int x1 = x + xy[i][0];
4147             int y1 = y + xy[i][1];
4148
4149             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4150             {
4151               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4152                 MovDir[x][y] = direction[0][i];
4153               else
4154                 MovDir[x][y] = direction[1][i];
4155
4156               break;
4157             }
4158           }
4159         }                
4160       }
4161       else
4162       {
4163         MovDir[x][y] = 1 << RND(4);
4164
4165         if (element != EL_BUG &&
4166             element != EL_SPACESHIP &&
4167             element != EL_BD_BUTTERFLY &&
4168             element != EL_BD_FIREFLY)
4169           break;
4170
4171         for (i = 0; i < NUM_DIRECTIONS; i++)
4172         {
4173           int x1 = x + xy[i][0];
4174           int y1 = y + xy[i][1];
4175
4176           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4177           {
4178             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4179             {
4180               MovDir[x][y] = direction[0][i];
4181               break;
4182             }
4183             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4184                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4185             {
4186               MovDir[x][y] = direction[1][i];
4187               break;
4188             }
4189           }
4190         }
4191       }
4192       break;
4193   }
4194
4195   GfxDir[x][y] = MovDir[x][y];
4196 }
4197
4198 void InitAmoebaNr(int x, int y)
4199 {
4200   int i;
4201   int group_nr = AmoebeNachbarNr(x, y);
4202
4203   if (group_nr == 0)
4204   {
4205     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4206     {
4207       if (AmoebaCnt[i] == 0)
4208       {
4209         group_nr = i;
4210         break;
4211       }
4212     }
4213   }
4214
4215   AmoebaNr[x][y] = group_nr;
4216   AmoebaCnt[group_nr]++;
4217   AmoebaCnt2[group_nr]++;
4218 }
4219
4220 static void PlayerWins(struct PlayerInfo *player)
4221 {
4222   player->LevelSolved = TRUE;
4223   player->GameOver = TRUE;
4224
4225   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4226                          level.native_em_level->lev->score : player->score);
4227
4228   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4229                                       TimeLeft);
4230   player->LevelSolved_CountingScore = player->score_final;
4231 }
4232
4233 void GameWon()
4234 {
4235   static int time, time_final;
4236   static int score, score_final;
4237   static int game_over_delay_1 = 0;
4238   static int game_over_delay_2 = 0;
4239   int game_over_delay_value_1 = 50;
4240   int game_over_delay_value_2 = 50;
4241
4242   if (!local_player->LevelSolved_GameWon)
4243   {
4244     int i;
4245
4246     /* do not start end game actions before the player stops moving (to exit) */
4247     if (local_player->MovPos)
4248       return;
4249
4250     local_player->LevelSolved_GameWon = TRUE;
4251     local_player->LevelSolved_SaveTape = tape.recording;
4252     local_player->LevelSolved_SaveScore = !tape.playing;
4253
4254     if (!tape.playing)
4255     {
4256       LevelStats_incSolved(level_nr);
4257
4258       SaveLevelSetup_SeriesInfo();
4259     }
4260
4261     if (tape.auto_play)         /* tape might already be stopped here */
4262       tape.auto_play_level_solved = TRUE;
4263
4264     TapeStop();
4265
4266     game_over_delay_1 = game_over_delay_value_1;
4267     game_over_delay_2 = game_over_delay_value_2;
4268
4269     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4270     score = score_final = local_player->score_final;
4271
4272     if (TimeLeft > 0)
4273     {
4274       time_final = 0;
4275       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4276     }
4277     else if (game.no_time_limit && TimePlayed < 999)
4278     {
4279       time_final = 999;
4280       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4281     }
4282
4283     local_player->score_final = score_final;
4284
4285     if (level_editor_test_game)
4286     {
4287       time = time_final;
4288       score = score_final;
4289
4290       local_player->LevelSolved_CountingTime = time;
4291       local_player->LevelSolved_CountingScore = score;
4292
4293       game_panel_controls[GAME_PANEL_TIME].value = time;
4294       game_panel_controls[GAME_PANEL_SCORE].value = score;
4295
4296       DisplayGameControlValues();
4297     }
4298
4299     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4300     {
4301       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4302       {
4303         /* close exit door after last player */
4304         if ((AllPlayersGone &&
4305              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4306               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4307               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4308             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4309             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4310         {
4311           int element = Feld[ExitX][ExitY];
4312
4313           Feld[ExitX][ExitY] =
4314             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4315              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4316              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4317              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4318              EL_EM_STEEL_EXIT_CLOSING);
4319
4320           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4321         }
4322
4323         /* player disappears */
4324         DrawLevelField(ExitX, ExitY);
4325       }
4326
4327       for (i = 0; i < MAX_PLAYERS; i++)
4328       {
4329         struct PlayerInfo *player = &stored_player[i];
4330
4331         if (player->present)
4332         {
4333           RemovePlayer(player);
4334
4335           /* player disappears */
4336           DrawLevelField(player->jx, player->jy);
4337         }
4338       }
4339     }
4340
4341     PlaySound(SND_GAME_WINNING);
4342   }
4343
4344   if (game_over_delay_1 > 0)
4345   {
4346     game_over_delay_1--;
4347
4348     return;
4349   }
4350
4351   if (time != time_final)
4352   {
4353     int time_to_go = ABS(time_final - time);
4354     int time_count_dir = (time < time_final ? +1 : -1);
4355     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4356
4357     time  += time_count_steps * time_count_dir;
4358     score += time_count_steps * level.score[SC_TIME_BONUS];
4359
4360     local_player->LevelSolved_CountingTime = time;
4361     local_player->LevelSolved_CountingScore = score;
4362
4363     game_panel_controls[GAME_PANEL_TIME].value = time;
4364     game_panel_controls[GAME_PANEL_SCORE].value = score;
4365
4366     DisplayGameControlValues();
4367
4368     if (time == time_final)
4369       StopSound(SND_GAME_LEVELTIME_BONUS);
4370     else if (setup.sound_loops)
4371       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4372     else
4373       PlaySound(SND_GAME_LEVELTIME_BONUS);
4374
4375     return;
4376   }
4377
4378   local_player->LevelSolved_PanelOff = TRUE;
4379
4380   if (game_over_delay_2 > 0)
4381   {
4382     game_over_delay_2--;
4383
4384     return;
4385   }
4386
4387   GameEnd();
4388 }
4389
4390 void GameEnd()
4391 {
4392   int hi_pos;
4393   boolean raise_level = FALSE;
4394
4395   local_player->LevelSolved_GameEnd = TRUE;
4396
4397   CloseDoor(DOOR_CLOSE_1);
4398
4399   if (local_player->LevelSolved_SaveTape)
4400   {
4401     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4402   }
4403
4404   if (level_editor_test_game)
4405   {
4406     game_status = GAME_MODE_MAIN;
4407
4408     DrawAndFadeInMainMenu(REDRAW_FIELD);
4409
4410     return;
4411   }
4412
4413   if (!local_player->LevelSolved_SaveScore)
4414   {
4415     FadeOut(REDRAW_FIELD);
4416
4417     game_status = GAME_MODE_MAIN;
4418
4419     DrawAndFadeInMainMenu(REDRAW_FIELD);
4420
4421     return;
4422   }
4423
4424   if (level_nr == leveldir_current->handicap_level)
4425   {
4426     leveldir_current->handicap_level++;
4427
4428     SaveLevelSetup_SeriesInfo();
4429   }
4430
4431   if (level_nr < leveldir_current->last_level)
4432     raise_level = TRUE;                 /* advance to next level */
4433
4434   if ((hi_pos = NewHiScore()) >= 0) 
4435   {
4436     game_status = GAME_MODE_SCORES;
4437
4438     DrawHallOfFame(hi_pos);
4439
4440     if (raise_level)
4441     {
4442       level_nr++;
4443       TapeErase();
4444     }
4445   }
4446   else
4447   {
4448     FadeOut(REDRAW_FIELD);
4449
4450     game_status = GAME_MODE_MAIN;
4451
4452     if (raise_level)
4453     {
4454       level_nr++;
4455       TapeErase();
4456     }
4457
4458     DrawAndFadeInMainMenu(REDRAW_FIELD);
4459   }
4460 }
4461
4462 int NewHiScore()
4463 {
4464   int k, l;
4465   int position = -1;
4466
4467   LoadScore(level_nr);
4468
4469   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4470       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4471     return -1;
4472
4473   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4474   {
4475     if (local_player->score_final > highscore[k].Score)
4476     {
4477       /* player has made it to the hall of fame */
4478
4479       if (k < MAX_SCORE_ENTRIES - 1)
4480       {
4481         int m = MAX_SCORE_ENTRIES - 1;
4482
4483 #ifdef ONE_PER_NAME
4484         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4485           if (strEqual(setup.player_name, highscore[l].Name))
4486             m = l;
4487         if (m == k)     /* player's new highscore overwrites his old one */
4488           goto put_into_list;
4489 #endif
4490
4491         for (l = m; l > k; l--)
4492         {
4493           strcpy(highscore[l].Name, highscore[l - 1].Name);
4494           highscore[l].Score = highscore[l - 1].Score;
4495         }
4496       }
4497
4498 #ifdef ONE_PER_NAME
4499       put_into_list:
4500 #endif
4501       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4502       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4503       highscore[k].Score = local_player->score_final; 
4504       position = k;
4505       break;
4506     }
4507
4508 #ifdef ONE_PER_NAME
4509     else if (!strncmp(setup.player_name, highscore[k].Name,
4510                       MAX_PLAYER_NAME_LEN))
4511       break;    /* player already there with a higher score */
4512 #endif
4513
4514   }
4515
4516   if (position >= 0) 
4517     SaveScore(level_nr);
4518
4519   return position;
4520 }
4521
4522 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4523 {
4524   int element = Feld[x][y];
4525   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4526   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4527   int horiz_move = (dx != 0);
4528   int sign = (horiz_move ? dx : dy);
4529   int step = sign * element_info[element].move_stepsize;
4530
4531   /* special values for move stepsize for spring and things on conveyor belt */
4532   if (horiz_move)
4533   {
4534     if (CAN_FALL(element) &&
4535         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4536       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4537     else if (element == EL_SPRING)
4538       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4539   }
4540
4541   return step;
4542 }
4543
4544 inline static int getElementMoveStepsize(int x, int y)
4545 {
4546   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4547 }
4548
4549 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4550 {
4551   if (player->GfxAction != action || player->GfxDir != dir)
4552   {
4553     player->GfxAction = action;
4554     player->GfxDir = dir;
4555     player->Frame = 0;
4556     player->StepFrame = 0;
4557   }
4558 }
4559
4560 static void ResetGfxFrame(int x, int y, boolean redraw)
4561 {
4562   int element = Feld[x][y];
4563   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4564   int last_gfx_frame = GfxFrame[x][y];
4565
4566   if (graphic_info[graphic].anim_global_sync)
4567     GfxFrame[x][y] = FrameCounter;
4568   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4569     GfxFrame[x][y] = CustomValue[x][y];
4570   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4571     GfxFrame[x][y] = element_info[element].collect_score;
4572   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4573     GfxFrame[x][y] = ChangeDelay[x][y];
4574
4575   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4576     DrawLevelGraphicAnimation(x, y, graphic);
4577 }
4578
4579 static void ResetGfxAnimation(int x, int y)
4580 {
4581   GfxAction[x][y] = ACTION_DEFAULT;
4582   GfxDir[x][y] = MovDir[x][y];
4583   GfxFrame[x][y] = 0;
4584
4585   ResetGfxFrame(x, y, FALSE);
4586 }
4587
4588 static void ResetRandomAnimationValue(int x, int y)
4589 {
4590   GfxRandom[x][y] = INIT_GFX_RANDOM();
4591 }
4592
4593 void InitMovingField(int x, int y, int direction)
4594 {
4595   int element = Feld[x][y];
4596   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4597   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4598   int newx = x + dx;
4599   int newy = y + dy;
4600   boolean is_moving_before, is_moving_after;
4601
4602   /* check if element was/is moving or being moved before/after mode change */
4603   is_moving_before = (WasJustMoving[x][y] != 0);
4604   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4605
4606   /* reset animation only for moving elements which change direction of moving
4607      or which just started or stopped moving
4608      (else CEs with property "can move" / "not moving" are reset each frame) */
4609   if (is_moving_before != is_moving_after ||
4610       direction != MovDir[x][y])
4611     ResetGfxAnimation(x, y);
4612
4613   MovDir[x][y] = direction;
4614   GfxDir[x][y] = direction;
4615
4616   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4617                      direction == MV_DOWN && CAN_FALL(element) ?
4618                      ACTION_FALLING : ACTION_MOVING);
4619
4620   /* this is needed for CEs with property "can move" / "not moving" */
4621
4622   if (is_moving_after)
4623   {
4624     if (Feld[newx][newy] == EL_EMPTY)
4625       Feld[newx][newy] = EL_BLOCKED;
4626
4627     MovDir[newx][newy] = MovDir[x][y];
4628
4629     CustomValue[newx][newy] = CustomValue[x][y];
4630
4631     GfxFrame[newx][newy] = GfxFrame[x][y];
4632     GfxRandom[newx][newy] = GfxRandom[x][y];
4633     GfxAction[newx][newy] = GfxAction[x][y];
4634     GfxDir[newx][newy] = GfxDir[x][y];
4635   }
4636 }
4637
4638 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4639 {
4640   int direction = MovDir[x][y];
4641   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4642   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4643
4644   *goes_to_x = newx;
4645   *goes_to_y = newy;
4646 }
4647
4648 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4649 {
4650   int oldx = x, oldy = y;
4651   int direction = MovDir[x][y];
4652
4653   if (direction == MV_LEFT)
4654     oldx++;
4655   else if (direction == MV_RIGHT)
4656     oldx--;
4657   else if (direction == MV_UP)
4658     oldy++;
4659   else if (direction == MV_DOWN)
4660     oldy--;
4661
4662   *comes_from_x = oldx;
4663   *comes_from_y = oldy;
4664 }
4665
4666 int MovingOrBlocked2Element(int x, int y)
4667 {
4668   int element = Feld[x][y];
4669
4670   if (element == EL_BLOCKED)
4671   {
4672     int oldx, oldy;
4673
4674     Blocked2Moving(x, y, &oldx, &oldy);
4675     return Feld[oldx][oldy];
4676   }
4677   else
4678     return element;
4679 }
4680
4681 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4682 {
4683   /* like MovingOrBlocked2Element(), but if element is moving
4684      and (x,y) is the field the moving element is just leaving,
4685      return EL_BLOCKED instead of the element value */
4686   int element = Feld[x][y];
4687
4688   if (IS_MOVING(x, y))
4689   {
4690     if (element == EL_BLOCKED)
4691     {
4692       int oldx, oldy;
4693
4694       Blocked2Moving(x, y, &oldx, &oldy);
4695       return Feld[oldx][oldy];
4696     }
4697     else
4698       return EL_BLOCKED;
4699   }
4700   else
4701     return element;
4702 }
4703
4704 static void RemoveField(int x, int y)
4705 {
4706   Feld[x][y] = EL_EMPTY;
4707
4708   MovPos[x][y] = 0;
4709   MovDir[x][y] = 0;
4710   MovDelay[x][y] = 0;
4711
4712   CustomValue[x][y] = 0;
4713
4714   AmoebaNr[x][y] = 0;
4715   ChangeDelay[x][y] = 0;
4716   ChangePage[x][y] = -1;
4717   Pushed[x][y] = FALSE;
4718
4719   GfxElement[x][y] = EL_UNDEFINED;
4720   GfxAction[x][y] = ACTION_DEFAULT;
4721   GfxDir[x][y] = MV_NONE;
4722 }
4723
4724 void RemoveMovingField(int x, int y)
4725 {
4726   int oldx = x, oldy = y, newx = x, newy = y;
4727   int element = Feld[x][y];
4728   int next_element = EL_UNDEFINED;
4729
4730   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4731     return;
4732
4733   if (IS_MOVING(x, y))
4734   {
4735     Moving2Blocked(x, y, &newx, &newy);
4736
4737     if (Feld[newx][newy] != EL_BLOCKED)
4738     {
4739       /* element is moving, but target field is not free (blocked), but
4740          already occupied by something different (example: acid pool);
4741          in this case, only remove the moving field, but not the target */
4742
4743       RemoveField(oldx, oldy);
4744
4745       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4746
4747       TEST_DrawLevelField(oldx, oldy);
4748
4749       return;
4750     }
4751   }
4752   else if (element == EL_BLOCKED)
4753   {
4754     Blocked2Moving(x, y, &oldx, &oldy);
4755     if (!IS_MOVING(oldx, oldy))
4756       return;
4757   }
4758
4759   if (element == EL_BLOCKED &&
4760       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4761        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4762        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4763        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4764        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4765        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4766     next_element = get_next_element(Feld[oldx][oldy]);
4767
4768   RemoveField(oldx, oldy);
4769   RemoveField(newx, newy);
4770
4771   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4772
4773   if (next_element != EL_UNDEFINED)
4774     Feld[oldx][oldy] = next_element;
4775
4776   TEST_DrawLevelField(oldx, oldy);
4777   TEST_DrawLevelField(newx, newy);
4778 }
4779
4780 void DrawDynamite(int x, int y)
4781 {
4782   int sx = SCREENX(x), sy = SCREENY(y);
4783   int graphic = el2img(Feld[x][y]);
4784   int frame;
4785
4786   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4787     return;
4788
4789   if (IS_WALKABLE_INSIDE(Back[x][y]))
4790     return;
4791
4792   if (Back[x][y])
4793     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4794   else if (Store[x][y])
4795     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4796
4797   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4798
4799   if (Back[x][y] || Store[x][y])
4800     DrawGraphicThruMask(sx, sy, graphic, frame);
4801   else
4802     DrawGraphic(sx, sy, graphic, frame);
4803 }
4804
4805 void CheckDynamite(int x, int y)
4806 {
4807   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4808   {
4809     MovDelay[x][y]--;
4810
4811     if (MovDelay[x][y] != 0)
4812     {
4813       DrawDynamite(x, y);
4814       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4815
4816       return;
4817     }
4818   }
4819
4820   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4821
4822   Bang(x, y);
4823 }
4824
4825 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4826 {
4827   boolean num_checked_players = 0;
4828   int i;
4829
4830   for (i = 0; i < MAX_PLAYERS; i++)
4831   {
4832     if (stored_player[i].active)
4833     {
4834       int sx = stored_player[i].jx;
4835       int sy = stored_player[i].jy;
4836
4837       if (num_checked_players == 0)
4838       {
4839         *sx1 = *sx2 = sx;
4840         *sy1 = *sy2 = sy;
4841       }
4842       else
4843       {
4844         *sx1 = MIN(*sx1, sx);
4845         *sy1 = MIN(*sy1, sy);
4846         *sx2 = MAX(*sx2, sx);
4847         *sy2 = MAX(*sy2, sy);
4848       }
4849
4850       num_checked_players++;
4851     }
4852   }
4853 }
4854
4855 static boolean checkIfAllPlayersFitToScreen_RND()
4856 {
4857   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4858
4859   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4860
4861   return (sx2 - sx1 < SCR_FIELDX &&
4862           sy2 - sy1 < SCR_FIELDY);
4863 }
4864
4865 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4866 {
4867   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4868
4869   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4870
4871   *sx = (sx1 + sx2) / 2;
4872   *sy = (sy1 + sy2) / 2;
4873 }
4874
4875 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4876                         boolean center_screen, boolean quick_relocation)
4877 {
4878   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4879   boolean no_delay = (tape.warp_forward);
4880   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4881   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4882
4883   if (quick_relocation)
4884   {
4885     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4886     {
4887       if (!level.shifted_relocation || center_screen)
4888       {
4889         /* quick relocation (without scrolling), with centering of screen */
4890
4891         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4892                     x > SBX_Right + MIDPOSX ? SBX_Right :
4893                     x - MIDPOSX);
4894
4895         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4896                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4897                     y - MIDPOSY);
4898       }
4899       else
4900       {
4901         /* quick relocation (without scrolling), but do not center screen */
4902
4903         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4904                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4905                                old_x - MIDPOSX);
4906
4907         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4908                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4909                                old_y - MIDPOSY);
4910
4911         int offset_x = x + (scroll_x - center_scroll_x);
4912         int offset_y = y + (scroll_y - center_scroll_y);
4913
4914         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4915                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4916                     offset_x - MIDPOSX);
4917
4918         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4919                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4920                     offset_y - MIDPOSY);
4921       }
4922     }
4923     else
4924     {
4925       if (!level.shifted_relocation || center_screen)
4926       {
4927         /* quick relocation (without scrolling), with centering of screen */
4928
4929         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4930                     x > SBX_Right + MIDPOSX ? SBX_Right :
4931                     x - MIDPOSX);
4932
4933         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4934                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4935                     y - MIDPOSY);
4936       }
4937       else
4938       {
4939         /* quick relocation (without scrolling), but do not center screen */
4940
4941         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4942                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4943                                old_x - MIDPOSX);
4944
4945         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4946                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4947                                old_y - MIDPOSY);
4948
4949         int offset_x = x + (scroll_x - center_scroll_x);
4950         int offset_y = y + (scroll_y - center_scroll_y);
4951
4952         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4953                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4954                     offset_x - MIDPOSX);
4955
4956         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4957                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4958                     offset_y - MIDPOSY);
4959       }
4960     }
4961
4962     RedrawPlayfield(TRUE, 0,0,0,0);
4963   }
4964   else
4965   {
4966     int scroll_xx, scroll_yy;
4967
4968     if (!level.shifted_relocation || center_screen)
4969     {
4970       /* visible relocation (with scrolling), with centering of screen */
4971
4972       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4973                    x > SBX_Right + MIDPOSX ? SBX_Right :
4974                    x - MIDPOSX);
4975
4976       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4977                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4978                    y - MIDPOSY);
4979     }
4980     else
4981     {
4982       /* visible relocation (with scrolling), but do not center screen */
4983
4984       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4985                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4986                              old_x - MIDPOSX);
4987
4988       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4989                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4990                              old_y - MIDPOSY);
4991
4992       int offset_x = x + (scroll_x - center_scroll_x);
4993       int offset_y = y + (scroll_y - center_scroll_y);
4994
4995       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4996                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4997                    offset_x - MIDPOSX);
4998
4999       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5000                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5001                    offset_y - MIDPOSY);
5002     }
5003
5004
5005     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5006
5007     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5008     {
5009       int dx = 0, dy = 0;
5010       int fx = FX, fy = FY;
5011
5012       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5013       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5014
5015       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5016         break;
5017
5018       scroll_x -= dx;
5019       scroll_y -= dy;
5020
5021       fx += dx * TILEX / 2;
5022       fy += dy * TILEY / 2;
5023
5024       ScrollLevel(dx, dy);
5025       DrawAllPlayers();
5026
5027       /* scroll in two steps of half tile size to make things smoother */
5028       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5029       Delay(wait_delay_value);
5030
5031       /* scroll second step to align at full tile size */
5032       BackToFront();
5033       Delay(wait_delay_value);
5034     }
5035
5036     DrawAllPlayers();
5037     BackToFront();
5038     Delay(wait_delay_value);
5039   }
5040 }
5041
5042 void RelocatePlayer(int jx, int jy, int el_player_raw)
5043 {
5044   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5045   int player_nr = GET_PLAYER_NR(el_player);
5046   struct PlayerInfo *player = &stored_player[player_nr];
5047   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5048   boolean no_delay = (tape.warp_forward);
5049   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5050   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5051   int old_jx = player->jx;
5052   int old_jy = player->jy;
5053   int old_element = Feld[old_jx][old_jy];
5054   int element = Feld[jx][jy];
5055   boolean player_relocated = (old_jx != jx || old_jy != jy);
5056
5057   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5058   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5059   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5060   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5061   int leave_side_horiz = move_dir_horiz;
5062   int leave_side_vert  = move_dir_vert;
5063   int enter_side = enter_side_horiz | enter_side_vert;
5064   int leave_side = leave_side_horiz | leave_side_vert;
5065
5066   if (player->GameOver)         /* do not reanimate dead player */
5067     return;
5068
5069   if (!player_relocated)        /* no need to relocate the player */
5070     return;
5071
5072   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5073   {
5074     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5075     DrawLevelField(jx, jy);
5076   }
5077
5078   if (player->present)
5079   {
5080     while (player->MovPos)
5081     {
5082       ScrollPlayer(player, SCROLL_GO_ON);
5083       ScrollScreen(NULL, SCROLL_GO_ON);
5084
5085       AdvanceFrameAndPlayerCounters(player->index_nr);
5086
5087       DrawPlayer(player);
5088
5089       BackToFront();
5090       Delay(wait_delay_value);
5091     }
5092
5093     DrawPlayer(player);         /* needed here only to cleanup last field */
5094     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5095
5096     player->is_moving = FALSE;
5097   }
5098
5099   if (IS_CUSTOM_ELEMENT(old_element))
5100     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5101                                CE_LEFT_BY_PLAYER,
5102                                player->index_bit, leave_side);
5103
5104   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5105                                       CE_PLAYER_LEAVES_X,
5106                                       player->index_bit, leave_side);
5107
5108   Feld[jx][jy] = el_player;
5109   InitPlayerField(jx, jy, el_player, TRUE);
5110
5111   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5112      possible that the relocation target field did not contain a player element,
5113      but a walkable element, to which the new player was relocated -- in this
5114      case, restore that (already initialized!) element on the player field */
5115   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5116   {
5117     Feld[jx][jy] = element;     /* restore previously existing element */
5118   }
5119
5120   /* only visually relocate centered player */
5121   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5122                      FALSE, level.instant_relocation);
5123
5124   TestIfPlayerTouchesBadThing(jx, jy);
5125   TestIfPlayerTouchesCustomElement(jx, jy);
5126
5127   if (IS_CUSTOM_ELEMENT(element))
5128     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5129                                player->index_bit, enter_side);
5130
5131   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5132                                       player->index_bit, enter_side);
5133
5134   if (player->is_switching)
5135   {
5136     /* ensure that relocation while still switching an element does not cause
5137        a new element to be treated as also switched directly after relocation
5138        (this is important for teleporter switches that teleport the player to
5139        a place where another teleporter switch is in the same direction, which
5140        would then incorrectly be treated as immediately switched before the
5141        direction key that caused the switch was released) */
5142
5143     player->switch_x += jx - old_jx;
5144     player->switch_y += jy - old_jy;
5145   }
5146 }
5147
5148 void Explode(int ex, int ey, int phase, int mode)
5149 {
5150   int x, y;
5151   int last_phase;
5152   int border_element;
5153
5154   /* !!! eliminate this variable !!! */
5155   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5156
5157   if (game.explosions_delayed)
5158   {
5159     ExplodeField[ex][ey] = mode;
5160     return;
5161   }
5162
5163   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5164   {
5165     int center_element = Feld[ex][ey];
5166     int artwork_element, explosion_element;     /* set these values later */
5167
5168     /* remove things displayed in background while burning dynamite */
5169     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5170       Back[ex][ey] = 0;
5171
5172     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5173     {
5174       /* put moving element to center field (and let it explode there) */
5175       center_element = MovingOrBlocked2Element(ex, ey);
5176       RemoveMovingField(ex, ey);
5177       Feld[ex][ey] = center_element;
5178     }
5179
5180     /* now "center_element" is finally determined -- set related values now */
5181     artwork_element = center_element;           /* for custom player artwork */
5182     explosion_element = center_element;         /* for custom player artwork */
5183
5184     if (IS_PLAYER(ex, ey))
5185     {
5186       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5187
5188       artwork_element = stored_player[player_nr].artwork_element;
5189
5190       if (level.use_explosion_element[player_nr])
5191       {
5192         explosion_element = level.explosion_element[player_nr];
5193         artwork_element = explosion_element;
5194       }
5195     }
5196
5197     if (mode == EX_TYPE_NORMAL ||
5198         mode == EX_TYPE_CENTER ||
5199         mode == EX_TYPE_CROSS)
5200       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5201
5202     last_phase = element_info[explosion_element].explosion_delay + 1;
5203
5204     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5205     {
5206       int xx = x - ex + 1;
5207       int yy = y - ey + 1;
5208       int element;
5209
5210       if (!IN_LEV_FIELD(x, y) ||
5211           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5212           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5213         continue;
5214
5215       element = Feld[x][y];
5216
5217       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5218       {
5219         element = MovingOrBlocked2Element(x, y);
5220
5221         if (!IS_EXPLOSION_PROOF(element))
5222           RemoveMovingField(x, y);
5223       }
5224
5225       /* indestructible elements can only explode in center (but not flames) */
5226       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5227                                            mode == EX_TYPE_BORDER)) ||
5228           element == EL_FLAMES)
5229         continue;
5230
5231       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5232          behaviour, for example when touching a yamyam that explodes to rocks
5233          with active deadly shield, a rock is created under the player !!! */
5234       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5235 #if 0
5236       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5237           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5238            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5239 #else
5240       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5241 #endif
5242       {
5243         if (IS_ACTIVE_BOMB(element))
5244         {
5245           /* re-activate things under the bomb like gate or penguin */
5246           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5247           Back[x][y] = 0;
5248         }
5249
5250         continue;
5251       }
5252
5253       /* save walkable background elements while explosion on same tile */
5254       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5255           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5256         Back[x][y] = element;
5257
5258       /* ignite explodable elements reached by other explosion */
5259       if (element == EL_EXPLOSION)
5260         element = Store2[x][y];
5261
5262       if (AmoebaNr[x][y] &&
5263           (element == EL_AMOEBA_FULL ||
5264            element == EL_BD_AMOEBA ||
5265            element == EL_AMOEBA_GROWING))
5266       {
5267         AmoebaCnt[AmoebaNr[x][y]]--;
5268         AmoebaCnt2[AmoebaNr[x][y]]--;
5269       }
5270
5271       RemoveField(x, y);
5272
5273       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5274       {
5275         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5276
5277         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5278
5279         if (PLAYERINFO(ex, ey)->use_murphy)
5280           Store[x][y] = EL_EMPTY;
5281       }
5282
5283       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5284          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5285       else if (ELEM_IS_PLAYER(center_element))
5286         Store[x][y] = EL_EMPTY;
5287       else if (center_element == EL_YAMYAM)
5288         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5289       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5290         Store[x][y] = element_info[center_element].content.e[xx][yy];
5291 #if 1
5292       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5293          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5294          otherwise) -- FIX THIS !!! */
5295       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5296         Store[x][y] = element_info[element].content.e[1][1];
5297 #else
5298       else if (!CAN_EXPLODE(element))
5299         Store[x][y] = element_info[element].content.e[1][1];
5300 #endif
5301       else
5302         Store[x][y] = EL_EMPTY;
5303
5304       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5305           center_element == EL_AMOEBA_TO_DIAMOND)
5306         Store2[x][y] = element;
5307
5308       Feld[x][y] = EL_EXPLOSION;
5309       GfxElement[x][y] = artwork_element;
5310
5311       ExplodePhase[x][y] = 1;
5312       ExplodeDelay[x][y] = last_phase;
5313
5314       Stop[x][y] = TRUE;
5315     }
5316
5317     if (center_element == EL_YAMYAM)
5318       game.yamyam_content_nr =
5319         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5320
5321     return;
5322   }
5323
5324   if (Stop[ex][ey])
5325     return;
5326
5327   x = ex;
5328   y = ey;
5329
5330   if (phase == 1)
5331     GfxFrame[x][y] = 0;         /* restart explosion animation */
5332
5333   last_phase = ExplodeDelay[x][y];
5334
5335   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5336
5337   /* this can happen if the player leaves an explosion just in time */
5338   if (GfxElement[x][y] == EL_UNDEFINED)
5339     GfxElement[x][y] = EL_EMPTY;
5340
5341   border_element = Store2[x][y];
5342   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5343     border_element = StorePlayer[x][y];
5344
5345   if (phase == element_info[border_element].ignition_delay ||
5346       phase == last_phase)
5347   {
5348     boolean border_explosion = FALSE;
5349
5350     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5351         !PLAYER_EXPLOSION_PROTECTED(x, y))
5352     {
5353       KillPlayerUnlessExplosionProtected(x, y);
5354       border_explosion = TRUE;
5355     }
5356     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5357     {
5358       Feld[x][y] = Store2[x][y];
5359       Store2[x][y] = 0;
5360       Bang(x, y);
5361       border_explosion = TRUE;
5362     }
5363     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5364     {
5365       AmoebeUmwandeln(x, y);
5366       Store2[x][y] = 0;
5367       border_explosion = TRUE;
5368     }
5369
5370     /* if an element just explodes due to another explosion (chain-reaction),
5371        do not immediately end the new explosion when it was the last frame of
5372        the explosion (as it would be done in the following "if"-statement!) */
5373     if (border_explosion && phase == last_phase)
5374       return;
5375   }
5376
5377   if (phase == last_phase)
5378   {
5379     int element;
5380
5381     element = Feld[x][y] = Store[x][y];
5382     Store[x][y] = Store2[x][y] = 0;
5383     GfxElement[x][y] = EL_UNDEFINED;
5384
5385     /* player can escape from explosions and might therefore be still alive */
5386     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5387         element <= EL_PLAYER_IS_EXPLODING_4)
5388     {
5389       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5390       int explosion_element = EL_PLAYER_1 + player_nr;
5391       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5392       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5393
5394       if (level.use_explosion_element[player_nr])
5395         explosion_element = level.explosion_element[player_nr];
5396
5397       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5398                     element_info[explosion_element].content.e[xx][yy]);
5399     }
5400
5401     /* restore probably existing indestructible background element */
5402     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5403       element = Feld[x][y] = Back[x][y];
5404     Back[x][y] = 0;
5405
5406     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5407     GfxDir[x][y] = MV_NONE;
5408     ChangeDelay[x][y] = 0;
5409     ChangePage[x][y] = -1;
5410
5411     CustomValue[x][y] = 0;
5412
5413     InitField_WithBug2(x, y, FALSE);
5414
5415     TEST_DrawLevelField(x, y);
5416
5417     TestIfElementTouchesCustomElement(x, y);
5418
5419     if (GFX_CRUMBLED(element))
5420       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5421
5422     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5423       StorePlayer[x][y] = 0;
5424
5425     if (ELEM_IS_PLAYER(element))
5426       RelocatePlayer(x, y, element);
5427   }
5428   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5429   {
5430     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5431     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5432
5433     if (phase == delay)
5434       TEST_DrawLevelFieldCrumbled(x, y);
5435
5436     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5437     {
5438       DrawLevelElement(x, y, Back[x][y]);
5439       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5440     }
5441     else if (IS_WALKABLE_UNDER(Back[x][y]))
5442     {
5443       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5444       DrawLevelElementThruMask(x, y, Back[x][y]);
5445     }
5446     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5447       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5448   }
5449 }
5450
5451 void DynaExplode(int ex, int ey)
5452 {
5453   int i, j;
5454   int dynabomb_element = Feld[ex][ey];
5455   int dynabomb_size = 1;
5456   boolean dynabomb_xl = FALSE;
5457   struct PlayerInfo *player;
5458   static int xy[4][2] =
5459   {
5460     { 0, -1 },
5461     { -1, 0 },
5462     { +1, 0 },
5463     { 0, +1 }
5464   };
5465
5466   if (IS_ACTIVE_BOMB(dynabomb_element))
5467   {
5468     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5469     dynabomb_size = player->dynabomb_size;
5470     dynabomb_xl = player->dynabomb_xl;
5471     player->dynabombs_left++;
5472   }
5473
5474   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5475
5476   for (i = 0; i < NUM_DIRECTIONS; i++)
5477   {
5478     for (j = 1; j <= dynabomb_size; j++)
5479     {
5480       int x = ex + j * xy[i][0];
5481       int y = ey + j * xy[i][1];
5482       int element;
5483
5484       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5485         break;
5486
5487       element = Feld[x][y];
5488
5489       /* do not restart explosions of fields with active bombs */
5490       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5491         continue;
5492
5493       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5494
5495       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5496           !IS_DIGGABLE(element) && !dynabomb_xl)
5497         break;
5498     }
5499   }
5500 }
5501
5502 void Bang(int x, int y)
5503 {
5504   int element = MovingOrBlocked2Element(x, y);
5505   int explosion_type = EX_TYPE_NORMAL;
5506
5507   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5508   {
5509     struct PlayerInfo *player = PLAYERINFO(x, y);
5510
5511     element = Feld[x][y] = player->initial_element;
5512
5513     if (level.use_explosion_element[player->index_nr])
5514     {
5515       int explosion_element = level.explosion_element[player->index_nr];
5516
5517       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5518         explosion_type = EX_TYPE_CROSS;
5519       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5520         explosion_type = EX_TYPE_CENTER;
5521     }
5522   }
5523
5524   switch (element)
5525   {
5526     case EL_BUG:
5527     case EL_SPACESHIP:
5528     case EL_BD_BUTTERFLY:
5529     case EL_BD_FIREFLY:
5530     case EL_YAMYAM:
5531     case EL_DARK_YAMYAM:
5532     case EL_ROBOT:
5533     case EL_PACMAN:
5534     case EL_MOLE:
5535       RaiseScoreElement(element);
5536       break;
5537
5538     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5539     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5540     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5541     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5542     case EL_DYNABOMB_INCREASE_NUMBER:
5543     case EL_DYNABOMB_INCREASE_SIZE:
5544     case EL_DYNABOMB_INCREASE_POWER:
5545       explosion_type = EX_TYPE_DYNA;
5546       break;
5547
5548     case EL_DC_LANDMINE:
5549       explosion_type = EX_TYPE_CENTER;
5550       break;
5551
5552     case EL_PENGUIN:
5553     case EL_LAMP:
5554     case EL_LAMP_ACTIVE:
5555     case EL_AMOEBA_TO_DIAMOND:
5556       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5557         explosion_type = EX_TYPE_CENTER;
5558       break;
5559
5560     default:
5561       if (element_info[element].explosion_type == EXPLODES_CROSS)
5562         explosion_type = EX_TYPE_CROSS;
5563       else if (element_info[element].explosion_type == EXPLODES_1X1)
5564         explosion_type = EX_TYPE_CENTER;
5565       break;
5566   }
5567
5568   if (explosion_type == EX_TYPE_DYNA)
5569     DynaExplode(x, y);
5570   else
5571     Explode(x, y, EX_PHASE_START, explosion_type);
5572
5573   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5574 }
5575
5576 void SplashAcid(int x, int y)
5577 {
5578   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5579       (!IN_LEV_FIELD(x - 1, y - 2) ||
5580        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5581     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5582
5583   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5584       (!IN_LEV_FIELD(x + 1, y - 2) ||
5585        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5586     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5587
5588   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5589 }
5590
5591 static void InitBeltMovement()
5592 {
5593   static int belt_base_element[4] =
5594   {
5595     EL_CONVEYOR_BELT_1_LEFT,
5596     EL_CONVEYOR_BELT_2_LEFT,
5597     EL_CONVEYOR_BELT_3_LEFT,
5598     EL_CONVEYOR_BELT_4_LEFT
5599   };
5600   static int belt_base_active_element[4] =
5601   {
5602     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5603     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5604     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5605     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5606   };
5607
5608   int x, y, i, j;
5609
5610   /* set frame order for belt animation graphic according to belt direction */
5611   for (i = 0; i < NUM_BELTS; i++)
5612   {
5613     int belt_nr = i;
5614
5615     for (j = 0; j < NUM_BELT_PARTS; j++)
5616     {
5617       int element = belt_base_active_element[belt_nr] + j;
5618       int graphic_1 = el2img(element);
5619       int graphic_2 = el2panelimg(element);
5620
5621       if (game.belt_dir[i] == MV_LEFT)
5622       {
5623         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5624         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5625       }
5626       else
5627       {
5628         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5629         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5630       }
5631     }
5632   }
5633
5634   SCAN_PLAYFIELD(x, y)
5635   {
5636     int element = Feld[x][y];
5637
5638     for (i = 0; i < NUM_BELTS; i++)
5639     {
5640       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5641       {
5642         int e_belt_nr = getBeltNrFromBeltElement(element);
5643         int belt_nr = i;
5644
5645         if (e_belt_nr == belt_nr)
5646         {
5647           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5648
5649           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5650         }
5651       }
5652     }
5653   }
5654 }
5655
5656 static void ToggleBeltSwitch(int x, int y)
5657 {
5658   static int belt_base_element[4] =
5659   {
5660     EL_CONVEYOR_BELT_1_LEFT,
5661     EL_CONVEYOR_BELT_2_LEFT,
5662     EL_CONVEYOR_BELT_3_LEFT,
5663     EL_CONVEYOR_BELT_4_LEFT
5664   };
5665   static int belt_base_active_element[4] =
5666   {
5667     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5668     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5669     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5670     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5671   };
5672   static int belt_base_switch_element[4] =
5673   {
5674     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5675     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5676     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5677     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5678   };
5679   static int belt_move_dir[4] =
5680   {
5681     MV_LEFT,
5682     MV_NONE,
5683     MV_RIGHT,
5684     MV_NONE,
5685   };
5686
5687   int element = Feld[x][y];
5688   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5689   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5690   int belt_dir = belt_move_dir[belt_dir_nr];
5691   int xx, yy, i;
5692
5693   if (!IS_BELT_SWITCH(element))
5694     return;
5695
5696   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5697   game.belt_dir[belt_nr] = belt_dir;
5698
5699   if (belt_dir_nr == 3)
5700     belt_dir_nr = 1;
5701
5702   /* set frame order for belt animation graphic according to belt direction */
5703   for (i = 0; i < NUM_BELT_PARTS; i++)
5704   {
5705     int element = belt_base_active_element[belt_nr] + i;
5706     int graphic_1 = el2img(element);
5707     int graphic_2 = el2panelimg(element);
5708
5709     if (belt_dir == MV_LEFT)
5710     {
5711       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5712       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5713     }
5714     else
5715     {
5716       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5717       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5718     }
5719   }
5720
5721   SCAN_PLAYFIELD(xx, yy)
5722   {
5723     int element = Feld[xx][yy];
5724
5725     if (IS_BELT_SWITCH(element))
5726     {
5727       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5728
5729       if (e_belt_nr == belt_nr)
5730       {
5731         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5732         TEST_DrawLevelField(xx, yy);
5733       }
5734     }
5735     else if (IS_BELT(element) && belt_dir != MV_NONE)
5736     {
5737       int e_belt_nr = getBeltNrFromBeltElement(element);
5738
5739       if (e_belt_nr == belt_nr)
5740       {
5741         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5742
5743         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5744         TEST_DrawLevelField(xx, yy);
5745       }
5746     }
5747     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5748     {
5749       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5750
5751       if (e_belt_nr == belt_nr)
5752       {
5753         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5754
5755         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5756         TEST_DrawLevelField(xx, yy);
5757       }
5758     }
5759   }
5760 }
5761
5762 static void ToggleSwitchgateSwitch(int x, int y)
5763 {
5764   int xx, yy;
5765
5766   game.switchgate_pos = !game.switchgate_pos;
5767
5768   SCAN_PLAYFIELD(xx, yy)
5769   {
5770     int element = Feld[xx][yy];
5771
5772     if (element == EL_SWITCHGATE_SWITCH_UP)
5773     {
5774       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5775       TEST_DrawLevelField(xx, yy);
5776     }
5777     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5778     {
5779       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5780       TEST_DrawLevelField(xx, yy);
5781     }
5782     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5783     {
5784       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5785       TEST_DrawLevelField(xx, yy);
5786     }
5787     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5788     {
5789       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5790       TEST_DrawLevelField(xx, yy);
5791     }
5792     else if (element == EL_SWITCHGATE_OPEN ||
5793              element == EL_SWITCHGATE_OPENING)
5794     {
5795       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5796
5797       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5798     }
5799     else if (element == EL_SWITCHGATE_CLOSED ||
5800              element == EL_SWITCHGATE_CLOSING)
5801     {
5802       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5803
5804       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5805     }
5806   }
5807 }
5808
5809 static int getInvisibleActiveFromInvisibleElement(int element)
5810 {
5811   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5812           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5813           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5814           element);
5815 }
5816
5817 static int getInvisibleFromInvisibleActiveElement(int element)
5818 {
5819   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5820           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5821           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5822           element);
5823 }
5824
5825 static void RedrawAllLightSwitchesAndInvisibleElements()
5826 {
5827   int x, y;
5828
5829   SCAN_PLAYFIELD(x, y)
5830   {
5831     int element = Feld[x][y];
5832
5833     if (element == EL_LIGHT_SWITCH &&
5834         game.light_time_left > 0)
5835     {
5836       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5837       TEST_DrawLevelField(x, y);
5838     }
5839     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5840              game.light_time_left == 0)
5841     {
5842       Feld[x][y] = EL_LIGHT_SWITCH;
5843       TEST_DrawLevelField(x, y);
5844     }
5845     else if (element == EL_EMC_DRIPPER &&
5846              game.light_time_left > 0)
5847     {
5848       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5849       TEST_DrawLevelField(x, y);
5850     }
5851     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5852              game.light_time_left == 0)
5853     {
5854       Feld[x][y] = EL_EMC_DRIPPER;
5855       TEST_DrawLevelField(x, y);
5856     }
5857     else if (element == EL_INVISIBLE_STEELWALL ||
5858              element == EL_INVISIBLE_WALL ||
5859              element == EL_INVISIBLE_SAND)
5860     {
5861       if (game.light_time_left > 0)
5862         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5863
5864       TEST_DrawLevelField(x, y);
5865
5866       /* uncrumble neighbour fields, if needed */
5867       if (element == EL_INVISIBLE_SAND)
5868         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5869     }
5870     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5871              element == EL_INVISIBLE_WALL_ACTIVE ||
5872              element == EL_INVISIBLE_SAND_ACTIVE)
5873     {
5874       if (game.light_time_left == 0)
5875         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5876
5877       TEST_DrawLevelField(x, y);
5878
5879       /* re-crumble neighbour fields, if needed */
5880       if (element == EL_INVISIBLE_SAND)
5881         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5882     }
5883   }
5884 }
5885
5886 static void RedrawAllInvisibleElementsForLenses()
5887 {
5888   int x, y;
5889
5890   SCAN_PLAYFIELD(x, y)
5891   {
5892     int element = Feld[x][y];
5893
5894     if (element == EL_EMC_DRIPPER &&
5895         game.lenses_time_left > 0)
5896     {
5897       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5898       TEST_DrawLevelField(x, y);
5899     }
5900     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5901              game.lenses_time_left == 0)
5902     {
5903       Feld[x][y] = EL_EMC_DRIPPER;
5904       TEST_DrawLevelField(x, y);
5905     }
5906     else if (element == EL_INVISIBLE_STEELWALL ||
5907              element == EL_INVISIBLE_WALL ||
5908              element == EL_INVISIBLE_SAND)
5909     {
5910       if (game.lenses_time_left > 0)
5911         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5912
5913       TEST_DrawLevelField(x, y);
5914
5915       /* uncrumble neighbour fields, if needed */
5916       if (element == EL_INVISIBLE_SAND)
5917         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5918     }
5919     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5920              element == EL_INVISIBLE_WALL_ACTIVE ||
5921              element == EL_INVISIBLE_SAND_ACTIVE)
5922     {
5923       if (game.lenses_time_left == 0)
5924         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5925
5926       TEST_DrawLevelField(x, y);
5927
5928       /* re-crumble neighbour fields, if needed */
5929       if (element == EL_INVISIBLE_SAND)
5930         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5931     }
5932   }
5933 }
5934
5935 static void RedrawAllInvisibleElementsForMagnifier()
5936 {
5937   int x, y;
5938
5939   SCAN_PLAYFIELD(x, y)
5940   {
5941     int element = Feld[x][y];
5942
5943     if (element == EL_EMC_FAKE_GRASS &&
5944         game.magnify_time_left > 0)
5945     {
5946       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5947       TEST_DrawLevelField(x, y);
5948     }
5949     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5950              game.magnify_time_left == 0)
5951     {
5952       Feld[x][y] = EL_EMC_FAKE_GRASS;
5953       TEST_DrawLevelField(x, y);
5954     }
5955     else if (IS_GATE_GRAY(element) &&
5956              game.magnify_time_left > 0)
5957     {
5958       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5959                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5960                     IS_EM_GATE_GRAY(element) ?
5961                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5962                     IS_EMC_GATE_GRAY(element) ?
5963                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5964                     IS_DC_GATE_GRAY(element) ?
5965                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5966                     element);
5967       TEST_DrawLevelField(x, y);
5968     }
5969     else if (IS_GATE_GRAY_ACTIVE(element) &&
5970              game.magnify_time_left == 0)
5971     {
5972       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5973                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5974                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5975                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5976                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5977                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5978                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5979                     EL_DC_GATE_WHITE_GRAY :
5980                     element);
5981       TEST_DrawLevelField(x, y);
5982     }
5983   }
5984 }
5985
5986 static void ToggleLightSwitch(int x, int y)
5987 {
5988   int element = Feld[x][y];
5989
5990   game.light_time_left =
5991     (element == EL_LIGHT_SWITCH ?
5992      level.time_light * FRAMES_PER_SECOND : 0);
5993
5994   RedrawAllLightSwitchesAndInvisibleElements();
5995 }
5996
5997 static void ActivateTimegateSwitch(int x, int y)
5998 {
5999   int xx, yy;
6000
6001   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6002
6003   SCAN_PLAYFIELD(xx, yy)
6004   {
6005     int element = Feld[xx][yy];
6006
6007     if (element == EL_TIMEGATE_CLOSED ||
6008         element == EL_TIMEGATE_CLOSING)
6009     {
6010       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6011       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6012     }
6013
6014     /*
6015     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6016     {
6017       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6018       TEST_DrawLevelField(xx, yy);
6019     }
6020     */
6021
6022   }
6023
6024   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6025                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6026 }
6027
6028 void Impact(int x, int y)
6029 {
6030   boolean last_line = (y == lev_fieldy - 1);
6031   boolean object_hit = FALSE;
6032   boolean impact = (last_line || object_hit);
6033   int element = Feld[x][y];
6034   int smashed = EL_STEELWALL;
6035
6036   if (!last_line)       /* check if element below was hit */
6037   {
6038     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6039       return;
6040
6041     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6042                                          MovDir[x][y + 1] != MV_DOWN ||
6043                                          MovPos[x][y + 1] <= TILEY / 2));
6044
6045     /* do not smash moving elements that left the smashed field in time */
6046     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6047         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6048       object_hit = FALSE;
6049
6050 #if USE_QUICKSAND_IMPACT_BUGFIX
6051     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6052     {
6053       RemoveMovingField(x, y + 1);
6054       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6055       Feld[x][y + 2] = EL_ROCK;
6056       TEST_DrawLevelField(x, y + 2);
6057
6058       object_hit = TRUE;
6059     }
6060
6061     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6062     {
6063       RemoveMovingField(x, y + 1);
6064       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6065       Feld[x][y + 2] = EL_ROCK;
6066       TEST_DrawLevelField(x, y + 2);
6067
6068       object_hit = TRUE;
6069     }
6070 #endif
6071
6072     if (object_hit)
6073       smashed = MovingOrBlocked2Element(x, y + 1);
6074
6075     impact = (last_line || object_hit);
6076   }
6077
6078   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6079   {
6080     SplashAcid(x, y + 1);
6081     return;
6082   }
6083
6084   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6085   /* only reset graphic animation if graphic really changes after impact */
6086   if (impact &&
6087       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6088   {
6089     ResetGfxAnimation(x, y);
6090     TEST_DrawLevelField(x, y);
6091   }
6092
6093   if (impact && CAN_EXPLODE_IMPACT(element))
6094   {
6095     Bang(x, y);
6096     return;
6097   }
6098   else if (impact && element == EL_PEARL &&
6099            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6100   {
6101     ResetGfxAnimation(x, y);
6102
6103     Feld[x][y] = EL_PEARL_BREAKING;
6104     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6105     return;
6106   }
6107   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6108   {
6109     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6110
6111     return;
6112   }
6113
6114   if (impact && element == EL_AMOEBA_DROP)
6115   {
6116     if (object_hit && IS_PLAYER(x, y + 1))
6117       KillPlayerUnlessEnemyProtected(x, y + 1);
6118     else if (object_hit && smashed == EL_PENGUIN)
6119       Bang(x, y + 1);
6120     else
6121     {
6122       Feld[x][y] = EL_AMOEBA_GROWING;
6123       Store[x][y] = EL_AMOEBA_WET;
6124
6125       ResetRandomAnimationValue(x, y);
6126     }
6127     return;
6128   }
6129
6130   if (object_hit)               /* check which object was hit */
6131   {
6132     if ((CAN_PASS_MAGIC_WALL(element) && 
6133          (smashed == EL_MAGIC_WALL ||
6134           smashed == EL_BD_MAGIC_WALL)) ||
6135         (CAN_PASS_DC_MAGIC_WALL(element) &&
6136          smashed == EL_DC_MAGIC_WALL))
6137     {
6138       int xx, yy;
6139       int activated_magic_wall =
6140         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6141          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6142          EL_DC_MAGIC_WALL_ACTIVE);
6143
6144       /* activate magic wall / mill */
6145       SCAN_PLAYFIELD(xx, yy)
6146       {
6147         if (Feld[xx][yy] == smashed)
6148           Feld[xx][yy] = activated_magic_wall;
6149       }
6150
6151       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6152       game.magic_wall_active = TRUE;
6153
6154       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6155                             SND_MAGIC_WALL_ACTIVATING :
6156                             smashed == EL_BD_MAGIC_WALL ?
6157                             SND_BD_MAGIC_WALL_ACTIVATING :
6158                             SND_DC_MAGIC_WALL_ACTIVATING));
6159     }
6160
6161     if (IS_PLAYER(x, y + 1))
6162     {
6163       if (CAN_SMASH_PLAYER(element))
6164       {
6165         KillPlayerUnlessEnemyProtected(x, y + 1);
6166         return;
6167       }
6168     }
6169     else if (smashed == EL_PENGUIN)
6170     {
6171       if (CAN_SMASH_PLAYER(element))
6172       {
6173         Bang(x, y + 1);
6174         return;
6175       }
6176     }
6177     else if (element == EL_BD_DIAMOND)
6178     {
6179       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6180       {
6181         Bang(x, y + 1);
6182         return;
6183       }
6184     }
6185     else if (((element == EL_SP_INFOTRON ||
6186                element == EL_SP_ZONK) &&
6187               (smashed == EL_SP_SNIKSNAK ||
6188                smashed == EL_SP_ELECTRON ||
6189                smashed == EL_SP_DISK_ORANGE)) ||
6190              (element == EL_SP_INFOTRON &&
6191               smashed == EL_SP_DISK_YELLOW))
6192     {
6193       Bang(x, y + 1);
6194       return;
6195     }
6196     else if (CAN_SMASH_EVERYTHING(element))
6197     {
6198       if (IS_CLASSIC_ENEMY(smashed) ||
6199           CAN_EXPLODE_SMASHED(smashed))
6200       {
6201         Bang(x, y + 1);
6202         return;
6203       }
6204       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6205       {
6206         if (smashed == EL_LAMP ||
6207             smashed == EL_LAMP_ACTIVE)
6208         {
6209           Bang(x, y + 1);
6210           return;
6211         }
6212         else if (smashed == EL_NUT)
6213         {
6214           Feld[x][y + 1] = EL_NUT_BREAKING;
6215           PlayLevelSound(x, y, SND_NUT_BREAKING);
6216           RaiseScoreElement(EL_NUT);
6217           return;
6218         }
6219         else if (smashed == EL_PEARL)
6220         {
6221           ResetGfxAnimation(x, y);
6222
6223           Feld[x][y + 1] = EL_PEARL_BREAKING;
6224           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6225           return;
6226         }
6227         else if (smashed == EL_DIAMOND)
6228         {
6229           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6230           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6231           return;
6232         }
6233         else if (IS_BELT_SWITCH(smashed))
6234         {
6235           ToggleBeltSwitch(x, y + 1);
6236         }
6237         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6238                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6239                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6240                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6241         {
6242           ToggleSwitchgateSwitch(x, y + 1);
6243         }
6244         else if (smashed == EL_LIGHT_SWITCH ||
6245                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6246         {
6247           ToggleLightSwitch(x, y + 1);
6248         }
6249         else
6250         {
6251           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6252
6253           CheckElementChangeBySide(x, y + 1, smashed, element,
6254                                    CE_SWITCHED, CH_SIDE_TOP);
6255           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6256                                             CH_SIDE_TOP);
6257         }
6258       }
6259       else
6260       {
6261         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6262       }
6263     }
6264   }
6265
6266   /* play sound of magic wall / mill */
6267   if (!last_line &&
6268       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6269        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6270        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6271   {
6272     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6273       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6274     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6275       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6276     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6277       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6278
6279     return;
6280   }
6281
6282   /* play sound of object that hits the ground */
6283   if (last_line || object_hit)
6284     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6285 }
6286
6287 inline static void TurnRoundExt(int x, int y)
6288 {
6289   static struct
6290   {
6291     int dx, dy;
6292   } move_xy[] =
6293   {
6294     {  0,  0 },
6295     { -1,  0 },
6296     { +1,  0 },
6297     {  0,  0 },
6298     {  0, -1 },
6299     {  0,  0 }, { 0, 0 }, { 0, 0 },
6300     {  0, +1 }
6301   };
6302   static struct
6303   {
6304     int left, right, back;
6305   } turn[] =
6306   {
6307     { 0,        0,              0        },
6308     { MV_DOWN,  MV_UP,          MV_RIGHT },
6309     { MV_UP,    MV_DOWN,        MV_LEFT  },
6310     { 0,        0,              0        },
6311     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6312     { 0,        0,              0        },
6313     { 0,        0,              0        },
6314     { 0,        0,              0        },
6315     { MV_RIGHT, MV_LEFT,        MV_UP    }
6316   };
6317
6318   int element = Feld[x][y];
6319   int move_pattern = element_info[element].move_pattern;
6320
6321   int old_move_dir = MovDir[x][y];
6322   int left_dir  = turn[old_move_dir].left;
6323   int right_dir = turn[old_move_dir].right;
6324   int back_dir  = turn[old_move_dir].back;
6325
6326   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6327   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6328   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6329   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6330
6331   int left_x  = x + left_dx,  left_y  = y + left_dy;
6332   int right_x = x + right_dx, right_y = y + right_dy;
6333   int move_x  = x + move_dx,  move_y  = y + move_dy;
6334
6335   int xx, yy;
6336
6337   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6338   {
6339     TestIfBadThingTouchesOtherBadThing(x, y);
6340
6341     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6342       MovDir[x][y] = right_dir;
6343     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6344       MovDir[x][y] = left_dir;
6345
6346     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6347       MovDelay[x][y] = 9;
6348     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6349       MovDelay[x][y] = 1;
6350   }
6351   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6352   {
6353     TestIfBadThingTouchesOtherBadThing(x, y);
6354
6355     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6356       MovDir[x][y] = left_dir;
6357     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6358       MovDir[x][y] = right_dir;
6359
6360     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6361       MovDelay[x][y] = 9;
6362     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6363       MovDelay[x][y] = 1;
6364   }
6365   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6366   {
6367     TestIfBadThingTouchesOtherBadThing(x, y);
6368
6369     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6370       MovDir[x][y] = left_dir;
6371     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6372       MovDir[x][y] = right_dir;
6373
6374     if (MovDir[x][y] != old_move_dir)
6375       MovDelay[x][y] = 9;
6376   }
6377   else if (element == EL_YAMYAM)
6378   {
6379     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6380     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6381
6382     if (can_turn_left && can_turn_right)
6383       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6384     else if (can_turn_left)
6385       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6386     else if (can_turn_right)
6387       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6388     else
6389       MovDir[x][y] = back_dir;
6390
6391     MovDelay[x][y] = 16 + 16 * RND(3);
6392   }
6393   else if (element == EL_DARK_YAMYAM)
6394   {
6395     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6396                                                          left_x, left_y);
6397     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6398                                                          right_x, right_y);
6399
6400     if (can_turn_left && can_turn_right)
6401       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6402     else if (can_turn_left)
6403       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6404     else if (can_turn_right)
6405       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6406     else
6407       MovDir[x][y] = back_dir;
6408
6409     MovDelay[x][y] = 16 + 16 * RND(3);
6410   }
6411   else if (element == EL_PACMAN)
6412   {
6413     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6414     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6415
6416     if (can_turn_left && can_turn_right)
6417       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6418     else if (can_turn_left)
6419       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6420     else if (can_turn_right)
6421       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6422     else
6423       MovDir[x][y] = back_dir;
6424
6425     MovDelay[x][y] = 6 + RND(40);
6426   }
6427   else if (element == EL_PIG)
6428   {
6429     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6430     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6431     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6432     boolean should_turn_left, should_turn_right, should_move_on;
6433     int rnd_value = 24;
6434     int rnd = RND(rnd_value);
6435
6436     should_turn_left = (can_turn_left &&
6437                         (!can_move_on ||
6438                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6439                                                    y + back_dy + left_dy)));
6440     should_turn_right = (can_turn_right &&
6441                          (!can_move_on ||
6442                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6443                                                     y + back_dy + right_dy)));
6444     should_move_on = (can_move_on &&
6445                       (!can_turn_left ||
6446                        !can_turn_right ||
6447                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6448                                                  y + move_dy + left_dy) ||
6449                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6450                                                  y + move_dy + right_dy)));
6451
6452     if (should_turn_left || should_turn_right || should_move_on)
6453     {
6454       if (should_turn_left && should_turn_right && should_move_on)
6455         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6456                         rnd < 2 * rnd_value / 3 ? right_dir :
6457                         old_move_dir);
6458       else if (should_turn_left && should_turn_right)
6459         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6460       else if (should_turn_left && should_move_on)
6461         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6462       else if (should_turn_right && should_move_on)
6463         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6464       else if (should_turn_left)
6465         MovDir[x][y] = left_dir;
6466       else if (should_turn_right)
6467         MovDir[x][y] = right_dir;
6468       else if (should_move_on)
6469         MovDir[x][y] = old_move_dir;
6470     }
6471     else if (can_move_on && rnd > rnd_value / 8)
6472       MovDir[x][y] = old_move_dir;
6473     else if (can_turn_left && can_turn_right)
6474       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6475     else if (can_turn_left && rnd > rnd_value / 8)
6476       MovDir[x][y] = left_dir;
6477     else if (can_turn_right && rnd > rnd_value/8)
6478       MovDir[x][y] = right_dir;
6479     else
6480       MovDir[x][y] = back_dir;
6481
6482     xx = x + move_xy[MovDir[x][y]].dx;
6483     yy = y + move_xy[MovDir[x][y]].dy;
6484
6485     if (!IN_LEV_FIELD(xx, yy) ||
6486         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6487       MovDir[x][y] = old_move_dir;
6488
6489     MovDelay[x][y] = 0;
6490   }
6491   else if (element == EL_DRAGON)
6492   {
6493     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6494     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6495     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6496     int rnd_value = 24;
6497     int rnd = RND(rnd_value);
6498
6499     if (can_move_on && rnd > rnd_value / 8)
6500       MovDir[x][y] = old_move_dir;
6501     else if (can_turn_left && can_turn_right)
6502       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6503     else if (can_turn_left && rnd > rnd_value / 8)
6504       MovDir[x][y] = left_dir;
6505     else if (can_turn_right && rnd > rnd_value / 8)
6506       MovDir[x][y] = right_dir;
6507     else
6508       MovDir[x][y] = back_dir;
6509
6510     xx = x + move_xy[MovDir[x][y]].dx;
6511     yy = y + move_xy[MovDir[x][y]].dy;
6512
6513     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6514       MovDir[x][y] = old_move_dir;
6515
6516     MovDelay[x][y] = 0;
6517   }
6518   else if (element == EL_MOLE)
6519   {
6520     boolean can_move_on =
6521       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6522                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6523                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6524     if (!can_move_on)
6525     {
6526       boolean can_turn_left =
6527         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6528                               IS_AMOEBOID(Feld[left_x][left_y])));
6529
6530       boolean can_turn_right =
6531         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6532                               IS_AMOEBOID(Feld[right_x][right_y])));
6533
6534       if (can_turn_left && can_turn_right)
6535         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6536       else if (can_turn_left)
6537         MovDir[x][y] = left_dir;
6538       else
6539         MovDir[x][y] = right_dir;
6540     }
6541
6542     if (MovDir[x][y] != old_move_dir)
6543       MovDelay[x][y] = 9;
6544   }
6545   else if (element == EL_BALLOON)
6546   {
6547     MovDir[x][y] = game.wind_direction;
6548     MovDelay[x][y] = 0;
6549   }
6550   else if (element == EL_SPRING)
6551   {
6552     if (MovDir[x][y] & MV_HORIZONTAL)
6553     {
6554       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6555           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6556       {
6557         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6558         ResetGfxAnimation(move_x, move_y);
6559         TEST_DrawLevelField(move_x, move_y);
6560
6561         MovDir[x][y] = back_dir;
6562       }
6563       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6564                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6565         MovDir[x][y] = MV_NONE;
6566     }
6567
6568     MovDelay[x][y] = 0;
6569   }
6570   else if (element == EL_ROBOT ||
6571            element == EL_SATELLITE ||
6572            element == EL_PENGUIN ||
6573            element == EL_EMC_ANDROID)
6574   {
6575     int attr_x = -1, attr_y = -1;
6576
6577     if (AllPlayersGone)
6578     {
6579       attr_x = ExitX;
6580       attr_y = ExitY;
6581     }
6582     else
6583     {
6584       int i;
6585
6586       for (i = 0; i < MAX_PLAYERS; i++)
6587       {
6588         struct PlayerInfo *player = &stored_player[i];
6589         int jx = player->jx, jy = player->jy;
6590
6591         if (!player->active)
6592           continue;
6593
6594         if (attr_x == -1 ||
6595             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6596         {
6597           attr_x = jx;
6598           attr_y = jy;
6599         }
6600       }
6601     }
6602
6603     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6604         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6605          game.engine_version < VERSION_IDENT(3,1,0,0)))
6606     {
6607       attr_x = ZX;
6608       attr_y = ZY;
6609     }
6610
6611     if (element == EL_PENGUIN)
6612     {
6613       int i;
6614       static int xy[4][2] =
6615       {
6616         { 0, -1 },
6617         { -1, 0 },
6618         { +1, 0 },
6619         { 0, +1 }
6620       };
6621
6622       for (i = 0; i < NUM_DIRECTIONS; i++)
6623       {
6624         int ex = x + xy[i][0];
6625         int ey = y + xy[i][1];
6626
6627         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6628                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6629                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6630                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6631         {
6632           attr_x = ex;
6633           attr_y = ey;
6634           break;
6635         }
6636       }
6637     }
6638
6639     MovDir[x][y] = MV_NONE;
6640     if (attr_x < x)
6641       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6642     else if (attr_x > x)
6643       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6644     if (attr_y < y)
6645       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6646     else if (attr_y > y)
6647       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6648
6649     if (element == EL_ROBOT)
6650     {
6651       int newx, newy;
6652
6653       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6654         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6655       Moving2Blocked(x, y, &newx, &newy);
6656
6657       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6658         MovDelay[x][y] = 8 + 8 * !RND(3);
6659       else
6660         MovDelay[x][y] = 16;
6661     }
6662     else if (element == EL_PENGUIN)
6663     {
6664       int newx, newy;
6665
6666       MovDelay[x][y] = 1;
6667
6668       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6669       {
6670         boolean first_horiz = RND(2);
6671         int new_move_dir = MovDir[x][y];
6672
6673         MovDir[x][y] =
6674           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6675         Moving2Blocked(x, y, &newx, &newy);
6676
6677         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6678           return;
6679
6680         MovDir[x][y] =
6681           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6682         Moving2Blocked(x, y, &newx, &newy);
6683
6684         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6685           return;
6686
6687         MovDir[x][y] = old_move_dir;
6688         return;
6689       }
6690     }
6691     else if (element == EL_SATELLITE)
6692     {
6693       int newx, newy;
6694
6695       MovDelay[x][y] = 1;
6696
6697       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6698       {
6699         boolean first_horiz = RND(2);
6700         int new_move_dir = MovDir[x][y];
6701
6702         MovDir[x][y] =
6703           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6704         Moving2Blocked(x, y, &newx, &newy);
6705
6706         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6707           return;
6708
6709         MovDir[x][y] =
6710           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6711         Moving2Blocked(x, y, &newx, &newy);
6712
6713         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6714           return;
6715
6716         MovDir[x][y] = old_move_dir;
6717         return;
6718       }
6719     }
6720     else if (element == EL_EMC_ANDROID)
6721     {
6722       static int check_pos[16] =
6723       {
6724         -1,             /*  0 => (invalid)          */
6725         7,              /*  1 => MV_LEFT            */
6726         3,              /*  2 => MV_RIGHT           */
6727         -1,             /*  3 => (invalid)          */
6728         1,              /*  4 =>            MV_UP   */
6729         0,              /*  5 => MV_LEFT  | MV_UP   */
6730         2,              /*  6 => MV_RIGHT | MV_UP   */
6731         -1,             /*  7 => (invalid)          */
6732         5,              /*  8 =>            MV_DOWN */
6733         6,              /*  9 => MV_LEFT  | MV_DOWN */
6734         4,              /* 10 => MV_RIGHT | MV_DOWN */
6735         -1,             /* 11 => (invalid)          */
6736         -1,             /* 12 => (invalid)          */
6737         -1,             /* 13 => (invalid)          */
6738         -1,             /* 14 => (invalid)          */
6739         -1,             /* 15 => (invalid)          */
6740       };
6741       static struct
6742       {
6743         int dx, dy;
6744         int dir;
6745       } check_xy[8] =
6746       {
6747         { -1, -1,       MV_LEFT  | MV_UP   },
6748         {  0, -1,                  MV_UP   },
6749         { +1, -1,       MV_RIGHT | MV_UP   },
6750         { +1,  0,       MV_RIGHT           },
6751         { +1, +1,       MV_RIGHT | MV_DOWN },
6752         {  0, +1,                  MV_DOWN },
6753         { -1, +1,       MV_LEFT  | MV_DOWN },
6754         { -1,  0,       MV_LEFT            },
6755       };
6756       int start_pos, check_order;
6757       boolean can_clone = FALSE;
6758       int i;
6759
6760       /* check if there is any free field around current position */
6761       for (i = 0; i < 8; i++)
6762       {
6763         int newx = x + check_xy[i].dx;
6764         int newy = y + check_xy[i].dy;
6765
6766         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6767         {
6768           can_clone = TRUE;
6769
6770           break;
6771         }
6772       }
6773
6774       if (can_clone)            /* randomly find an element to clone */
6775       {
6776         can_clone = FALSE;
6777
6778         start_pos = check_pos[RND(8)];
6779         check_order = (RND(2) ? -1 : +1);
6780
6781         for (i = 0; i < 8; i++)
6782         {
6783           int pos_raw = start_pos + i * check_order;
6784           int pos = (pos_raw + 8) % 8;
6785           int newx = x + check_xy[pos].dx;
6786           int newy = y + check_xy[pos].dy;
6787
6788           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6789           {
6790             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6791             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6792
6793             Store[x][y] = Feld[newx][newy];
6794
6795             can_clone = TRUE;
6796
6797             break;
6798           }
6799         }
6800       }
6801
6802       if (can_clone)            /* randomly find a direction to move */
6803       {
6804         can_clone = FALSE;
6805
6806         start_pos = check_pos[RND(8)];
6807         check_order = (RND(2) ? -1 : +1);
6808
6809         for (i = 0; i < 8; i++)
6810         {
6811           int pos_raw = start_pos + i * check_order;
6812           int pos = (pos_raw + 8) % 8;
6813           int newx = x + check_xy[pos].dx;
6814           int newy = y + check_xy[pos].dy;
6815           int new_move_dir = check_xy[pos].dir;
6816
6817           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6818           {
6819             MovDir[x][y] = new_move_dir;
6820             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6821
6822             can_clone = TRUE;
6823
6824             break;
6825           }
6826         }
6827       }
6828
6829       if (can_clone)            /* cloning and moving successful */
6830         return;
6831
6832       /* cannot clone -- try to move towards player */
6833
6834       start_pos = check_pos[MovDir[x][y] & 0x0f];
6835       check_order = (RND(2) ? -1 : +1);
6836
6837       for (i = 0; i < 3; i++)
6838       {
6839         /* first check start_pos, then previous/next or (next/previous) pos */
6840         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6841         int pos = (pos_raw + 8) % 8;
6842         int newx = x + check_xy[pos].dx;
6843         int newy = y + check_xy[pos].dy;
6844         int new_move_dir = check_xy[pos].dir;
6845
6846         if (IS_PLAYER(newx, newy))
6847           break;
6848
6849         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6850         {
6851           MovDir[x][y] = new_move_dir;
6852           MovDelay[x][y] = level.android_move_time * 8 + 1;
6853
6854           break;
6855         }
6856       }
6857     }
6858   }
6859   else if (move_pattern == MV_TURNING_LEFT ||
6860            move_pattern == MV_TURNING_RIGHT ||
6861            move_pattern == MV_TURNING_LEFT_RIGHT ||
6862            move_pattern == MV_TURNING_RIGHT_LEFT ||
6863            move_pattern == MV_TURNING_RANDOM ||
6864            move_pattern == MV_ALL_DIRECTIONS)
6865   {
6866     boolean can_turn_left =
6867       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6868     boolean can_turn_right =
6869       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6870
6871     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6872       return;
6873
6874     if (move_pattern == MV_TURNING_LEFT)
6875       MovDir[x][y] = left_dir;
6876     else if (move_pattern == MV_TURNING_RIGHT)
6877       MovDir[x][y] = right_dir;
6878     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6879       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6880     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6881       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6882     else if (move_pattern == MV_TURNING_RANDOM)
6883       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6884                       can_turn_right && !can_turn_left ? right_dir :
6885                       RND(2) ? left_dir : right_dir);
6886     else if (can_turn_left && can_turn_right)
6887       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6888     else if (can_turn_left)
6889       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6890     else if (can_turn_right)
6891       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6892     else
6893       MovDir[x][y] = back_dir;
6894
6895     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6896   }
6897   else if (move_pattern == MV_HORIZONTAL ||
6898            move_pattern == MV_VERTICAL)
6899   {
6900     if (move_pattern & old_move_dir)
6901       MovDir[x][y] = back_dir;
6902     else if (move_pattern == MV_HORIZONTAL)
6903       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6904     else if (move_pattern == MV_VERTICAL)
6905       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6906
6907     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6908   }
6909   else if (move_pattern & MV_ANY_DIRECTION)
6910   {
6911     MovDir[x][y] = move_pattern;
6912     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6913   }
6914   else if (move_pattern & MV_WIND_DIRECTION)
6915   {
6916     MovDir[x][y] = game.wind_direction;
6917     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6918   }
6919   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6920   {
6921     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6922       MovDir[x][y] = left_dir;
6923     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6924       MovDir[x][y] = right_dir;
6925
6926     if (MovDir[x][y] != old_move_dir)
6927       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6928   }
6929   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6930   {
6931     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6932       MovDir[x][y] = right_dir;
6933     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6934       MovDir[x][y] = left_dir;
6935
6936     if (MovDir[x][y] != old_move_dir)
6937       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6938   }
6939   else if (move_pattern == MV_TOWARDS_PLAYER ||
6940            move_pattern == MV_AWAY_FROM_PLAYER)
6941   {
6942     int attr_x = -1, attr_y = -1;
6943     int newx, newy;
6944     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6945
6946     if (AllPlayersGone)
6947     {
6948       attr_x = ExitX;
6949       attr_y = ExitY;
6950     }
6951     else
6952     {
6953       int i;
6954
6955       for (i = 0; i < MAX_PLAYERS; i++)
6956       {
6957         struct PlayerInfo *player = &stored_player[i];
6958         int jx = player->jx, jy = player->jy;
6959
6960         if (!player->active)
6961           continue;
6962
6963         if (attr_x == -1 ||
6964             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6965         {
6966           attr_x = jx;
6967           attr_y = jy;
6968         }
6969       }
6970     }
6971
6972     MovDir[x][y] = MV_NONE;
6973     if (attr_x < x)
6974       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6975     else if (attr_x > x)
6976       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6977     if (attr_y < y)
6978       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6979     else if (attr_y > y)
6980       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6981
6982     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6983
6984     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6985     {
6986       boolean first_horiz = RND(2);
6987       int new_move_dir = MovDir[x][y];
6988
6989       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6990       {
6991         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6992         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6993
6994         return;
6995       }
6996
6997       MovDir[x][y] =
6998         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6999       Moving2Blocked(x, y, &newx, &newy);
7000
7001       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7002         return;
7003
7004       MovDir[x][y] =
7005         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7006       Moving2Blocked(x, y, &newx, &newy);
7007
7008       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7009         return;
7010
7011       MovDir[x][y] = old_move_dir;
7012     }
7013   }
7014   else if (move_pattern == MV_WHEN_PUSHED ||
7015            move_pattern == MV_WHEN_DROPPED)
7016   {
7017     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7018       MovDir[x][y] = MV_NONE;
7019
7020     MovDelay[x][y] = 0;
7021   }
7022   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7023   {
7024     static int test_xy[7][2] =
7025     {
7026       { 0, -1 },
7027       { -1, 0 },
7028       { +1, 0 },
7029       { 0, +1 },
7030       { 0, -1 },
7031       { -1, 0 },
7032       { +1, 0 },
7033     };
7034     static int test_dir[7] =
7035     {
7036       MV_UP,
7037       MV_LEFT,
7038       MV_RIGHT,
7039       MV_DOWN,
7040       MV_UP,
7041       MV_LEFT,
7042       MV_RIGHT,
7043     };
7044     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7045     int move_preference = -1000000;     /* start with very low preference */
7046     int new_move_dir = MV_NONE;
7047     int start_test = RND(4);
7048     int i;
7049
7050     for (i = 0; i < NUM_DIRECTIONS; i++)
7051     {
7052       int move_dir = test_dir[start_test + i];
7053       int move_dir_preference;
7054
7055       xx = x + test_xy[start_test + i][0];
7056       yy = y + test_xy[start_test + i][1];
7057
7058       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7059           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7060       {
7061         new_move_dir = move_dir;
7062
7063         break;
7064       }
7065
7066       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7067         continue;
7068
7069       move_dir_preference = -1 * RunnerVisit[xx][yy];
7070       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7071         move_dir_preference = PlayerVisit[xx][yy];
7072
7073       if (move_dir_preference > move_preference)
7074       {
7075         /* prefer field that has not been visited for the longest time */
7076         move_preference = move_dir_preference;
7077         new_move_dir = move_dir;
7078       }
7079       else if (move_dir_preference == move_preference &&
7080                move_dir == old_move_dir)
7081       {
7082         /* prefer last direction when all directions are preferred equally */
7083         move_preference = move_dir_preference;
7084         new_move_dir = move_dir;
7085       }
7086     }
7087
7088     MovDir[x][y] = new_move_dir;
7089     if (old_move_dir != new_move_dir)
7090       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7091   }
7092 }
7093
7094 static void TurnRound(int x, int y)
7095 {
7096   int direction = MovDir[x][y];
7097
7098   TurnRoundExt(x, y);
7099
7100   GfxDir[x][y] = MovDir[x][y];
7101
7102   if (direction != MovDir[x][y])
7103     GfxFrame[x][y] = 0;
7104
7105   if (MovDelay[x][y])
7106     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7107
7108   ResetGfxFrame(x, y, FALSE);
7109 }
7110
7111 static boolean JustBeingPushed(int x, int y)
7112 {
7113   int i;
7114
7115   for (i = 0; i < MAX_PLAYERS; i++)
7116   {
7117     struct PlayerInfo *player = &stored_player[i];
7118
7119     if (player->active && player->is_pushing && player->MovPos)
7120     {
7121       int next_jx = player->jx + (player->jx - player->last_jx);
7122       int next_jy = player->jy + (player->jy - player->last_jy);
7123
7124       if (x == next_jx && y == next_jy)
7125         return TRUE;
7126     }
7127   }
7128
7129   return FALSE;
7130 }
7131
7132 void StartMoving(int x, int y)
7133 {
7134   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7135   int element = Feld[x][y];
7136
7137   if (Stop[x][y])
7138     return;
7139
7140   if (MovDelay[x][y] == 0)
7141     GfxAction[x][y] = ACTION_DEFAULT;
7142
7143   if (CAN_FALL(element) && y < lev_fieldy - 1)
7144   {
7145     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7146         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7147       if (JustBeingPushed(x, y))
7148         return;
7149
7150     if (element == EL_QUICKSAND_FULL)
7151     {
7152       if (IS_FREE(x, y + 1))
7153       {
7154         InitMovingField(x, y, MV_DOWN);
7155         started_moving = TRUE;
7156
7157         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7158 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7159         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7160           Store[x][y] = EL_ROCK;
7161 #else
7162         Store[x][y] = EL_ROCK;
7163 #endif
7164
7165         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7166       }
7167       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7168       {
7169         if (!MovDelay[x][y])
7170         {
7171           MovDelay[x][y] = TILEY + 1;
7172
7173           ResetGfxAnimation(x, y);
7174           ResetGfxAnimation(x, y + 1);
7175         }
7176
7177         if (MovDelay[x][y])
7178         {
7179           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7180           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7181
7182           MovDelay[x][y]--;
7183           if (MovDelay[x][y])
7184             return;
7185         }
7186
7187         Feld[x][y] = EL_QUICKSAND_EMPTY;
7188         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7189         Store[x][y + 1] = Store[x][y];
7190         Store[x][y] = 0;
7191
7192         PlayLevelSoundAction(x, y, ACTION_FILLING);
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_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_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     }
7222     else if (element == EL_QUICKSAND_FAST_FULL)
7223     {
7224       if (IS_FREE(x, y + 1))
7225       {
7226         InitMovingField(x, y, MV_DOWN);
7227         started_moving = TRUE;
7228
7229         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7230 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7231         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7232           Store[x][y] = EL_ROCK;
7233 #else
7234         Store[x][y] = EL_ROCK;
7235 #endif
7236
7237         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7238       }
7239       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7240       {
7241         if (!MovDelay[x][y])
7242         {
7243           MovDelay[x][y] = TILEY + 1;
7244
7245           ResetGfxAnimation(x, y);
7246           ResetGfxAnimation(x, y + 1);
7247         }
7248
7249         if (MovDelay[x][y])
7250         {
7251           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7252           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7253
7254           MovDelay[x][y]--;
7255           if (MovDelay[x][y])
7256             return;
7257         }
7258
7259         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7260         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7261         Store[x][y + 1] = Store[x][y];
7262         Store[x][y] = 0;
7263
7264         PlayLevelSoundAction(x, y, ACTION_FILLING);
7265       }
7266       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7267       {
7268         if (!MovDelay[x][y])
7269         {
7270           MovDelay[x][y] = TILEY + 1;
7271
7272           ResetGfxAnimation(x, y);
7273           ResetGfxAnimation(x, y + 1);
7274         }
7275
7276         if (MovDelay[x][y])
7277         {
7278           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7279           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7280
7281           MovDelay[x][y]--;
7282           if (MovDelay[x][y])
7283             return;
7284         }
7285
7286         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7287         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7288         Store[x][y + 1] = Store[x][y];
7289         Store[x][y] = 0;
7290
7291         PlayLevelSoundAction(x, y, ACTION_FILLING);
7292       }
7293     }
7294     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7295              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7296     {
7297       InitMovingField(x, y, MV_DOWN);
7298       started_moving = TRUE;
7299
7300       Feld[x][y] = EL_QUICKSAND_FILLING;
7301       Store[x][y] = element;
7302
7303       PlayLevelSoundAction(x, y, ACTION_FILLING);
7304     }
7305     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7306              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7307     {
7308       InitMovingField(x, y, MV_DOWN);
7309       started_moving = TRUE;
7310
7311       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7312       Store[x][y] = element;
7313
7314       PlayLevelSoundAction(x, y, ACTION_FILLING);
7315     }
7316     else if (element == EL_MAGIC_WALL_FULL)
7317     {
7318       if (IS_FREE(x, y + 1))
7319       {
7320         InitMovingField(x, y, MV_DOWN);
7321         started_moving = TRUE;
7322
7323         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7324         Store[x][y] = EL_CHANGED(Store[x][y]);
7325       }
7326       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7327       {
7328         if (!MovDelay[x][y])
7329           MovDelay[x][y] = TILEY / 4 + 1;
7330
7331         if (MovDelay[x][y])
7332         {
7333           MovDelay[x][y]--;
7334           if (MovDelay[x][y])
7335             return;
7336         }
7337
7338         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7339         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7340         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7341         Store[x][y] = 0;
7342       }
7343     }
7344     else if (element == EL_BD_MAGIC_WALL_FULL)
7345     {
7346       if (IS_FREE(x, y + 1))
7347       {
7348         InitMovingField(x, y, MV_DOWN);
7349         started_moving = TRUE;
7350
7351         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7352         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7353       }
7354       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7355       {
7356         if (!MovDelay[x][y])
7357           MovDelay[x][y] = TILEY / 4 + 1;
7358
7359         if (MovDelay[x][y])
7360         {
7361           MovDelay[x][y]--;
7362           if (MovDelay[x][y])
7363             return;
7364         }
7365
7366         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7367         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7368         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7369         Store[x][y] = 0;
7370       }
7371     }
7372     else if (element == EL_DC_MAGIC_WALL_FULL)
7373     {
7374       if (IS_FREE(x, y + 1))
7375       {
7376         InitMovingField(x, y, MV_DOWN);
7377         started_moving = TRUE;
7378
7379         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7380         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7381       }
7382       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7383       {
7384         if (!MovDelay[x][y])
7385           MovDelay[x][y] = TILEY / 4 + 1;
7386
7387         if (MovDelay[x][y])
7388         {
7389           MovDelay[x][y]--;
7390           if (MovDelay[x][y])
7391             return;
7392         }
7393
7394         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7395         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7396         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7397         Store[x][y] = 0;
7398       }
7399     }
7400     else if ((CAN_PASS_MAGIC_WALL(element) &&
7401               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7402                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7403              (CAN_PASS_DC_MAGIC_WALL(element) &&
7404               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7405
7406     {
7407       InitMovingField(x, y, MV_DOWN);
7408       started_moving = TRUE;
7409
7410       Feld[x][y] =
7411         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7412          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7413          EL_DC_MAGIC_WALL_FILLING);
7414       Store[x][y] = element;
7415     }
7416     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7417     {
7418       SplashAcid(x, y + 1);
7419
7420       InitMovingField(x, y, MV_DOWN);
7421       started_moving = TRUE;
7422
7423       Store[x][y] = EL_ACID;
7424     }
7425     else if (
7426              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7427               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7428              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7429               CAN_FALL(element) && WasJustFalling[x][y] &&
7430               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7431
7432              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7433               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7434               (Feld[x][y + 1] == EL_BLOCKED)))
7435     {
7436       /* this is needed for a special case not covered by calling "Impact()"
7437          from "ContinueMoving()": if an element moves to a tile directly below
7438          another element which was just falling on that tile (which was empty
7439          in the previous frame), the falling element above would just stop
7440          instead of smashing the element below (in previous version, the above
7441          element was just checked for "moving" instead of "falling", resulting
7442          in incorrect smashes caused by horizontal movement of the above
7443          element; also, the case of the player being the element to smash was
7444          simply not covered here... :-/ ) */
7445
7446       CheckCollision[x][y] = 0;
7447       CheckImpact[x][y] = 0;
7448
7449       Impact(x, y);
7450     }
7451     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7452     {
7453       if (MovDir[x][y] == MV_NONE)
7454       {
7455         InitMovingField(x, y, MV_DOWN);
7456         started_moving = TRUE;
7457       }
7458     }
7459     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7460     {
7461       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7462         MovDir[x][y] = MV_DOWN;
7463
7464       InitMovingField(x, y, MV_DOWN);
7465       started_moving = TRUE;
7466     }
7467     else if (element == EL_AMOEBA_DROP)
7468     {
7469       Feld[x][y] = EL_AMOEBA_GROWING;
7470       Store[x][y] = EL_AMOEBA_WET;
7471     }
7472     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7473               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7474              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7475              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7476     {
7477       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7478                                 (IS_FREE(x - 1, y + 1) ||
7479                                  Feld[x - 1][y + 1] == EL_ACID));
7480       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7481                                 (IS_FREE(x + 1, y + 1) ||
7482                                  Feld[x + 1][y + 1] == EL_ACID));
7483       boolean can_fall_any  = (can_fall_left || can_fall_right);
7484       boolean can_fall_both = (can_fall_left && can_fall_right);
7485       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7486
7487       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7488       {
7489         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7490           can_fall_right = FALSE;
7491         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7492           can_fall_left = FALSE;
7493         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7494           can_fall_right = FALSE;
7495         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7496           can_fall_left = FALSE;
7497
7498         can_fall_any  = (can_fall_left || can_fall_right);
7499         can_fall_both = FALSE;
7500       }
7501
7502       if (can_fall_both)
7503       {
7504         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7505           can_fall_right = FALSE;       /* slip down on left side */
7506         else
7507           can_fall_left = !(can_fall_right = RND(2));
7508
7509         can_fall_both = FALSE;
7510       }
7511
7512       if (can_fall_any)
7513       {
7514         /* if not determined otherwise, prefer left side for slipping down */
7515         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7516         started_moving = TRUE;
7517       }
7518     }
7519     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7520     {
7521       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7522       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7523       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7524       int belt_dir = game.belt_dir[belt_nr];
7525
7526       if ((belt_dir == MV_LEFT  && left_is_free) ||
7527           (belt_dir == MV_RIGHT && right_is_free))
7528       {
7529         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7530
7531         InitMovingField(x, y, belt_dir);
7532         started_moving = TRUE;
7533
7534         Pushed[x][y] = TRUE;
7535         Pushed[nextx][y] = TRUE;
7536
7537         GfxAction[x][y] = ACTION_DEFAULT;
7538       }
7539       else
7540       {
7541         MovDir[x][y] = 0;       /* if element was moving, stop it */
7542       }
7543     }
7544   }
7545
7546   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7547   if (CAN_MOVE(element) && !started_moving)
7548   {
7549     int move_pattern = element_info[element].move_pattern;
7550     int newx, newy;
7551
7552     Moving2Blocked(x, y, &newx, &newy);
7553
7554     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7555       return;
7556
7557     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7558         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7559     {
7560       WasJustMoving[x][y] = 0;
7561       CheckCollision[x][y] = 0;
7562
7563       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7564
7565       if (Feld[x][y] != element)        /* element has changed */
7566         return;
7567     }
7568
7569     if (!MovDelay[x][y])        /* start new movement phase */
7570     {
7571       /* all objects that can change their move direction after each step
7572          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7573
7574       if (element != EL_YAMYAM &&
7575           element != EL_DARK_YAMYAM &&
7576           element != EL_PACMAN &&
7577           !(move_pattern & MV_ANY_DIRECTION) &&
7578           move_pattern != MV_TURNING_LEFT &&
7579           move_pattern != MV_TURNING_RIGHT &&
7580           move_pattern != MV_TURNING_LEFT_RIGHT &&
7581           move_pattern != MV_TURNING_RIGHT_LEFT &&
7582           move_pattern != MV_TURNING_RANDOM)
7583       {
7584         TurnRound(x, y);
7585
7586         if (MovDelay[x][y] && (element == EL_BUG ||
7587                                element == EL_SPACESHIP ||
7588                                element == EL_SP_SNIKSNAK ||
7589                                element == EL_SP_ELECTRON ||
7590                                element == EL_MOLE))
7591           TEST_DrawLevelField(x, y);
7592       }
7593     }
7594
7595     if (MovDelay[x][y])         /* wait some time before next movement */
7596     {
7597       MovDelay[x][y]--;
7598
7599       if (element == EL_ROBOT ||
7600           element == EL_YAMYAM ||
7601           element == EL_DARK_YAMYAM)
7602       {
7603         DrawLevelElementAnimationIfNeeded(x, y, element);
7604         PlayLevelSoundAction(x, y, ACTION_WAITING);
7605       }
7606       else if (element == EL_SP_ELECTRON)
7607         DrawLevelElementAnimationIfNeeded(x, y, element);
7608       else if (element == EL_DRAGON)
7609       {
7610         int i;
7611         int dir = MovDir[x][y];
7612         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7613         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7614         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7615                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7616                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7617                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7618         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7619
7620         GfxAction[x][y] = ACTION_ATTACKING;
7621
7622         if (IS_PLAYER(x, y))
7623           DrawPlayerField(x, y);
7624         else
7625           TEST_DrawLevelField(x, y);
7626
7627         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7628
7629         for (i = 1; i <= 3; i++)
7630         {
7631           int xx = x + i * dx;
7632           int yy = y + i * dy;
7633           int sx = SCREENX(xx);
7634           int sy = SCREENY(yy);
7635           int flame_graphic = graphic + (i - 1);
7636
7637           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7638             break;
7639
7640           if (MovDelay[x][y])
7641           {
7642             int flamed = MovingOrBlocked2Element(xx, yy);
7643
7644             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7645               Bang(xx, yy);
7646             else
7647               RemoveMovingField(xx, yy);
7648
7649             ChangeDelay[xx][yy] = 0;
7650
7651             Feld[xx][yy] = EL_FLAMES;
7652
7653             if (IN_SCR_FIELD(sx, sy))
7654             {
7655               TEST_DrawLevelFieldCrumbled(xx, yy);
7656               DrawGraphic(sx, sy, flame_graphic, frame);
7657             }
7658           }
7659           else
7660           {
7661             if (Feld[xx][yy] == EL_FLAMES)
7662               Feld[xx][yy] = EL_EMPTY;
7663             TEST_DrawLevelField(xx, yy);
7664           }
7665         }
7666       }
7667
7668       if (MovDelay[x][y])       /* element still has to wait some time */
7669       {
7670         PlayLevelSoundAction(x, y, ACTION_WAITING);
7671
7672         return;
7673       }
7674     }
7675
7676     /* now make next step */
7677
7678     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7679
7680     if (DONT_COLLIDE_WITH(element) &&
7681         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7682         !PLAYER_ENEMY_PROTECTED(newx, newy))
7683     {
7684       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7685
7686       return;
7687     }
7688
7689     else if (CAN_MOVE_INTO_ACID(element) &&
7690              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7691              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7692              (MovDir[x][y] == MV_DOWN ||
7693               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7694     {
7695       SplashAcid(newx, newy);
7696       Store[x][y] = EL_ACID;
7697     }
7698     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7699     {
7700       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7701           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7702           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7703           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7704       {
7705         RemoveField(x, y);
7706         TEST_DrawLevelField(x, y);
7707
7708         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7709         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7710           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7711
7712         local_player->friends_still_needed--;
7713         if (!local_player->friends_still_needed &&
7714             !local_player->GameOver && AllPlayersGone)
7715           PlayerWins(local_player);
7716
7717         return;
7718       }
7719       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7720       {
7721         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7722           TEST_DrawLevelField(newx, newy);
7723         else
7724           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7725       }
7726       else if (!IS_FREE(newx, newy))
7727       {
7728         GfxAction[x][y] = ACTION_WAITING;
7729
7730         if (IS_PLAYER(x, y))
7731           DrawPlayerField(x, y);
7732         else
7733           TEST_DrawLevelField(x, y);
7734
7735         return;
7736       }
7737     }
7738     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7739     {
7740       if (IS_FOOD_PIG(Feld[newx][newy]))
7741       {
7742         if (IS_MOVING(newx, newy))
7743           RemoveMovingField(newx, newy);
7744         else
7745         {
7746           Feld[newx][newy] = EL_EMPTY;
7747           TEST_DrawLevelField(newx, newy);
7748         }
7749
7750         PlayLevelSound(x, y, SND_PIG_DIGGING);
7751       }
7752       else if (!IS_FREE(newx, newy))
7753       {
7754         if (IS_PLAYER(x, y))
7755           DrawPlayerField(x, y);
7756         else
7757           TEST_DrawLevelField(x, y);
7758
7759         return;
7760       }
7761     }
7762     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7763     {
7764       if (Store[x][y] != EL_EMPTY)
7765       {
7766         boolean can_clone = FALSE;
7767         int xx, yy;
7768
7769         /* check if element to clone is still there */
7770         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7771         {
7772           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7773           {
7774             can_clone = TRUE;
7775
7776             break;
7777           }
7778         }
7779
7780         /* cannot clone or target field not free anymore -- do not clone */
7781         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7782           Store[x][y] = EL_EMPTY;
7783       }
7784
7785       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7786       {
7787         if (IS_MV_DIAGONAL(MovDir[x][y]))
7788         {
7789           int diagonal_move_dir = MovDir[x][y];
7790           int stored = Store[x][y];
7791           int change_delay = 8;
7792           int graphic;
7793
7794           /* android is moving diagonally */
7795
7796           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7797
7798           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7799           GfxElement[x][y] = EL_EMC_ANDROID;
7800           GfxAction[x][y] = ACTION_SHRINKING;
7801           GfxDir[x][y] = diagonal_move_dir;
7802           ChangeDelay[x][y] = change_delay;
7803
7804           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7805                                    GfxDir[x][y]);
7806
7807           DrawLevelGraphicAnimation(x, y, graphic);
7808           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7809
7810           if (Feld[newx][newy] == EL_ACID)
7811           {
7812             SplashAcid(newx, newy);
7813
7814             return;
7815           }
7816
7817           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7818
7819           Store[newx][newy] = EL_EMC_ANDROID;
7820           GfxElement[newx][newy] = EL_EMC_ANDROID;
7821           GfxAction[newx][newy] = ACTION_GROWING;
7822           GfxDir[newx][newy] = diagonal_move_dir;
7823           ChangeDelay[newx][newy] = change_delay;
7824
7825           graphic = el_act_dir2img(GfxElement[newx][newy],
7826                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7827
7828           DrawLevelGraphicAnimation(newx, newy, graphic);
7829           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7830
7831           return;
7832         }
7833         else
7834         {
7835           Feld[newx][newy] = EL_EMPTY;
7836           TEST_DrawLevelField(newx, newy);
7837
7838           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7839         }
7840       }
7841       else if (!IS_FREE(newx, newy))
7842       {
7843         return;
7844       }
7845     }
7846     else if (IS_CUSTOM_ELEMENT(element) &&
7847              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7848     {
7849       if (!DigFieldByCE(newx, newy, element))
7850         return;
7851
7852       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7853       {
7854         RunnerVisit[x][y] = FrameCounter;
7855         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7856       }
7857     }
7858     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7859     {
7860       if (!IS_FREE(newx, newy))
7861       {
7862         if (IS_PLAYER(x, y))
7863           DrawPlayerField(x, y);
7864         else
7865           TEST_DrawLevelField(x, y);
7866
7867         return;
7868       }
7869       else
7870       {
7871         boolean wanna_flame = !RND(10);
7872         int dx = newx - x, dy = newy - y;
7873         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7874         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7875         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7876                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7877         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7878                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7879
7880         if ((wanna_flame ||
7881              IS_CLASSIC_ENEMY(element1) ||
7882              IS_CLASSIC_ENEMY(element2)) &&
7883             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7884             element1 != EL_FLAMES && element2 != EL_FLAMES)
7885         {
7886           ResetGfxAnimation(x, y);
7887           GfxAction[x][y] = ACTION_ATTACKING;
7888
7889           if (IS_PLAYER(x, y))
7890             DrawPlayerField(x, y);
7891           else
7892             TEST_DrawLevelField(x, y);
7893
7894           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7895
7896           MovDelay[x][y] = 50;
7897
7898           Feld[newx][newy] = EL_FLAMES;
7899           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7900             Feld[newx1][newy1] = EL_FLAMES;
7901           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7902             Feld[newx2][newy2] = EL_FLAMES;
7903
7904           return;
7905         }
7906       }
7907     }
7908     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7909              Feld[newx][newy] == EL_DIAMOND)
7910     {
7911       if (IS_MOVING(newx, newy))
7912         RemoveMovingField(newx, newy);
7913       else
7914       {
7915         Feld[newx][newy] = EL_EMPTY;
7916         TEST_DrawLevelField(newx, newy);
7917       }
7918
7919       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7920     }
7921     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7922              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7923     {
7924       if (AmoebaNr[newx][newy])
7925       {
7926         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7927         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7928             Feld[newx][newy] == EL_BD_AMOEBA)
7929           AmoebaCnt[AmoebaNr[newx][newy]]--;
7930       }
7931
7932       if (IS_MOVING(newx, newy))
7933       {
7934         RemoveMovingField(newx, newy);
7935       }
7936       else
7937       {
7938         Feld[newx][newy] = EL_EMPTY;
7939         TEST_DrawLevelField(newx, newy);
7940       }
7941
7942       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7943     }
7944     else if ((element == EL_PACMAN || element == EL_MOLE)
7945              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7946     {
7947       if (AmoebaNr[newx][newy])
7948       {
7949         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7950         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7951             Feld[newx][newy] == EL_BD_AMOEBA)
7952           AmoebaCnt[AmoebaNr[newx][newy]]--;
7953       }
7954
7955       if (element == EL_MOLE)
7956       {
7957         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7958         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7959
7960         ResetGfxAnimation(x, y);
7961         GfxAction[x][y] = ACTION_DIGGING;
7962         TEST_DrawLevelField(x, y);
7963
7964         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7965
7966         return;                         /* wait for shrinking amoeba */
7967       }
7968       else      /* element == EL_PACMAN */
7969       {
7970         Feld[newx][newy] = EL_EMPTY;
7971         TEST_DrawLevelField(newx, newy);
7972         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7973       }
7974     }
7975     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7976              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7977               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7978     {
7979       /* wait for shrinking amoeba to completely disappear */
7980       return;
7981     }
7982     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7983     {
7984       /* object was running against a wall */
7985
7986       TurnRound(x, y);
7987
7988       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7989         DrawLevelElementAnimation(x, y, element);
7990
7991       if (DONT_TOUCH(element))
7992         TestIfBadThingTouchesPlayer(x, y);
7993
7994       return;
7995     }
7996
7997     InitMovingField(x, y, MovDir[x][y]);
7998
7999     PlayLevelSoundAction(x, y, ACTION_MOVING);
8000   }
8001
8002   if (MovDir[x][y])
8003     ContinueMoving(x, y);
8004 }
8005
8006 void ContinueMoving(int x, int y)
8007 {
8008   int element = Feld[x][y];
8009   struct ElementInfo *ei = &element_info[element];
8010   int direction = MovDir[x][y];
8011   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8012   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8013   int newx = x + dx, newy = y + dy;
8014   int stored = Store[x][y];
8015   int stored_new = Store[newx][newy];
8016   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8017   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8018   boolean last_line = (newy == lev_fieldy - 1);
8019
8020   MovPos[x][y] += getElementMoveStepsize(x, y);
8021
8022   if (pushed_by_player) /* special case: moving object pushed by player */
8023     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8024
8025   if (ABS(MovPos[x][y]) < TILEX)
8026   {
8027     TEST_DrawLevelField(x, y);
8028
8029     return;     /* element is still moving */
8030   }
8031
8032   /* element reached destination field */
8033
8034   Feld[x][y] = EL_EMPTY;
8035   Feld[newx][newy] = element;
8036   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8037
8038   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8039   {
8040     element = Feld[newx][newy] = EL_ACID;
8041   }
8042   else if (element == EL_MOLE)
8043   {
8044     Feld[x][y] = EL_SAND;
8045
8046     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8047   }
8048   else if (element == EL_QUICKSAND_FILLING)
8049   {
8050     element = Feld[newx][newy] = get_next_element(element);
8051     Store[newx][newy] = Store[x][y];
8052   }
8053   else if (element == EL_QUICKSAND_EMPTYING)
8054   {
8055     Feld[x][y] = get_next_element(element);
8056     element = Feld[newx][newy] = Store[x][y];
8057   }
8058   else if (element == EL_QUICKSAND_FAST_FILLING)
8059   {
8060     element = Feld[newx][newy] = get_next_element(element);
8061     Store[newx][newy] = Store[x][y];
8062   }
8063   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8064   {
8065     Feld[x][y] = get_next_element(element);
8066     element = Feld[newx][newy] = Store[x][y];
8067   }
8068   else if (element == EL_MAGIC_WALL_FILLING)
8069   {
8070     element = Feld[newx][newy] = get_next_element(element);
8071     if (!game.magic_wall_active)
8072       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8073     Store[newx][newy] = Store[x][y];
8074   }
8075   else if (element == EL_MAGIC_WALL_EMPTYING)
8076   {
8077     Feld[x][y] = get_next_element(element);
8078     if (!game.magic_wall_active)
8079       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8080     element = Feld[newx][newy] = Store[x][y];
8081
8082     InitField(newx, newy, FALSE);
8083   }
8084   else if (element == EL_BD_MAGIC_WALL_FILLING)
8085   {
8086     element = Feld[newx][newy] = get_next_element(element);
8087     if (!game.magic_wall_active)
8088       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8089     Store[newx][newy] = Store[x][y];
8090   }
8091   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8092   {
8093     Feld[x][y] = get_next_element(element);
8094     if (!game.magic_wall_active)
8095       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8096     element = Feld[newx][newy] = Store[x][y];
8097
8098     InitField(newx, newy, FALSE);
8099   }
8100   else if (element == EL_DC_MAGIC_WALL_FILLING)
8101   {
8102     element = Feld[newx][newy] = get_next_element(element);
8103     if (!game.magic_wall_active)
8104       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8105     Store[newx][newy] = Store[x][y];
8106   }
8107   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8108   {
8109     Feld[x][y] = get_next_element(element);
8110     if (!game.magic_wall_active)
8111       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8112     element = Feld[newx][newy] = Store[x][y];
8113
8114     InitField(newx, newy, FALSE);
8115   }
8116   else if (element == EL_AMOEBA_DROPPING)
8117   {
8118     Feld[x][y] = get_next_element(element);
8119     element = Feld[newx][newy] = Store[x][y];
8120   }
8121   else if (element == EL_SOKOBAN_OBJECT)
8122   {
8123     if (Back[x][y])
8124       Feld[x][y] = Back[x][y];
8125
8126     if (Back[newx][newy])
8127       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8128
8129     Back[x][y] = Back[newx][newy] = 0;
8130   }
8131
8132   Store[x][y] = EL_EMPTY;
8133   MovPos[x][y] = 0;
8134   MovDir[x][y] = 0;
8135   MovDelay[x][y] = 0;
8136
8137   MovDelay[newx][newy] = 0;
8138
8139   if (CAN_CHANGE_OR_HAS_ACTION(element))
8140   {
8141     /* copy element change control values to new field */
8142     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8143     ChangePage[newx][newy]  = ChangePage[x][y];
8144     ChangeCount[newx][newy] = ChangeCount[x][y];
8145     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8146   }
8147
8148   CustomValue[newx][newy] = CustomValue[x][y];
8149
8150   ChangeDelay[x][y] = 0;
8151   ChangePage[x][y] = -1;
8152   ChangeCount[x][y] = 0;
8153   ChangeEvent[x][y] = -1;
8154
8155   CustomValue[x][y] = 0;
8156
8157   /* copy animation control values to new field */
8158   GfxFrame[newx][newy]  = GfxFrame[x][y];
8159   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8160   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8161   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8162
8163   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8164
8165   /* some elements can leave other elements behind after moving */
8166   if (ei->move_leave_element != EL_EMPTY &&
8167       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8168       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8169   {
8170     int move_leave_element = ei->move_leave_element;
8171
8172     /* this makes it possible to leave the removed element again */
8173     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8174       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8175
8176     Feld[x][y] = move_leave_element;
8177
8178     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8179       MovDir[x][y] = direction;
8180
8181     InitField(x, y, FALSE);
8182
8183     if (GFX_CRUMBLED(Feld[x][y]))
8184       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8185
8186     if (ELEM_IS_PLAYER(move_leave_element))
8187       RelocatePlayer(x, y, move_leave_element);
8188   }
8189
8190   /* do this after checking for left-behind element */
8191   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8192
8193   if (!CAN_MOVE(element) ||
8194       (CAN_FALL(element) && direction == MV_DOWN &&
8195        (element == EL_SPRING ||
8196         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8197         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8198     GfxDir[x][y] = MovDir[newx][newy] = 0;
8199
8200   TEST_DrawLevelField(x, y);
8201   TEST_DrawLevelField(newx, newy);
8202
8203   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8204
8205   /* prevent pushed element from moving on in pushed direction */
8206   if (pushed_by_player && CAN_MOVE(element) &&
8207       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8208       !(element_info[element].move_pattern & direction))
8209     TurnRound(newx, newy);
8210
8211   /* prevent elements on conveyor belt from moving on in last direction */
8212   if (pushed_by_conveyor && CAN_FALL(element) &&
8213       direction & MV_HORIZONTAL)
8214     MovDir[newx][newy] = 0;
8215
8216   if (!pushed_by_player)
8217   {
8218     int nextx = newx + dx, nexty = newy + dy;
8219     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8220
8221     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8222
8223     if (CAN_FALL(element) && direction == MV_DOWN)
8224       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8225
8226     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8227       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8228
8229     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8230       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8231   }
8232
8233   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8234   {
8235     TestIfBadThingTouchesPlayer(newx, newy);
8236     TestIfBadThingTouchesFriend(newx, newy);
8237
8238     if (!IS_CUSTOM_ELEMENT(element))
8239       TestIfBadThingTouchesOtherBadThing(newx, newy);
8240   }
8241   else if (element == EL_PENGUIN)
8242     TestIfFriendTouchesBadThing(newx, newy);
8243
8244   if (DONT_GET_HIT_BY(element))
8245   {
8246     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8247   }
8248
8249   /* give the player one last chance (one more frame) to move away */
8250   if (CAN_FALL(element) && direction == MV_DOWN &&
8251       (last_line || (!IS_FREE(x, newy + 1) &&
8252                      (!IS_PLAYER(x, newy + 1) ||
8253                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8254     Impact(x, newy);
8255
8256   if (pushed_by_player && !game.use_change_when_pushing_bug)
8257   {
8258     int push_side = MV_DIR_OPPOSITE(direction);
8259     struct PlayerInfo *player = PLAYERINFO(x, y);
8260
8261     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8262                                player->index_bit, push_side);
8263     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8264                                         player->index_bit, push_side);
8265   }
8266
8267   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8268     MovDelay[newx][newy] = 1;
8269
8270   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8271
8272   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8273   TestIfElementHitsCustomElement(newx, newy, direction);
8274   TestIfPlayerTouchesCustomElement(newx, newy);
8275   TestIfElementTouchesCustomElement(newx, newy);
8276
8277   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8278       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8279     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8280                              MV_DIR_OPPOSITE(direction));
8281 }
8282
8283 int AmoebeNachbarNr(int ax, int ay)
8284 {
8285   int i;
8286   int element = Feld[ax][ay];
8287   int group_nr = 0;
8288   static int xy[4][2] =
8289   {
8290     { 0, -1 },
8291     { -1, 0 },
8292     { +1, 0 },
8293     { 0, +1 }
8294   };
8295
8296   for (i = 0; i < NUM_DIRECTIONS; i++)
8297   {
8298     int x = ax + xy[i][0];
8299     int y = ay + xy[i][1];
8300
8301     if (!IN_LEV_FIELD(x, y))
8302       continue;
8303
8304     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8305       group_nr = AmoebaNr[x][y];
8306   }
8307
8308   return group_nr;
8309 }
8310
8311 void AmoebenVereinigen(int ax, int ay)
8312 {
8313   int i, x, y, xx, yy;
8314   int new_group_nr = AmoebaNr[ax][ay];
8315   static int xy[4][2] =
8316   {
8317     { 0, -1 },
8318     { -1, 0 },
8319     { +1, 0 },
8320     { 0, +1 }
8321   };
8322
8323   if (new_group_nr == 0)
8324     return;
8325
8326   for (i = 0; i < NUM_DIRECTIONS; i++)
8327   {
8328     x = ax + xy[i][0];
8329     y = ay + xy[i][1];
8330
8331     if (!IN_LEV_FIELD(x, y))
8332       continue;
8333
8334     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8335          Feld[x][y] == EL_BD_AMOEBA ||
8336          Feld[x][y] == EL_AMOEBA_DEAD) &&
8337         AmoebaNr[x][y] != new_group_nr)
8338     {
8339       int old_group_nr = AmoebaNr[x][y];
8340
8341       if (old_group_nr == 0)
8342         return;
8343
8344       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8345       AmoebaCnt[old_group_nr] = 0;
8346       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8347       AmoebaCnt2[old_group_nr] = 0;
8348
8349       SCAN_PLAYFIELD(xx, yy)
8350       {
8351         if (AmoebaNr[xx][yy] == old_group_nr)
8352           AmoebaNr[xx][yy] = new_group_nr;
8353       }
8354     }
8355   }
8356 }
8357
8358 void AmoebeUmwandeln(int ax, int ay)
8359 {
8360   int i, x, y;
8361
8362   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8363   {
8364     int group_nr = AmoebaNr[ax][ay];
8365
8366 #ifdef DEBUG
8367     if (group_nr == 0)
8368     {
8369       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8370       printf("AmoebeUmwandeln(): This should never happen!\n");
8371       return;
8372     }
8373 #endif
8374
8375     SCAN_PLAYFIELD(x, y)
8376     {
8377       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8378       {
8379         AmoebaNr[x][y] = 0;
8380         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8381       }
8382     }
8383
8384     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8385                             SND_AMOEBA_TURNING_TO_GEM :
8386                             SND_AMOEBA_TURNING_TO_ROCK));
8387     Bang(ax, ay);
8388   }
8389   else
8390   {
8391     static int xy[4][2] =
8392     {
8393       { 0, -1 },
8394       { -1, 0 },
8395       { +1, 0 },
8396       { 0, +1 }
8397     };
8398
8399     for (i = 0; i < NUM_DIRECTIONS; i++)
8400     {
8401       x = ax + xy[i][0];
8402       y = ay + xy[i][1];
8403
8404       if (!IN_LEV_FIELD(x, y))
8405         continue;
8406
8407       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8408       {
8409         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8410                               SND_AMOEBA_TURNING_TO_GEM :
8411                               SND_AMOEBA_TURNING_TO_ROCK));
8412         Bang(x, y);
8413       }
8414     }
8415   }
8416 }
8417
8418 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8419 {
8420   int x, y;
8421   int group_nr = AmoebaNr[ax][ay];
8422   boolean done = FALSE;
8423
8424 #ifdef DEBUG
8425   if (group_nr == 0)
8426   {
8427     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8428     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8429     return;
8430   }
8431 #endif
8432
8433   SCAN_PLAYFIELD(x, y)
8434   {
8435     if (AmoebaNr[x][y] == group_nr &&
8436         (Feld[x][y] == EL_AMOEBA_DEAD ||
8437          Feld[x][y] == EL_BD_AMOEBA ||
8438          Feld[x][y] == EL_AMOEBA_GROWING))
8439     {
8440       AmoebaNr[x][y] = 0;
8441       Feld[x][y] = new_element;
8442       InitField(x, y, FALSE);
8443       TEST_DrawLevelField(x, y);
8444       done = TRUE;
8445     }
8446   }
8447
8448   if (done)
8449     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8450                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8451                             SND_BD_AMOEBA_TURNING_TO_GEM));
8452 }
8453
8454 void AmoebeWaechst(int x, int y)
8455 {
8456   static unsigned int sound_delay = 0;
8457   static unsigned int sound_delay_value = 0;
8458
8459   if (!MovDelay[x][y])          /* start new growing cycle */
8460   {
8461     MovDelay[x][y] = 7;
8462
8463     if (DelayReached(&sound_delay, sound_delay_value))
8464     {
8465       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8466       sound_delay_value = 30;
8467     }
8468   }
8469
8470   if (MovDelay[x][y])           /* wait some time before growing bigger */
8471   {
8472     MovDelay[x][y]--;
8473     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8474     {
8475       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8476                                            6 - MovDelay[x][y]);
8477
8478       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8479     }
8480
8481     if (!MovDelay[x][y])
8482     {
8483       Feld[x][y] = Store[x][y];
8484       Store[x][y] = 0;
8485       TEST_DrawLevelField(x, y);
8486     }
8487   }
8488 }
8489
8490 void AmoebaDisappearing(int x, int y)
8491 {
8492   static unsigned int sound_delay = 0;
8493   static unsigned int sound_delay_value = 0;
8494
8495   if (!MovDelay[x][y])          /* start new shrinking cycle */
8496   {
8497     MovDelay[x][y] = 7;
8498
8499     if (DelayReached(&sound_delay, sound_delay_value))
8500       sound_delay_value = 30;
8501   }
8502
8503   if (MovDelay[x][y])           /* wait some time before shrinking */
8504   {
8505     MovDelay[x][y]--;
8506     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8507     {
8508       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8509                                            6 - MovDelay[x][y]);
8510
8511       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8512     }
8513
8514     if (!MovDelay[x][y])
8515     {
8516       Feld[x][y] = EL_EMPTY;
8517       TEST_DrawLevelField(x, y);
8518
8519       /* don't let mole enter this field in this cycle;
8520          (give priority to objects falling to this field from above) */
8521       Stop[x][y] = TRUE;
8522     }
8523   }
8524 }
8525
8526 void AmoebeAbleger(int ax, int ay)
8527 {
8528   int i;
8529   int element = Feld[ax][ay];
8530   int graphic = el2img(element);
8531   int newax = ax, neway = ay;
8532   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8533   static int xy[4][2] =
8534   {
8535     { 0, -1 },
8536     { -1, 0 },
8537     { +1, 0 },
8538     { 0, +1 }
8539   };
8540
8541   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8542   {
8543     Feld[ax][ay] = EL_AMOEBA_DEAD;
8544     TEST_DrawLevelField(ax, ay);
8545     return;
8546   }
8547
8548   if (IS_ANIMATED(graphic))
8549     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8550
8551   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8552     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8553
8554   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8555   {
8556     MovDelay[ax][ay]--;
8557     if (MovDelay[ax][ay])
8558       return;
8559   }
8560
8561   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8562   {
8563     int start = RND(4);
8564     int x = ax + xy[start][0];
8565     int y = ay + xy[start][1];
8566
8567     if (!IN_LEV_FIELD(x, y))
8568       return;
8569
8570     if (IS_FREE(x, y) ||
8571         CAN_GROW_INTO(Feld[x][y]) ||
8572         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8573         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8574     {
8575       newax = x;
8576       neway = y;
8577     }
8578
8579     if (newax == ax && neway == ay)
8580       return;
8581   }
8582   else                          /* normal or "filled" (BD style) amoeba */
8583   {
8584     int start = RND(4);
8585     boolean waiting_for_player = FALSE;
8586
8587     for (i = 0; i < NUM_DIRECTIONS; i++)
8588     {
8589       int j = (start + i) % 4;
8590       int x = ax + xy[j][0];
8591       int y = ay + xy[j][1];
8592
8593       if (!IN_LEV_FIELD(x, y))
8594         continue;
8595
8596       if (IS_FREE(x, y) ||
8597           CAN_GROW_INTO(Feld[x][y]) ||
8598           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8599           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8600       {
8601         newax = x;
8602         neway = y;
8603         break;
8604       }
8605       else if (IS_PLAYER(x, y))
8606         waiting_for_player = TRUE;
8607     }
8608
8609     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8610     {
8611       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8612       {
8613         Feld[ax][ay] = EL_AMOEBA_DEAD;
8614         TEST_DrawLevelField(ax, ay);
8615         AmoebaCnt[AmoebaNr[ax][ay]]--;
8616
8617         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8618         {
8619           if (element == EL_AMOEBA_FULL)
8620             AmoebeUmwandeln(ax, ay);
8621           else if (element == EL_BD_AMOEBA)
8622             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8623         }
8624       }
8625       return;
8626     }
8627     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8628     {
8629       /* amoeba gets larger by growing in some direction */
8630
8631       int new_group_nr = AmoebaNr[ax][ay];
8632
8633 #ifdef DEBUG
8634   if (new_group_nr == 0)
8635   {
8636     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8637     printf("AmoebeAbleger(): This should never happen!\n");
8638     return;
8639   }
8640 #endif
8641
8642       AmoebaNr[newax][neway] = new_group_nr;
8643       AmoebaCnt[new_group_nr]++;
8644       AmoebaCnt2[new_group_nr]++;
8645
8646       /* if amoeba touches other amoeba(s) after growing, unify them */
8647       AmoebenVereinigen(newax, neway);
8648
8649       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8650       {
8651         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8652         return;
8653       }
8654     }
8655   }
8656
8657   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8658       (neway == lev_fieldy - 1 && newax != ax))
8659   {
8660     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8661     Store[newax][neway] = element;
8662   }
8663   else if (neway == ay || element == EL_EMC_DRIPPER)
8664   {
8665     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8666
8667     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8668   }
8669   else
8670   {
8671     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8672     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8673     Store[ax][ay] = EL_AMOEBA_DROP;
8674     ContinueMoving(ax, ay);
8675     return;
8676   }
8677
8678   TEST_DrawLevelField(newax, neway);
8679 }
8680
8681 void Life(int ax, int ay)
8682 {
8683   int x1, y1, x2, y2;
8684   int life_time = 40;
8685   int element = Feld[ax][ay];
8686   int graphic = el2img(element);
8687   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8688                          level.biomaze);
8689   boolean changed = FALSE;
8690
8691   if (IS_ANIMATED(graphic))
8692     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8693
8694   if (Stop[ax][ay])
8695     return;
8696
8697   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8698     MovDelay[ax][ay] = life_time;
8699
8700   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8701   {
8702     MovDelay[ax][ay]--;
8703     if (MovDelay[ax][ay])
8704       return;
8705   }
8706
8707   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8708   {
8709     int xx = ax+x1, yy = ay+y1;
8710     int nachbarn = 0;
8711
8712     if (!IN_LEV_FIELD(xx, yy))
8713       continue;
8714
8715     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8716     {
8717       int x = xx+x2, y = yy+y2;
8718
8719       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8720         continue;
8721
8722       if (((Feld[x][y] == element ||
8723             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8724            !Stop[x][y]) ||
8725           (IS_FREE(x, y) && Stop[x][y]))
8726         nachbarn++;
8727     }
8728
8729     if (xx == ax && yy == ay)           /* field in the middle */
8730     {
8731       if (nachbarn < life_parameter[0] ||
8732           nachbarn > life_parameter[1])
8733       {
8734         Feld[xx][yy] = EL_EMPTY;
8735         if (!Stop[xx][yy])
8736           TEST_DrawLevelField(xx, yy);
8737         Stop[xx][yy] = TRUE;
8738         changed = TRUE;
8739       }
8740     }
8741     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8742     {                                   /* free border field */
8743       if (nachbarn >= life_parameter[2] &&
8744           nachbarn <= life_parameter[3])
8745       {
8746         Feld[xx][yy] = element;
8747         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8748         if (!Stop[xx][yy])
8749           TEST_DrawLevelField(xx, yy);
8750         Stop[xx][yy] = TRUE;
8751         changed = TRUE;
8752       }
8753     }
8754   }
8755
8756   if (changed)
8757     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8758                    SND_GAME_OF_LIFE_GROWING);
8759 }
8760
8761 static void InitRobotWheel(int x, int y)
8762 {
8763   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8764 }
8765
8766 static void RunRobotWheel(int x, int y)
8767 {
8768   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8769 }
8770
8771 static void StopRobotWheel(int x, int y)
8772 {
8773   if (ZX == x && ZY == y)
8774   {
8775     ZX = ZY = -1;
8776
8777     game.robot_wheel_active = FALSE;
8778   }
8779 }
8780
8781 static void InitTimegateWheel(int x, int y)
8782 {
8783   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8784 }
8785
8786 static void RunTimegateWheel(int x, int y)
8787 {
8788   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8789 }
8790
8791 static void InitMagicBallDelay(int x, int y)
8792 {
8793   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8794 }
8795
8796 static void ActivateMagicBall(int bx, int by)
8797 {
8798   int x, y;
8799
8800   if (level.ball_random)
8801   {
8802     int pos_border = RND(8);    /* select one of the eight border elements */
8803     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8804     int xx = pos_content % 3;
8805     int yy = pos_content / 3;
8806
8807     x = bx - 1 + xx;
8808     y = by - 1 + yy;
8809
8810     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8811       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8812   }
8813   else
8814   {
8815     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8816     {
8817       int xx = x - bx + 1;
8818       int yy = y - by + 1;
8819
8820       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8821         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8822     }
8823   }
8824
8825   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8826 }
8827
8828 void CheckExit(int x, int y)
8829 {
8830   if (local_player->gems_still_needed > 0 ||
8831       local_player->sokobanfields_still_needed > 0 ||
8832       local_player->lights_still_needed > 0)
8833   {
8834     int element = Feld[x][y];
8835     int graphic = el2img(element);
8836
8837     if (IS_ANIMATED(graphic))
8838       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8839
8840     return;
8841   }
8842
8843   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8844     return;
8845
8846   Feld[x][y] = EL_EXIT_OPENING;
8847
8848   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8849 }
8850
8851 void CheckExitEM(int x, int y)
8852 {
8853   if (local_player->gems_still_needed > 0 ||
8854       local_player->sokobanfields_still_needed > 0 ||
8855       local_player->lights_still_needed > 0)
8856   {
8857     int element = Feld[x][y];
8858     int graphic = el2img(element);
8859
8860     if (IS_ANIMATED(graphic))
8861       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8862
8863     return;
8864   }
8865
8866   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8867     return;
8868
8869   Feld[x][y] = EL_EM_EXIT_OPENING;
8870
8871   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8872 }
8873
8874 void CheckExitSteel(int x, int y)
8875 {
8876   if (local_player->gems_still_needed > 0 ||
8877       local_player->sokobanfields_still_needed > 0 ||
8878       local_player->lights_still_needed > 0)
8879   {
8880     int element = Feld[x][y];
8881     int graphic = el2img(element);
8882
8883     if (IS_ANIMATED(graphic))
8884       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8885
8886     return;
8887   }
8888
8889   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8890     return;
8891
8892   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8893
8894   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8895 }
8896
8897 void CheckExitSteelEM(int x, int y)
8898 {
8899   if (local_player->gems_still_needed > 0 ||
8900       local_player->sokobanfields_still_needed > 0 ||
8901       local_player->lights_still_needed > 0)
8902   {
8903     int element = Feld[x][y];
8904     int graphic = el2img(element);
8905
8906     if (IS_ANIMATED(graphic))
8907       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8908
8909     return;
8910   }
8911
8912   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8913     return;
8914
8915   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8916
8917   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8918 }
8919
8920 void CheckExitSP(int x, int y)
8921 {
8922   if (local_player->gems_still_needed > 0)
8923   {
8924     int element = Feld[x][y];
8925     int graphic = el2img(element);
8926
8927     if (IS_ANIMATED(graphic))
8928       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8929
8930     return;
8931   }
8932
8933   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8934     return;
8935
8936   Feld[x][y] = EL_SP_EXIT_OPENING;
8937
8938   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8939 }
8940
8941 static void CloseAllOpenTimegates()
8942 {
8943   int x, y;
8944
8945   SCAN_PLAYFIELD(x, y)
8946   {
8947     int element = Feld[x][y];
8948
8949     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8950     {
8951       Feld[x][y] = EL_TIMEGATE_CLOSING;
8952
8953       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8954     }
8955   }
8956 }
8957
8958 void DrawTwinkleOnField(int x, int y)
8959 {
8960   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8961     return;
8962
8963   if (Feld[x][y] == EL_BD_DIAMOND)
8964     return;
8965
8966   if (MovDelay[x][y] == 0)      /* next animation frame */
8967     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8968
8969   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8970   {
8971     MovDelay[x][y]--;
8972
8973     DrawLevelElementAnimation(x, y, Feld[x][y]);
8974
8975     if (MovDelay[x][y] != 0)
8976     {
8977       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8978                                            10 - MovDelay[x][y]);
8979
8980       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8981     }
8982   }
8983 }
8984
8985 void MauerWaechst(int x, int y)
8986 {
8987   int delay = 6;
8988
8989   if (!MovDelay[x][y])          /* next animation frame */
8990     MovDelay[x][y] = 3 * delay;
8991
8992   if (MovDelay[x][y])           /* wait some time before next frame */
8993   {
8994     MovDelay[x][y]--;
8995
8996     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8997     {
8998       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8999       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9000
9001       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9002     }
9003
9004     if (!MovDelay[x][y])
9005     {
9006       if (MovDir[x][y] == MV_LEFT)
9007       {
9008         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9009           TEST_DrawLevelField(x - 1, y);
9010       }
9011       else if (MovDir[x][y] == MV_RIGHT)
9012       {
9013         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9014           TEST_DrawLevelField(x + 1, y);
9015       }
9016       else if (MovDir[x][y] == MV_UP)
9017       {
9018         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9019           TEST_DrawLevelField(x, y - 1);
9020       }
9021       else
9022       {
9023         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9024           TEST_DrawLevelField(x, y + 1);
9025       }
9026
9027       Feld[x][y] = Store[x][y];
9028       Store[x][y] = 0;
9029       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9030       TEST_DrawLevelField(x, y);
9031     }
9032   }
9033 }
9034
9035 void MauerAbleger(int ax, int ay)
9036 {
9037   int element = Feld[ax][ay];
9038   int graphic = el2img(element);
9039   boolean oben_frei = FALSE, unten_frei = FALSE;
9040   boolean links_frei = FALSE, rechts_frei = FALSE;
9041   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9042   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9043   boolean new_wall = FALSE;
9044
9045   if (IS_ANIMATED(graphic))
9046     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9047
9048   if (!MovDelay[ax][ay])        /* start building new wall */
9049     MovDelay[ax][ay] = 6;
9050
9051   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9052   {
9053     MovDelay[ax][ay]--;
9054     if (MovDelay[ax][ay])
9055       return;
9056   }
9057
9058   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9059     oben_frei = TRUE;
9060   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9061     unten_frei = TRUE;
9062   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9063     links_frei = TRUE;
9064   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9065     rechts_frei = TRUE;
9066
9067   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9068       element == EL_EXPANDABLE_WALL_ANY)
9069   {
9070     if (oben_frei)
9071     {
9072       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9073       Store[ax][ay-1] = element;
9074       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9075       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9076         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9077                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9078       new_wall = TRUE;
9079     }
9080     if (unten_frei)
9081     {
9082       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9083       Store[ax][ay+1] = element;
9084       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9085       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9086         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9087                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9088       new_wall = TRUE;
9089     }
9090   }
9091
9092   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9093       element == EL_EXPANDABLE_WALL_ANY ||
9094       element == EL_EXPANDABLE_WALL ||
9095       element == EL_BD_EXPANDABLE_WALL)
9096   {
9097     if (links_frei)
9098     {
9099       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9100       Store[ax-1][ay] = element;
9101       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9102       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9103         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9104                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9105       new_wall = TRUE;
9106     }
9107
9108     if (rechts_frei)
9109     {
9110       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9111       Store[ax+1][ay] = element;
9112       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9113       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9114         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9115                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9116       new_wall = TRUE;
9117     }
9118   }
9119
9120   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9121     TEST_DrawLevelField(ax, ay);
9122
9123   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9124     oben_massiv = TRUE;
9125   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9126     unten_massiv = TRUE;
9127   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9128     links_massiv = TRUE;
9129   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9130     rechts_massiv = TRUE;
9131
9132   if (((oben_massiv && unten_massiv) ||
9133        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9134        element == EL_EXPANDABLE_WALL) &&
9135       ((links_massiv && rechts_massiv) ||
9136        element == EL_EXPANDABLE_WALL_VERTICAL))
9137     Feld[ax][ay] = EL_WALL;
9138
9139   if (new_wall)
9140     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9141 }
9142
9143 void MauerAblegerStahl(int ax, int ay)
9144 {
9145   int element = Feld[ax][ay];
9146   int graphic = el2img(element);
9147   boolean oben_frei = FALSE, unten_frei = FALSE;
9148   boolean links_frei = FALSE, rechts_frei = FALSE;
9149   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9150   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9151   boolean new_wall = FALSE;
9152
9153   if (IS_ANIMATED(graphic))
9154     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9155
9156   if (!MovDelay[ax][ay])        /* start building new wall */
9157     MovDelay[ax][ay] = 6;
9158
9159   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9160   {
9161     MovDelay[ax][ay]--;
9162     if (MovDelay[ax][ay])
9163       return;
9164   }
9165
9166   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9167     oben_frei = TRUE;
9168   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9169     unten_frei = TRUE;
9170   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9171     links_frei = TRUE;
9172   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9173     rechts_frei = TRUE;
9174
9175   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9176       element == EL_EXPANDABLE_STEELWALL_ANY)
9177   {
9178     if (oben_frei)
9179     {
9180       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9181       Store[ax][ay-1] = element;
9182       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9183       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9184         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9185                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9186       new_wall = TRUE;
9187     }
9188     if (unten_frei)
9189     {
9190       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9191       Store[ax][ay+1] = element;
9192       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9193       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9194         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9195                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9196       new_wall = TRUE;
9197     }
9198   }
9199
9200   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9201       element == EL_EXPANDABLE_STEELWALL_ANY)
9202   {
9203     if (links_frei)
9204     {
9205       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9206       Store[ax-1][ay] = element;
9207       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9208       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9209         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9210                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9211       new_wall = TRUE;
9212     }
9213
9214     if (rechts_frei)
9215     {
9216       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9217       Store[ax+1][ay] = element;
9218       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9219       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9220         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9221                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9222       new_wall = TRUE;
9223     }
9224   }
9225
9226   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9227     oben_massiv = TRUE;
9228   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9229     unten_massiv = TRUE;
9230   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9231     links_massiv = TRUE;
9232   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9233     rechts_massiv = TRUE;
9234
9235   if (((oben_massiv && unten_massiv) ||
9236        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9237       ((links_massiv && rechts_massiv) ||
9238        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9239     Feld[ax][ay] = EL_STEELWALL;
9240
9241   if (new_wall)
9242     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9243 }
9244
9245 void CheckForDragon(int x, int y)
9246 {
9247   int i, j;
9248   boolean dragon_found = FALSE;
9249   static int xy[4][2] =
9250   {
9251     { 0, -1 },
9252     { -1, 0 },
9253     { +1, 0 },
9254     { 0, +1 }
9255   };
9256
9257   for (i = 0; i < NUM_DIRECTIONS; i++)
9258   {
9259     for (j = 0; j < 4; j++)
9260     {
9261       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9262
9263       if (IN_LEV_FIELD(xx, yy) &&
9264           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9265       {
9266         if (Feld[xx][yy] == EL_DRAGON)
9267           dragon_found = TRUE;
9268       }
9269       else
9270         break;
9271     }
9272   }
9273
9274   if (!dragon_found)
9275   {
9276     for (i = 0; i < NUM_DIRECTIONS; i++)
9277     {
9278       for (j = 0; j < 3; j++)
9279       {
9280         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9281   
9282         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9283         {
9284           Feld[xx][yy] = EL_EMPTY;
9285           TEST_DrawLevelField(xx, yy);
9286         }
9287         else
9288           break;
9289       }
9290     }
9291   }
9292 }
9293
9294 static void InitBuggyBase(int x, int y)
9295 {
9296   int element = Feld[x][y];
9297   int activating_delay = FRAMES_PER_SECOND / 4;
9298
9299   ChangeDelay[x][y] =
9300     (element == EL_SP_BUGGY_BASE ?
9301      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9302      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9303      activating_delay :
9304      element == EL_SP_BUGGY_BASE_ACTIVE ?
9305      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9306 }
9307
9308 static void WarnBuggyBase(int x, int y)
9309 {
9310   int i;
9311   static int xy[4][2] =
9312   {
9313     { 0, -1 },
9314     { -1, 0 },
9315     { +1, 0 },
9316     { 0, +1 }
9317   };
9318
9319   for (i = 0; i < NUM_DIRECTIONS; i++)
9320   {
9321     int xx = x + xy[i][0];
9322     int yy = y + xy[i][1];
9323
9324     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9325     {
9326       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9327
9328       break;
9329     }
9330   }
9331 }
9332
9333 static void InitTrap(int x, int y)
9334 {
9335   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9336 }
9337
9338 static void ActivateTrap(int x, int y)
9339 {
9340   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9341 }
9342
9343 static void ChangeActiveTrap(int x, int y)
9344 {
9345   int graphic = IMG_TRAP_ACTIVE;
9346
9347   /* if new animation frame was drawn, correct crumbled sand border */
9348   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9349     TEST_DrawLevelFieldCrumbled(x, y);
9350 }
9351
9352 static int getSpecialActionElement(int element, int number, int base_element)
9353 {
9354   return (element != EL_EMPTY ? element :
9355           number != -1 ? base_element + number - 1 :
9356           EL_EMPTY);
9357 }
9358
9359 static int getModifiedActionNumber(int value_old, int operator, int operand,
9360                                    int value_min, int value_max)
9361 {
9362   int value_new = (operator == CA_MODE_SET      ? operand :
9363                    operator == CA_MODE_ADD      ? value_old + operand :
9364                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9365                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9366                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9367                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9368                    value_old);
9369
9370   return (value_new < value_min ? value_min :
9371           value_new > value_max ? value_max :
9372           value_new);
9373 }
9374
9375 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9376 {
9377   struct ElementInfo *ei = &element_info[element];
9378   struct ElementChangeInfo *change = &ei->change_page[page];
9379   int target_element = change->target_element;
9380   int action_type = change->action_type;
9381   int action_mode = change->action_mode;
9382   int action_arg = change->action_arg;
9383   int action_element = change->action_element;
9384   int i;
9385
9386   if (!change->has_action)
9387     return;
9388
9389   /* ---------- determine action paramater values -------------------------- */
9390
9391   int level_time_value =
9392     (level.time > 0 ? TimeLeft :
9393      TimePlayed);
9394
9395   int action_arg_element_raw =
9396     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9397      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9398      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9399      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9400      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9401      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9402      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9403      EL_EMPTY);
9404   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9405
9406   int action_arg_direction =
9407     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9408      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9409      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9410      change->actual_trigger_side :
9411      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9412      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9413      MV_NONE);
9414
9415   int action_arg_number_min =
9416     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9417      CA_ARG_MIN);
9418
9419   int action_arg_number_max =
9420     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9421      action_type == CA_SET_LEVEL_GEMS ? 999 :
9422      action_type == CA_SET_LEVEL_TIME ? 9999 :
9423      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9424      action_type == CA_SET_CE_VALUE ? 9999 :
9425      action_type == CA_SET_CE_SCORE ? 9999 :
9426      CA_ARG_MAX);
9427
9428   int action_arg_number_reset =
9429     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9430      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9431      action_type == CA_SET_LEVEL_TIME ? level.time :
9432      action_type == CA_SET_LEVEL_SCORE ? 0 :
9433      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9434      action_type == CA_SET_CE_SCORE ? 0 :
9435      0);
9436
9437   int action_arg_number =
9438     (action_arg <= CA_ARG_MAX ? action_arg :
9439      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9440      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9441      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9442      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9443      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9444      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9445      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9446      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9447      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9448      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9449      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9450      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9451      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9452      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9453      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9454      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9455      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9456      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9457      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9458      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9459      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9460      -1);
9461
9462   int action_arg_number_old =
9463     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9464      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9465      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9466      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9467      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9468      0);
9469
9470   int action_arg_number_new =
9471     getModifiedActionNumber(action_arg_number_old,
9472                             action_mode, action_arg_number,
9473                             action_arg_number_min, action_arg_number_max);
9474
9475   int trigger_player_bits =
9476     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9477      change->actual_trigger_player_bits : change->trigger_player);
9478
9479   int action_arg_player_bits =
9480     (action_arg >= CA_ARG_PLAYER_1 &&
9481      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9482      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9483      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9484      PLAYER_BITS_ANY);
9485
9486   /* ---------- execute action  -------------------------------------------- */
9487
9488   switch (action_type)
9489   {
9490     case CA_NO_ACTION:
9491     {
9492       return;
9493     }
9494
9495     /* ---------- level actions  ------------------------------------------- */
9496
9497     case CA_RESTART_LEVEL:
9498     {
9499       game.restart_level = TRUE;
9500
9501       break;
9502     }
9503
9504     case CA_SHOW_ENVELOPE:
9505     {
9506       int element = getSpecialActionElement(action_arg_element,
9507                                             action_arg_number, EL_ENVELOPE_1);
9508
9509       if (IS_ENVELOPE(element))
9510         local_player->show_envelope = element;
9511
9512       break;
9513     }
9514
9515     case CA_SET_LEVEL_TIME:
9516     {
9517       if (level.time > 0)       /* only modify limited time value */
9518       {
9519         TimeLeft = action_arg_number_new;
9520
9521         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9522
9523         DisplayGameControlValues();
9524
9525         if (!TimeLeft && setup.time_limit)
9526           for (i = 0; i < MAX_PLAYERS; i++)
9527             KillPlayer(&stored_player[i]);
9528       }
9529
9530       break;
9531     }
9532
9533     case CA_SET_LEVEL_SCORE:
9534     {
9535       local_player->score = action_arg_number_new;
9536
9537       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9538
9539       DisplayGameControlValues();
9540
9541       break;
9542     }
9543
9544     case CA_SET_LEVEL_GEMS:
9545     {
9546       local_player->gems_still_needed = action_arg_number_new;
9547
9548       game_panel_controls[GAME_PANEL_GEMS].value =
9549         local_player->gems_still_needed;
9550
9551       DisplayGameControlValues();
9552
9553       break;
9554     }
9555
9556     case CA_SET_LEVEL_WIND:
9557     {
9558       game.wind_direction = action_arg_direction;
9559
9560       break;
9561     }
9562
9563     case CA_SET_LEVEL_RANDOM_SEED:
9564     {
9565       /* ensure that setting a new random seed while playing is predictable */
9566       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9567
9568       break;
9569     }
9570
9571     /* ---------- player actions  ------------------------------------------ */
9572
9573     case CA_MOVE_PLAYER:
9574     {
9575       /* automatically move to the next field in specified direction */
9576       for (i = 0; i < MAX_PLAYERS; i++)
9577         if (trigger_player_bits & (1 << i))
9578           stored_player[i].programmed_action = action_arg_direction;
9579
9580       break;
9581     }
9582
9583     case CA_EXIT_PLAYER:
9584     {
9585       for (i = 0; i < MAX_PLAYERS; i++)
9586         if (action_arg_player_bits & (1 << i))
9587           PlayerWins(&stored_player[i]);
9588
9589       break;
9590     }
9591
9592     case CA_KILL_PLAYER:
9593     {
9594       for (i = 0; i < MAX_PLAYERS; i++)
9595         if (action_arg_player_bits & (1 << i))
9596           KillPlayer(&stored_player[i]);
9597
9598       break;
9599     }
9600
9601     case CA_SET_PLAYER_KEYS:
9602     {
9603       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9604       int element = getSpecialActionElement(action_arg_element,
9605                                             action_arg_number, EL_KEY_1);
9606
9607       if (IS_KEY(element))
9608       {
9609         for (i = 0; i < MAX_PLAYERS; i++)
9610         {
9611           if (trigger_player_bits & (1 << i))
9612           {
9613             stored_player[i].key[KEY_NR(element)] = key_state;
9614
9615             DrawGameDoorValues();
9616           }
9617         }
9618       }
9619
9620       break;
9621     }
9622
9623     case CA_SET_PLAYER_SPEED:
9624     {
9625       for (i = 0; i < MAX_PLAYERS; i++)
9626       {
9627         if (trigger_player_bits & (1 << i))
9628         {
9629           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9630
9631           if (action_arg == CA_ARG_SPEED_FASTER &&
9632               stored_player[i].cannot_move)
9633           {
9634             action_arg_number = STEPSIZE_VERY_SLOW;
9635           }
9636           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9637                    action_arg == CA_ARG_SPEED_FASTER)
9638           {
9639             action_arg_number = 2;
9640             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9641                            CA_MODE_MULTIPLY);
9642           }
9643           else if (action_arg == CA_ARG_NUMBER_RESET)
9644           {
9645             action_arg_number = level.initial_player_stepsize[i];
9646           }
9647
9648           move_stepsize =
9649             getModifiedActionNumber(move_stepsize,
9650                                     action_mode,
9651                                     action_arg_number,
9652                                     action_arg_number_min,
9653                                     action_arg_number_max);
9654
9655           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9656         }
9657       }
9658
9659       break;
9660     }
9661
9662     case CA_SET_PLAYER_SHIELD:
9663     {
9664       for (i = 0; i < MAX_PLAYERS; i++)
9665       {
9666         if (trigger_player_bits & (1 << i))
9667         {
9668           if (action_arg == CA_ARG_SHIELD_OFF)
9669           {
9670             stored_player[i].shield_normal_time_left = 0;
9671             stored_player[i].shield_deadly_time_left = 0;
9672           }
9673           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9674           {
9675             stored_player[i].shield_normal_time_left = 999999;
9676           }
9677           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9678           {
9679             stored_player[i].shield_normal_time_left = 999999;
9680             stored_player[i].shield_deadly_time_left = 999999;
9681           }
9682         }
9683       }
9684
9685       break;
9686     }
9687
9688     case CA_SET_PLAYER_GRAVITY:
9689     {
9690       for (i = 0; i < MAX_PLAYERS; i++)
9691       {
9692         if (trigger_player_bits & (1 << i))
9693         {
9694           stored_player[i].gravity =
9695             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9696              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9697              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9698              stored_player[i].gravity);
9699         }
9700       }
9701
9702       break;
9703     }
9704
9705     case CA_SET_PLAYER_ARTWORK:
9706     {
9707       for (i = 0; i < MAX_PLAYERS; i++)
9708       {
9709         if (trigger_player_bits & (1 << i))
9710         {
9711           int artwork_element = action_arg_element;
9712
9713           if (action_arg == CA_ARG_ELEMENT_RESET)
9714             artwork_element =
9715               (level.use_artwork_element[i] ? level.artwork_element[i] :
9716                stored_player[i].element_nr);
9717
9718           if (stored_player[i].artwork_element != artwork_element)
9719             stored_player[i].Frame = 0;
9720
9721           stored_player[i].artwork_element = artwork_element;
9722
9723           SetPlayerWaiting(&stored_player[i], FALSE);
9724
9725           /* set number of special actions for bored and sleeping animation */
9726           stored_player[i].num_special_action_bored =
9727             get_num_special_action(artwork_element,
9728                                    ACTION_BORING_1, ACTION_BORING_LAST);
9729           stored_player[i].num_special_action_sleeping =
9730             get_num_special_action(artwork_element,
9731                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9732         }
9733       }
9734
9735       break;
9736     }
9737
9738     case CA_SET_PLAYER_INVENTORY:
9739     {
9740       for (i = 0; i < MAX_PLAYERS; i++)
9741       {
9742         struct PlayerInfo *player = &stored_player[i];
9743         int j, k;
9744
9745         if (trigger_player_bits & (1 << i))
9746         {
9747           int inventory_element = action_arg_element;
9748
9749           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9750               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9751               action_arg == CA_ARG_ELEMENT_ACTION)
9752           {
9753             int element = inventory_element;
9754             int collect_count = element_info[element].collect_count_initial;
9755
9756             if (!IS_CUSTOM_ELEMENT(element))
9757               collect_count = 1;
9758
9759             if (collect_count == 0)
9760               player->inventory_infinite_element = element;
9761             else
9762               for (k = 0; k < collect_count; k++)
9763                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9764                   player->inventory_element[player->inventory_size++] =
9765                     element;
9766           }
9767           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9768                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9769                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9770           {
9771             if (player->inventory_infinite_element != EL_UNDEFINED &&
9772                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9773                                      action_arg_element_raw))
9774               player->inventory_infinite_element = EL_UNDEFINED;
9775
9776             for (k = 0, j = 0; j < player->inventory_size; j++)
9777             {
9778               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9779                                         action_arg_element_raw))
9780                 player->inventory_element[k++] = player->inventory_element[j];
9781             }
9782
9783             player->inventory_size = k;
9784           }
9785           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9786           {
9787             if (player->inventory_size > 0)
9788             {
9789               for (j = 0; j < player->inventory_size - 1; j++)
9790                 player->inventory_element[j] = player->inventory_element[j + 1];
9791
9792               player->inventory_size--;
9793             }
9794           }
9795           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9796           {
9797             if (player->inventory_size > 0)
9798               player->inventory_size--;
9799           }
9800           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9801           {
9802             player->inventory_infinite_element = EL_UNDEFINED;
9803             player->inventory_size = 0;
9804           }
9805           else if (action_arg == CA_ARG_INVENTORY_RESET)
9806           {
9807             player->inventory_infinite_element = EL_UNDEFINED;
9808             player->inventory_size = 0;
9809
9810             if (level.use_initial_inventory[i])
9811             {
9812               for (j = 0; j < level.initial_inventory_size[i]; j++)
9813               {
9814                 int element = level.initial_inventory_content[i][j];
9815                 int collect_count = element_info[element].collect_count_initial;
9816
9817                 if (!IS_CUSTOM_ELEMENT(element))
9818                   collect_count = 1;
9819
9820                 if (collect_count == 0)
9821                   player->inventory_infinite_element = element;
9822                 else
9823                   for (k = 0; k < collect_count; k++)
9824                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9825                       player->inventory_element[player->inventory_size++] =
9826                         element;
9827               }
9828             }
9829           }
9830         }
9831       }
9832
9833       break;
9834     }
9835
9836     /* ---------- CE actions  ---------------------------------------------- */
9837
9838     case CA_SET_CE_VALUE:
9839     {
9840       int last_ce_value = CustomValue[x][y];
9841
9842       CustomValue[x][y] = action_arg_number_new;
9843
9844       if (CustomValue[x][y] != last_ce_value)
9845       {
9846         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9847         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9848
9849         if (CustomValue[x][y] == 0)
9850         {
9851           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9852           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9853         }
9854       }
9855
9856       break;
9857     }
9858
9859     case CA_SET_CE_SCORE:
9860     {
9861       int last_ce_score = ei->collect_score;
9862
9863       ei->collect_score = action_arg_number_new;
9864
9865       if (ei->collect_score != last_ce_score)
9866       {
9867         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9868         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9869
9870         if (ei->collect_score == 0)
9871         {
9872           int xx, yy;
9873
9874           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9875           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9876
9877           /*
9878             This is a very special case that seems to be a mixture between
9879             CheckElementChange() and CheckTriggeredElementChange(): while
9880             the first one only affects single elements that are triggered
9881             directly, the second one affects multiple elements in the playfield
9882             that are triggered indirectly by another element. This is a third
9883             case: Changing the CE score always affects multiple identical CEs,
9884             so every affected CE must be checked, not only the single CE for
9885             which the CE score was changed in the first place (as every instance
9886             of that CE shares the same CE score, and therefore also can change)!
9887           */
9888           SCAN_PLAYFIELD(xx, yy)
9889           {
9890             if (Feld[xx][yy] == element)
9891               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9892                                  CE_SCORE_GETS_ZERO);
9893           }
9894         }
9895       }
9896
9897       break;
9898     }
9899
9900     case CA_SET_CE_ARTWORK:
9901     {
9902       int artwork_element = action_arg_element;
9903       boolean reset_frame = FALSE;
9904       int xx, yy;
9905
9906       if (action_arg == CA_ARG_ELEMENT_RESET)
9907         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9908                            element);
9909
9910       if (ei->gfx_element != artwork_element)
9911         reset_frame = TRUE;
9912
9913       ei->gfx_element = artwork_element;
9914
9915       SCAN_PLAYFIELD(xx, yy)
9916       {
9917         if (Feld[xx][yy] == element)
9918         {
9919           if (reset_frame)
9920           {
9921             ResetGfxAnimation(xx, yy);
9922             ResetRandomAnimationValue(xx, yy);
9923           }
9924
9925           TEST_DrawLevelField(xx, yy);
9926         }
9927       }
9928
9929       break;
9930     }
9931
9932     /* ---------- engine actions  ------------------------------------------ */
9933
9934     case CA_SET_ENGINE_SCAN_MODE:
9935     {
9936       InitPlayfieldScanMode(action_arg);
9937
9938       break;
9939     }
9940
9941     default:
9942       break;
9943   }
9944 }
9945
9946 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9947 {
9948   int old_element = Feld[x][y];
9949   int new_element = GetElementFromGroupElement(element);
9950   int previous_move_direction = MovDir[x][y];
9951   int last_ce_value = CustomValue[x][y];
9952   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9953   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9954   boolean add_player_onto_element = (new_element_is_player &&
9955                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9956                                      IS_WALKABLE(old_element));
9957
9958   if (!add_player_onto_element)
9959   {
9960     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9961       RemoveMovingField(x, y);
9962     else
9963       RemoveField(x, y);
9964
9965     Feld[x][y] = new_element;
9966
9967     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9968       MovDir[x][y] = previous_move_direction;
9969
9970     if (element_info[new_element].use_last_ce_value)
9971       CustomValue[x][y] = last_ce_value;
9972
9973     InitField_WithBug1(x, y, FALSE);
9974
9975     new_element = Feld[x][y];   /* element may have changed */
9976
9977     ResetGfxAnimation(x, y);
9978     ResetRandomAnimationValue(x, y);
9979
9980     TEST_DrawLevelField(x, y);
9981
9982     if (GFX_CRUMBLED(new_element))
9983       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9984   }
9985
9986   /* check if element under the player changes from accessible to unaccessible
9987      (needed for special case of dropping element which then changes) */
9988   /* (must be checked after creating new element for walkable group elements) */
9989   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9990       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9991   {
9992     Bang(x, y);
9993
9994     return;
9995   }
9996
9997   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9998   if (new_element_is_player)
9999     RelocatePlayer(x, y, new_element);
10000
10001   if (is_change)
10002     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10003
10004   TestIfBadThingTouchesPlayer(x, y);
10005   TestIfPlayerTouchesCustomElement(x, y);
10006   TestIfElementTouchesCustomElement(x, y);
10007 }
10008
10009 static void CreateField(int x, int y, int element)
10010 {
10011   CreateFieldExt(x, y, element, FALSE);
10012 }
10013
10014 static void CreateElementFromChange(int x, int y, int element)
10015 {
10016   element = GET_VALID_RUNTIME_ELEMENT(element);
10017
10018   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10019   {
10020     int old_element = Feld[x][y];
10021
10022     /* prevent changed element from moving in same engine frame
10023        unless both old and new element can either fall or move */
10024     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10025         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10026       Stop[x][y] = TRUE;
10027   }
10028
10029   CreateFieldExt(x, y, element, TRUE);
10030 }
10031
10032 static boolean ChangeElement(int x, int y, int element, int page)
10033 {
10034   struct ElementInfo *ei = &element_info[element];
10035   struct ElementChangeInfo *change = &ei->change_page[page];
10036   int ce_value = CustomValue[x][y];
10037   int ce_score = ei->collect_score;
10038   int target_element;
10039   int old_element = Feld[x][y];
10040
10041   /* always use default change event to prevent running into a loop */
10042   if (ChangeEvent[x][y] == -1)
10043     ChangeEvent[x][y] = CE_DELAY;
10044
10045   if (ChangeEvent[x][y] == CE_DELAY)
10046   {
10047     /* reset actual trigger element, trigger player and action element */
10048     change->actual_trigger_element = EL_EMPTY;
10049     change->actual_trigger_player = EL_EMPTY;
10050     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10051     change->actual_trigger_side = CH_SIDE_NONE;
10052     change->actual_trigger_ce_value = 0;
10053     change->actual_trigger_ce_score = 0;
10054   }
10055
10056   /* do not change elements more than a specified maximum number of changes */
10057   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10058     return FALSE;
10059
10060   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10061
10062   if (change->explode)
10063   {
10064     Bang(x, y);
10065
10066     return TRUE;
10067   }
10068
10069   if (change->use_target_content)
10070   {
10071     boolean complete_replace = TRUE;
10072     boolean can_replace[3][3];
10073     int xx, yy;
10074
10075     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10076     {
10077       boolean is_empty;
10078       boolean is_walkable;
10079       boolean is_diggable;
10080       boolean is_collectible;
10081       boolean is_removable;
10082       boolean is_destructible;
10083       int ex = x + xx - 1;
10084       int ey = y + yy - 1;
10085       int content_element = change->target_content.e[xx][yy];
10086       int e;
10087
10088       can_replace[xx][yy] = TRUE;
10089
10090       if (ex == x && ey == y)   /* do not check changing element itself */
10091         continue;
10092
10093       if (content_element == EL_EMPTY_SPACE)
10094       {
10095         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10096
10097         continue;
10098       }
10099
10100       if (!IN_LEV_FIELD(ex, ey))
10101       {
10102         can_replace[xx][yy] = FALSE;
10103         complete_replace = FALSE;
10104
10105         continue;
10106       }
10107
10108       e = Feld[ex][ey];
10109
10110       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10111         e = MovingOrBlocked2Element(ex, ey);
10112
10113       is_empty = (IS_FREE(ex, ey) ||
10114                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10115
10116       is_walkable     = (is_empty || IS_WALKABLE(e));
10117       is_diggable     = (is_empty || IS_DIGGABLE(e));
10118       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10119       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10120       is_removable    = (is_diggable || is_collectible);
10121
10122       can_replace[xx][yy] =
10123         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10124           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10125           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10126           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10127           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10128           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10129          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10130
10131       if (!can_replace[xx][yy])
10132         complete_replace = FALSE;
10133     }
10134
10135     if (!change->only_if_complete || complete_replace)
10136     {
10137       boolean something_has_changed = FALSE;
10138
10139       if (change->only_if_complete && change->use_random_replace &&
10140           RND(100) < change->random_percentage)
10141         return FALSE;
10142
10143       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10144       {
10145         int ex = x + xx - 1;
10146         int ey = y + yy - 1;
10147         int content_element;
10148
10149         if (can_replace[xx][yy] && (!change->use_random_replace ||
10150                                     RND(100) < change->random_percentage))
10151         {
10152           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10153             RemoveMovingField(ex, ey);
10154
10155           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10156
10157           content_element = change->target_content.e[xx][yy];
10158           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10159                                               ce_value, ce_score);
10160
10161           CreateElementFromChange(ex, ey, target_element);
10162
10163           something_has_changed = TRUE;
10164
10165           /* for symmetry reasons, freeze newly created border elements */
10166           if (ex != x || ey != y)
10167             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10168         }
10169       }
10170
10171       if (something_has_changed)
10172       {
10173         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10174         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10175       }
10176     }
10177   }
10178   else
10179   {
10180     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10181                                         ce_value, ce_score);
10182
10183     if (element == EL_DIAGONAL_GROWING ||
10184         element == EL_DIAGONAL_SHRINKING)
10185     {
10186       target_element = Store[x][y];
10187
10188       Store[x][y] = EL_EMPTY;
10189     }
10190
10191     CreateElementFromChange(x, y, target_element);
10192
10193     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10194     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10195   }
10196
10197   /* this uses direct change before indirect change */
10198   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10199
10200   return TRUE;
10201 }
10202
10203 static void HandleElementChange(int x, int y, int page)
10204 {
10205   int element = MovingOrBlocked2Element(x, y);
10206   struct ElementInfo *ei = &element_info[element];
10207   struct ElementChangeInfo *change = &ei->change_page[page];
10208   boolean handle_action_before_change = FALSE;
10209
10210 #ifdef DEBUG
10211   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10212       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10213   {
10214     printf("\n\n");
10215     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10216            x, y, element, element_info[element].token_name);
10217     printf("HandleElementChange(): This should never happen!\n");
10218     printf("\n\n");
10219   }
10220 #endif
10221
10222   /* this can happen with classic bombs on walkable, changing elements */
10223   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10224   {
10225     return;
10226   }
10227
10228   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10229   {
10230     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10231
10232     if (change->can_change)
10233     {
10234       /* !!! not clear why graphic animation should be reset at all here !!! */
10235       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10236       /* when a custom element is about to change (for example by change delay),
10237          do not reset graphic animation when the custom element is moving */
10238       if (!IS_MOVING(x, y))
10239       {
10240         ResetGfxAnimation(x, y);
10241         ResetRandomAnimationValue(x, y);
10242       }
10243
10244       if (change->pre_change_function)
10245         change->pre_change_function(x, y);
10246     }
10247   }
10248
10249   ChangeDelay[x][y]--;
10250
10251   if (ChangeDelay[x][y] != 0)           /* continue element change */
10252   {
10253     if (change->can_change)
10254     {
10255       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10256
10257       if (IS_ANIMATED(graphic))
10258         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10259
10260       if (change->change_function)
10261         change->change_function(x, y);
10262     }
10263   }
10264   else                                  /* finish element change */
10265   {
10266     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10267     {
10268       page = ChangePage[x][y];
10269       ChangePage[x][y] = -1;
10270
10271       change = &ei->change_page[page];
10272     }
10273
10274     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10275     {
10276       ChangeDelay[x][y] = 1;            /* try change after next move step */
10277       ChangePage[x][y] = page;          /* remember page to use for change */
10278
10279       return;
10280     }
10281
10282     /* special case: set new level random seed before changing element */
10283     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10284       handle_action_before_change = TRUE;
10285
10286     if (change->has_action && handle_action_before_change)
10287       ExecuteCustomElementAction(x, y, element, page);
10288
10289     if (change->can_change)
10290     {
10291       if (ChangeElement(x, y, element, page))
10292       {
10293         if (change->post_change_function)
10294           change->post_change_function(x, y);
10295       }
10296     }
10297
10298     if (change->has_action && !handle_action_before_change)
10299       ExecuteCustomElementAction(x, y, element, page);
10300   }
10301 }
10302
10303 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10304                                               int trigger_element,
10305                                               int trigger_event,
10306                                               int trigger_player,
10307                                               int trigger_side,
10308                                               int trigger_page)
10309 {
10310   boolean change_done_any = FALSE;
10311   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10312   int i;
10313
10314   if (!(trigger_events[trigger_element][trigger_event]))
10315     return FALSE;
10316
10317   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10318
10319   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10320   {
10321     int element = EL_CUSTOM_START + i;
10322     boolean change_done = FALSE;
10323     int p;
10324
10325     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10326         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10327       continue;
10328
10329     for (p = 0; p < element_info[element].num_change_pages; p++)
10330     {
10331       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10332
10333       if (change->can_change_or_has_action &&
10334           change->has_event[trigger_event] &&
10335           change->trigger_side & trigger_side &&
10336           change->trigger_player & trigger_player &&
10337           change->trigger_page & trigger_page_bits &&
10338           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10339       {
10340         change->actual_trigger_element = trigger_element;
10341         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10342         change->actual_trigger_player_bits = trigger_player;
10343         change->actual_trigger_side = trigger_side;
10344         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10345         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10346
10347         if ((change->can_change && !change_done) || change->has_action)
10348         {
10349           int x, y;
10350
10351           SCAN_PLAYFIELD(x, y)
10352           {
10353             if (Feld[x][y] == element)
10354             {
10355               if (change->can_change && !change_done)
10356               {
10357                 /* if element already changed in this frame, not only prevent
10358                    another element change (checked in ChangeElement()), but
10359                    also prevent additional element actions for this element */
10360
10361                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10362                     !level.use_action_after_change_bug)
10363                   continue;
10364
10365                 ChangeDelay[x][y] = 1;
10366                 ChangeEvent[x][y] = trigger_event;
10367
10368                 HandleElementChange(x, y, p);
10369               }
10370               else if (change->has_action)
10371               {
10372                 /* if element already changed in this frame, not only prevent
10373                    another element change (checked in ChangeElement()), but
10374                    also prevent additional element actions for this element */
10375
10376                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10377                     !level.use_action_after_change_bug)
10378                   continue;
10379
10380                 ExecuteCustomElementAction(x, y, element, p);
10381                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10382               }
10383             }
10384           }
10385
10386           if (change->can_change)
10387           {
10388             change_done = TRUE;
10389             change_done_any = TRUE;
10390           }
10391         }
10392       }
10393     }
10394   }
10395
10396   RECURSION_LOOP_DETECTION_END();
10397
10398   return change_done_any;
10399 }
10400
10401 static boolean CheckElementChangeExt(int x, int y,
10402                                      int element,
10403                                      int trigger_element,
10404                                      int trigger_event,
10405                                      int trigger_player,
10406                                      int trigger_side)
10407 {
10408   boolean change_done = FALSE;
10409   int p;
10410
10411   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10412       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10413     return FALSE;
10414
10415   if (Feld[x][y] == EL_BLOCKED)
10416   {
10417     Blocked2Moving(x, y, &x, &y);
10418     element = Feld[x][y];
10419   }
10420
10421   /* check if element has already changed or is about to change after moving */
10422   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10423        Feld[x][y] != element) ||
10424
10425       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10426        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10427         ChangePage[x][y] != -1)))
10428     return FALSE;
10429
10430   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10431
10432   for (p = 0; p < element_info[element].num_change_pages; p++)
10433   {
10434     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10435
10436     /* check trigger element for all events where the element that is checked
10437        for changing interacts with a directly adjacent element -- this is
10438        different to element changes that affect other elements to change on the
10439        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10440     boolean check_trigger_element =
10441       (trigger_event == CE_TOUCHING_X ||
10442        trigger_event == CE_HITTING_X ||
10443        trigger_event == CE_HIT_BY_X ||
10444        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10445
10446     if (change->can_change_or_has_action &&
10447         change->has_event[trigger_event] &&
10448         change->trigger_side & trigger_side &&
10449         change->trigger_player & trigger_player &&
10450         (!check_trigger_element ||
10451          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10452     {
10453       change->actual_trigger_element = trigger_element;
10454       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10455       change->actual_trigger_player_bits = trigger_player;
10456       change->actual_trigger_side = trigger_side;
10457       change->actual_trigger_ce_value = CustomValue[x][y];
10458       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10459
10460       /* special case: trigger element not at (x,y) position for some events */
10461       if (check_trigger_element)
10462       {
10463         static struct
10464         {
10465           int dx, dy;
10466         } move_xy[] =
10467           {
10468             {  0,  0 },
10469             { -1,  0 },
10470             { +1,  0 },
10471             {  0,  0 },
10472             {  0, -1 },
10473             {  0,  0 }, { 0, 0 }, { 0, 0 },
10474             {  0, +1 }
10475           };
10476
10477         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10478         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10479
10480         change->actual_trigger_ce_value = CustomValue[xx][yy];
10481         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10482       }
10483
10484       if (change->can_change && !change_done)
10485       {
10486         ChangeDelay[x][y] = 1;
10487         ChangeEvent[x][y] = trigger_event;
10488
10489         HandleElementChange(x, y, p);
10490
10491         change_done = TRUE;
10492       }
10493       else if (change->has_action)
10494       {
10495         ExecuteCustomElementAction(x, y, element, p);
10496         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10497       }
10498     }
10499   }
10500
10501   RECURSION_LOOP_DETECTION_END();
10502
10503   return change_done;
10504 }
10505
10506 static void PlayPlayerSound(struct PlayerInfo *player)
10507 {
10508   int jx = player->jx, jy = player->jy;
10509   int sound_element = player->artwork_element;
10510   int last_action = player->last_action_waiting;
10511   int action = player->action_waiting;
10512
10513   if (player->is_waiting)
10514   {
10515     if (action != last_action)
10516       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10517     else
10518       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10519   }
10520   else
10521   {
10522     if (action != last_action)
10523       StopSound(element_info[sound_element].sound[last_action]);
10524
10525     if (last_action == ACTION_SLEEPING)
10526       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10527   }
10528 }
10529
10530 static void PlayAllPlayersSound()
10531 {
10532   int i;
10533
10534   for (i = 0; i < MAX_PLAYERS; i++)
10535     if (stored_player[i].active)
10536       PlayPlayerSound(&stored_player[i]);
10537 }
10538
10539 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10540 {
10541   boolean last_waiting = player->is_waiting;
10542   int move_dir = player->MovDir;
10543
10544   player->dir_waiting = move_dir;
10545   player->last_action_waiting = player->action_waiting;
10546
10547   if (is_waiting)
10548   {
10549     if (!last_waiting)          /* not waiting -> waiting */
10550     {
10551       player->is_waiting = TRUE;
10552
10553       player->frame_counter_bored =
10554         FrameCounter +
10555         game.player_boring_delay_fixed +
10556         GetSimpleRandom(game.player_boring_delay_random);
10557       player->frame_counter_sleeping =
10558         FrameCounter +
10559         game.player_sleeping_delay_fixed +
10560         GetSimpleRandom(game.player_sleeping_delay_random);
10561
10562       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10563     }
10564
10565     if (game.player_sleeping_delay_fixed +
10566         game.player_sleeping_delay_random > 0 &&
10567         player->anim_delay_counter == 0 &&
10568         player->post_delay_counter == 0 &&
10569         FrameCounter >= player->frame_counter_sleeping)
10570       player->is_sleeping = TRUE;
10571     else if (game.player_boring_delay_fixed +
10572              game.player_boring_delay_random > 0 &&
10573              FrameCounter >= player->frame_counter_bored)
10574       player->is_bored = TRUE;
10575
10576     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10577                               player->is_bored ? ACTION_BORING :
10578                               ACTION_WAITING);
10579
10580     if (player->is_sleeping && player->use_murphy)
10581     {
10582       /* special case for sleeping Murphy when leaning against non-free tile */
10583
10584       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10585           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10586            !IS_MOVING(player->jx - 1, player->jy)))
10587         move_dir = MV_LEFT;
10588       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10589                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10590                 !IS_MOVING(player->jx + 1, player->jy)))
10591         move_dir = MV_RIGHT;
10592       else
10593         player->is_sleeping = FALSE;
10594
10595       player->dir_waiting = move_dir;
10596     }
10597
10598     if (player->is_sleeping)
10599     {
10600       if (player->num_special_action_sleeping > 0)
10601       {
10602         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10603         {
10604           int last_special_action = player->special_action_sleeping;
10605           int num_special_action = player->num_special_action_sleeping;
10606           int special_action =
10607             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10608              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10609              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10610              last_special_action + 1 : ACTION_SLEEPING);
10611           int special_graphic =
10612             el_act_dir2img(player->artwork_element, special_action, move_dir);
10613
10614           player->anim_delay_counter =
10615             graphic_info[special_graphic].anim_delay_fixed +
10616             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10617           player->post_delay_counter =
10618             graphic_info[special_graphic].post_delay_fixed +
10619             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10620
10621           player->special_action_sleeping = special_action;
10622         }
10623
10624         if (player->anim_delay_counter > 0)
10625         {
10626           player->action_waiting = player->special_action_sleeping;
10627           player->anim_delay_counter--;
10628         }
10629         else if (player->post_delay_counter > 0)
10630         {
10631           player->post_delay_counter--;
10632         }
10633       }
10634     }
10635     else if (player->is_bored)
10636     {
10637       if (player->num_special_action_bored > 0)
10638       {
10639         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10640         {
10641           int special_action =
10642             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10643           int special_graphic =
10644             el_act_dir2img(player->artwork_element, special_action, move_dir);
10645
10646           player->anim_delay_counter =
10647             graphic_info[special_graphic].anim_delay_fixed +
10648             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10649           player->post_delay_counter =
10650             graphic_info[special_graphic].post_delay_fixed +
10651             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10652
10653           player->special_action_bored = special_action;
10654         }
10655
10656         if (player->anim_delay_counter > 0)
10657         {
10658           player->action_waiting = player->special_action_bored;
10659           player->anim_delay_counter--;
10660         }
10661         else if (player->post_delay_counter > 0)
10662         {
10663           player->post_delay_counter--;
10664         }
10665       }
10666     }
10667   }
10668   else if (last_waiting)        /* waiting -> not waiting */
10669   {
10670     player->is_waiting = FALSE;
10671     player->is_bored = FALSE;
10672     player->is_sleeping = FALSE;
10673
10674     player->frame_counter_bored = -1;
10675     player->frame_counter_sleeping = -1;
10676
10677     player->anim_delay_counter = 0;
10678     player->post_delay_counter = 0;
10679
10680     player->dir_waiting = player->MovDir;
10681     player->action_waiting = ACTION_DEFAULT;
10682
10683     player->special_action_bored = ACTION_DEFAULT;
10684     player->special_action_sleeping = ACTION_DEFAULT;
10685   }
10686 }
10687
10688 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10689 {
10690   static boolean player_was_moving = FALSE;
10691   static boolean player_was_snapping = FALSE;
10692   static boolean player_was_dropping = FALSE;
10693
10694   if ((!player->is_moving  && player_was_moving) ||
10695       (player->MovPos == 0 && player_was_moving) ||
10696       (player->is_snapping && !player_was_snapping) ||
10697       (player->is_dropping && !player_was_dropping))
10698   {
10699     if (!SaveEngineSnapshotToList())
10700       return;
10701
10702     player_was_moving = FALSE;
10703     player_was_snapping = TRUE;
10704     player_was_dropping = TRUE;
10705   }
10706   else
10707   {
10708     if (player->is_moving)
10709       player_was_moving = TRUE;
10710
10711     if (!player->is_snapping)
10712       player_was_snapping = FALSE;
10713
10714     if (!player->is_dropping)
10715       player_was_dropping = FALSE;
10716   }
10717 }
10718
10719 static void CheckSingleStepMode(struct PlayerInfo *player)
10720 {
10721   if (tape.single_step && tape.recording && !tape.pausing)
10722   {
10723     /* as it is called "single step mode", just return to pause mode when the
10724        player stopped moving after one tile (or never starts moving at all) */
10725     if (!player->is_moving && !player->is_pushing)
10726     {
10727       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10728       SnapField(player, 0, 0);                  /* stop snapping */
10729     }
10730   }
10731
10732   CheckSaveEngineSnapshot(player);
10733 }
10734
10735 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10736 {
10737   int left      = player_action & JOY_LEFT;
10738   int right     = player_action & JOY_RIGHT;
10739   int up        = player_action & JOY_UP;
10740   int down      = player_action & JOY_DOWN;
10741   int button1   = player_action & JOY_BUTTON_1;
10742   int button2   = player_action & JOY_BUTTON_2;
10743   int dx        = (left ? -1 : right ? 1 : 0);
10744   int dy        = (up   ? -1 : down  ? 1 : 0);
10745
10746   if (!player->active || tape.pausing)
10747     return 0;
10748
10749   if (player_action)
10750   {
10751     if (button1)
10752       SnapField(player, dx, dy);
10753     else
10754     {
10755       if (button2)
10756         DropElement(player);
10757
10758       MovePlayer(player, dx, dy);
10759     }
10760
10761     CheckSingleStepMode(player);
10762
10763     SetPlayerWaiting(player, FALSE);
10764
10765     return player_action;
10766   }
10767   else
10768   {
10769     /* no actions for this player (no input at player's configured device) */
10770
10771     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10772     SnapField(player, 0, 0);
10773     CheckGravityMovementWhenNotMoving(player);
10774
10775     if (player->MovPos == 0)
10776       SetPlayerWaiting(player, TRUE);
10777
10778     if (player->MovPos == 0)    /* needed for tape.playing */
10779       player->is_moving = FALSE;
10780
10781     player->is_dropping = FALSE;
10782     player->is_dropping_pressed = FALSE;
10783     player->drop_pressed_delay = 0;
10784
10785     CheckSingleStepMode(player);
10786
10787     return 0;
10788   }
10789 }
10790
10791 static void CheckLevelTime()
10792 {
10793   int i;
10794
10795   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10796   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10797   {
10798     if (level.native_em_level->lev->home == 0)  /* all players at home */
10799     {
10800       PlayerWins(local_player);
10801
10802       AllPlayersGone = TRUE;
10803
10804       level.native_em_level->lev->home = -1;
10805     }
10806
10807     if (level.native_em_level->ply[0]->alive == 0 &&
10808         level.native_em_level->ply[1]->alive == 0 &&
10809         level.native_em_level->ply[2]->alive == 0 &&
10810         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10811       AllPlayersGone = TRUE;
10812   }
10813   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10814   {
10815     if (game_sp.LevelSolved &&
10816         !game_sp.GameOver)                              /* game won */
10817     {
10818       PlayerWins(local_player);
10819
10820       game_sp.GameOver = TRUE;
10821
10822       AllPlayersGone = TRUE;
10823     }
10824
10825     if (game_sp.GameOver)                               /* game lost */
10826       AllPlayersGone = TRUE;
10827   }
10828
10829   if (TimeFrames >= FRAMES_PER_SECOND)
10830   {
10831     TimeFrames = 0;
10832     TapeTime++;
10833
10834     for (i = 0; i < MAX_PLAYERS; i++)
10835     {
10836       struct PlayerInfo *player = &stored_player[i];
10837
10838       if (SHIELD_ON(player))
10839       {
10840         player->shield_normal_time_left--;
10841
10842         if (player->shield_deadly_time_left > 0)
10843           player->shield_deadly_time_left--;
10844       }
10845     }
10846
10847     if (!local_player->LevelSolved && !level.use_step_counter)
10848     {
10849       TimePlayed++;
10850
10851       if (TimeLeft > 0)
10852       {
10853         TimeLeft--;
10854
10855         if (TimeLeft <= 10 && setup.time_limit)
10856           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10857
10858         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10859            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10860
10861         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10862
10863         if (!TimeLeft && setup.time_limit)
10864         {
10865           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10866             level.native_em_level->lev->killed_out_of_time = TRUE;
10867           else
10868             for (i = 0; i < MAX_PLAYERS; i++)
10869               KillPlayer(&stored_player[i]);
10870         }
10871       }
10872       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10873       {
10874         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10875       }
10876
10877       level.native_em_level->lev->time =
10878         (game.no_time_limit ? TimePlayed : TimeLeft);
10879     }
10880
10881     if (tape.recording || tape.playing)
10882       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10883   }
10884
10885   if (tape.recording || tape.playing)
10886     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10887
10888   UpdateAndDisplayGameControlValues();
10889 }
10890
10891 void AdvanceFrameAndPlayerCounters(int player_nr)
10892 {
10893   int i;
10894
10895   /* advance frame counters (global frame counter and time frame counter) */
10896   FrameCounter++;
10897   TimeFrames++;
10898
10899   /* advance player counters (counters for move delay, move animation etc.) */
10900   for (i = 0; i < MAX_PLAYERS; i++)
10901   {
10902     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10903     int move_delay_value = stored_player[i].move_delay_value;
10904     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10905
10906     if (!advance_player_counters)       /* not all players may be affected */
10907       continue;
10908
10909     if (move_frames == 0)       /* less than one move per game frame */
10910     {
10911       int stepsize = TILEX / move_delay_value;
10912       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10913       int count = (stored_player[i].is_moving ?
10914                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10915
10916       if (count % delay == 0)
10917         move_frames = 1;
10918     }
10919
10920     stored_player[i].Frame += move_frames;
10921
10922     if (stored_player[i].MovPos != 0)
10923       stored_player[i].StepFrame += move_frames;
10924
10925     if (stored_player[i].move_delay > 0)
10926       stored_player[i].move_delay--;
10927
10928     /* due to bugs in previous versions, counter must count up, not down */
10929     if (stored_player[i].push_delay != -1)
10930       stored_player[i].push_delay++;
10931
10932     if (stored_player[i].drop_delay > 0)
10933       stored_player[i].drop_delay--;
10934
10935     if (stored_player[i].is_dropping_pressed)
10936       stored_player[i].drop_pressed_delay++;
10937   }
10938 }
10939
10940 void StartGameActions(boolean init_network_game, boolean record_tape,
10941                       int random_seed)
10942 {
10943   unsigned int new_random_seed = InitRND(random_seed);
10944
10945   if (record_tape)
10946     TapeStartRecording(new_random_seed);
10947
10948 #if defined(NETWORK_AVALIABLE)
10949   if (init_network_game)
10950   {
10951     SendToServer_StartPlaying();
10952
10953     return;
10954   }
10955 #endif
10956
10957   InitGame();
10958 }
10959
10960 void GameActions()
10961 {
10962   static unsigned int game_frame_delay = 0;
10963   unsigned int game_frame_delay_value;
10964   byte *recorded_player_action;
10965   byte summarized_player_action = 0;
10966   byte tape_action[MAX_PLAYERS];
10967   int i;
10968
10969   for (i = 0; i < MAX_PLAYERS; i++)
10970   {
10971     struct PlayerInfo *player = &stored_player[i];
10972
10973     // allow engine snapshot if movement attempt was stopped
10974     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10975         (player->action & KEY_MOTION) == 0)
10976       game.snapshot.changed_action = TRUE;
10977
10978     // allow engine snapshot in case of snapping/dropping attempt
10979     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10980         (player->action & KEY_BUTTON) != 0)
10981       game.snapshot.changed_action = TRUE;
10982
10983     game.snapshot.last_action[i] = player->action;
10984   }
10985
10986   /* detect endless loops, caused by custom element programming */
10987   if (recursion_loop_detected && recursion_loop_depth == 0)
10988   {
10989     char *message = getStringCat3("Internal Error! Element ",
10990                                   EL_NAME(recursion_loop_element),
10991                                   " caused endless loop! Quit the game?");
10992
10993     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10994           EL_NAME(recursion_loop_element));
10995
10996     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10997
10998     recursion_loop_detected = FALSE;    /* if game should be continued */
10999
11000     free(message);
11001
11002     return;
11003   }
11004
11005   if (game.restart_level)
11006     StartGameActions(options.network, setup.autorecord, level.random_seed);
11007
11008   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11009   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11010   {
11011     if (level.native_em_level->lev->home == 0)  /* all players at home */
11012     {
11013       PlayerWins(local_player);
11014
11015       AllPlayersGone = TRUE;
11016
11017       level.native_em_level->lev->home = -1;
11018     }
11019
11020     if (level.native_em_level->ply[0]->alive == 0 &&
11021         level.native_em_level->ply[1]->alive == 0 &&
11022         level.native_em_level->ply[2]->alive == 0 &&
11023         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11024       AllPlayersGone = TRUE;
11025   }
11026   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11027   {
11028     if (game_sp.LevelSolved &&
11029         !game_sp.GameOver)                              /* game won */
11030     {
11031       PlayerWins(local_player);
11032
11033       game_sp.GameOver = TRUE;
11034
11035       AllPlayersGone = TRUE;
11036     }
11037
11038     if (game_sp.GameOver)                               /* game lost */
11039       AllPlayersGone = TRUE;
11040   }
11041
11042   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11043     GameWon();
11044
11045   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11046     TapeStop();
11047
11048   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11049     return;
11050
11051   game_frame_delay_value =
11052     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11053
11054   if (tape.playing && tape.warp_forward && !tape.pausing)
11055     game_frame_delay_value = 0;
11056
11057   /* ---------- main game synchronization point ---------- */
11058
11059   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11060
11061   if (network_playing && !network_player_action_received)
11062   {
11063     /* try to get network player actions in time */
11064
11065 #if defined(NETWORK_AVALIABLE)
11066     /* last chance to get network player actions without main loop delay */
11067     HandleNetworking();
11068 #endif
11069
11070     /* game was quit by network peer */
11071     if (game_status != GAME_MODE_PLAYING)
11072       return;
11073
11074     if (!network_player_action_received)
11075       return;           /* failed to get network player actions in time */
11076
11077     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11078   }
11079
11080   if (tape.pausing)
11081     return;
11082
11083   /* at this point we know that we really continue executing the game */
11084
11085   network_player_action_received = FALSE;
11086
11087   /* when playing tape, read previously recorded player input from tape data */
11088   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11089
11090   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11091   if (tape.pausing)
11092     return;
11093
11094   if (tape.set_centered_player)
11095   {
11096     game.centered_player_nr_next = tape.centered_player_nr_next;
11097     game.set_centered_player = TRUE;
11098   }
11099
11100   for (i = 0; i < MAX_PLAYERS; i++)
11101   {
11102     summarized_player_action |= stored_player[i].action;
11103
11104     if (!network_playing && (game.team_mode || tape.playing))
11105       stored_player[i].effective_action = stored_player[i].action;
11106   }
11107
11108 #if defined(NETWORK_AVALIABLE)
11109   if (network_playing)
11110     SendToServer_MovePlayer(summarized_player_action);
11111 #endif
11112
11113   if (!options.network && !game.team_mode)
11114     local_player->effective_action = summarized_player_action;
11115
11116   if (tape.recording &&
11117       setup.team_mode &&
11118       setup.input_on_focus &&
11119       game.centered_player_nr != -1)
11120   {
11121     for (i = 0; i < MAX_PLAYERS; i++)
11122       stored_player[i].effective_action =
11123         (i == game.centered_player_nr ? summarized_player_action : 0);
11124   }
11125
11126   if (recorded_player_action != NULL)
11127     for (i = 0; i < MAX_PLAYERS; i++)
11128       stored_player[i].effective_action = recorded_player_action[i];
11129
11130   for (i = 0; i < MAX_PLAYERS; i++)
11131   {
11132     tape_action[i] = stored_player[i].effective_action;
11133
11134     /* (this may happen in the RND game engine if a player was not present on
11135        the playfield on level start, but appeared later from a custom element */
11136     if (setup.team_mode &&
11137         tape.recording &&
11138         tape_action[i] &&
11139         !tape.player_participates[i])
11140       tape.player_participates[i] = TRUE;
11141   }
11142
11143   /* only record actions from input devices, but not programmed actions */
11144   if (tape.recording)
11145     TapeRecordAction(tape_action);
11146
11147 #if USE_NEW_PLAYER_ASSIGNMENTS
11148   // !!! also map player actions in single player mode !!!
11149   // if (game.team_mode)
11150   {
11151     byte mapped_action[MAX_PLAYERS];
11152
11153 #if DEBUG_PLAYER_ACTIONS
11154     printf(":::");
11155     for (i = 0; i < MAX_PLAYERS; i++)
11156       printf(" %d, ", stored_player[i].effective_action);
11157 #endif
11158
11159     for (i = 0; i < MAX_PLAYERS; i++)
11160       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11161
11162     for (i = 0; i < MAX_PLAYERS; i++)
11163       stored_player[i].effective_action = mapped_action[i];
11164
11165 #if DEBUG_PLAYER_ACTIONS
11166     printf(" =>");
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       printf(" %d, ", stored_player[i].effective_action);
11169     printf("\n");
11170 #endif
11171   }
11172 #if DEBUG_PLAYER_ACTIONS
11173   else
11174   {
11175     printf(":::");
11176     for (i = 0; i < MAX_PLAYERS; i++)
11177       printf(" %d, ", stored_player[i].effective_action);
11178     printf("\n");
11179   }
11180 #endif
11181 #endif
11182
11183   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11184   {
11185     GameActions_EM_Main();
11186   }
11187   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11188   {
11189     GameActions_SP_Main();
11190   }
11191   else
11192   {
11193     GameActions_RND();
11194   }
11195 }
11196
11197 void GameActions_EM_Main()
11198 {
11199   byte effective_action[MAX_PLAYERS];
11200   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11201   int i;
11202
11203   for (i = 0; i < MAX_PLAYERS; i++)
11204     effective_action[i] = stored_player[i].effective_action;
11205
11206   GameActions_EM(effective_action, warp_mode);
11207
11208   CheckLevelTime();
11209
11210   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11211 }
11212
11213 void GameActions_SP_Main()
11214 {
11215   byte effective_action[MAX_PLAYERS];
11216   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11217   int i;
11218
11219   for (i = 0; i < MAX_PLAYERS; i++)
11220     effective_action[i] = stored_player[i].effective_action;
11221
11222   GameActions_SP(effective_action, warp_mode);
11223
11224   CheckLevelTime();
11225
11226   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11227 }
11228
11229 void GameActions_RND()
11230 {
11231   int magic_wall_x = 0, magic_wall_y = 0;
11232   int i, x, y, element, graphic;
11233
11234   InitPlayfieldScanModeVars();
11235
11236   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11237   {
11238     SCAN_PLAYFIELD(x, y)
11239     {
11240       ChangeCount[x][y] = 0;
11241       ChangeEvent[x][y] = -1;
11242     }
11243   }
11244
11245   if (game.set_centered_player)
11246   {
11247     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11248
11249     /* switching to "all players" only possible if all players fit to screen */
11250     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11251     {
11252       game.centered_player_nr_next = game.centered_player_nr;
11253       game.set_centered_player = FALSE;
11254     }
11255
11256     /* do not switch focus to non-existing (or non-active) player */
11257     if (game.centered_player_nr_next >= 0 &&
11258         !stored_player[game.centered_player_nr_next].active)
11259     {
11260       game.centered_player_nr_next = game.centered_player_nr;
11261       game.set_centered_player = FALSE;
11262     }
11263   }
11264
11265   if (game.set_centered_player &&
11266       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11267   {
11268     int sx, sy;
11269
11270     if (game.centered_player_nr_next == -1)
11271     {
11272       setScreenCenteredToAllPlayers(&sx, &sy);
11273     }
11274     else
11275     {
11276       sx = stored_player[game.centered_player_nr_next].jx;
11277       sy = stored_player[game.centered_player_nr_next].jy;
11278     }
11279
11280     game.centered_player_nr = game.centered_player_nr_next;
11281     game.set_centered_player = FALSE;
11282
11283     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11284     DrawGameDoorValues();
11285   }
11286
11287   for (i = 0; i < MAX_PLAYERS; i++)
11288   {
11289     int actual_player_action = stored_player[i].effective_action;
11290
11291 #if 1
11292     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11293        - rnd_equinox_tetrachloride 048
11294        - rnd_equinox_tetrachloride_ii 096
11295        - rnd_emanuel_schmieg 002
11296        - doctor_sloan_ww 001, 020
11297     */
11298     if (stored_player[i].MovPos == 0)
11299       CheckGravityMovement(&stored_player[i]);
11300 #endif
11301
11302     /* overwrite programmed action with tape action */
11303     if (stored_player[i].programmed_action)
11304       actual_player_action = stored_player[i].programmed_action;
11305
11306     PlayerActions(&stored_player[i], actual_player_action);
11307
11308     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11309   }
11310
11311   ScrollScreen(NULL, SCROLL_GO_ON);
11312
11313   /* for backwards compatibility, the following code emulates a fixed bug that
11314      occured when pushing elements (causing elements that just made their last
11315      pushing step to already (if possible) make their first falling step in the
11316      same game frame, which is bad); this code is also needed to use the famous
11317      "spring push bug" which is used in older levels and might be wanted to be
11318      used also in newer levels, but in this case the buggy pushing code is only
11319      affecting the "spring" element and no other elements */
11320
11321   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11322   {
11323     for (i = 0; i < MAX_PLAYERS; i++)
11324     {
11325       struct PlayerInfo *player = &stored_player[i];
11326       int x = player->jx;
11327       int y = player->jy;
11328
11329       if (player->active && player->is_pushing && player->is_moving &&
11330           IS_MOVING(x, y) &&
11331           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11332            Feld[x][y] == EL_SPRING))
11333       {
11334         ContinueMoving(x, y);
11335
11336         /* continue moving after pushing (this is actually a bug) */
11337         if (!IS_MOVING(x, y))
11338           Stop[x][y] = FALSE;
11339       }
11340     }
11341   }
11342
11343   SCAN_PLAYFIELD(x, y)
11344   {
11345     ChangeCount[x][y] = 0;
11346     ChangeEvent[x][y] = -1;
11347
11348     /* this must be handled before main playfield loop */
11349     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11350     {
11351       MovDelay[x][y]--;
11352       if (MovDelay[x][y] <= 0)
11353         RemoveField(x, y);
11354     }
11355
11356     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11357     {
11358       MovDelay[x][y]--;
11359       if (MovDelay[x][y] <= 0)
11360       {
11361         RemoveField(x, y);
11362         TEST_DrawLevelField(x, y);
11363
11364         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11365       }
11366     }
11367
11368 #if DEBUG
11369     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11370     {
11371       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11372       printf("GameActions(): This should never happen!\n");
11373
11374       ChangePage[x][y] = -1;
11375     }
11376 #endif
11377
11378     Stop[x][y] = FALSE;
11379     if (WasJustMoving[x][y] > 0)
11380       WasJustMoving[x][y]--;
11381     if (WasJustFalling[x][y] > 0)
11382       WasJustFalling[x][y]--;
11383     if (CheckCollision[x][y] > 0)
11384       CheckCollision[x][y]--;
11385     if (CheckImpact[x][y] > 0)
11386       CheckImpact[x][y]--;
11387
11388     GfxFrame[x][y]++;
11389
11390     /* reset finished pushing action (not done in ContinueMoving() to allow
11391        continuous pushing animation for elements with zero push delay) */
11392     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11393     {
11394       ResetGfxAnimation(x, y);
11395       TEST_DrawLevelField(x, y);
11396     }
11397
11398 #if DEBUG
11399     if (IS_BLOCKED(x, y))
11400     {
11401       int oldx, oldy;
11402
11403       Blocked2Moving(x, y, &oldx, &oldy);
11404       if (!IS_MOVING(oldx, oldy))
11405       {
11406         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11407         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11408         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11409         printf("GameActions(): This should never happen!\n");
11410       }
11411     }
11412 #endif
11413   }
11414
11415   SCAN_PLAYFIELD(x, y)
11416   {
11417     element = Feld[x][y];
11418     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11419
11420     ResetGfxFrame(x, y, TRUE);
11421
11422     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11423         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11424       ResetRandomAnimationValue(x, y);
11425
11426     SetRandomAnimationValue(x, y);
11427
11428     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11429
11430     if (IS_INACTIVE(element))
11431     {
11432       if (IS_ANIMATED(graphic))
11433         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11434
11435       continue;
11436     }
11437
11438     /* this may take place after moving, so 'element' may have changed */
11439     if (IS_CHANGING(x, y) &&
11440         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11441     {
11442       int page = element_info[element].event_page_nr[CE_DELAY];
11443
11444       HandleElementChange(x, y, page);
11445
11446       element = Feld[x][y];
11447       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11448     }
11449
11450     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11451     {
11452       StartMoving(x, y);
11453
11454       element = Feld[x][y];
11455       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11456
11457       if (IS_ANIMATED(graphic) &&
11458           !IS_MOVING(x, y) &&
11459           !Stop[x][y])
11460         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11461
11462       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11463         TEST_DrawTwinkleOnField(x, y);
11464     }
11465     else if ((element == EL_ACID ||
11466               element == EL_EXIT_OPEN ||
11467               element == EL_EM_EXIT_OPEN ||
11468               element == EL_SP_EXIT_OPEN ||
11469               element == EL_STEEL_EXIT_OPEN ||
11470               element == EL_EM_STEEL_EXIT_OPEN ||
11471               element == EL_SP_TERMINAL ||
11472               element == EL_SP_TERMINAL_ACTIVE ||
11473               element == EL_EXTRA_TIME ||
11474               element == EL_SHIELD_NORMAL ||
11475               element == EL_SHIELD_DEADLY) &&
11476              IS_ANIMATED(graphic))
11477       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11478     else if (IS_MOVING(x, y))
11479       ContinueMoving(x, y);
11480     else if (IS_ACTIVE_BOMB(element))
11481       CheckDynamite(x, y);
11482     else if (element == EL_AMOEBA_GROWING)
11483       AmoebeWaechst(x, y);
11484     else if (element == EL_AMOEBA_SHRINKING)
11485       AmoebaDisappearing(x, y);
11486
11487 #if !USE_NEW_AMOEBA_CODE
11488     else if (IS_AMOEBALIVE(element))
11489       AmoebeAbleger(x, y);
11490 #endif
11491
11492     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11493       Life(x, y);
11494     else if (element == EL_EXIT_CLOSED)
11495       CheckExit(x, y);
11496     else if (element == EL_EM_EXIT_CLOSED)
11497       CheckExitEM(x, y);
11498     else if (element == EL_STEEL_EXIT_CLOSED)
11499       CheckExitSteel(x, y);
11500     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11501       CheckExitSteelEM(x, y);
11502     else if (element == EL_SP_EXIT_CLOSED)
11503       CheckExitSP(x, y);
11504     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11505              element == EL_EXPANDABLE_STEELWALL_GROWING)
11506       MauerWaechst(x, y);
11507     else if (element == EL_EXPANDABLE_WALL ||
11508              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11509              element == EL_EXPANDABLE_WALL_VERTICAL ||
11510              element == EL_EXPANDABLE_WALL_ANY ||
11511              element == EL_BD_EXPANDABLE_WALL)
11512       MauerAbleger(x, y);
11513     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11514              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11515              element == EL_EXPANDABLE_STEELWALL_ANY)
11516       MauerAblegerStahl(x, y);
11517     else if (element == EL_FLAMES)
11518       CheckForDragon(x, y);
11519     else if (element == EL_EXPLOSION)
11520       ; /* drawing of correct explosion animation is handled separately */
11521     else if (element == EL_ELEMENT_SNAPPING ||
11522              element == EL_DIAGONAL_SHRINKING ||
11523              element == EL_DIAGONAL_GROWING)
11524     {
11525       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11526
11527       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11528     }
11529     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11530       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11531
11532     if (IS_BELT_ACTIVE(element))
11533       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11534
11535     if (game.magic_wall_active)
11536     {
11537       int jx = local_player->jx, jy = local_player->jy;
11538
11539       /* play the element sound at the position nearest to the player */
11540       if ((element == EL_MAGIC_WALL_FULL ||
11541            element == EL_MAGIC_WALL_ACTIVE ||
11542            element == EL_MAGIC_WALL_EMPTYING ||
11543            element == EL_BD_MAGIC_WALL_FULL ||
11544            element == EL_BD_MAGIC_WALL_ACTIVE ||
11545            element == EL_BD_MAGIC_WALL_EMPTYING ||
11546            element == EL_DC_MAGIC_WALL_FULL ||
11547            element == EL_DC_MAGIC_WALL_ACTIVE ||
11548            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11549           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11550       {
11551         magic_wall_x = x;
11552         magic_wall_y = y;
11553       }
11554     }
11555   }
11556
11557 #if USE_NEW_AMOEBA_CODE
11558   /* new experimental amoeba growth stuff */
11559   if (!(FrameCounter % 8))
11560   {
11561     static unsigned int random = 1684108901;
11562
11563     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11564     {
11565       x = RND(lev_fieldx);
11566       y = RND(lev_fieldy);
11567       element = Feld[x][y];
11568
11569       if (!IS_PLAYER(x,y) &&
11570           (element == EL_EMPTY ||
11571            CAN_GROW_INTO(element) ||
11572            element == EL_QUICKSAND_EMPTY ||
11573            element == EL_QUICKSAND_FAST_EMPTY ||
11574            element == EL_ACID_SPLASH_LEFT ||
11575            element == EL_ACID_SPLASH_RIGHT))
11576       {
11577         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11578             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11579             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11580             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11581           Feld[x][y] = EL_AMOEBA_DROP;
11582       }
11583
11584       random = random * 129 + 1;
11585     }
11586   }
11587 #endif
11588
11589   game.explosions_delayed = FALSE;
11590
11591   SCAN_PLAYFIELD(x, y)
11592   {
11593     element = Feld[x][y];
11594
11595     if (ExplodeField[x][y])
11596       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11597     else if (element == EL_EXPLOSION)
11598       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11599
11600     ExplodeField[x][y] = EX_TYPE_NONE;
11601   }
11602
11603   game.explosions_delayed = TRUE;
11604
11605   if (game.magic_wall_active)
11606   {
11607     if (!(game.magic_wall_time_left % 4))
11608     {
11609       int element = Feld[magic_wall_x][magic_wall_y];
11610
11611       if (element == EL_BD_MAGIC_WALL_FULL ||
11612           element == EL_BD_MAGIC_WALL_ACTIVE ||
11613           element == EL_BD_MAGIC_WALL_EMPTYING)
11614         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11615       else if (element == EL_DC_MAGIC_WALL_FULL ||
11616                element == EL_DC_MAGIC_WALL_ACTIVE ||
11617                element == EL_DC_MAGIC_WALL_EMPTYING)
11618         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11619       else
11620         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11621     }
11622
11623     if (game.magic_wall_time_left > 0)
11624     {
11625       game.magic_wall_time_left--;
11626
11627       if (!game.magic_wall_time_left)
11628       {
11629         SCAN_PLAYFIELD(x, y)
11630         {
11631           element = Feld[x][y];
11632
11633           if (element == EL_MAGIC_WALL_ACTIVE ||
11634               element == EL_MAGIC_WALL_FULL)
11635           {
11636             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11637             TEST_DrawLevelField(x, y);
11638           }
11639           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11640                    element == EL_BD_MAGIC_WALL_FULL)
11641           {
11642             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11643             TEST_DrawLevelField(x, y);
11644           }
11645           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11646                    element == EL_DC_MAGIC_WALL_FULL)
11647           {
11648             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11649             TEST_DrawLevelField(x, y);
11650           }
11651         }
11652
11653         game.magic_wall_active = FALSE;
11654       }
11655     }
11656   }
11657
11658   if (game.light_time_left > 0)
11659   {
11660     game.light_time_left--;
11661
11662     if (game.light_time_left == 0)
11663       RedrawAllLightSwitchesAndInvisibleElements();
11664   }
11665
11666   if (game.timegate_time_left > 0)
11667   {
11668     game.timegate_time_left--;
11669
11670     if (game.timegate_time_left == 0)
11671       CloseAllOpenTimegates();
11672   }
11673
11674   if (game.lenses_time_left > 0)
11675   {
11676     game.lenses_time_left--;
11677
11678     if (game.lenses_time_left == 0)
11679       RedrawAllInvisibleElementsForLenses();
11680   }
11681
11682   if (game.magnify_time_left > 0)
11683   {
11684     game.magnify_time_left--;
11685
11686     if (game.magnify_time_left == 0)
11687       RedrawAllInvisibleElementsForMagnifier();
11688   }
11689
11690   for (i = 0; i < MAX_PLAYERS; i++)
11691   {
11692     struct PlayerInfo *player = &stored_player[i];
11693
11694     if (SHIELD_ON(player))
11695     {
11696       if (player->shield_deadly_time_left)
11697         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11698       else if (player->shield_normal_time_left)
11699         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11700     }
11701   }
11702
11703 #if USE_DELAYED_GFX_REDRAW
11704   SCAN_PLAYFIELD(x, y)
11705   {
11706     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11707     {
11708       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11709          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11710
11711       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11712         DrawLevelField(x, y);
11713
11714       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11715         DrawLevelFieldCrumbled(x, y);
11716
11717       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11718         DrawLevelFieldCrumbledNeighbours(x, y);
11719
11720       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11721         DrawTwinkleOnField(x, y);
11722     }
11723
11724     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11725   }
11726 #endif
11727
11728   CheckLevelTime();
11729
11730   DrawAllPlayers();
11731   PlayAllPlayersSound();
11732
11733   if (options.debug)                    /* calculate frames per second */
11734   {
11735     static unsigned int fps_counter = 0;
11736     static int fps_frames = 0;
11737     unsigned int fps_delay_ms = Counter() - fps_counter;
11738
11739     fps_frames++;
11740
11741     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11742     {
11743       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11744
11745       fps_frames = 0;
11746       fps_counter = Counter();
11747     }
11748
11749     redraw_mask |= REDRAW_FPS;
11750   }
11751
11752   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11753
11754   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11755   {
11756     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11757
11758     local_player->show_envelope = 0;
11759   }
11760
11761   /* use random number generator in every frame to make it less predictable */
11762   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11763     RND(1);
11764 }
11765
11766 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11767 {
11768   int min_x = x, min_y = y, max_x = x, max_y = y;
11769   int i;
11770
11771   for (i = 0; i < MAX_PLAYERS; i++)
11772   {
11773     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11774
11775     if (!stored_player[i].active || &stored_player[i] == player)
11776       continue;
11777
11778     min_x = MIN(min_x, jx);
11779     min_y = MIN(min_y, jy);
11780     max_x = MAX(max_x, jx);
11781     max_y = MAX(max_y, jy);
11782   }
11783
11784   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11785 }
11786
11787 static boolean AllPlayersInVisibleScreen()
11788 {
11789   int i;
11790
11791   for (i = 0; i < MAX_PLAYERS; i++)
11792   {
11793     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11794
11795     if (!stored_player[i].active)
11796       continue;
11797
11798     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11799       return FALSE;
11800   }
11801
11802   return TRUE;
11803 }
11804
11805 void ScrollLevel(int dx, int dy)
11806 {
11807   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11808   int x, y;
11809
11810   BlitBitmap(drawto_field, drawto_field,
11811              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11812              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11813              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11814              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11815              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11816              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11817
11818   if (dx != 0)
11819   {
11820     x = (dx == 1 ? BX1 : BX2);
11821     for (y = BY1; y <= BY2; y++)
11822       DrawScreenField(x, y);
11823   }
11824
11825   if (dy != 0)
11826   {
11827     y = (dy == 1 ? BY1 : BY2);
11828     for (x = BX1; x <= BX2; x++)
11829       DrawScreenField(x, y);
11830   }
11831
11832   redraw_mask |= REDRAW_FIELD;
11833 }
11834
11835 static boolean canFallDown(struct PlayerInfo *player)
11836 {
11837   int jx = player->jx, jy = player->jy;
11838
11839   return (IN_LEV_FIELD(jx, jy + 1) &&
11840           (IS_FREE(jx, jy + 1) ||
11841            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11842           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11843           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11844 }
11845
11846 static boolean canPassField(int x, int y, int move_dir)
11847 {
11848   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11849   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11850   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11851   int nextx = x + dx;
11852   int nexty = y + dy;
11853   int element = Feld[x][y];
11854
11855   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11856           !CAN_MOVE(element) &&
11857           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11858           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11859           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11860 }
11861
11862 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11863 {
11864   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11865   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11866   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11867   int newx = x + dx;
11868   int newy = y + dy;
11869
11870   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11871           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11872           (IS_DIGGABLE(Feld[newx][newy]) ||
11873            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11874            canPassField(newx, newy, move_dir)));
11875 }
11876
11877 static void CheckGravityMovement(struct PlayerInfo *player)
11878 {
11879   if (player->gravity && !player->programmed_action)
11880   {
11881     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11882     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11883     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11884     int jx = player->jx, jy = player->jy;
11885     boolean player_is_moving_to_valid_field =
11886       (!player_is_snapping &&
11887        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11888         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11889     boolean player_can_fall_down = canFallDown(player);
11890
11891     if (player_can_fall_down &&
11892         !player_is_moving_to_valid_field)
11893       player->programmed_action = MV_DOWN;
11894   }
11895 }
11896
11897 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11898 {
11899   return CheckGravityMovement(player);
11900
11901   if (player->gravity && !player->programmed_action)
11902   {
11903     int jx = player->jx, jy = player->jy;
11904     boolean field_under_player_is_free =
11905       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11906     boolean player_is_standing_on_valid_field =
11907       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11908        (IS_WALKABLE(Feld[jx][jy]) &&
11909         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11910
11911     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11912       player->programmed_action = MV_DOWN;
11913   }
11914 }
11915
11916 /*
11917   MovePlayerOneStep()
11918   -----------------------------------------------------------------------------
11919   dx, dy:               direction (non-diagonal) to try to move the player to
11920   real_dx, real_dy:     direction as read from input device (can be diagonal)
11921 */
11922
11923 boolean MovePlayerOneStep(struct PlayerInfo *player,
11924                           int dx, int dy, int real_dx, int real_dy)
11925 {
11926   int jx = player->jx, jy = player->jy;
11927   int new_jx = jx + dx, new_jy = jy + dy;
11928   int can_move;
11929   boolean player_can_move = !player->cannot_move;
11930
11931   if (!player->active || (!dx && !dy))
11932     return MP_NO_ACTION;
11933
11934   player->MovDir = (dx < 0 ? MV_LEFT :
11935                     dx > 0 ? MV_RIGHT :
11936                     dy < 0 ? MV_UP :
11937                     dy > 0 ? MV_DOWN :  MV_NONE);
11938
11939   if (!IN_LEV_FIELD(new_jx, new_jy))
11940     return MP_NO_ACTION;
11941
11942   if (!player_can_move)
11943   {
11944     if (player->MovPos == 0)
11945     {
11946       player->is_moving = FALSE;
11947       player->is_digging = FALSE;
11948       player->is_collecting = FALSE;
11949       player->is_snapping = FALSE;
11950       player->is_pushing = FALSE;
11951     }
11952   }
11953
11954   if (!options.network && game.centered_player_nr == -1 &&
11955       !AllPlayersInSight(player, new_jx, new_jy))
11956     return MP_NO_ACTION;
11957
11958   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11959   if (can_move != MP_MOVING)
11960     return can_move;
11961
11962   /* check if DigField() has caused relocation of the player */
11963   if (player->jx != jx || player->jy != jy)
11964     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11965
11966   StorePlayer[jx][jy] = 0;
11967   player->last_jx = jx;
11968   player->last_jy = jy;
11969   player->jx = new_jx;
11970   player->jy = new_jy;
11971   StorePlayer[new_jx][new_jy] = player->element_nr;
11972
11973   if (player->move_delay_value_next != -1)
11974   {
11975     player->move_delay_value = player->move_delay_value_next;
11976     player->move_delay_value_next = -1;
11977   }
11978
11979   player->MovPos =
11980     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11981
11982   player->step_counter++;
11983
11984   PlayerVisit[jx][jy] = FrameCounter;
11985
11986   player->is_moving = TRUE;
11987
11988 #if 1
11989   /* should better be called in MovePlayer(), but this breaks some tapes */
11990   ScrollPlayer(player, SCROLL_INIT);
11991 #endif
11992
11993   return MP_MOVING;
11994 }
11995
11996 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11997 {
11998   int jx = player->jx, jy = player->jy;
11999   int old_jx = jx, old_jy = jy;
12000   int moved = MP_NO_ACTION;
12001
12002   if (!player->active)
12003     return FALSE;
12004
12005   if (!dx && !dy)
12006   {
12007     if (player->MovPos == 0)
12008     {
12009       player->is_moving = FALSE;
12010       player->is_digging = FALSE;
12011       player->is_collecting = FALSE;
12012       player->is_snapping = FALSE;
12013       player->is_pushing = FALSE;
12014     }
12015
12016     return FALSE;
12017   }
12018
12019   if (player->move_delay > 0)
12020     return FALSE;
12021
12022   player->move_delay = -1;              /* set to "uninitialized" value */
12023
12024   /* store if player is automatically moved to next field */
12025   player->is_auto_moving = (player->programmed_action != MV_NONE);
12026
12027   /* remove the last programmed player action */
12028   player->programmed_action = 0;
12029
12030   if (player->MovPos)
12031   {
12032     /* should only happen if pre-1.2 tape recordings are played */
12033     /* this is only for backward compatibility */
12034
12035     int original_move_delay_value = player->move_delay_value;
12036
12037 #if DEBUG
12038     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12039            tape.counter);
12040 #endif
12041
12042     /* scroll remaining steps with finest movement resolution */
12043     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12044
12045     while (player->MovPos)
12046     {
12047       ScrollPlayer(player, SCROLL_GO_ON);
12048       ScrollScreen(NULL, SCROLL_GO_ON);
12049
12050       AdvanceFrameAndPlayerCounters(player->index_nr);
12051
12052       DrawAllPlayers();
12053       BackToFront();
12054     }
12055
12056     player->move_delay_value = original_move_delay_value;
12057   }
12058
12059   player->is_active = FALSE;
12060
12061   if (player->last_move_dir & MV_HORIZONTAL)
12062   {
12063     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12064       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12065   }
12066   else
12067   {
12068     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12069       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12070   }
12071
12072   if (!moved && !player->is_active)
12073   {
12074     player->is_moving = FALSE;
12075     player->is_digging = FALSE;
12076     player->is_collecting = FALSE;
12077     player->is_snapping = FALSE;
12078     player->is_pushing = FALSE;
12079   }
12080
12081   jx = player->jx;
12082   jy = player->jy;
12083
12084   if (moved & MP_MOVING && !ScreenMovPos &&
12085       (player->index_nr == game.centered_player_nr ||
12086        game.centered_player_nr == -1))
12087   {
12088     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12089     int offset = game.scroll_delay_value;
12090
12091     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12092     {
12093       /* actual player has left the screen -- scroll in that direction */
12094       if (jx != old_jx)         /* player has moved horizontally */
12095         scroll_x += (jx - old_jx);
12096       else                      /* player has moved vertically */
12097         scroll_y += (jy - old_jy);
12098     }
12099     else
12100     {
12101       if (jx != old_jx)         /* player has moved horizontally */
12102       {
12103         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12104             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12105           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12106
12107         /* don't scroll over playfield boundaries */
12108         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12109           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12110
12111         /* don't scroll more than one field at a time */
12112         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12113
12114         /* don't scroll against the player's moving direction */
12115         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12116             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12117           scroll_x = old_scroll_x;
12118       }
12119       else                      /* player has moved vertically */
12120       {
12121         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12122             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12123           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12124
12125         /* don't scroll over playfield boundaries */
12126         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12127           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12128
12129         /* don't scroll more than one field at a time */
12130         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12131
12132         /* don't scroll against the player's moving direction */
12133         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12134             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12135           scroll_y = old_scroll_y;
12136       }
12137     }
12138
12139     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12140     {
12141       if (!options.network && game.centered_player_nr == -1 &&
12142           !AllPlayersInVisibleScreen())
12143       {
12144         scroll_x = old_scroll_x;
12145         scroll_y = old_scroll_y;
12146       }
12147       else
12148       {
12149         ScrollScreen(player, SCROLL_INIT);
12150         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12151       }
12152     }
12153   }
12154
12155   player->StepFrame = 0;
12156
12157   if (moved & MP_MOVING)
12158   {
12159     if (old_jx != jx && old_jy == jy)
12160       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12161     else if (old_jx == jx && old_jy != jy)
12162       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12163
12164     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12165
12166     player->last_move_dir = player->MovDir;
12167     player->is_moving = TRUE;
12168     player->is_snapping = FALSE;
12169     player->is_switching = FALSE;
12170     player->is_dropping = FALSE;
12171     player->is_dropping_pressed = FALSE;
12172     player->drop_pressed_delay = 0;
12173
12174 #if 0
12175     /* should better be called here than above, but this breaks some tapes */
12176     ScrollPlayer(player, SCROLL_INIT);
12177 #endif
12178   }
12179   else
12180   {
12181     CheckGravityMovementWhenNotMoving(player);
12182
12183     player->is_moving = FALSE;
12184
12185     /* at this point, the player is allowed to move, but cannot move right now
12186        (e.g. because of something blocking the way) -- ensure that the player
12187        is also allowed to move in the next frame (in old versions before 3.1.1,
12188        the player was forced to wait again for eight frames before next try) */
12189
12190     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12191       player->move_delay = 0;   /* allow direct movement in the next frame */
12192   }
12193
12194   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12195     player->move_delay = player->move_delay_value;
12196
12197   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12198   {
12199     TestIfPlayerTouchesBadThing(jx, jy);
12200     TestIfPlayerTouchesCustomElement(jx, jy);
12201   }
12202
12203   if (!player->active)
12204     RemovePlayer(player);
12205
12206   return moved;
12207 }
12208
12209 void ScrollPlayer(struct PlayerInfo *player, int mode)
12210 {
12211   int jx = player->jx, jy = player->jy;
12212   int last_jx = player->last_jx, last_jy = player->last_jy;
12213   int move_stepsize = TILEX / player->move_delay_value;
12214
12215   if (!player->active)
12216     return;
12217
12218   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12219     return;
12220
12221   if (mode == SCROLL_INIT)
12222   {
12223     player->actual_frame_counter = FrameCounter;
12224     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12225
12226     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12227         Feld[last_jx][last_jy] == EL_EMPTY)
12228     {
12229       int last_field_block_delay = 0;   /* start with no blocking at all */
12230       int block_delay_adjustment = player->block_delay_adjustment;
12231
12232       /* if player blocks last field, add delay for exactly one move */
12233       if (player->block_last_field)
12234       {
12235         last_field_block_delay += player->move_delay_value;
12236
12237         /* when blocking enabled, prevent moving up despite gravity */
12238         if (player->gravity && player->MovDir == MV_UP)
12239           block_delay_adjustment = -1;
12240       }
12241
12242       /* add block delay adjustment (also possible when not blocking) */
12243       last_field_block_delay += block_delay_adjustment;
12244
12245       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12246       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12247     }
12248
12249     if (player->MovPos != 0)    /* player has not yet reached destination */
12250       return;
12251   }
12252   else if (!FrameReached(&player->actual_frame_counter, 1))
12253     return;
12254
12255   if (player->MovPos != 0)
12256   {
12257     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12258     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12259
12260     /* before DrawPlayer() to draw correct player graphic for this case */
12261     if (player->MovPos == 0)
12262       CheckGravityMovement(player);
12263   }
12264
12265   if (player->MovPos == 0)      /* player reached destination field */
12266   {
12267     if (player->move_delay_reset_counter > 0)
12268     {
12269       player->move_delay_reset_counter--;
12270
12271       if (player->move_delay_reset_counter == 0)
12272       {
12273         /* continue with normal speed after quickly moving through gate */
12274         HALVE_PLAYER_SPEED(player);
12275
12276         /* be able to make the next move without delay */
12277         player->move_delay = 0;
12278       }
12279     }
12280
12281     player->last_jx = jx;
12282     player->last_jy = jy;
12283
12284     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12285         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12286         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12287         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12288         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12289         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12290         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12291         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12292     {
12293       DrawPlayer(player);       /* needed here only to cleanup last field */
12294       RemovePlayer(player);
12295
12296       if (local_player->friends_still_needed == 0 ||
12297           IS_SP_ELEMENT(Feld[jx][jy]))
12298         PlayerWins(player);
12299     }
12300
12301     /* this breaks one level: "machine", level 000 */
12302     {
12303       int move_direction = player->MovDir;
12304       int enter_side = MV_DIR_OPPOSITE(move_direction);
12305       int leave_side = move_direction;
12306       int old_jx = last_jx;
12307       int old_jy = last_jy;
12308       int old_element = Feld[old_jx][old_jy];
12309       int new_element = Feld[jx][jy];
12310
12311       if (IS_CUSTOM_ELEMENT(old_element))
12312         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12313                                    CE_LEFT_BY_PLAYER,
12314                                    player->index_bit, leave_side);
12315
12316       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12317                                           CE_PLAYER_LEAVES_X,
12318                                           player->index_bit, leave_side);
12319
12320       if (IS_CUSTOM_ELEMENT(new_element))
12321         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12322                                    player->index_bit, enter_side);
12323
12324       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12325                                           CE_PLAYER_ENTERS_X,
12326                                           player->index_bit, enter_side);
12327
12328       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12329                                         CE_MOVE_OF_X, move_direction);
12330     }
12331
12332     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12333     {
12334       TestIfPlayerTouchesBadThing(jx, jy);
12335       TestIfPlayerTouchesCustomElement(jx, jy);
12336
12337       /* needed because pushed element has not yet reached its destination,
12338          so it would trigger a change event at its previous field location */
12339       if (!player->is_pushing)
12340         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12341
12342       if (!player->active)
12343         RemovePlayer(player);
12344     }
12345
12346     if (!local_player->LevelSolved && level.use_step_counter)
12347     {
12348       int i;
12349
12350       TimePlayed++;
12351
12352       if (TimeLeft > 0)
12353       {
12354         TimeLeft--;
12355
12356         if (TimeLeft <= 10 && setup.time_limit)
12357           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12358
12359         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12360
12361         DisplayGameControlValues();
12362
12363         if (!TimeLeft && setup.time_limit)
12364           for (i = 0; i < MAX_PLAYERS; i++)
12365             KillPlayer(&stored_player[i]);
12366       }
12367       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12368       {
12369         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12370
12371         DisplayGameControlValues();
12372       }
12373     }
12374
12375     if (tape.single_step && tape.recording && !tape.pausing &&
12376         !player->programmed_action)
12377       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12378
12379     if (!player->programmed_action)
12380       CheckSaveEngineSnapshot(player);
12381   }
12382 }
12383
12384 void ScrollScreen(struct PlayerInfo *player, int mode)
12385 {
12386   static unsigned int screen_frame_counter = 0;
12387
12388   if (mode == SCROLL_INIT)
12389   {
12390     /* set scrolling step size according to actual player's moving speed */
12391     ScrollStepSize = TILEX / player->move_delay_value;
12392
12393     screen_frame_counter = FrameCounter;
12394     ScreenMovDir = player->MovDir;
12395     ScreenMovPos = player->MovPos;
12396     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12397     return;
12398   }
12399   else if (!FrameReached(&screen_frame_counter, 1))
12400     return;
12401
12402   if (ScreenMovPos)
12403   {
12404     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12405     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12406     redraw_mask |= REDRAW_FIELD;
12407   }
12408   else
12409     ScreenMovDir = MV_NONE;
12410 }
12411
12412 void TestIfPlayerTouchesCustomElement(int x, int y)
12413 {
12414   static int xy[4][2] =
12415   {
12416     { 0, -1 },
12417     { -1, 0 },
12418     { +1, 0 },
12419     { 0, +1 }
12420   };
12421   static int trigger_sides[4][2] =
12422   {
12423     /* center side       border side */
12424     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12425     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12426     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12427     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12428   };
12429   static int touch_dir[4] =
12430   {
12431     MV_LEFT | MV_RIGHT,
12432     MV_UP   | MV_DOWN,
12433     MV_UP   | MV_DOWN,
12434     MV_LEFT | MV_RIGHT
12435   };
12436   int center_element = Feld[x][y];      /* should always be non-moving! */
12437   int i;
12438
12439   for (i = 0; i < NUM_DIRECTIONS; i++)
12440   {
12441     int xx = x + xy[i][0];
12442     int yy = y + xy[i][1];
12443     int center_side = trigger_sides[i][0];
12444     int border_side = trigger_sides[i][1];
12445     int border_element;
12446
12447     if (!IN_LEV_FIELD(xx, yy))
12448       continue;
12449
12450     if (IS_PLAYER(x, y))                /* player found at center element */
12451     {
12452       struct PlayerInfo *player = PLAYERINFO(x, y);
12453
12454       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12455         border_element = Feld[xx][yy];          /* may be moving! */
12456       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12457         border_element = Feld[xx][yy];
12458       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12459         border_element = MovingOrBlocked2Element(xx, yy);
12460       else
12461         continue;               /* center and border element do not touch */
12462
12463       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12464                                  player->index_bit, border_side);
12465       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12466                                           CE_PLAYER_TOUCHES_X,
12467                                           player->index_bit, border_side);
12468
12469       {
12470         /* use player element that is initially defined in the level playfield,
12471            not the player element that corresponds to the runtime player number
12472            (example: a level that contains EL_PLAYER_3 as the only player would
12473            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12474         int player_element = PLAYERINFO(x, y)->initial_element;
12475
12476         CheckElementChangeBySide(xx, yy, border_element, player_element,
12477                                  CE_TOUCHING_X, border_side);
12478       }
12479     }
12480     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12481     {
12482       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12483
12484       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12485       {
12486         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12487           continue;             /* center and border element do not touch */
12488       }
12489
12490       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12491                                  player->index_bit, center_side);
12492       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12493                                           CE_PLAYER_TOUCHES_X,
12494                                           player->index_bit, center_side);
12495
12496       {
12497         /* use player element that is initially defined in the level playfield,
12498            not the player element that corresponds to the runtime player number
12499            (example: a level that contains EL_PLAYER_3 as the only player would
12500            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12501         int player_element = PLAYERINFO(xx, yy)->initial_element;
12502
12503         CheckElementChangeBySide(x, y, center_element, player_element,
12504                                  CE_TOUCHING_X, center_side);
12505       }
12506
12507       break;
12508     }
12509   }
12510 }
12511
12512 void TestIfElementTouchesCustomElement(int x, int y)
12513 {
12514   static int xy[4][2] =
12515   {
12516     { 0, -1 },
12517     { -1, 0 },
12518     { +1, 0 },
12519     { 0, +1 }
12520   };
12521   static int trigger_sides[4][2] =
12522   {
12523     /* center side      border side */
12524     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12525     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12526     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12527     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12528   };
12529   static int touch_dir[4] =
12530   {
12531     MV_LEFT | MV_RIGHT,
12532     MV_UP   | MV_DOWN,
12533     MV_UP   | MV_DOWN,
12534     MV_LEFT | MV_RIGHT
12535   };
12536   boolean change_center_element = FALSE;
12537   int center_element = Feld[x][y];      /* should always be non-moving! */
12538   int border_element_old[NUM_DIRECTIONS];
12539   int i;
12540
12541   for (i = 0; i < NUM_DIRECTIONS; i++)
12542   {
12543     int xx = x + xy[i][0];
12544     int yy = y + xy[i][1];
12545     int border_element;
12546
12547     border_element_old[i] = -1;
12548
12549     if (!IN_LEV_FIELD(xx, yy))
12550       continue;
12551
12552     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12553       border_element = Feld[xx][yy];    /* may be moving! */
12554     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12555       border_element = Feld[xx][yy];
12556     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12557       border_element = MovingOrBlocked2Element(xx, yy);
12558     else
12559       continue;                 /* center and border element do not touch */
12560
12561     border_element_old[i] = border_element;
12562   }
12563
12564   for (i = 0; i < NUM_DIRECTIONS; i++)
12565   {
12566     int xx = x + xy[i][0];
12567     int yy = y + xy[i][1];
12568     int center_side = trigger_sides[i][0];
12569     int border_element = border_element_old[i];
12570
12571     if (border_element == -1)
12572       continue;
12573
12574     /* check for change of border element */
12575     CheckElementChangeBySide(xx, yy, border_element, center_element,
12576                              CE_TOUCHING_X, center_side);
12577
12578     /* (center element cannot be player, so we dont have to check this here) */
12579   }
12580
12581   for (i = 0; i < NUM_DIRECTIONS; i++)
12582   {
12583     int xx = x + xy[i][0];
12584     int yy = y + xy[i][1];
12585     int border_side = trigger_sides[i][1];
12586     int border_element = border_element_old[i];
12587
12588     if (border_element == -1)
12589       continue;
12590
12591     /* check for change of center element (but change it only once) */
12592     if (!change_center_element)
12593       change_center_element =
12594         CheckElementChangeBySide(x, y, center_element, border_element,
12595                                  CE_TOUCHING_X, border_side);
12596
12597     if (IS_PLAYER(xx, yy))
12598     {
12599       /* use player element that is initially defined in the level playfield,
12600          not the player element that corresponds to the runtime player number
12601          (example: a level that contains EL_PLAYER_3 as the only player would
12602          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12603       int player_element = PLAYERINFO(xx, yy)->initial_element;
12604
12605       CheckElementChangeBySide(x, y, center_element, player_element,
12606                                CE_TOUCHING_X, border_side);
12607     }
12608   }
12609 }
12610
12611 void TestIfElementHitsCustomElement(int x, int y, int direction)
12612 {
12613   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12614   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12615   int hitx = x + dx, hity = y + dy;
12616   int hitting_element = Feld[x][y];
12617   int touched_element;
12618
12619   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12620     return;
12621
12622   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12623                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12624
12625   if (IN_LEV_FIELD(hitx, hity))
12626   {
12627     int opposite_direction = MV_DIR_OPPOSITE(direction);
12628     int hitting_side = direction;
12629     int touched_side = opposite_direction;
12630     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12631                           MovDir[hitx][hity] != direction ||
12632                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12633
12634     object_hit = TRUE;
12635
12636     if (object_hit)
12637     {
12638       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12639                                CE_HITTING_X, touched_side);
12640
12641       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12642                                CE_HIT_BY_X, hitting_side);
12643
12644       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12645                                CE_HIT_BY_SOMETHING, opposite_direction);
12646
12647       if (IS_PLAYER(hitx, hity))
12648       {
12649         /* use player element that is initially defined in the level playfield,
12650            not the player element that corresponds to the runtime player number
12651            (example: a level that contains EL_PLAYER_3 as the only player would
12652            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12653         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12654
12655         CheckElementChangeBySide(x, y, hitting_element, player_element,
12656                                  CE_HITTING_X, touched_side);
12657       }
12658     }
12659   }
12660
12661   /* "hitting something" is also true when hitting the playfield border */
12662   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12663                            CE_HITTING_SOMETHING, direction);
12664 }
12665
12666 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12667 {
12668   int i, kill_x = -1, kill_y = -1;
12669
12670   int bad_element = -1;
12671   static int test_xy[4][2] =
12672   {
12673     { 0, -1 },
12674     { -1, 0 },
12675     { +1, 0 },
12676     { 0, +1 }
12677   };
12678   static int test_dir[4] =
12679   {
12680     MV_UP,
12681     MV_LEFT,
12682     MV_RIGHT,
12683     MV_DOWN
12684   };
12685
12686   for (i = 0; i < NUM_DIRECTIONS; i++)
12687   {
12688     int test_x, test_y, test_move_dir, test_element;
12689
12690     test_x = good_x + test_xy[i][0];
12691     test_y = good_y + test_xy[i][1];
12692
12693     if (!IN_LEV_FIELD(test_x, test_y))
12694       continue;
12695
12696     test_move_dir =
12697       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12698
12699     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12700
12701     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12702        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12703     */
12704     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12705         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12706     {
12707       kill_x = test_x;
12708       kill_y = test_y;
12709       bad_element = test_element;
12710
12711       break;
12712     }
12713   }
12714
12715   if (kill_x != -1 || kill_y != -1)
12716   {
12717     if (IS_PLAYER(good_x, good_y))
12718     {
12719       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12720
12721       if (player->shield_deadly_time_left > 0 &&
12722           !IS_INDESTRUCTIBLE(bad_element))
12723         Bang(kill_x, kill_y);
12724       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12725         KillPlayer(player);
12726     }
12727     else
12728       Bang(good_x, good_y);
12729   }
12730 }
12731
12732 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12733 {
12734   int i, kill_x = -1, kill_y = -1;
12735   int bad_element = Feld[bad_x][bad_y];
12736   static int test_xy[4][2] =
12737   {
12738     { 0, -1 },
12739     { -1, 0 },
12740     { +1, 0 },
12741     { 0, +1 }
12742   };
12743   static int touch_dir[4] =
12744   {
12745     MV_LEFT | MV_RIGHT,
12746     MV_UP   | MV_DOWN,
12747     MV_UP   | MV_DOWN,
12748     MV_LEFT | MV_RIGHT
12749   };
12750   static int test_dir[4] =
12751   {
12752     MV_UP,
12753     MV_LEFT,
12754     MV_RIGHT,
12755     MV_DOWN
12756   };
12757
12758   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12759     return;
12760
12761   for (i = 0; i < NUM_DIRECTIONS; i++)
12762   {
12763     int test_x, test_y, test_move_dir, test_element;
12764
12765     test_x = bad_x + test_xy[i][0];
12766     test_y = bad_y + test_xy[i][1];
12767
12768     if (!IN_LEV_FIELD(test_x, test_y))
12769       continue;
12770
12771     test_move_dir =
12772       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12773
12774     test_element = Feld[test_x][test_y];
12775
12776     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12777        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12778     */
12779     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12780         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12781     {
12782       /* good thing is player or penguin that does not move away */
12783       if (IS_PLAYER(test_x, test_y))
12784       {
12785         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12786
12787         if (bad_element == EL_ROBOT && player->is_moving)
12788           continue;     /* robot does not kill player if he is moving */
12789
12790         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12791         {
12792           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12793             continue;           /* center and border element do not touch */
12794         }
12795
12796         kill_x = test_x;
12797         kill_y = test_y;
12798
12799         break;
12800       }
12801       else if (test_element == EL_PENGUIN)
12802       {
12803         kill_x = test_x;
12804         kill_y = test_y;
12805
12806         break;
12807       }
12808     }
12809   }
12810
12811   if (kill_x != -1 || kill_y != -1)
12812   {
12813     if (IS_PLAYER(kill_x, kill_y))
12814     {
12815       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12816
12817       if (player->shield_deadly_time_left > 0 &&
12818           !IS_INDESTRUCTIBLE(bad_element))
12819         Bang(bad_x, bad_y);
12820       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12821         KillPlayer(player);
12822     }
12823     else
12824       Bang(kill_x, kill_y);
12825   }
12826 }
12827
12828 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12829 {
12830   int bad_element = Feld[bad_x][bad_y];
12831   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12832   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12833   int test_x = bad_x + dx, test_y = bad_y + dy;
12834   int test_move_dir, test_element;
12835   int kill_x = -1, kill_y = -1;
12836
12837   if (!IN_LEV_FIELD(test_x, test_y))
12838     return;
12839
12840   test_move_dir =
12841     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12842
12843   test_element = Feld[test_x][test_y];
12844
12845   if (test_move_dir != bad_move_dir)
12846   {
12847     /* good thing can be player or penguin that does not move away */
12848     if (IS_PLAYER(test_x, test_y))
12849     {
12850       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12851
12852       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12853          player as being hit when he is moving towards the bad thing, because
12854          the "get hit by" condition would be lost after the player stops) */
12855       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12856         return;         /* player moves away from bad thing */
12857
12858       kill_x = test_x;
12859       kill_y = test_y;
12860     }
12861     else if (test_element == EL_PENGUIN)
12862     {
12863       kill_x = test_x;
12864       kill_y = test_y;
12865     }
12866   }
12867
12868   if (kill_x != -1 || kill_y != -1)
12869   {
12870     if (IS_PLAYER(kill_x, kill_y))
12871     {
12872       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12873
12874       if (player->shield_deadly_time_left > 0 &&
12875           !IS_INDESTRUCTIBLE(bad_element))
12876         Bang(bad_x, bad_y);
12877       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12878         KillPlayer(player);
12879     }
12880     else
12881       Bang(kill_x, kill_y);
12882   }
12883 }
12884
12885 void TestIfPlayerTouchesBadThing(int x, int y)
12886 {
12887   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12888 }
12889
12890 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12891 {
12892   TestIfGoodThingHitsBadThing(x, y, move_dir);
12893 }
12894
12895 void TestIfBadThingTouchesPlayer(int x, int y)
12896 {
12897   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12898 }
12899
12900 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12901 {
12902   TestIfBadThingHitsGoodThing(x, y, move_dir);
12903 }
12904
12905 void TestIfFriendTouchesBadThing(int x, int y)
12906 {
12907   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12908 }
12909
12910 void TestIfBadThingTouchesFriend(int x, int y)
12911 {
12912   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12913 }
12914
12915 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12916 {
12917   int i, kill_x = bad_x, kill_y = bad_y;
12918   static int xy[4][2] =
12919   {
12920     { 0, -1 },
12921     { -1, 0 },
12922     { +1, 0 },
12923     { 0, +1 }
12924   };
12925
12926   for (i = 0; i < NUM_DIRECTIONS; i++)
12927   {
12928     int x, y, element;
12929
12930     x = bad_x + xy[i][0];
12931     y = bad_y + xy[i][1];
12932     if (!IN_LEV_FIELD(x, y))
12933       continue;
12934
12935     element = Feld[x][y];
12936     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12937         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12938     {
12939       kill_x = x;
12940       kill_y = y;
12941       break;
12942     }
12943   }
12944
12945   if (kill_x != bad_x || kill_y != bad_y)
12946     Bang(bad_x, bad_y);
12947 }
12948
12949 void KillPlayer(struct PlayerInfo *player)
12950 {
12951   int jx = player->jx, jy = player->jy;
12952
12953   if (!player->active)
12954     return;
12955
12956 #if 0
12957   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12958          player->killed, player->active, player->reanimated);
12959 #endif
12960
12961   /* the following code was introduced to prevent an infinite loop when calling
12962      -> Bang()
12963      -> CheckTriggeredElementChangeExt()
12964      -> ExecuteCustomElementAction()
12965      -> KillPlayer()
12966      -> (infinitely repeating the above sequence of function calls)
12967      which occurs when killing the player while having a CE with the setting
12968      "kill player X when explosion of <player X>"; the solution using a new
12969      field "player->killed" was chosen for backwards compatibility, although
12970      clever use of the fields "player->active" etc. would probably also work */
12971 #if 1
12972   if (player->killed)
12973     return;
12974 #endif
12975
12976   player->killed = TRUE;
12977
12978   /* remove accessible field at the player's position */
12979   Feld[jx][jy] = EL_EMPTY;
12980
12981   /* deactivate shield (else Bang()/Explode() would not work right) */
12982   player->shield_normal_time_left = 0;
12983   player->shield_deadly_time_left = 0;
12984
12985 #if 0
12986   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12987          player->killed, player->active, player->reanimated);
12988 #endif
12989
12990   Bang(jx, jy);
12991
12992 #if 0
12993   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12994          player->killed, player->active, player->reanimated);
12995 #endif
12996
12997   if (player->reanimated)       /* killed player may have been reanimated */
12998     player->killed = player->reanimated = FALSE;
12999   else
13000     BuryPlayer(player);
13001 }
13002
13003 static void KillPlayerUnlessEnemyProtected(int x, int y)
13004 {
13005   if (!PLAYER_ENEMY_PROTECTED(x, y))
13006     KillPlayer(PLAYERINFO(x, y));
13007 }
13008
13009 static void KillPlayerUnlessExplosionProtected(int x, int y)
13010 {
13011   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13012     KillPlayer(PLAYERINFO(x, y));
13013 }
13014
13015 void BuryPlayer(struct PlayerInfo *player)
13016 {
13017   int jx = player->jx, jy = player->jy;
13018
13019   if (!player->active)
13020     return;
13021
13022   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13023   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13024
13025   player->GameOver = TRUE;
13026   RemovePlayer(player);
13027 }
13028
13029 void RemovePlayer(struct PlayerInfo *player)
13030 {
13031   int jx = player->jx, jy = player->jy;
13032   int i, found = FALSE;
13033
13034   player->present = FALSE;
13035   player->active = FALSE;
13036
13037   if (!ExplodeField[jx][jy])
13038     StorePlayer[jx][jy] = 0;
13039
13040   if (player->is_moving)
13041     TEST_DrawLevelField(player->last_jx, player->last_jy);
13042
13043   for (i = 0; i < MAX_PLAYERS; i++)
13044     if (stored_player[i].active)
13045       found = TRUE;
13046
13047   if (!found)
13048     AllPlayersGone = TRUE;
13049
13050   ExitX = ZX = jx;
13051   ExitY = ZY = jy;
13052 }
13053
13054 static void setFieldForSnapping(int x, int y, int element, int direction)
13055 {
13056   struct ElementInfo *ei = &element_info[element];
13057   int direction_bit = MV_DIR_TO_BIT(direction);
13058   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13059   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13060                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13061
13062   Feld[x][y] = EL_ELEMENT_SNAPPING;
13063   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13064
13065   ResetGfxAnimation(x, y);
13066
13067   GfxElement[x][y] = element;
13068   GfxAction[x][y] = action;
13069   GfxDir[x][y] = direction;
13070   GfxFrame[x][y] = -1;
13071 }
13072
13073 /*
13074   =============================================================================
13075   checkDiagonalPushing()
13076   -----------------------------------------------------------------------------
13077   check if diagonal input device direction results in pushing of object
13078   (by checking if the alternative direction is walkable, diggable, ...)
13079   =============================================================================
13080 */
13081
13082 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13083                                     int x, int y, int real_dx, int real_dy)
13084 {
13085   int jx, jy, dx, dy, xx, yy;
13086
13087   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13088     return TRUE;
13089
13090   /* diagonal direction: check alternative direction */
13091   jx = player->jx;
13092   jy = player->jy;
13093   dx = x - jx;
13094   dy = y - jy;
13095   xx = jx + (dx == 0 ? real_dx : 0);
13096   yy = jy + (dy == 0 ? real_dy : 0);
13097
13098   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13099 }
13100
13101 /*
13102   =============================================================================
13103   DigField()
13104   -----------------------------------------------------------------------------
13105   x, y:                 field next to player (non-diagonal) to try to dig to
13106   real_dx, real_dy:     direction as read from input device (can be diagonal)
13107   =============================================================================
13108 */
13109
13110 static int DigField(struct PlayerInfo *player,
13111                     int oldx, int oldy, int x, int y,
13112                     int real_dx, int real_dy, int mode)
13113 {
13114   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13115   boolean player_was_pushing = player->is_pushing;
13116   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13117   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13118   int jx = oldx, jy = oldy;
13119   int dx = x - jx, dy = y - jy;
13120   int nextx = x + dx, nexty = y + dy;
13121   int move_direction = (dx == -1 ? MV_LEFT  :
13122                         dx == +1 ? MV_RIGHT :
13123                         dy == -1 ? MV_UP    :
13124                         dy == +1 ? MV_DOWN  : MV_NONE);
13125   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13126   int dig_side = MV_DIR_OPPOSITE(move_direction);
13127   int old_element = Feld[jx][jy];
13128   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13129   int collect_count;
13130
13131   if (is_player)                /* function can also be called by EL_PENGUIN */
13132   {
13133     if (player->MovPos == 0)
13134     {
13135       player->is_digging = FALSE;
13136       player->is_collecting = FALSE;
13137     }
13138
13139     if (player->MovPos == 0)    /* last pushing move finished */
13140       player->is_pushing = FALSE;
13141
13142     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13143     {
13144       player->is_switching = FALSE;
13145       player->push_delay = -1;
13146
13147       return MP_NO_ACTION;
13148     }
13149   }
13150
13151   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13152     old_element = Back[jx][jy];
13153
13154   /* in case of element dropped at player position, check background */
13155   else if (Back[jx][jy] != EL_EMPTY &&
13156            game.engine_version >= VERSION_IDENT(2,2,0,0))
13157     old_element = Back[jx][jy];
13158
13159   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13160     return MP_NO_ACTION;        /* field has no opening in this direction */
13161
13162   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13163     return MP_NO_ACTION;        /* field has no opening in this direction */
13164
13165   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13166   {
13167     SplashAcid(x, y);
13168
13169     Feld[jx][jy] = player->artwork_element;
13170     InitMovingField(jx, jy, MV_DOWN);
13171     Store[jx][jy] = EL_ACID;
13172     ContinueMoving(jx, jy);
13173     BuryPlayer(player);
13174
13175     return MP_DONT_RUN_INTO;
13176   }
13177
13178   if (player_can_move && DONT_RUN_INTO(element))
13179   {
13180     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13181
13182     return MP_DONT_RUN_INTO;
13183   }
13184
13185   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13186     return MP_NO_ACTION;
13187
13188   collect_count = element_info[element].collect_count_initial;
13189
13190   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13191     return MP_NO_ACTION;
13192
13193   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13194     player_can_move = player_can_move_or_snap;
13195
13196   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13197       game.engine_version >= VERSION_IDENT(2,2,0,0))
13198   {
13199     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13200                                player->index_bit, dig_side);
13201     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13202                                         player->index_bit, dig_side);
13203
13204     if (element == EL_DC_LANDMINE)
13205       Bang(x, y);
13206
13207     if (Feld[x][y] != element)          /* field changed by snapping */
13208       return MP_ACTION;
13209
13210     return MP_NO_ACTION;
13211   }
13212
13213   if (player->gravity && is_player && !player->is_auto_moving &&
13214       canFallDown(player) && move_direction != MV_DOWN &&
13215       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13216     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13217
13218   if (player_can_move &&
13219       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13220   {
13221     int sound_element = SND_ELEMENT(element);
13222     int sound_action = ACTION_WALKING;
13223
13224     if (IS_RND_GATE(element))
13225     {
13226       if (!player->key[RND_GATE_NR(element)])
13227         return MP_NO_ACTION;
13228     }
13229     else if (IS_RND_GATE_GRAY(element))
13230     {
13231       if (!player->key[RND_GATE_GRAY_NR(element)])
13232         return MP_NO_ACTION;
13233     }
13234     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13235     {
13236       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13237         return MP_NO_ACTION;
13238     }
13239     else if (element == EL_EXIT_OPEN ||
13240              element == EL_EM_EXIT_OPEN ||
13241              element == EL_EM_EXIT_OPENING ||
13242              element == EL_STEEL_EXIT_OPEN ||
13243              element == EL_EM_STEEL_EXIT_OPEN ||
13244              element == EL_EM_STEEL_EXIT_OPENING ||
13245              element == EL_SP_EXIT_OPEN ||
13246              element == EL_SP_EXIT_OPENING)
13247     {
13248       sound_action = ACTION_PASSING;    /* player is passing exit */
13249     }
13250     else if (element == EL_EMPTY)
13251     {
13252       sound_action = ACTION_MOVING;             /* nothing to walk on */
13253     }
13254
13255     /* play sound from background or player, whatever is available */
13256     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13257       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13258     else
13259       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13260   }
13261   else if (player_can_move &&
13262            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13263   {
13264     if (!ACCESS_FROM(element, opposite_direction))
13265       return MP_NO_ACTION;      /* field not accessible from this direction */
13266
13267     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13268       return MP_NO_ACTION;
13269
13270     if (IS_EM_GATE(element))
13271     {
13272       if (!player->key[EM_GATE_NR(element)])
13273         return MP_NO_ACTION;
13274     }
13275     else if (IS_EM_GATE_GRAY(element))
13276     {
13277       if (!player->key[EM_GATE_GRAY_NR(element)])
13278         return MP_NO_ACTION;
13279     }
13280     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13281     {
13282       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13283         return MP_NO_ACTION;
13284     }
13285     else if (IS_EMC_GATE(element))
13286     {
13287       if (!player->key[EMC_GATE_NR(element)])
13288         return MP_NO_ACTION;
13289     }
13290     else if (IS_EMC_GATE_GRAY(element))
13291     {
13292       if (!player->key[EMC_GATE_GRAY_NR(element)])
13293         return MP_NO_ACTION;
13294     }
13295     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13296     {
13297       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13298         return MP_NO_ACTION;
13299     }
13300     else if (element == EL_DC_GATE_WHITE ||
13301              element == EL_DC_GATE_WHITE_GRAY ||
13302              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13303     {
13304       if (player->num_white_keys == 0)
13305         return MP_NO_ACTION;
13306
13307       player->num_white_keys--;
13308     }
13309     else if (IS_SP_PORT(element))
13310     {
13311       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13312           element == EL_SP_GRAVITY_PORT_RIGHT ||
13313           element == EL_SP_GRAVITY_PORT_UP ||
13314           element == EL_SP_GRAVITY_PORT_DOWN)
13315         player->gravity = !player->gravity;
13316       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13317                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13318                element == EL_SP_GRAVITY_ON_PORT_UP ||
13319                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13320         player->gravity = TRUE;
13321       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13322                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13323                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13324                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13325         player->gravity = FALSE;
13326     }
13327
13328     /* automatically move to the next field with double speed */
13329     player->programmed_action = move_direction;
13330
13331     if (player->move_delay_reset_counter == 0)
13332     {
13333       player->move_delay_reset_counter = 2;     /* two double speed steps */
13334
13335       DOUBLE_PLAYER_SPEED(player);
13336     }
13337
13338     PlayLevelSoundAction(x, y, ACTION_PASSING);
13339   }
13340   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13341   {
13342     RemoveField(x, y);
13343
13344     if (mode != DF_SNAP)
13345     {
13346       GfxElement[x][y] = GFX_ELEMENT(element);
13347       player->is_digging = TRUE;
13348     }
13349
13350     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13351
13352     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13353                                         player->index_bit, dig_side);
13354
13355     if (mode == DF_SNAP)
13356     {
13357       if (level.block_snap_field)
13358         setFieldForSnapping(x, y, element, move_direction);
13359       else
13360         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13361
13362       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13363                                           player->index_bit, dig_side);
13364     }
13365   }
13366   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13367   {
13368     RemoveField(x, y);
13369
13370     if (is_player && mode != DF_SNAP)
13371     {
13372       GfxElement[x][y] = element;
13373       player->is_collecting = TRUE;
13374     }
13375
13376     if (element == EL_SPEED_PILL)
13377     {
13378       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13379     }
13380     else if (element == EL_EXTRA_TIME && level.time > 0)
13381     {
13382       TimeLeft += level.extra_time;
13383
13384       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13385
13386       DisplayGameControlValues();
13387     }
13388     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13389     {
13390       player->shield_normal_time_left += level.shield_normal_time;
13391       if (element == EL_SHIELD_DEADLY)
13392         player->shield_deadly_time_left += level.shield_deadly_time;
13393     }
13394     else if (element == EL_DYNAMITE ||
13395              element == EL_EM_DYNAMITE ||
13396              element == EL_SP_DISK_RED)
13397     {
13398       if (player->inventory_size < MAX_INVENTORY_SIZE)
13399         player->inventory_element[player->inventory_size++] = element;
13400
13401       DrawGameDoorValues();
13402     }
13403     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13404     {
13405       player->dynabomb_count++;
13406       player->dynabombs_left++;
13407     }
13408     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13409     {
13410       player->dynabomb_size++;
13411     }
13412     else if (element == EL_DYNABOMB_INCREASE_POWER)
13413     {
13414       player->dynabomb_xl = TRUE;
13415     }
13416     else if (IS_KEY(element))
13417     {
13418       player->key[KEY_NR(element)] = TRUE;
13419
13420       DrawGameDoorValues();
13421     }
13422     else if (element == EL_DC_KEY_WHITE)
13423     {
13424       player->num_white_keys++;
13425
13426       /* display white keys? */
13427       /* DrawGameDoorValues(); */
13428     }
13429     else if (IS_ENVELOPE(element))
13430     {
13431       player->show_envelope = element;
13432     }
13433     else if (element == EL_EMC_LENSES)
13434     {
13435       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13436
13437       RedrawAllInvisibleElementsForLenses();
13438     }
13439     else if (element == EL_EMC_MAGNIFIER)
13440     {
13441       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13442
13443       RedrawAllInvisibleElementsForMagnifier();
13444     }
13445     else if (IS_DROPPABLE(element) ||
13446              IS_THROWABLE(element))     /* can be collected and dropped */
13447     {
13448       int i;
13449
13450       if (collect_count == 0)
13451         player->inventory_infinite_element = element;
13452       else
13453         for (i = 0; i < collect_count; i++)
13454           if (player->inventory_size < MAX_INVENTORY_SIZE)
13455             player->inventory_element[player->inventory_size++] = element;
13456
13457       DrawGameDoorValues();
13458     }
13459     else if (collect_count > 0)
13460     {
13461       local_player->gems_still_needed -= collect_count;
13462       if (local_player->gems_still_needed < 0)
13463         local_player->gems_still_needed = 0;
13464
13465       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13466
13467       DisplayGameControlValues();
13468     }
13469
13470     RaiseScoreElement(element);
13471     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13472
13473     if (is_player)
13474       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13475                                           player->index_bit, dig_side);
13476
13477     if (mode == DF_SNAP)
13478     {
13479       if (level.block_snap_field)
13480         setFieldForSnapping(x, y, element, move_direction);
13481       else
13482         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13483
13484       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13485                                           player->index_bit, dig_side);
13486     }
13487   }
13488   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13489   {
13490     if (mode == DF_SNAP && element != EL_BD_ROCK)
13491       return MP_NO_ACTION;
13492
13493     if (CAN_FALL(element) && dy)
13494       return MP_NO_ACTION;
13495
13496     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13497         !(element == EL_SPRING && level.use_spring_bug))
13498       return MP_NO_ACTION;
13499
13500     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13501         ((move_direction & MV_VERTICAL &&
13502           ((element_info[element].move_pattern & MV_LEFT &&
13503             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13504            (element_info[element].move_pattern & MV_RIGHT &&
13505             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13506          (move_direction & MV_HORIZONTAL &&
13507           ((element_info[element].move_pattern & MV_UP &&
13508             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13509            (element_info[element].move_pattern & MV_DOWN &&
13510             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13511       return MP_NO_ACTION;
13512
13513     /* do not push elements already moving away faster than player */
13514     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13515         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13516       return MP_NO_ACTION;
13517
13518     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13519     {
13520       if (player->push_delay_value == -1 || !player_was_pushing)
13521         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13522     }
13523     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13524     {
13525       if (player->push_delay_value == -1)
13526         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13527     }
13528     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13529     {
13530       if (!player->is_pushing)
13531         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13532     }
13533
13534     player->is_pushing = TRUE;
13535     player->is_active = TRUE;
13536
13537     if (!(IN_LEV_FIELD(nextx, nexty) &&
13538           (IS_FREE(nextx, nexty) ||
13539            (IS_SB_ELEMENT(element) &&
13540             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13541            (IS_CUSTOM_ELEMENT(element) &&
13542             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13543       return MP_NO_ACTION;
13544
13545     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13546       return MP_NO_ACTION;
13547
13548     if (player->push_delay == -1)       /* new pushing; restart delay */
13549       player->push_delay = 0;
13550
13551     if (player->push_delay < player->push_delay_value &&
13552         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13553         element != EL_SPRING && element != EL_BALLOON)
13554     {
13555       /* make sure that there is no move delay before next try to push */
13556       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13557         player->move_delay = 0;
13558
13559       return MP_NO_ACTION;
13560     }
13561
13562     if (IS_CUSTOM_ELEMENT(element) &&
13563         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13564     {
13565       if (!DigFieldByCE(nextx, nexty, element))
13566         return MP_NO_ACTION;
13567     }
13568
13569     if (IS_SB_ELEMENT(element))
13570     {
13571       if (element == EL_SOKOBAN_FIELD_FULL)
13572       {
13573         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13574         local_player->sokobanfields_still_needed++;
13575       }
13576
13577       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13578       {
13579         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13580         local_player->sokobanfields_still_needed--;
13581       }
13582
13583       Feld[x][y] = EL_SOKOBAN_OBJECT;
13584
13585       if (Back[x][y] == Back[nextx][nexty])
13586         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13587       else if (Back[x][y] != 0)
13588         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13589                                     ACTION_EMPTYING);
13590       else
13591         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13592                                     ACTION_FILLING);
13593
13594       if (local_player->sokobanfields_still_needed == 0 &&
13595           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13596       {
13597         PlayerWins(player);
13598
13599         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13600       }
13601     }
13602     else
13603       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13604
13605     InitMovingField(x, y, move_direction);
13606     GfxAction[x][y] = ACTION_PUSHING;
13607
13608     if (mode == DF_SNAP)
13609       ContinueMoving(x, y);
13610     else
13611       MovPos[x][y] = (dx != 0 ? dx : dy);
13612
13613     Pushed[x][y] = TRUE;
13614     Pushed[nextx][nexty] = TRUE;
13615
13616     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13617       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13618     else
13619       player->push_delay_value = -1;    /* get new value later */
13620
13621     /* check for element change _after_ element has been pushed */
13622     if (game.use_change_when_pushing_bug)
13623     {
13624       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13625                                  player->index_bit, dig_side);
13626       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13627                                           player->index_bit, dig_side);
13628     }
13629   }
13630   else if (IS_SWITCHABLE(element))
13631   {
13632     if (PLAYER_SWITCHING(player, x, y))
13633     {
13634       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13635                                           player->index_bit, dig_side);
13636
13637       return MP_ACTION;
13638     }
13639
13640     player->is_switching = TRUE;
13641     player->switch_x = x;
13642     player->switch_y = y;
13643
13644     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13645
13646     if (element == EL_ROBOT_WHEEL)
13647     {
13648       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13649       ZX = x;
13650       ZY = y;
13651
13652       game.robot_wheel_active = TRUE;
13653
13654       TEST_DrawLevelField(x, y);
13655     }
13656     else if (element == EL_SP_TERMINAL)
13657     {
13658       int xx, yy;
13659
13660       SCAN_PLAYFIELD(xx, yy)
13661       {
13662         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13663           Bang(xx, yy);
13664         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13665           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13666       }
13667     }
13668     else if (IS_BELT_SWITCH(element))
13669     {
13670       ToggleBeltSwitch(x, y);
13671     }
13672     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13673              element == EL_SWITCHGATE_SWITCH_DOWN ||
13674              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13675              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13676     {
13677       ToggleSwitchgateSwitch(x, y);
13678     }
13679     else if (element == EL_LIGHT_SWITCH ||
13680              element == EL_LIGHT_SWITCH_ACTIVE)
13681     {
13682       ToggleLightSwitch(x, y);
13683     }
13684     else if (element == EL_TIMEGATE_SWITCH ||
13685              element == EL_DC_TIMEGATE_SWITCH)
13686     {
13687       ActivateTimegateSwitch(x, y);
13688     }
13689     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13690              element == EL_BALLOON_SWITCH_RIGHT ||
13691              element == EL_BALLOON_SWITCH_UP    ||
13692              element == EL_BALLOON_SWITCH_DOWN  ||
13693              element == EL_BALLOON_SWITCH_NONE  ||
13694              element == EL_BALLOON_SWITCH_ANY)
13695     {
13696       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13697                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13698                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13699                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13700                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13701                              move_direction);
13702     }
13703     else if (element == EL_LAMP)
13704     {
13705       Feld[x][y] = EL_LAMP_ACTIVE;
13706       local_player->lights_still_needed--;
13707
13708       ResetGfxAnimation(x, y);
13709       TEST_DrawLevelField(x, y);
13710     }
13711     else if (element == EL_TIME_ORB_FULL)
13712     {
13713       Feld[x][y] = EL_TIME_ORB_EMPTY;
13714
13715       if (level.time > 0 || level.use_time_orb_bug)
13716       {
13717         TimeLeft += level.time_orb_time;
13718         game.no_time_limit = FALSE;
13719
13720         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13721
13722         DisplayGameControlValues();
13723       }
13724
13725       ResetGfxAnimation(x, y);
13726       TEST_DrawLevelField(x, y);
13727     }
13728     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13729              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13730     {
13731       int xx, yy;
13732
13733       game.ball_state = !game.ball_state;
13734
13735       SCAN_PLAYFIELD(xx, yy)
13736       {
13737         int e = Feld[xx][yy];
13738
13739         if (game.ball_state)
13740         {
13741           if (e == EL_EMC_MAGIC_BALL)
13742             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13743           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13744             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13745         }
13746         else
13747         {
13748           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13749             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13750           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13751             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13752         }
13753       }
13754     }
13755
13756     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13757                                         player->index_bit, dig_side);
13758
13759     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13760                                         player->index_bit, dig_side);
13761
13762     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13763                                         player->index_bit, dig_side);
13764
13765     return MP_ACTION;
13766   }
13767   else
13768   {
13769     if (!PLAYER_SWITCHING(player, x, y))
13770     {
13771       player->is_switching = TRUE;
13772       player->switch_x = x;
13773       player->switch_y = y;
13774
13775       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13776                                  player->index_bit, dig_side);
13777       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13778                                           player->index_bit, dig_side);
13779
13780       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13781                                  player->index_bit, dig_side);
13782       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13783                                           player->index_bit, dig_side);
13784     }
13785
13786     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13787                                player->index_bit, dig_side);
13788     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13789                                         player->index_bit, dig_side);
13790
13791     return MP_NO_ACTION;
13792   }
13793
13794   player->push_delay = -1;
13795
13796   if (is_player)                /* function can also be called by EL_PENGUIN */
13797   {
13798     if (Feld[x][y] != element)          /* really digged/collected something */
13799     {
13800       player->is_collecting = !player->is_digging;
13801       player->is_active = TRUE;
13802     }
13803   }
13804
13805   return MP_MOVING;
13806 }
13807
13808 static boolean DigFieldByCE(int x, int y, int digging_element)
13809 {
13810   int element = Feld[x][y];
13811
13812   if (!IS_FREE(x, y))
13813   {
13814     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13815                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13816                   ACTION_BREAKING);
13817
13818     /* no element can dig solid indestructible elements */
13819     if (IS_INDESTRUCTIBLE(element) &&
13820         !IS_DIGGABLE(element) &&
13821         !IS_COLLECTIBLE(element))
13822       return FALSE;
13823
13824     if (AmoebaNr[x][y] &&
13825         (element == EL_AMOEBA_FULL ||
13826          element == EL_BD_AMOEBA ||
13827          element == EL_AMOEBA_GROWING))
13828     {
13829       AmoebaCnt[AmoebaNr[x][y]]--;
13830       AmoebaCnt2[AmoebaNr[x][y]]--;
13831     }
13832
13833     if (IS_MOVING(x, y))
13834       RemoveMovingField(x, y);
13835     else
13836     {
13837       RemoveField(x, y);
13838       TEST_DrawLevelField(x, y);
13839     }
13840
13841     /* if digged element was about to explode, prevent the explosion */
13842     ExplodeField[x][y] = EX_TYPE_NONE;
13843
13844     PlayLevelSoundAction(x, y, action);
13845   }
13846
13847   Store[x][y] = EL_EMPTY;
13848
13849   /* this makes it possible to leave the removed element again */
13850   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13851     Store[x][y] = element;
13852
13853   return TRUE;
13854 }
13855
13856 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13857 {
13858   int jx = player->jx, jy = player->jy;
13859   int x = jx + dx, y = jy + dy;
13860   int snap_direction = (dx == -1 ? MV_LEFT  :
13861                         dx == +1 ? MV_RIGHT :
13862                         dy == -1 ? MV_UP    :
13863                         dy == +1 ? MV_DOWN  : MV_NONE);
13864   boolean can_continue_snapping = (level.continuous_snapping &&
13865                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13866
13867   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13868     return FALSE;
13869
13870   if (!player->active || !IN_LEV_FIELD(x, y))
13871     return FALSE;
13872
13873   if (dx && dy)
13874     return FALSE;
13875
13876   if (!dx && !dy)
13877   {
13878     if (player->MovPos == 0)
13879       player->is_pushing = FALSE;
13880
13881     player->is_snapping = FALSE;
13882
13883     if (player->MovPos == 0)
13884     {
13885       player->is_moving = FALSE;
13886       player->is_digging = FALSE;
13887       player->is_collecting = FALSE;
13888     }
13889
13890     return FALSE;
13891   }
13892
13893   /* prevent snapping with already pressed snap key when not allowed */
13894   if (player->is_snapping && !can_continue_snapping)
13895     return FALSE;
13896
13897   player->MovDir = snap_direction;
13898
13899   if (player->MovPos == 0)
13900   {
13901     player->is_moving = FALSE;
13902     player->is_digging = FALSE;
13903     player->is_collecting = FALSE;
13904   }
13905
13906   player->is_dropping = FALSE;
13907   player->is_dropping_pressed = FALSE;
13908   player->drop_pressed_delay = 0;
13909
13910   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13911     return FALSE;
13912
13913   player->is_snapping = TRUE;
13914   player->is_active = TRUE;
13915
13916   if (player->MovPos == 0)
13917   {
13918     player->is_moving = FALSE;
13919     player->is_digging = FALSE;
13920     player->is_collecting = FALSE;
13921   }
13922
13923   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13924     TEST_DrawLevelField(player->last_jx, player->last_jy);
13925
13926   TEST_DrawLevelField(x, y);
13927
13928   return TRUE;
13929 }
13930
13931 static boolean DropElement(struct PlayerInfo *player)
13932 {
13933   int old_element, new_element;
13934   int dropx = player->jx, dropy = player->jy;
13935   int drop_direction = player->MovDir;
13936   int drop_side = drop_direction;
13937   int drop_element = get_next_dropped_element(player);
13938
13939   player->is_dropping_pressed = TRUE;
13940
13941   /* do not drop an element on top of another element; when holding drop key
13942      pressed without moving, dropped element must move away before the next
13943      element can be dropped (this is especially important if the next element
13944      is dynamite, which can be placed on background for historical reasons) */
13945   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13946     return MP_ACTION;
13947
13948   if (IS_THROWABLE(drop_element))
13949   {
13950     dropx += GET_DX_FROM_DIR(drop_direction);
13951     dropy += GET_DY_FROM_DIR(drop_direction);
13952
13953     if (!IN_LEV_FIELD(dropx, dropy))
13954       return FALSE;
13955   }
13956
13957   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13958   new_element = drop_element;           /* default: no change when dropping */
13959
13960   /* check if player is active, not moving and ready to drop */
13961   if (!player->active || player->MovPos || player->drop_delay > 0)
13962     return FALSE;
13963
13964   /* check if player has anything that can be dropped */
13965   if (new_element == EL_UNDEFINED)
13966     return FALSE;
13967
13968   /* check if drop key was pressed long enough for EM style dynamite */
13969   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13970     return FALSE;
13971
13972   /* check if anything can be dropped at the current position */
13973   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13974     return FALSE;
13975
13976   /* collected custom elements can only be dropped on empty fields */
13977   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13978     return FALSE;
13979
13980   if (old_element != EL_EMPTY)
13981     Back[dropx][dropy] = old_element;   /* store old element on this field */
13982
13983   ResetGfxAnimation(dropx, dropy);
13984   ResetRandomAnimationValue(dropx, dropy);
13985
13986   if (player->inventory_size > 0 ||
13987       player->inventory_infinite_element != EL_UNDEFINED)
13988   {
13989     if (player->inventory_size > 0)
13990     {
13991       player->inventory_size--;
13992
13993       DrawGameDoorValues();
13994
13995       if (new_element == EL_DYNAMITE)
13996         new_element = EL_DYNAMITE_ACTIVE;
13997       else if (new_element == EL_EM_DYNAMITE)
13998         new_element = EL_EM_DYNAMITE_ACTIVE;
13999       else if (new_element == EL_SP_DISK_RED)
14000         new_element = EL_SP_DISK_RED_ACTIVE;
14001     }
14002
14003     Feld[dropx][dropy] = new_element;
14004
14005     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14006       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14007                           el2img(Feld[dropx][dropy]), 0);
14008
14009     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14010
14011     /* needed if previous element just changed to "empty" in the last frame */
14012     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14013
14014     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14015                                player->index_bit, drop_side);
14016     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14017                                         CE_PLAYER_DROPS_X,
14018                                         player->index_bit, drop_side);
14019
14020     TestIfElementTouchesCustomElement(dropx, dropy);
14021   }
14022   else          /* player is dropping a dyna bomb */
14023   {
14024     player->dynabombs_left--;
14025
14026     Feld[dropx][dropy] = new_element;
14027
14028     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14029       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14030                           el2img(Feld[dropx][dropy]), 0);
14031
14032     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14033   }
14034
14035   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14036     InitField_WithBug1(dropx, dropy, FALSE);
14037
14038   new_element = Feld[dropx][dropy];     /* element might have changed */
14039
14040   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14041       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14042   {
14043     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14044       MovDir[dropx][dropy] = drop_direction;
14045
14046     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14047
14048     /* do not cause impact style collision by dropping elements that can fall */
14049     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14050   }
14051
14052   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14053   player->is_dropping = TRUE;
14054
14055   player->drop_pressed_delay = 0;
14056   player->is_dropping_pressed = FALSE;
14057
14058   player->drop_x = dropx;
14059   player->drop_y = dropy;
14060
14061   return TRUE;
14062 }
14063
14064 /* ------------------------------------------------------------------------- */
14065 /* game sound playing functions                                              */
14066 /* ------------------------------------------------------------------------- */
14067
14068 static int *loop_sound_frame = NULL;
14069 static int *loop_sound_volume = NULL;
14070
14071 void InitPlayLevelSound()
14072 {
14073   int num_sounds = getSoundListSize();
14074
14075   checked_free(loop_sound_frame);
14076   checked_free(loop_sound_volume);
14077
14078   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14079   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14080 }
14081
14082 static void PlayLevelSound(int x, int y, int nr)
14083 {
14084   int sx = SCREENX(x), sy = SCREENY(y);
14085   int volume, stereo_position;
14086   int max_distance = 8;
14087   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14088
14089   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14090       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14091     return;
14092
14093   if (!IN_LEV_FIELD(x, y) ||
14094       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14095       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14096     return;
14097
14098   volume = SOUND_MAX_VOLUME;
14099
14100   if (!IN_SCR_FIELD(sx, sy))
14101   {
14102     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14103     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14104
14105     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14106   }
14107
14108   stereo_position = (SOUND_MAX_LEFT +
14109                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14110                      (SCR_FIELDX + 2 * max_distance));
14111
14112   if (IS_LOOP_SOUND(nr))
14113   {
14114     /* This assures that quieter loop sounds do not overwrite louder ones,
14115        while restarting sound volume comparison with each new game frame. */
14116
14117     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14118       return;
14119
14120     loop_sound_volume[nr] = volume;
14121     loop_sound_frame[nr] = FrameCounter;
14122   }
14123
14124   PlaySoundExt(nr, volume, stereo_position, type);
14125 }
14126
14127 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14128 {
14129   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14130                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14131                  y < LEVELY(BY1) ? LEVELY(BY1) :
14132                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14133                  sound_action);
14134 }
14135
14136 static void PlayLevelSoundAction(int x, int y, int action)
14137 {
14138   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14139 }
14140
14141 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14142 {
14143   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14144
14145   if (sound_effect != SND_UNDEFINED)
14146     PlayLevelSound(x, y, sound_effect);
14147 }
14148
14149 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14150                                               int action)
14151 {
14152   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14153
14154   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14155     PlayLevelSound(x, y, sound_effect);
14156 }
14157
14158 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14159 {
14160   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14161
14162   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14163     PlayLevelSound(x, y, sound_effect);
14164 }
14165
14166 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14167 {
14168   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14169
14170   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14171     StopSound(sound_effect);
14172 }
14173
14174 static void PlayLevelMusic()
14175 {
14176   if (levelset.music[level_nr] != MUS_UNDEFINED)
14177     PlayMusic(levelset.music[level_nr]);        /* from config file */
14178   else
14179     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14180 }
14181
14182 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14183 {
14184   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14185   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14186   int x = xx - 1 - offset;
14187   int y = yy - 1 - offset;
14188
14189   switch (sample)
14190   {
14191     case SAMPLE_blank:
14192       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14193       break;
14194
14195     case SAMPLE_roll:
14196       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14197       break;
14198
14199     case SAMPLE_stone:
14200       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14201       break;
14202
14203     case SAMPLE_nut:
14204       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14205       break;
14206
14207     case SAMPLE_crack:
14208       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14209       break;
14210
14211     case SAMPLE_bug:
14212       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14213       break;
14214
14215     case SAMPLE_tank:
14216       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14217       break;
14218
14219     case SAMPLE_android_clone:
14220       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14221       break;
14222
14223     case SAMPLE_android_move:
14224       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14225       break;
14226
14227     case SAMPLE_spring:
14228       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14229       break;
14230
14231     case SAMPLE_slurp:
14232       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14233       break;
14234
14235     case SAMPLE_eater:
14236       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14237       break;
14238
14239     case SAMPLE_eater_eat:
14240       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14241       break;
14242
14243     case SAMPLE_alien:
14244       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14245       break;
14246
14247     case SAMPLE_collect:
14248       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14249       break;
14250
14251     case SAMPLE_diamond:
14252       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14253       break;
14254
14255     case SAMPLE_squash:
14256       /* !!! CHECK THIS !!! */
14257 #if 1
14258       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14259 #else
14260       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14261 #endif
14262       break;
14263
14264     case SAMPLE_wonderfall:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14266       break;
14267
14268     case SAMPLE_drip:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14270       break;
14271
14272     case SAMPLE_push:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14274       break;
14275
14276     case SAMPLE_dirt:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14278       break;
14279
14280     case SAMPLE_acid:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14282       break;
14283
14284     case SAMPLE_ball:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14286       break;
14287
14288     case SAMPLE_grow:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14290       break;
14291
14292     case SAMPLE_wonder:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14294       break;
14295
14296     case SAMPLE_door:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14298       break;
14299
14300     case SAMPLE_exit_open:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14302       break;
14303
14304     case SAMPLE_exit_leave:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14306       break;
14307
14308     case SAMPLE_dynamite:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14310       break;
14311
14312     case SAMPLE_tick:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14314       break;
14315
14316     case SAMPLE_press:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14318       break;
14319
14320     case SAMPLE_wheel:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14322       break;
14323
14324     case SAMPLE_boom:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14326       break;
14327
14328     case SAMPLE_die:
14329       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14330       break;
14331
14332     case SAMPLE_time:
14333       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14334       break;
14335
14336     default:
14337       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14338       break;
14339   }
14340 }
14341
14342 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14343 {
14344   int element = map_element_SP_to_RND(element_sp);
14345   int action = map_action_SP_to_RND(action_sp);
14346   int offset = (setup.sp_show_border_elements ? 0 : 1);
14347   int x = xx - offset;
14348   int y = yy - offset;
14349
14350   PlayLevelSoundElementAction(x, y, element, action);
14351 }
14352
14353 void RaiseScore(int value)
14354 {
14355   local_player->score += value;
14356
14357   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14358
14359   DisplayGameControlValues();
14360 }
14361
14362 void RaiseScoreElement(int element)
14363 {
14364   switch (element)
14365   {
14366     case EL_EMERALD:
14367     case EL_BD_DIAMOND:
14368     case EL_EMERALD_YELLOW:
14369     case EL_EMERALD_RED:
14370     case EL_EMERALD_PURPLE:
14371     case EL_SP_INFOTRON:
14372       RaiseScore(level.score[SC_EMERALD]);
14373       break;
14374     case EL_DIAMOND:
14375       RaiseScore(level.score[SC_DIAMOND]);
14376       break;
14377     case EL_CRYSTAL:
14378       RaiseScore(level.score[SC_CRYSTAL]);
14379       break;
14380     case EL_PEARL:
14381       RaiseScore(level.score[SC_PEARL]);
14382       break;
14383     case EL_BUG:
14384     case EL_BD_BUTTERFLY:
14385     case EL_SP_ELECTRON:
14386       RaiseScore(level.score[SC_BUG]);
14387       break;
14388     case EL_SPACESHIP:
14389     case EL_BD_FIREFLY:
14390     case EL_SP_SNIKSNAK:
14391       RaiseScore(level.score[SC_SPACESHIP]);
14392       break;
14393     case EL_YAMYAM:
14394     case EL_DARK_YAMYAM:
14395       RaiseScore(level.score[SC_YAMYAM]);
14396       break;
14397     case EL_ROBOT:
14398       RaiseScore(level.score[SC_ROBOT]);
14399       break;
14400     case EL_PACMAN:
14401       RaiseScore(level.score[SC_PACMAN]);
14402       break;
14403     case EL_NUT:
14404       RaiseScore(level.score[SC_NUT]);
14405       break;
14406     case EL_DYNAMITE:
14407     case EL_EM_DYNAMITE:
14408     case EL_SP_DISK_RED:
14409     case EL_DYNABOMB_INCREASE_NUMBER:
14410     case EL_DYNABOMB_INCREASE_SIZE:
14411     case EL_DYNABOMB_INCREASE_POWER:
14412       RaiseScore(level.score[SC_DYNAMITE]);
14413       break;
14414     case EL_SHIELD_NORMAL:
14415     case EL_SHIELD_DEADLY:
14416       RaiseScore(level.score[SC_SHIELD]);
14417       break;
14418     case EL_EXTRA_TIME:
14419       RaiseScore(level.extra_time_score);
14420       break;
14421     case EL_KEY_1:
14422     case EL_KEY_2:
14423     case EL_KEY_3:
14424     case EL_KEY_4:
14425     case EL_EM_KEY_1:
14426     case EL_EM_KEY_2:
14427     case EL_EM_KEY_3:
14428     case EL_EM_KEY_4:
14429     case EL_EMC_KEY_5:
14430     case EL_EMC_KEY_6:
14431     case EL_EMC_KEY_7:
14432     case EL_EMC_KEY_8:
14433     case EL_DC_KEY_WHITE:
14434       RaiseScore(level.score[SC_KEY]);
14435       break;
14436     default:
14437       RaiseScore(element_info[element].collect_score);
14438       break;
14439   }
14440 }
14441
14442 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14443 {
14444   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14445   {
14446     /* closing door required in case of envelope style request dialogs */
14447     if (!skip_request)
14448       CloseDoor(DOOR_CLOSE_1);
14449
14450 #if defined(NETWORK_AVALIABLE)
14451     if (options.network)
14452       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14453     else
14454 #endif
14455     {
14456       if (quick_quit)
14457       {
14458         FadeSkipNextFadeIn();
14459
14460         game_status = GAME_MODE_MAIN;
14461
14462         DrawAndFadeInMainMenu(REDRAW_FIELD);
14463       }
14464       else
14465       {
14466         game_status = GAME_MODE_MAIN;
14467
14468         DrawAndFadeInMainMenu(REDRAW_FIELD);
14469       }
14470     }
14471   }
14472   else          /* continue playing the game */
14473   {
14474     if (tape.playing && tape.deactivate_display)
14475       TapeDeactivateDisplayOff(TRUE);
14476
14477     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14478
14479     if (tape.playing && tape.deactivate_display)
14480       TapeDeactivateDisplayOn();
14481   }
14482 }
14483
14484 void RequestQuitGame(boolean ask_if_really_quit)
14485 {
14486   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14487   boolean skip_request = AllPlayersGone || quick_quit;
14488
14489   RequestQuitGameExt(skip_request, quick_quit,
14490                      "Do you really want to quit the game?");
14491 }
14492
14493
14494 /* ------------------------------------------------------------------------- */
14495 /* random generator functions                                                */
14496 /* ------------------------------------------------------------------------- */
14497
14498 unsigned int InitEngineRandom_RND(int seed)
14499 {
14500   game.num_random_calls = 0;
14501
14502   return InitEngineRandom(seed);
14503 }
14504
14505 unsigned int RND(int max)
14506 {
14507   if (max > 0)
14508   {
14509     game.num_random_calls++;
14510
14511     return GetEngineRandom(max);
14512   }
14513
14514   return 0;
14515 }
14516
14517
14518 /* ------------------------------------------------------------------------- */
14519 /* game engine snapshot handling functions                                   */
14520 /* ------------------------------------------------------------------------- */
14521
14522 struct EngineSnapshotInfo
14523 {
14524   /* runtime values for custom element collect score */
14525   int collect_score[NUM_CUSTOM_ELEMENTS];
14526
14527   /* runtime values for group element choice position */
14528   int choice_pos[NUM_GROUP_ELEMENTS];
14529
14530   /* runtime values for belt position animations */
14531   int belt_graphic[4][NUM_BELT_PARTS];
14532   int belt_anim_mode[4][NUM_BELT_PARTS];
14533 };
14534
14535 static struct EngineSnapshotInfo engine_snapshot_rnd;
14536 static char *snapshot_level_identifier = NULL;
14537 static int snapshot_level_nr = -1;
14538
14539 static void SaveEngineSnapshotValues_RND()
14540 {
14541   static int belt_base_active_element[4] =
14542   {
14543     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14544     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14545     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14546     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14547   };
14548   int i, j;
14549
14550   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14551   {
14552     int element = EL_CUSTOM_START + i;
14553
14554     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14555   }
14556
14557   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14558   {
14559     int element = EL_GROUP_START + i;
14560
14561     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14562   }
14563
14564   for (i = 0; i < 4; i++)
14565   {
14566     for (j = 0; j < NUM_BELT_PARTS; j++)
14567     {
14568       int element = belt_base_active_element[i] + j;
14569       int graphic = el2img(element);
14570       int anim_mode = graphic_info[graphic].anim_mode;
14571
14572       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14573       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14574     }
14575   }
14576 }
14577
14578 static void LoadEngineSnapshotValues_RND()
14579 {
14580   unsigned int num_random_calls = game.num_random_calls;
14581   int i, j;
14582
14583   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14584   {
14585     int element = EL_CUSTOM_START + i;
14586
14587     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14588   }
14589
14590   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14591   {
14592     int element = EL_GROUP_START + i;
14593
14594     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14595   }
14596
14597   for (i = 0; i < 4; i++)
14598   {
14599     for (j = 0; j < NUM_BELT_PARTS; j++)
14600     {
14601       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14602       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14603
14604       graphic_info[graphic].anim_mode = anim_mode;
14605     }
14606   }
14607
14608   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14609   {
14610     InitRND(tape.random_seed);
14611     for (i = 0; i < num_random_calls; i++)
14612       RND(1);
14613   }
14614
14615   if (game.num_random_calls != num_random_calls)
14616   {
14617     Error(ERR_INFO, "number of random calls out of sync");
14618     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14619     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14620     Error(ERR_EXIT, "this should not happen -- please debug");
14621   }
14622 }
14623
14624 void FreeEngineSnapshotSingle()
14625 {
14626   FreeSnapshotSingle();
14627
14628   setString(&snapshot_level_identifier, NULL);
14629   snapshot_level_nr = -1;
14630 }
14631
14632 void FreeEngineSnapshotList()
14633 {
14634   FreeSnapshotList();
14635 }
14636
14637 ListNode *SaveEngineSnapshotBuffers()
14638 {
14639   ListNode *buffers = NULL;
14640
14641   /* copy some special values to a structure better suited for the snapshot */
14642
14643   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14644     SaveEngineSnapshotValues_RND();
14645   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14646     SaveEngineSnapshotValues_EM();
14647   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14648     SaveEngineSnapshotValues_SP(&buffers);
14649
14650   /* save values stored in special snapshot structure */
14651
14652   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14653     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14654   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14655     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14656   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14657     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14658
14659   /* save further RND engine values */
14660
14661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14664
14665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14669
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14675
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14679
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14683
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14686
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14705
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14708
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14712
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14715
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14721
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14724
14725   /* save level identification information */
14726
14727   setString(&snapshot_level_identifier, leveldir_current->identifier);
14728   snapshot_level_nr = level_nr;
14729
14730 #if 0
14731   ListNode *node = engine_snapshot_list_rnd;
14732   int num_bytes = 0;
14733
14734   while (node != NULL)
14735   {
14736     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14737
14738     node = node->next;
14739   }
14740
14741   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14742 #endif
14743
14744   return buffers;
14745 }
14746
14747 void SaveEngineSnapshotSingle()
14748 {
14749   ListNode *buffers = SaveEngineSnapshotBuffers();
14750
14751   /* finally save all snapshot buffers to single snapshot */
14752   SaveSnapshotSingle(buffers);
14753 }
14754
14755 boolean SaveEngineSnapshotToList()
14756 {
14757   boolean save_snapshot =
14758     (FrameCounter == 0 ||
14759      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14760      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14761       game.snapshot.changed_action));
14762
14763   game.snapshot.changed_action = FALSE;
14764
14765   if (game.snapshot.mode == SNAPSHOT_MODE_OFF || !save_snapshot)
14766     return FALSE;
14767
14768   ListNode *buffers = SaveEngineSnapshotBuffers();
14769
14770   /* finally save all snapshot buffers to snapshot list */
14771   SaveSnapshotToList(buffers);
14772
14773   return TRUE;
14774 }
14775
14776 void LoadEngineSnapshotValues()
14777 {
14778   /* restore special values from snapshot structure */
14779
14780   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14781     LoadEngineSnapshotValues_RND();
14782   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14783     LoadEngineSnapshotValues_EM();
14784   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14785     LoadEngineSnapshotValues_SP();
14786 }
14787
14788 void LoadEngineSnapshotSingle()
14789 {
14790   LoadSnapshotSingle();
14791
14792   LoadEngineSnapshotValues();
14793 }
14794
14795 void LoadEngineSnapshot_Undo(int steps)
14796 {
14797   LoadSnapshotFromList_Older(steps);
14798
14799   LoadEngineSnapshotValues();
14800 }
14801
14802 void LoadEngineSnapshot_Redo(int steps)
14803 {
14804   LoadSnapshotFromList_Newer(steps);
14805
14806   LoadEngineSnapshotValues();
14807 }
14808
14809 boolean CheckEngineSnapshotSingle()
14810 {
14811   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14812           snapshot_level_nr == level_nr);
14813 }
14814
14815 boolean CheckEngineSnapshotList()
14816 {
14817   return CheckSnapshotList();
14818 }
14819
14820
14821 /* ---------- new game button stuff ---------------------------------------- */
14822
14823 static struct
14824 {
14825   int graphic;
14826   struct XY *pos;
14827   int gadget_id;
14828   char *infotext;
14829 } gamebutton_info[NUM_GAME_BUTTONS] =
14830 {
14831   {
14832     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14833     GAME_CTRL_ID_STOP,                  "stop game"
14834   },
14835   {
14836     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14837     GAME_CTRL_ID_PAUSE,                 "pause game"
14838   },
14839   {
14840     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14841     GAME_CTRL_ID_PLAY,                  "play game"
14842   },
14843   {
14844     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14845     GAME_CTRL_ID_UNDO,                  "undo step"
14846   },
14847   {
14848     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14849     GAME_CTRL_ID_REDO,                  "redo step"
14850   },
14851   {
14852     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14853     GAME_CTRL_ID_SAVE,                  "save game"
14854   },
14855   {
14856     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14857     GAME_CTRL_ID_LOAD,                  "load game"
14858   },
14859   {
14860     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14861     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14862   },
14863   {
14864     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14865     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14866   },
14867   {
14868     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14869     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14870   }
14871 };
14872
14873 void CreateGameButtons()
14874 {
14875   int i;
14876
14877   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14878   {
14879     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14880     struct XY *pos = gamebutton_info[i].pos;
14881     struct GadgetInfo *gi;
14882     int button_type;
14883     boolean checked;
14884     unsigned int event_mask;
14885     int base_x = (tape.show_game_buttons ? VX : DX);
14886     int base_y = (tape.show_game_buttons ? VY : DY);
14887     int gd_x   = gfx->src_x;
14888     int gd_y   = gfx->src_y;
14889     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14890     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14891     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14892     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14893     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14894     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14895     int id = i;
14896
14897     if (gfx->bitmap == NULL)
14898     {
14899       game_gadget[id] = NULL;
14900
14901       continue;
14902     }
14903
14904     if (id == GAME_CTRL_ID_STOP ||
14905         id == GAME_CTRL_ID_PAUSE ||
14906         id == GAME_CTRL_ID_PLAY ||
14907         id == GAME_CTRL_ID_SAVE ||
14908         id == GAME_CTRL_ID_LOAD)
14909     {
14910       button_type = GD_TYPE_NORMAL_BUTTON;
14911       checked = FALSE;
14912       event_mask = GD_EVENT_RELEASED;
14913     }
14914     else if (id == GAME_CTRL_ID_UNDO ||
14915              id == GAME_CTRL_ID_REDO)
14916     {
14917       button_type = GD_TYPE_NORMAL_BUTTON;
14918       checked = FALSE;
14919       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14920     }
14921     else
14922     {
14923       button_type = GD_TYPE_CHECK_BUTTON;
14924       checked =
14925         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14926          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14927          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14928       event_mask = GD_EVENT_PRESSED;
14929     }
14930
14931     gi = CreateGadget(GDI_CUSTOM_ID, id,
14932                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14933                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14934                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14935                       GDI_WIDTH, gfx->width,
14936                       GDI_HEIGHT, gfx->height,
14937                       GDI_TYPE, button_type,
14938                       GDI_STATE, GD_BUTTON_UNPRESSED,
14939                       GDI_CHECKED, checked,
14940                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14941                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14942                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14943                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14944                       GDI_DIRECT_DRAW, FALSE,
14945                       GDI_EVENT_MASK, event_mask,
14946                       GDI_CALLBACK_ACTION, HandleGameButtons,
14947                       GDI_END);
14948
14949     if (gi == NULL)
14950       Error(ERR_EXIT, "cannot create gadget");
14951
14952     game_gadget[id] = gi;
14953   }
14954 }
14955
14956 void FreeGameButtons()
14957 {
14958   int i;
14959
14960   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14961     FreeGadget(game_gadget[i]);
14962 }
14963
14964 void MapStopPlayButtons()
14965 {
14966   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14967   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14968
14969   MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14970   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14971 }
14972
14973 void MapUndoRedoButtons()
14974 {
14975   UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14976   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14977
14978   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14979   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14980 }
14981
14982 void MapGameButtons()
14983 {
14984   int i;
14985
14986   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14987     if (i != GAME_CTRL_ID_UNDO &&
14988         i != GAME_CTRL_ID_REDO)
14989       MapGadget(game_gadget[i]);
14990 }
14991
14992 void UnmapGameButtons()
14993 {
14994   int i;
14995
14996   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14997     UnmapGadget(game_gadget[i]);
14998 }
14999
15000 void RedrawGameButtons()
15001 {
15002   int i;
15003
15004   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15005     RedrawGadget(game_gadget[i]);
15006
15007   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15008   redraw_mask &= ~REDRAW_ALL;
15009 }
15010
15011 void GameUndoRedoExt()
15012 {
15013   ClearPlayerAction();
15014
15015   tape.pausing = TRUE;
15016
15017   RedrawPlayfield();
15018   UpdateAndDisplayGameControlValues();
15019
15020   DrawCompleteVideoDisplay();
15021   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15022   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15023   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15024                     VIDEO_STATE_1STEP_OFF), 0);
15025
15026   BackToFront();
15027 }
15028
15029 void GameUndo(int steps)
15030 {
15031   if (!CheckEngineSnapshotList())
15032     return;
15033
15034   LoadEngineSnapshot_Undo(steps);
15035
15036   GameUndoRedoExt();
15037 }
15038
15039 void GameRedo(int steps)
15040 {
15041   if (!CheckEngineSnapshotList())
15042     return;
15043
15044   LoadEngineSnapshot_Redo(steps);
15045
15046   GameUndoRedoExt();
15047 }
15048
15049 static void HandleGameButtonsExt(int id, int button)
15050 {
15051   int steps = BUTTON_STEPSIZE(button);
15052   boolean handle_game_buttons =
15053     (game_status == GAME_MODE_PLAYING ||
15054      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15055
15056   if (!handle_game_buttons)
15057     return;
15058
15059   switch (id)
15060   {
15061     case GAME_CTRL_ID_STOP:
15062       if (game_status == GAME_MODE_MAIN)
15063         break;
15064
15065       if (tape.playing)
15066         TapeStop();
15067       else
15068         RequestQuitGame(TRUE);
15069
15070       break;
15071
15072     case GAME_CTRL_ID_PAUSE:
15073       if (options.network && game_status == GAME_MODE_PLAYING)
15074       {
15075 #if defined(NETWORK_AVALIABLE)
15076         if (tape.pausing)
15077           SendToServer_ContinuePlaying();
15078         else
15079           SendToServer_PausePlaying();
15080 #endif
15081       }
15082       else
15083         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15084       break;
15085
15086     case GAME_CTRL_ID_PLAY:
15087       if (game_status == GAME_MODE_MAIN)
15088       {
15089         StartGameActions(options.network, setup.autorecord, level.random_seed);
15090       }
15091       else if (tape.pausing)
15092       {
15093 #if defined(NETWORK_AVALIABLE)
15094         if (options.network)
15095           SendToServer_ContinuePlaying();
15096         else
15097 #endif
15098         {
15099           tape.pausing = FALSE;
15100           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15101         }
15102       }
15103       break;
15104
15105     case GAME_CTRL_ID_UNDO:
15106       GameUndo(steps);
15107       break;
15108
15109     case GAME_CTRL_ID_REDO:
15110       GameRedo(steps);
15111       break;
15112
15113     case GAME_CTRL_ID_SAVE:
15114       TapeQuickSave();
15115       break;
15116
15117     case GAME_CTRL_ID_LOAD:
15118       TapeQuickLoad();
15119       break;
15120
15121     case SOUND_CTRL_ID_MUSIC:
15122       if (setup.sound_music)
15123       { 
15124         setup.sound_music = FALSE;
15125
15126         FadeMusic();
15127       }
15128       else if (audio.music_available)
15129       { 
15130         setup.sound = setup.sound_music = TRUE;
15131
15132         SetAudioMode(setup.sound);
15133
15134         PlayLevelMusic();
15135       }
15136       break;
15137
15138     case SOUND_CTRL_ID_LOOPS:
15139       if (setup.sound_loops)
15140         setup.sound_loops = FALSE;
15141       else if (audio.loops_available)
15142       {
15143         setup.sound = setup.sound_loops = TRUE;
15144
15145         SetAudioMode(setup.sound);
15146       }
15147       break;
15148
15149     case SOUND_CTRL_ID_SIMPLE:
15150       if (setup.sound_simple)
15151         setup.sound_simple = FALSE;
15152       else if (audio.sound_available)
15153       {
15154         setup.sound = setup.sound_simple = TRUE;
15155
15156         SetAudioMode(setup.sound);
15157       }
15158       break;
15159
15160     default:
15161       break;
15162   }
15163 }
15164
15165 static void HandleGameButtons(struct GadgetInfo *gi)
15166 {
15167   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15168 }
15169
15170 void HandleSoundButtonKeys(Key key)
15171 {
15172
15173   if (key == setup.shortcut.sound_simple)
15174     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15175   else if (key == setup.shortcut.sound_loops)
15176     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15177   else if (key == setup.shortcut.sound_music)
15178     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15179 }