added setup option to enable/disable (now optional) snapshot buttons
[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_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086
3087   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3088   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3089   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3090   int initial_move_dir = MV_DOWN;
3091   int i, j, x, y;
3092
3093   game_status = GAME_MODE_PLAYING;
3094
3095   StopAnimation();
3096
3097   if (!game.restart_level)
3098     CloseDoor(DOOR_CLOSE_1);
3099
3100   if (level_editor_test_game)
3101     FadeSkipNextFadeIn();
3102   else
3103     FadeSetEnterScreen();
3104
3105   FadeOut(REDRAW_FIELD);
3106
3107   /* needed if different viewport properties defined for playing */
3108   ChangeViewportPropertiesIfNeeded();
3109
3110   DrawCompleteVideoDisplay();
3111
3112   InitGameEngine();
3113   InitGameControlValues();
3114
3115   /* don't play tapes over network */
3116   network_playing = (options.network && !tape.playing);
3117
3118   for (i = 0; i < MAX_PLAYERS; i++)
3119   {
3120     struct PlayerInfo *player = &stored_player[i];
3121
3122     player->index_nr = i;
3123     player->index_bit = (1 << i);
3124     player->element_nr = EL_PLAYER_1 + i;
3125
3126     player->present = FALSE;
3127     player->active = FALSE;
3128     player->mapped = FALSE;
3129
3130     player->killed = FALSE;
3131     player->reanimated = FALSE;
3132
3133     player->action = 0;
3134     player->effective_action = 0;
3135     player->programmed_action = 0;
3136
3137     player->score = 0;
3138     player->score_final = 0;
3139
3140     player->gems_still_needed = level.gems_needed;
3141     player->sokobanfields_still_needed = 0;
3142     player->lights_still_needed = 0;
3143     player->friends_still_needed = 0;
3144
3145     for (j = 0; j < MAX_NUM_KEYS; j++)
3146       player->key[j] = FALSE;
3147
3148     player->num_white_keys = 0;
3149
3150     player->dynabomb_count = 0;
3151     player->dynabomb_size = 1;
3152     player->dynabombs_left = 0;
3153     player->dynabomb_xl = FALSE;
3154
3155     player->MovDir = initial_move_dir;
3156     player->MovPos = 0;
3157     player->GfxPos = 0;
3158     player->GfxDir = initial_move_dir;
3159     player->GfxAction = ACTION_DEFAULT;
3160     player->Frame = 0;
3161     player->StepFrame = 0;
3162
3163     player->initial_element = player->element_nr;
3164     player->artwork_element =
3165       (level.use_artwork_element[i] ? level.artwork_element[i] :
3166        player->element_nr);
3167     player->use_murphy = FALSE;
3168
3169     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3170     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3171
3172     player->gravity = level.initial_player_gravity[i];
3173
3174     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3175
3176     player->actual_frame_counter = 0;
3177
3178     player->step_counter = 0;
3179
3180     player->last_move_dir = initial_move_dir;
3181
3182     player->is_active = FALSE;
3183
3184     player->is_waiting = FALSE;
3185     player->is_moving = FALSE;
3186     player->is_auto_moving = FALSE;
3187     player->is_digging = FALSE;
3188     player->is_snapping = FALSE;
3189     player->is_collecting = FALSE;
3190     player->is_pushing = FALSE;
3191     player->is_switching = FALSE;
3192     player->is_dropping = FALSE;
3193     player->is_dropping_pressed = FALSE;
3194
3195     player->is_bored = FALSE;
3196     player->is_sleeping = FALSE;
3197
3198     player->frame_counter_bored = -1;
3199     player->frame_counter_sleeping = -1;
3200
3201     player->anim_delay_counter = 0;
3202     player->post_delay_counter = 0;
3203
3204     player->dir_waiting = initial_move_dir;
3205     player->action_waiting = ACTION_DEFAULT;
3206     player->last_action_waiting = ACTION_DEFAULT;
3207     player->special_action_bored = ACTION_DEFAULT;
3208     player->special_action_sleeping = ACTION_DEFAULT;
3209
3210     player->switch_x = -1;
3211     player->switch_y = -1;
3212
3213     player->drop_x = -1;
3214     player->drop_y = -1;
3215
3216     player->show_envelope = 0;
3217
3218     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3219
3220     player->push_delay       = -1;      /* initialized when pushing starts */
3221     player->push_delay_value = game.initial_push_delay_value;
3222
3223     player->drop_delay = 0;
3224     player->drop_pressed_delay = 0;
3225
3226     player->last_jx = -1;
3227     player->last_jy = -1;
3228     player->jx = -1;
3229     player->jy = -1;
3230
3231     player->shield_normal_time_left = 0;
3232     player->shield_deadly_time_left = 0;
3233
3234     player->inventory_infinite_element = EL_UNDEFINED;
3235     player->inventory_size = 0;
3236
3237     if (level.use_initial_inventory[i])
3238     {
3239       for (j = 0; j < level.initial_inventory_size[i]; j++)
3240       {
3241         int element = level.initial_inventory_content[i][j];
3242         int collect_count = element_info[element].collect_count_initial;
3243         int k;
3244
3245         if (!IS_CUSTOM_ELEMENT(element))
3246           collect_count = 1;
3247
3248         if (collect_count == 0)
3249           player->inventory_infinite_element = element;
3250         else
3251           for (k = 0; k < collect_count; k++)
3252             if (player->inventory_size < MAX_INVENTORY_SIZE)
3253               player->inventory_element[player->inventory_size++] = element;
3254       }
3255     }
3256
3257     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3258     SnapField(player, 0, 0);
3259
3260     player->LevelSolved = FALSE;
3261     player->GameOver = FALSE;
3262
3263     player->LevelSolved_GameWon = FALSE;
3264     player->LevelSolved_GameEnd = FALSE;
3265     player->LevelSolved_PanelOff = FALSE;
3266     player->LevelSolved_SaveTape = FALSE;
3267     player->LevelSolved_SaveScore = FALSE;
3268     player->LevelSolved_CountingTime = 0;
3269     player->LevelSolved_CountingScore = 0;
3270
3271     map_player_action[i] = i;
3272   }
3273
3274   network_player_action_received = FALSE;
3275
3276 #if defined(NETWORK_AVALIABLE)
3277   /* initial null action */
3278   if (network_playing)
3279     SendToServer_MovePlayer(MV_NONE);
3280 #endif
3281
3282   ZX = ZY = -1;
3283   ExitX = ExitY = -1;
3284
3285   FrameCounter = 0;
3286   TimeFrames = 0;
3287   TimePlayed = 0;
3288   TimeLeft = level.time;
3289   TapeTime = 0;
3290
3291   ScreenMovDir = MV_NONE;
3292   ScreenMovPos = 0;
3293   ScreenGfxPos = 0;
3294
3295   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3296
3297   AllPlayersGone = FALSE;
3298
3299   game.no_time_limit = (level.time == 0);
3300
3301   game.yamyam_content_nr = 0;
3302   game.robot_wheel_active = FALSE;
3303   game.magic_wall_active = FALSE;
3304   game.magic_wall_time_left = 0;
3305   game.light_time_left = 0;
3306   game.timegate_time_left = 0;
3307   game.switchgate_pos = 0;
3308   game.wind_direction = level.wind_direction_initial;
3309
3310   game.lenses_time_left = 0;
3311   game.magnify_time_left = 0;
3312
3313   game.ball_state = level.ball_state_initial;
3314   game.ball_content_nr = 0;
3315
3316   game.envelope_active = FALSE;
3317
3318   /* set focus to local player for network games, else to all players */
3319   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3320   game.centered_player_nr_next = game.centered_player_nr;
3321   game.set_centered_player = FALSE;
3322
3323   if (network_playing && tape.recording)
3324   {
3325     /* store client dependent player focus when recording network games */
3326     tape.centered_player_nr_next = game.centered_player_nr_next;
3327     tape.set_centered_player = TRUE;
3328   }
3329
3330   for (i = 0; i < NUM_BELTS; i++)
3331   {
3332     game.belt_dir[i] = MV_NONE;
3333     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3334   }
3335
3336   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3337     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3338
3339 #if DEBUG_INIT_PLAYER
3340   if (options.debug)
3341   {
3342     printf("Player status at level initialization:\n");
3343   }
3344 #endif
3345
3346   SCAN_PLAYFIELD(x, y)
3347   {
3348     Feld[x][y] = level.field[x][y];
3349     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3350     ChangeDelay[x][y] = 0;
3351     ChangePage[x][y] = -1;
3352     CustomValue[x][y] = 0;              /* initialized in InitField() */
3353     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3354     AmoebaNr[x][y] = 0;
3355     WasJustMoving[x][y] = 0;
3356     WasJustFalling[x][y] = 0;
3357     CheckCollision[x][y] = 0;
3358     CheckImpact[x][y] = 0;
3359     Stop[x][y] = FALSE;
3360     Pushed[x][y] = FALSE;
3361
3362     ChangeCount[x][y] = 0;
3363     ChangeEvent[x][y] = -1;
3364
3365     ExplodePhase[x][y] = 0;
3366     ExplodeDelay[x][y] = 0;
3367     ExplodeField[x][y] = EX_TYPE_NONE;
3368
3369     RunnerVisit[x][y] = 0;
3370     PlayerVisit[x][y] = 0;
3371
3372     GfxFrame[x][y] = 0;
3373     GfxRandom[x][y] = INIT_GFX_RANDOM();
3374     GfxElement[x][y] = EL_UNDEFINED;
3375     GfxAction[x][y] = ACTION_DEFAULT;
3376     GfxDir[x][y] = MV_NONE;
3377     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3378   }
3379
3380   SCAN_PLAYFIELD(x, y)
3381   {
3382     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3383       emulate_bd = FALSE;
3384     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3385       emulate_sb = FALSE;
3386     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3387       emulate_sp = FALSE;
3388
3389     InitField(x, y, TRUE);
3390
3391     ResetGfxAnimation(x, y);
3392   }
3393
3394   InitBeltMovement();
3395
3396   for (i = 0; i < MAX_PLAYERS; i++)
3397   {
3398     struct PlayerInfo *player = &stored_player[i];
3399
3400     /* set number of special actions for bored and sleeping animation */
3401     player->num_special_action_bored =
3402       get_num_special_action(player->artwork_element,
3403                              ACTION_BORING_1, ACTION_BORING_LAST);
3404     player->num_special_action_sleeping =
3405       get_num_special_action(player->artwork_element,
3406                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3407   }
3408
3409   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3410                     emulate_sb ? EMU_SOKOBAN :
3411                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3412
3413   /* initialize type of slippery elements */
3414   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3415   {
3416     if (!IS_CUSTOM_ELEMENT(i))
3417     {
3418       /* default: elements slip down either to the left or right randomly */
3419       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3420
3421       /* SP style elements prefer to slip down on the left side */
3422       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3423         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3424
3425       /* BD style elements prefer to slip down on the left side */
3426       if (game.emulation == EMU_BOULDERDASH)
3427         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3428     }
3429   }
3430
3431   /* initialize explosion and ignition delay */
3432   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433   {
3434     if (!IS_CUSTOM_ELEMENT(i))
3435     {
3436       int num_phase = 8;
3437       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3438                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3439                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3440       int last_phase = (num_phase + 1) * delay;
3441       int half_phase = (num_phase / 2) * delay;
3442
3443       element_info[i].explosion_delay = last_phase - 1;
3444       element_info[i].ignition_delay = half_phase;
3445
3446       if (i == EL_BLACK_ORB)
3447         element_info[i].ignition_delay = 1;
3448     }
3449   }
3450
3451   /* correct non-moving belts to start moving left */
3452   for (i = 0; i < NUM_BELTS; i++)
3453     if (game.belt_dir[i] == MV_NONE)
3454       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3455
3456 #if USE_NEW_PLAYER_ASSIGNMENTS
3457   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3458   /* choose default local player */
3459   local_player = &stored_player[0];
3460
3461   for (i = 0; i < MAX_PLAYERS; i++)
3462     stored_player[i].connected = FALSE;
3463
3464   local_player->connected = TRUE;
3465   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3466
3467   if (tape.playing)
3468   {
3469     for (i = 0; i < MAX_PLAYERS; i++)
3470       stored_player[i].connected = tape.player_participates[i];
3471   }
3472   else if (game.team_mode && !options.network)
3473   {
3474     /* try to guess locally connected team mode players (needed for correct
3475        assignment of player figures from level to locally playing players) */
3476
3477     for (i = 0; i < MAX_PLAYERS; i++)
3478       if (setup.input[i].use_joystick ||
3479           setup.input[i].key.left != KSYM_UNDEFINED)
3480         stored_player[i].connected = TRUE;
3481   }
3482
3483 #if DEBUG_INIT_PLAYER
3484   if (options.debug)
3485   {
3486     printf("Player status after level initialization:\n");
3487
3488     for (i = 0; i < MAX_PLAYERS; i++)
3489     {
3490       struct PlayerInfo *player = &stored_player[i];
3491
3492       printf("- player %d: present == %d, connected == %d, active == %d",
3493              i + 1,
3494              player->present,
3495              player->connected,
3496              player->active);
3497
3498       if (local_player == player)
3499         printf(" (local player)");
3500
3501       printf("\n");
3502     }
3503   }
3504 #endif
3505
3506 #if DEBUG_INIT_PLAYER
3507   if (options.debug)
3508     printf("Reassigning players ...\n");
3509 #endif
3510
3511   /* check if any connected player was not found in playfield */
3512   for (i = 0; i < MAX_PLAYERS; i++)
3513   {
3514     struct PlayerInfo *player = &stored_player[i];
3515
3516     if (player->connected && !player->present)
3517     {
3518       struct PlayerInfo *field_player = NULL;
3519
3520 #if DEBUG_INIT_PLAYER
3521       if (options.debug)
3522         printf("- looking for field player for player %d ...\n", i + 1);
3523 #endif
3524
3525       /* assign first free player found that is present in the playfield */
3526
3527       /* first try: look for unmapped playfield player that is not connected */
3528       for (j = 0; j < MAX_PLAYERS; j++)
3529         if (field_player == NULL &&
3530             stored_player[j].present &&
3531             !stored_player[j].mapped &&
3532             !stored_player[j].connected)
3533           field_player = &stored_player[j];
3534
3535       /* second try: look for *any* unmapped playfield player */
3536       for (j = 0; j < MAX_PLAYERS; j++)
3537         if (field_player == NULL &&
3538             stored_player[j].present &&
3539             !stored_player[j].mapped)
3540           field_player = &stored_player[j];
3541
3542       if (field_player != NULL)
3543       {
3544         int jx = field_player->jx, jy = field_player->jy;
3545
3546 #if DEBUG_INIT_PLAYER
3547         if (options.debug)
3548           printf("- found player %d\n", field_player->index_nr + 1);
3549 #endif
3550
3551         player->present = FALSE;
3552         player->active = FALSE;
3553
3554         field_player->present = TRUE;
3555         field_player->active = TRUE;
3556
3557         /*
3558         player->initial_element = field_player->initial_element;
3559         player->artwork_element = field_player->artwork_element;
3560
3561         player->block_last_field       = field_player->block_last_field;
3562         player->block_delay_adjustment = field_player->block_delay_adjustment;
3563         */
3564
3565         StorePlayer[jx][jy] = field_player->element_nr;
3566
3567         field_player->jx = field_player->last_jx = jx;
3568         field_player->jy = field_player->last_jy = jy;
3569
3570         if (local_player == player)
3571           local_player = field_player;
3572
3573         map_player_action[field_player->index_nr] = i;
3574
3575         field_player->mapped = TRUE;
3576
3577 #if DEBUG_INIT_PLAYER
3578         if (options.debug)
3579           printf("- map_player_action[%d] == %d\n",
3580                  field_player->index_nr + 1, i + 1);
3581 #endif
3582       }
3583     }
3584
3585     if (player->connected && player->present)
3586       player->mapped = TRUE;
3587   }
3588
3589 #if DEBUG_INIT_PLAYER
3590   if (options.debug)
3591   {
3592     printf("Player status after player assignment (first stage):\n");
3593
3594     for (i = 0; i < MAX_PLAYERS; i++)
3595     {
3596       struct PlayerInfo *player = &stored_player[i];
3597
3598       printf("- player %d: present == %d, connected == %d, active == %d",
3599              i + 1,
3600              player->present,
3601              player->connected,
3602              player->active);
3603
3604       if (local_player == player)
3605         printf(" (local player)");
3606
3607       printf("\n");
3608     }
3609   }
3610 #endif
3611
3612 #else
3613
3614   /* check if any connected player was not found in playfield */
3615   for (i = 0; i < MAX_PLAYERS; i++)
3616   {
3617     struct PlayerInfo *player = &stored_player[i];
3618
3619     if (player->connected && !player->present)
3620     {
3621       for (j = 0; j < MAX_PLAYERS; j++)
3622       {
3623         struct PlayerInfo *field_player = &stored_player[j];
3624         int jx = field_player->jx, jy = field_player->jy;
3625
3626         /* assign first free player found that is present in the playfield */
3627         if (field_player->present && !field_player->connected)
3628         {
3629           player->present = TRUE;
3630           player->active = TRUE;
3631
3632           field_player->present = FALSE;
3633           field_player->active = FALSE;
3634
3635           player->initial_element = field_player->initial_element;
3636           player->artwork_element = field_player->artwork_element;
3637
3638           player->block_last_field       = field_player->block_last_field;
3639           player->block_delay_adjustment = field_player->block_delay_adjustment;
3640
3641           StorePlayer[jx][jy] = player->element_nr;
3642
3643           player->jx = player->last_jx = jx;
3644           player->jy = player->last_jy = jy;
3645
3646           break;
3647         }
3648       }
3649     }
3650   }
3651 #endif
3652
3653 #if 0
3654   printf("::: local_player->present == %d\n", local_player->present);
3655 #endif
3656
3657   if (tape.playing)
3658   {
3659     /* when playing a tape, eliminate all players who do not participate */
3660
3661 #if USE_NEW_PLAYER_ASSIGNMENTS
3662
3663     if (!game.team_mode)
3664     {
3665       for (i = 0; i < MAX_PLAYERS; i++)
3666       {
3667         if (stored_player[i].active &&
3668             !tape.player_participates[map_player_action[i]])
3669         {
3670           struct PlayerInfo *player = &stored_player[i];
3671           int jx = player->jx, jy = player->jy;
3672
3673 #if DEBUG_INIT_PLAYER
3674           if (options.debug)
3675             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3676 #endif
3677
3678           player->active = FALSE;
3679           StorePlayer[jx][jy] = 0;
3680           Feld[jx][jy] = EL_EMPTY;
3681         }
3682       }
3683     }
3684
3685 #else
3686
3687     for (i = 0; i < MAX_PLAYERS; i++)
3688     {
3689       if (stored_player[i].active &&
3690           !tape.player_participates[i])
3691       {
3692         struct PlayerInfo *player = &stored_player[i];
3693         int jx = player->jx, jy = player->jy;
3694
3695         player->active = FALSE;
3696         StorePlayer[jx][jy] = 0;
3697         Feld[jx][jy] = EL_EMPTY;
3698       }
3699     }
3700 #endif
3701   }
3702   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3703   {
3704     /* when in single player mode, eliminate all but the first active player */
3705
3706     for (i = 0; i < MAX_PLAYERS; i++)
3707     {
3708       if (stored_player[i].active)
3709       {
3710         for (j = i + 1; j < MAX_PLAYERS; j++)
3711         {
3712           if (stored_player[j].active)
3713           {
3714             struct PlayerInfo *player = &stored_player[j];
3715             int jx = player->jx, jy = player->jy;
3716
3717             player->active = FALSE;
3718             player->present = FALSE;
3719
3720             StorePlayer[jx][jy] = 0;
3721             Feld[jx][jy] = EL_EMPTY;
3722           }
3723         }
3724       }
3725     }
3726   }
3727
3728   /* when recording the game, store which players take part in the game */
3729   if (tape.recording)
3730   {
3731 #if USE_NEW_PLAYER_ASSIGNMENTS
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733       if (stored_player[i].connected)
3734         tape.player_participates[i] = TRUE;
3735 #else
3736     for (i = 0; i < MAX_PLAYERS; i++)
3737       if (stored_player[i].active)
3738         tape.player_participates[i] = TRUE;
3739 #endif
3740   }
3741
3742 #if DEBUG_INIT_PLAYER
3743   if (options.debug)
3744   {
3745     printf("Player status after player assignment (final stage):\n");
3746
3747     for (i = 0; i < MAX_PLAYERS; i++)
3748     {
3749       struct PlayerInfo *player = &stored_player[i];
3750
3751       printf("- player %d: present == %d, connected == %d, active == %d",
3752              i + 1,
3753              player->present,
3754              player->connected,
3755              player->active);
3756
3757       if (local_player == player)
3758         printf(" (local player)");
3759
3760       printf("\n");
3761     }
3762   }
3763 #endif
3764
3765   if (BorderElement == EL_EMPTY)
3766   {
3767     SBX_Left = 0;
3768     SBX_Right = lev_fieldx - SCR_FIELDX;
3769     SBY_Upper = 0;
3770     SBY_Lower = lev_fieldy - SCR_FIELDY;
3771   }
3772   else
3773   {
3774     SBX_Left = -1;
3775     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3776     SBY_Upper = -1;
3777     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3778   }
3779
3780   if (full_lev_fieldx <= SCR_FIELDX)
3781     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3782   if (full_lev_fieldy <= SCR_FIELDY)
3783     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3784
3785   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3786     SBX_Left--;
3787   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3788     SBY_Upper--;
3789
3790   /* if local player not found, look for custom element that might create
3791      the player (make some assumptions about the right custom element) */
3792   if (!local_player->present)
3793   {
3794     int start_x = 0, start_y = 0;
3795     int found_rating = 0;
3796     int found_element = EL_UNDEFINED;
3797     int player_nr = local_player->index_nr;
3798
3799     SCAN_PLAYFIELD(x, y)
3800     {
3801       int element = Feld[x][y];
3802       int content;
3803       int xx, yy;
3804       boolean is_player;
3805
3806       if (level.use_start_element[player_nr] &&
3807           level.start_element[player_nr] == element &&
3808           found_rating < 4)
3809       {
3810         start_x = x;
3811         start_y = y;
3812
3813         found_rating = 4;
3814         found_element = element;
3815       }
3816
3817       if (!IS_CUSTOM_ELEMENT(element))
3818         continue;
3819
3820       if (CAN_CHANGE(element))
3821       {
3822         for (i = 0; i < element_info[element].num_change_pages; i++)
3823         {
3824           /* check for player created from custom element as single target */
3825           content = element_info[element].change_page[i].target_element;
3826           is_player = ELEM_IS_PLAYER(content);
3827
3828           if (is_player && (found_rating < 3 ||
3829                             (found_rating == 3 && element < found_element)))
3830           {
3831             start_x = x;
3832             start_y = y;
3833
3834             found_rating = 3;
3835             found_element = element;
3836           }
3837         }
3838       }
3839
3840       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3841       {
3842         /* check for player created from custom element as explosion content */
3843         content = element_info[element].content.e[xx][yy];
3844         is_player = ELEM_IS_PLAYER(content);
3845
3846         if (is_player && (found_rating < 2 ||
3847                           (found_rating == 2 && element < found_element)))
3848         {
3849           start_x = x + xx - 1;
3850           start_y = y + yy - 1;
3851
3852           found_rating = 2;
3853           found_element = element;
3854         }
3855
3856         if (!CAN_CHANGE(element))
3857           continue;
3858
3859         for (i = 0; i < element_info[element].num_change_pages; i++)
3860         {
3861           /* check for player created from custom element as extended target */
3862           content =
3863             element_info[element].change_page[i].target_content.e[xx][yy];
3864
3865           is_player = ELEM_IS_PLAYER(content);
3866
3867           if (is_player && (found_rating < 1 ||
3868                             (found_rating == 1 && element < found_element)))
3869           {
3870             start_x = x + xx - 1;
3871             start_y = y + yy - 1;
3872
3873             found_rating = 1;
3874             found_element = element;
3875           }
3876         }
3877       }
3878     }
3879
3880     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3881                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3882                 start_x - MIDPOSX);
3883
3884     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3885                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3886                 start_y - MIDPOSY);
3887   }
3888   else
3889   {
3890     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3891                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3892                 local_player->jx - MIDPOSX);
3893
3894     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3895                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3896                 local_player->jy - MIDPOSY);
3897   }
3898
3899   /* !!! FIX THIS (START) !!! */
3900   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3901   {
3902     InitGameEngine_EM();
3903   }
3904   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3905   {
3906     InitGameEngine_SP();
3907   }
3908   else
3909   {
3910     DrawLevel(REDRAW_FIELD);
3911     DrawAllPlayers();
3912
3913     /* after drawing the level, correct some elements */
3914     if (game.timegate_time_left == 0)
3915       CloseAllOpenTimegates();
3916   }
3917
3918   /* blit playfield from scroll buffer to normal back buffer for fading in */
3919   BlitScreenToBitmap(backbuffer);
3920   /* !!! FIX THIS (END) !!! */
3921
3922   FadeIn(REDRAW_FIELD);
3923
3924 #if 1
3925   // full screen redraw is required at this point in the following cases:
3926   // - special editor door undrawn when game was started from level editor
3927   // - drawing area (playfield) was changed and has to be removed completely
3928   redraw_mask = REDRAW_ALL;
3929   BackToFront();
3930 #endif
3931
3932   if (!game.restart_level)
3933   {
3934     /* copy default game door content to main double buffer */
3935
3936     /* !!! CHECK AGAIN !!! */
3937     SetPanelBackground();
3938     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3939     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3940   }
3941
3942   SetPanelBackground();
3943   SetDrawBackgroundMask(REDRAW_DOOR_1);
3944
3945   UpdateAndDisplayGameControlValues();
3946
3947   if (!game.restart_level)
3948   {
3949     UnmapGameButtons();
3950     UnmapTapeButtons();
3951     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3952     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3953     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3954     MapGameButtons();
3955     MapTapeButtons();
3956
3957     /* copy actual game door content to door double buffer for OpenDoor() */
3958     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3959
3960     OpenDoor(DOOR_OPEN_ALL);
3961
3962     PlaySound(SND_GAME_STARTING);
3963
3964     if (setup.sound_music)
3965       PlayLevelMusic();
3966
3967     KeyboardAutoRepeatOffUnlessAutoplay();
3968
3969 #if DEBUG_INIT_PLAYER
3970     if (options.debug)
3971     {
3972       printf("Player status (final):\n");
3973
3974       for (i = 0; i < MAX_PLAYERS; i++)
3975       {
3976         struct PlayerInfo *player = &stored_player[i];
3977
3978         printf("- player %d: present == %d, connected == %d, active == %d",
3979                i + 1,
3980                player->present,
3981                player->connected,
3982                player->active);
3983
3984         if (local_player == player)
3985           printf(" (local player)");
3986
3987         printf("\n");
3988       }
3989     }
3990 #endif
3991   }
3992
3993   UnmapAllGadgets();
3994
3995   MapGameButtons();
3996   MapTapeButtons();
3997
3998   if (!game.restart_level && !tape.playing)
3999   {
4000     LevelStats_incPlayed(level_nr);
4001
4002     SaveLevelSetup_SeriesInfo();
4003   }
4004
4005   game.restart_level = FALSE;
4006
4007   SaveEngineSnapshotToListInitial();
4008 }
4009
4010 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4011 {
4012   /* this is used for non-R'n'D game engines to update certain engine values */
4013
4014   /* needed to determine if sounds are played within the visible screen area */
4015   scroll_x = actual_scroll_x;
4016   scroll_y = actual_scroll_y;
4017 }
4018
4019 void InitMovDir(int x, int y)
4020 {
4021   int i, element = Feld[x][y];
4022   static int xy[4][2] =
4023   {
4024     {  0, +1 },
4025     { +1,  0 },
4026     {  0, -1 },
4027     { -1,  0 }
4028   };
4029   static int direction[3][4] =
4030   {
4031     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4032     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4033     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4034   };
4035
4036   switch (element)
4037   {
4038     case EL_BUG_RIGHT:
4039     case EL_BUG_UP:
4040     case EL_BUG_LEFT:
4041     case EL_BUG_DOWN:
4042       Feld[x][y] = EL_BUG;
4043       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4044       break;
4045
4046     case EL_SPACESHIP_RIGHT:
4047     case EL_SPACESHIP_UP:
4048     case EL_SPACESHIP_LEFT:
4049     case EL_SPACESHIP_DOWN:
4050       Feld[x][y] = EL_SPACESHIP;
4051       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4052       break;
4053
4054     case EL_BD_BUTTERFLY_RIGHT:
4055     case EL_BD_BUTTERFLY_UP:
4056     case EL_BD_BUTTERFLY_LEFT:
4057     case EL_BD_BUTTERFLY_DOWN:
4058       Feld[x][y] = EL_BD_BUTTERFLY;
4059       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4060       break;
4061
4062     case EL_BD_FIREFLY_RIGHT:
4063     case EL_BD_FIREFLY_UP:
4064     case EL_BD_FIREFLY_LEFT:
4065     case EL_BD_FIREFLY_DOWN:
4066       Feld[x][y] = EL_BD_FIREFLY;
4067       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4068       break;
4069
4070     case EL_PACMAN_RIGHT:
4071     case EL_PACMAN_UP:
4072     case EL_PACMAN_LEFT:
4073     case EL_PACMAN_DOWN:
4074       Feld[x][y] = EL_PACMAN;
4075       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4076       break;
4077
4078     case EL_YAMYAM_LEFT:
4079     case EL_YAMYAM_RIGHT:
4080     case EL_YAMYAM_UP:
4081     case EL_YAMYAM_DOWN:
4082       Feld[x][y] = EL_YAMYAM;
4083       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4084       break;
4085
4086     case EL_SP_SNIKSNAK:
4087       MovDir[x][y] = MV_UP;
4088       break;
4089
4090     case EL_SP_ELECTRON:
4091       MovDir[x][y] = MV_LEFT;
4092       break;
4093
4094     case EL_MOLE_LEFT:
4095     case EL_MOLE_RIGHT:
4096     case EL_MOLE_UP:
4097     case EL_MOLE_DOWN:
4098       Feld[x][y] = EL_MOLE;
4099       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4100       break;
4101
4102     default:
4103       if (IS_CUSTOM_ELEMENT(element))
4104       {
4105         struct ElementInfo *ei = &element_info[element];
4106         int move_direction_initial = ei->move_direction_initial;
4107         int move_pattern = ei->move_pattern;
4108
4109         if (move_direction_initial == MV_START_PREVIOUS)
4110         {
4111           if (MovDir[x][y] != MV_NONE)
4112             return;
4113
4114           move_direction_initial = MV_START_AUTOMATIC;
4115         }
4116
4117         if (move_direction_initial == MV_START_RANDOM)
4118           MovDir[x][y] = 1 << RND(4);
4119         else if (move_direction_initial & MV_ANY_DIRECTION)
4120           MovDir[x][y] = move_direction_initial;
4121         else if (move_pattern == MV_ALL_DIRECTIONS ||
4122                  move_pattern == MV_TURNING_LEFT ||
4123                  move_pattern == MV_TURNING_RIGHT ||
4124                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4125                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4126                  move_pattern == MV_TURNING_RANDOM)
4127           MovDir[x][y] = 1 << RND(4);
4128         else if (move_pattern == MV_HORIZONTAL)
4129           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4130         else if (move_pattern == MV_VERTICAL)
4131           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4132         else if (move_pattern & MV_ANY_DIRECTION)
4133           MovDir[x][y] = element_info[element].move_pattern;
4134         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4135                  move_pattern == MV_ALONG_RIGHT_SIDE)
4136         {
4137           /* use random direction as default start direction */
4138           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4139             MovDir[x][y] = 1 << RND(4);
4140
4141           for (i = 0; i < NUM_DIRECTIONS; i++)
4142           {
4143             int x1 = x + xy[i][0];
4144             int y1 = y + xy[i][1];
4145
4146             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4147             {
4148               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4149                 MovDir[x][y] = direction[0][i];
4150               else
4151                 MovDir[x][y] = direction[1][i];
4152
4153               break;
4154             }
4155           }
4156         }                
4157       }
4158       else
4159       {
4160         MovDir[x][y] = 1 << RND(4);
4161
4162         if (element != EL_BUG &&
4163             element != EL_SPACESHIP &&
4164             element != EL_BD_BUTTERFLY &&
4165             element != EL_BD_FIREFLY)
4166           break;
4167
4168         for (i = 0; i < NUM_DIRECTIONS; i++)
4169         {
4170           int x1 = x + xy[i][0];
4171           int y1 = y + xy[i][1];
4172
4173           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4174           {
4175             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4176             {
4177               MovDir[x][y] = direction[0][i];
4178               break;
4179             }
4180             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4181                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4182             {
4183               MovDir[x][y] = direction[1][i];
4184               break;
4185             }
4186           }
4187         }
4188       }
4189       break;
4190   }
4191
4192   GfxDir[x][y] = MovDir[x][y];
4193 }
4194
4195 void InitAmoebaNr(int x, int y)
4196 {
4197   int i;
4198   int group_nr = AmoebeNachbarNr(x, y);
4199
4200   if (group_nr == 0)
4201   {
4202     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4203     {
4204       if (AmoebaCnt[i] == 0)
4205       {
4206         group_nr = i;
4207         break;
4208       }
4209     }
4210   }
4211
4212   AmoebaNr[x][y] = group_nr;
4213   AmoebaCnt[group_nr]++;
4214   AmoebaCnt2[group_nr]++;
4215 }
4216
4217 static void PlayerWins(struct PlayerInfo *player)
4218 {
4219   player->LevelSolved = TRUE;
4220   player->GameOver = TRUE;
4221
4222   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4223                          level.native_em_level->lev->score : player->score);
4224
4225   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4226                                       TimeLeft);
4227   player->LevelSolved_CountingScore = player->score_final;
4228 }
4229
4230 void GameWon()
4231 {
4232   static int time, time_final;
4233   static int score, score_final;
4234   static int game_over_delay_1 = 0;
4235   static int game_over_delay_2 = 0;
4236   int game_over_delay_value_1 = 50;
4237   int game_over_delay_value_2 = 50;
4238
4239   if (!local_player->LevelSolved_GameWon)
4240   {
4241     int i;
4242
4243     /* do not start end game actions before the player stops moving (to exit) */
4244     if (local_player->MovPos)
4245       return;
4246
4247     local_player->LevelSolved_GameWon = TRUE;
4248     local_player->LevelSolved_SaveTape = tape.recording;
4249     local_player->LevelSolved_SaveScore = !tape.playing;
4250
4251     if (!tape.playing)
4252     {
4253       LevelStats_incSolved(level_nr);
4254
4255       SaveLevelSetup_SeriesInfo();
4256     }
4257
4258     if (tape.auto_play)         /* tape might already be stopped here */
4259       tape.auto_play_level_solved = TRUE;
4260
4261     TapeStop();
4262
4263     game_over_delay_1 = game_over_delay_value_1;
4264     game_over_delay_2 = game_over_delay_value_2;
4265
4266     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4267     score = score_final = local_player->score_final;
4268
4269     if (TimeLeft > 0)
4270     {
4271       time_final = 0;
4272       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4273     }
4274     else if (game.no_time_limit && TimePlayed < 999)
4275     {
4276       time_final = 999;
4277       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4278     }
4279
4280     local_player->score_final = score_final;
4281
4282     if (level_editor_test_game)
4283     {
4284       time = time_final;
4285       score = score_final;
4286
4287       local_player->LevelSolved_CountingTime = time;
4288       local_player->LevelSolved_CountingScore = score;
4289
4290       game_panel_controls[GAME_PANEL_TIME].value = time;
4291       game_panel_controls[GAME_PANEL_SCORE].value = score;
4292
4293       DisplayGameControlValues();
4294     }
4295
4296     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4297     {
4298       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4299       {
4300         /* close exit door after last player */
4301         if ((AllPlayersGone &&
4302              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4303               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4304               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4305             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4306             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4307         {
4308           int element = Feld[ExitX][ExitY];
4309
4310           Feld[ExitX][ExitY] =
4311             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4312              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4313              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4314              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4315              EL_EM_STEEL_EXIT_CLOSING);
4316
4317           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4318         }
4319
4320         /* player disappears */
4321         DrawLevelField(ExitX, ExitY);
4322       }
4323
4324       for (i = 0; i < MAX_PLAYERS; i++)
4325       {
4326         struct PlayerInfo *player = &stored_player[i];
4327
4328         if (player->present)
4329         {
4330           RemovePlayer(player);
4331
4332           /* player disappears */
4333           DrawLevelField(player->jx, player->jy);
4334         }
4335       }
4336     }
4337
4338     PlaySound(SND_GAME_WINNING);
4339   }
4340
4341   if (game_over_delay_1 > 0)
4342   {
4343     game_over_delay_1--;
4344
4345     return;
4346   }
4347
4348   if (time != time_final)
4349   {
4350     int time_to_go = ABS(time_final - time);
4351     int time_count_dir = (time < time_final ? +1 : -1);
4352     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4353
4354     time  += time_count_steps * time_count_dir;
4355     score += time_count_steps * level.score[SC_TIME_BONUS];
4356
4357     local_player->LevelSolved_CountingTime = time;
4358     local_player->LevelSolved_CountingScore = score;
4359
4360     game_panel_controls[GAME_PANEL_TIME].value = time;
4361     game_panel_controls[GAME_PANEL_SCORE].value = score;
4362
4363     DisplayGameControlValues();
4364
4365     if (time == time_final)
4366       StopSound(SND_GAME_LEVELTIME_BONUS);
4367     else if (setup.sound_loops)
4368       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4369     else
4370       PlaySound(SND_GAME_LEVELTIME_BONUS);
4371
4372     return;
4373   }
4374
4375   local_player->LevelSolved_PanelOff = TRUE;
4376
4377   if (game_over_delay_2 > 0)
4378   {
4379     game_over_delay_2--;
4380
4381     return;
4382   }
4383
4384   GameEnd();
4385 }
4386
4387 void GameEnd()
4388 {
4389   int hi_pos;
4390   boolean raise_level = FALSE;
4391
4392   local_player->LevelSolved_GameEnd = TRUE;
4393
4394   if (!global.use_envelope_request)
4395     CloseDoor(DOOR_CLOSE_1);
4396
4397   if (local_player->LevelSolved_SaveTape)
4398   {
4399     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4400   }
4401
4402   CloseDoor(DOOR_CLOSE_ALL);
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     /* needed if different viewport properties defined for scores */
4439     ChangeViewportPropertiesIfNeeded();
4440
4441     DrawHallOfFame(hi_pos);
4442
4443     if (raise_level)
4444     {
4445       level_nr++;
4446       TapeErase();
4447     }
4448   }
4449   else
4450   {
4451     FadeOut(REDRAW_FIELD);
4452
4453     game_status = GAME_MODE_MAIN;
4454
4455     if (raise_level)
4456     {
4457       level_nr++;
4458       TapeErase();
4459     }
4460
4461     DrawAndFadeInMainMenu(REDRAW_FIELD);
4462   }
4463 }
4464
4465 int NewHiScore()
4466 {
4467   int k, l;
4468   int position = -1;
4469
4470   LoadScore(level_nr);
4471
4472   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4473       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4474     return -1;
4475
4476   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4477   {
4478     if (local_player->score_final > highscore[k].Score)
4479     {
4480       /* player has made it to the hall of fame */
4481
4482       if (k < MAX_SCORE_ENTRIES - 1)
4483       {
4484         int m = MAX_SCORE_ENTRIES - 1;
4485
4486 #ifdef ONE_PER_NAME
4487         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4488           if (strEqual(setup.player_name, highscore[l].Name))
4489             m = l;
4490         if (m == k)     /* player's new highscore overwrites his old one */
4491           goto put_into_list;
4492 #endif
4493
4494         for (l = m; l > k; l--)
4495         {
4496           strcpy(highscore[l].Name, highscore[l - 1].Name);
4497           highscore[l].Score = highscore[l - 1].Score;
4498         }
4499       }
4500
4501 #ifdef ONE_PER_NAME
4502       put_into_list:
4503 #endif
4504       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4505       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4506       highscore[k].Score = local_player->score_final; 
4507       position = k;
4508       break;
4509     }
4510
4511 #ifdef ONE_PER_NAME
4512     else if (!strncmp(setup.player_name, highscore[k].Name,
4513                       MAX_PLAYER_NAME_LEN))
4514       break;    /* player already there with a higher score */
4515 #endif
4516
4517   }
4518
4519   if (position >= 0) 
4520     SaveScore(level_nr);
4521
4522   return position;
4523 }
4524
4525 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4526 {
4527   int element = Feld[x][y];
4528   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4529   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4530   int horiz_move = (dx != 0);
4531   int sign = (horiz_move ? dx : dy);
4532   int step = sign * element_info[element].move_stepsize;
4533
4534   /* special values for move stepsize for spring and things on conveyor belt */
4535   if (horiz_move)
4536   {
4537     if (CAN_FALL(element) &&
4538         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4539       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4540     else if (element == EL_SPRING)
4541       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4542   }
4543
4544   return step;
4545 }
4546
4547 inline static int getElementMoveStepsize(int x, int y)
4548 {
4549   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4550 }
4551
4552 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4553 {
4554   if (player->GfxAction != action || player->GfxDir != dir)
4555   {
4556     player->GfxAction = action;
4557     player->GfxDir = dir;
4558     player->Frame = 0;
4559     player->StepFrame = 0;
4560   }
4561 }
4562
4563 static void ResetGfxFrame(int x, int y, boolean redraw)
4564 {
4565   int element = Feld[x][y];
4566   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4567   int last_gfx_frame = GfxFrame[x][y];
4568
4569   if (graphic_info[graphic].anim_global_sync)
4570     GfxFrame[x][y] = FrameCounter;
4571   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4572     GfxFrame[x][y] = CustomValue[x][y];
4573   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4574     GfxFrame[x][y] = element_info[element].collect_score;
4575   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4576     GfxFrame[x][y] = ChangeDelay[x][y];
4577
4578   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4579     DrawLevelGraphicAnimation(x, y, graphic);
4580 }
4581
4582 static void ResetGfxAnimation(int x, int y)
4583 {
4584   GfxAction[x][y] = ACTION_DEFAULT;
4585   GfxDir[x][y] = MovDir[x][y];
4586   GfxFrame[x][y] = 0;
4587
4588   ResetGfxFrame(x, y, FALSE);
4589 }
4590
4591 static void ResetRandomAnimationValue(int x, int y)
4592 {
4593   GfxRandom[x][y] = INIT_GFX_RANDOM();
4594 }
4595
4596 void InitMovingField(int x, int y, int direction)
4597 {
4598   int element = Feld[x][y];
4599   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4600   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4601   int newx = x + dx;
4602   int newy = y + dy;
4603   boolean is_moving_before, is_moving_after;
4604
4605   /* check if element was/is moving or being moved before/after mode change */
4606   is_moving_before = (WasJustMoving[x][y] != 0);
4607   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4608
4609   /* reset animation only for moving elements which change direction of moving
4610      or which just started or stopped moving
4611      (else CEs with property "can move" / "not moving" are reset each frame) */
4612   if (is_moving_before != is_moving_after ||
4613       direction != MovDir[x][y])
4614     ResetGfxAnimation(x, y);
4615
4616   MovDir[x][y] = direction;
4617   GfxDir[x][y] = direction;
4618
4619   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4620                      direction == MV_DOWN && CAN_FALL(element) ?
4621                      ACTION_FALLING : ACTION_MOVING);
4622
4623   /* this is needed for CEs with property "can move" / "not moving" */
4624
4625   if (is_moving_after)
4626   {
4627     if (Feld[newx][newy] == EL_EMPTY)
4628       Feld[newx][newy] = EL_BLOCKED;
4629
4630     MovDir[newx][newy] = MovDir[x][y];
4631
4632     CustomValue[newx][newy] = CustomValue[x][y];
4633
4634     GfxFrame[newx][newy] = GfxFrame[x][y];
4635     GfxRandom[newx][newy] = GfxRandom[x][y];
4636     GfxAction[newx][newy] = GfxAction[x][y];
4637     GfxDir[newx][newy] = GfxDir[x][y];
4638   }
4639 }
4640
4641 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4642 {
4643   int direction = MovDir[x][y];
4644   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4645   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4646
4647   *goes_to_x = newx;
4648   *goes_to_y = newy;
4649 }
4650
4651 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4652 {
4653   int oldx = x, oldy = y;
4654   int direction = MovDir[x][y];
4655
4656   if (direction == MV_LEFT)
4657     oldx++;
4658   else if (direction == MV_RIGHT)
4659     oldx--;
4660   else if (direction == MV_UP)
4661     oldy++;
4662   else if (direction == MV_DOWN)
4663     oldy--;
4664
4665   *comes_from_x = oldx;
4666   *comes_from_y = oldy;
4667 }
4668
4669 int MovingOrBlocked2Element(int x, int y)
4670 {
4671   int element = Feld[x][y];
4672
4673   if (element == EL_BLOCKED)
4674   {
4675     int oldx, oldy;
4676
4677     Blocked2Moving(x, y, &oldx, &oldy);
4678     return Feld[oldx][oldy];
4679   }
4680   else
4681     return element;
4682 }
4683
4684 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4685 {
4686   /* like MovingOrBlocked2Element(), but if element is moving
4687      and (x,y) is the field the moving element is just leaving,
4688      return EL_BLOCKED instead of the element value */
4689   int element = Feld[x][y];
4690
4691   if (IS_MOVING(x, y))
4692   {
4693     if (element == EL_BLOCKED)
4694     {
4695       int oldx, oldy;
4696
4697       Blocked2Moving(x, y, &oldx, &oldy);
4698       return Feld[oldx][oldy];
4699     }
4700     else
4701       return EL_BLOCKED;
4702   }
4703   else
4704     return element;
4705 }
4706
4707 static void RemoveField(int x, int y)
4708 {
4709   Feld[x][y] = EL_EMPTY;
4710
4711   MovPos[x][y] = 0;
4712   MovDir[x][y] = 0;
4713   MovDelay[x][y] = 0;
4714
4715   CustomValue[x][y] = 0;
4716
4717   AmoebaNr[x][y] = 0;
4718   ChangeDelay[x][y] = 0;
4719   ChangePage[x][y] = -1;
4720   Pushed[x][y] = FALSE;
4721
4722   GfxElement[x][y] = EL_UNDEFINED;
4723   GfxAction[x][y] = ACTION_DEFAULT;
4724   GfxDir[x][y] = MV_NONE;
4725 }
4726
4727 void RemoveMovingField(int x, int y)
4728 {
4729   int oldx = x, oldy = y, newx = x, newy = y;
4730   int element = Feld[x][y];
4731   int next_element = EL_UNDEFINED;
4732
4733   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4734     return;
4735
4736   if (IS_MOVING(x, y))
4737   {
4738     Moving2Blocked(x, y, &newx, &newy);
4739
4740     if (Feld[newx][newy] != EL_BLOCKED)
4741     {
4742       /* element is moving, but target field is not free (blocked), but
4743          already occupied by something different (example: acid pool);
4744          in this case, only remove the moving field, but not the target */
4745
4746       RemoveField(oldx, oldy);
4747
4748       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4749
4750       TEST_DrawLevelField(oldx, oldy);
4751
4752       return;
4753     }
4754   }
4755   else if (element == EL_BLOCKED)
4756   {
4757     Blocked2Moving(x, y, &oldx, &oldy);
4758     if (!IS_MOVING(oldx, oldy))
4759       return;
4760   }
4761
4762   if (element == EL_BLOCKED &&
4763       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4764        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4765        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4766        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4767        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4768        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4769     next_element = get_next_element(Feld[oldx][oldy]);
4770
4771   RemoveField(oldx, oldy);
4772   RemoveField(newx, newy);
4773
4774   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4775
4776   if (next_element != EL_UNDEFINED)
4777     Feld[oldx][oldy] = next_element;
4778
4779   TEST_DrawLevelField(oldx, oldy);
4780   TEST_DrawLevelField(newx, newy);
4781 }
4782
4783 void DrawDynamite(int x, int y)
4784 {
4785   int sx = SCREENX(x), sy = SCREENY(y);
4786   int graphic = el2img(Feld[x][y]);
4787   int frame;
4788
4789   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4790     return;
4791
4792   if (IS_WALKABLE_INSIDE(Back[x][y]))
4793     return;
4794
4795   if (Back[x][y])
4796     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4797   else if (Store[x][y])
4798     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4799
4800   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4801
4802   if (Back[x][y] || Store[x][y])
4803     DrawGraphicThruMask(sx, sy, graphic, frame);
4804   else
4805     DrawGraphic(sx, sy, graphic, frame);
4806 }
4807
4808 void CheckDynamite(int x, int y)
4809 {
4810   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4811   {
4812     MovDelay[x][y]--;
4813
4814     if (MovDelay[x][y] != 0)
4815     {
4816       DrawDynamite(x, y);
4817       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4818
4819       return;
4820     }
4821   }
4822
4823   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4824
4825   Bang(x, y);
4826 }
4827
4828 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4829 {
4830   boolean num_checked_players = 0;
4831   int i;
4832
4833   for (i = 0; i < MAX_PLAYERS; i++)
4834   {
4835     if (stored_player[i].active)
4836     {
4837       int sx = stored_player[i].jx;
4838       int sy = stored_player[i].jy;
4839
4840       if (num_checked_players == 0)
4841       {
4842         *sx1 = *sx2 = sx;
4843         *sy1 = *sy2 = sy;
4844       }
4845       else
4846       {
4847         *sx1 = MIN(*sx1, sx);
4848         *sy1 = MIN(*sy1, sy);
4849         *sx2 = MAX(*sx2, sx);
4850         *sy2 = MAX(*sy2, sy);
4851       }
4852
4853       num_checked_players++;
4854     }
4855   }
4856 }
4857
4858 static boolean checkIfAllPlayersFitToScreen_RND()
4859 {
4860   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4861
4862   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4863
4864   return (sx2 - sx1 < SCR_FIELDX &&
4865           sy2 - sy1 < SCR_FIELDY);
4866 }
4867
4868 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4869 {
4870   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4871
4872   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4873
4874   *sx = (sx1 + sx2) / 2;
4875   *sy = (sy1 + sy2) / 2;
4876 }
4877
4878 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4879                         boolean center_screen, boolean quick_relocation)
4880 {
4881   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4882   boolean no_delay = (tape.warp_forward);
4883   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4884   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4885
4886   if (quick_relocation)
4887   {
4888     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4889     {
4890       if (!level.shifted_relocation || center_screen)
4891       {
4892         /* quick relocation (without scrolling), with centering of screen */
4893
4894         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4895                     x > SBX_Right + MIDPOSX ? SBX_Right :
4896                     x - MIDPOSX);
4897
4898         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4899                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4900                     y - MIDPOSY);
4901       }
4902       else
4903       {
4904         /* quick relocation (without scrolling), but do not center screen */
4905
4906         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4907                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4908                                old_x - MIDPOSX);
4909
4910         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4911                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4912                                old_y - MIDPOSY);
4913
4914         int offset_x = x + (scroll_x - center_scroll_x);
4915         int offset_y = y + (scroll_y - center_scroll_y);
4916
4917         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4918                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4919                     offset_x - MIDPOSX);
4920
4921         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4922                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4923                     offset_y - MIDPOSY);
4924       }
4925     }
4926     else
4927     {
4928       if (!level.shifted_relocation || center_screen)
4929       {
4930         /* quick relocation (without scrolling), with centering of screen */
4931
4932         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4933                     x > SBX_Right + MIDPOSX ? SBX_Right :
4934                     x - MIDPOSX);
4935
4936         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4937                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4938                     y - MIDPOSY);
4939       }
4940       else
4941       {
4942         /* quick relocation (without scrolling), but do not center screen */
4943
4944         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4945                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4946                                old_x - MIDPOSX);
4947
4948         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4949                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4950                                old_y - MIDPOSY);
4951
4952         int offset_x = x + (scroll_x - center_scroll_x);
4953         int offset_y = y + (scroll_y - center_scroll_y);
4954
4955         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4956                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4957                     offset_x - MIDPOSX);
4958
4959         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4960                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4961                     offset_y - MIDPOSY);
4962       }
4963     }
4964
4965     RedrawPlayfield(TRUE, 0,0,0,0);
4966   }
4967   else
4968   {
4969     int scroll_xx, scroll_yy;
4970
4971     if (!level.shifted_relocation || center_screen)
4972     {
4973       /* visible relocation (with scrolling), with centering of screen */
4974
4975       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4976                    x > SBX_Right + MIDPOSX ? SBX_Right :
4977                    x - MIDPOSX);
4978
4979       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4980                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4981                    y - MIDPOSY);
4982     }
4983     else
4984     {
4985       /* visible relocation (with scrolling), but do not center screen */
4986
4987       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4988                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4989                              old_x - MIDPOSX);
4990
4991       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4992                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4993                              old_y - MIDPOSY);
4994
4995       int offset_x = x + (scroll_x - center_scroll_x);
4996       int offset_y = y + (scroll_y - center_scroll_y);
4997
4998       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4999                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5000                    offset_x - MIDPOSX);
5001
5002       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5003                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5004                    offset_y - MIDPOSY);
5005     }
5006
5007
5008     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5009
5010     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5011     {
5012       int dx = 0, dy = 0;
5013       int fx = FX, fy = FY;
5014
5015       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5016       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5017
5018       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5019         break;
5020
5021       scroll_x -= dx;
5022       scroll_y -= dy;
5023
5024       fx += dx * TILEX / 2;
5025       fy += dy * TILEY / 2;
5026
5027       ScrollLevel(dx, dy);
5028       DrawAllPlayers();
5029
5030       /* scroll in two steps of half tile size to make things smoother */
5031       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5032       Delay(wait_delay_value);
5033
5034       /* scroll second step to align at full tile size */
5035       BackToFront();
5036       Delay(wait_delay_value);
5037     }
5038
5039     DrawAllPlayers();
5040     BackToFront();
5041     Delay(wait_delay_value);
5042   }
5043 }
5044
5045 void RelocatePlayer(int jx, int jy, int el_player_raw)
5046 {
5047   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5048   int player_nr = GET_PLAYER_NR(el_player);
5049   struct PlayerInfo *player = &stored_player[player_nr];
5050   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5051   boolean no_delay = (tape.warp_forward);
5052   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5053   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5054   int old_jx = player->jx;
5055   int old_jy = player->jy;
5056   int old_element = Feld[old_jx][old_jy];
5057   int element = Feld[jx][jy];
5058   boolean player_relocated = (old_jx != jx || old_jy != jy);
5059
5060   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5061   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5062   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5063   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5064   int leave_side_horiz = move_dir_horiz;
5065   int leave_side_vert  = move_dir_vert;
5066   int enter_side = enter_side_horiz | enter_side_vert;
5067   int leave_side = leave_side_horiz | leave_side_vert;
5068
5069   if (player->GameOver)         /* do not reanimate dead player */
5070     return;
5071
5072   if (!player_relocated)        /* no need to relocate the player */
5073     return;
5074
5075   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5076   {
5077     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5078     DrawLevelField(jx, jy);
5079   }
5080
5081   if (player->present)
5082   {
5083     while (player->MovPos)
5084     {
5085       ScrollPlayer(player, SCROLL_GO_ON);
5086       ScrollScreen(NULL, SCROLL_GO_ON);
5087
5088       AdvanceFrameAndPlayerCounters(player->index_nr);
5089
5090       DrawPlayer(player);
5091
5092       BackToFront();
5093       Delay(wait_delay_value);
5094     }
5095
5096     DrawPlayer(player);         /* needed here only to cleanup last field */
5097     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5098
5099     player->is_moving = FALSE;
5100   }
5101
5102   if (IS_CUSTOM_ELEMENT(old_element))
5103     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5104                                CE_LEFT_BY_PLAYER,
5105                                player->index_bit, leave_side);
5106
5107   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5108                                       CE_PLAYER_LEAVES_X,
5109                                       player->index_bit, leave_side);
5110
5111   Feld[jx][jy] = el_player;
5112   InitPlayerField(jx, jy, el_player, TRUE);
5113
5114   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5115      possible that the relocation target field did not contain a player element,
5116      but a walkable element, to which the new player was relocated -- in this
5117      case, restore that (already initialized!) element on the player field */
5118   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5119   {
5120     Feld[jx][jy] = element;     /* restore previously existing element */
5121   }
5122
5123   /* only visually relocate centered player */
5124   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5125                      FALSE, level.instant_relocation);
5126
5127   TestIfPlayerTouchesBadThing(jx, jy);
5128   TestIfPlayerTouchesCustomElement(jx, jy);
5129
5130   if (IS_CUSTOM_ELEMENT(element))
5131     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5132                                player->index_bit, enter_side);
5133
5134   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5135                                       player->index_bit, enter_side);
5136
5137   if (player->is_switching)
5138   {
5139     /* ensure that relocation while still switching an element does not cause
5140        a new element to be treated as also switched directly after relocation
5141        (this is important for teleporter switches that teleport the player to
5142        a place where another teleporter switch is in the same direction, which
5143        would then incorrectly be treated as immediately switched before the
5144        direction key that caused the switch was released) */
5145
5146     player->switch_x += jx - old_jx;
5147     player->switch_y += jy - old_jy;
5148   }
5149 }
5150
5151 void Explode(int ex, int ey, int phase, int mode)
5152 {
5153   int x, y;
5154   int last_phase;
5155   int border_element;
5156
5157   /* !!! eliminate this variable !!! */
5158   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5159
5160   if (game.explosions_delayed)
5161   {
5162     ExplodeField[ex][ey] = mode;
5163     return;
5164   }
5165
5166   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5167   {
5168     int center_element = Feld[ex][ey];
5169     int artwork_element, explosion_element;     /* set these values later */
5170
5171     /* remove things displayed in background while burning dynamite */
5172     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5173       Back[ex][ey] = 0;
5174
5175     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5176     {
5177       /* put moving element to center field (and let it explode there) */
5178       center_element = MovingOrBlocked2Element(ex, ey);
5179       RemoveMovingField(ex, ey);
5180       Feld[ex][ey] = center_element;
5181     }
5182
5183     /* now "center_element" is finally determined -- set related values now */
5184     artwork_element = center_element;           /* for custom player artwork */
5185     explosion_element = center_element;         /* for custom player artwork */
5186
5187     if (IS_PLAYER(ex, ey))
5188     {
5189       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5190
5191       artwork_element = stored_player[player_nr].artwork_element;
5192
5193       if (level.use_explosion_element[player_nr])
5194       {
5195         explosion_element = level.explosion_element[player_nr];
5196         artwork_element = explosion_element;
5197       }
5198     }
5199
5200     if (mode == EX_TYPE_NORMAL ||
5201         mode == EX_TYPE_CENTER ||
5202         mode == EX_TYPE_CROSS)
5203       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5204
5205     last_phase = element_info[explosion_element].explosion_delay + 1;
5206
5207     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5208     {
5209       int xx = x - ex + 1;
5210       int yy = y - ey + 1;
5211       int element;
5212
5213       if (!IN_LEV_FIELD(x, y) ||
5214           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5215           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5216         continue;
5217
5218       element = Feld[x][y];
5219
5220       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5221       {
5222         element = MovingOrBlocked2Element(x, y);
5223
5224         if (!IS_EXPLOSION_PROOF(element))
5225           RemoveMovingField(x, y);
5226       }
5227
5228       /* indestructible elements can only explode in center (but not flames) */
5229       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5230                                            mode == EX_TYPE_BORDER)) ||
5231           element == EL_FLAMES)
5232         continue;
5233
5234       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5235          behaviour, for example when touching a yamyam that explodes to rocks
5236          with active deadly shield, a rock is created under the player !!! */
5237       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5238 #if 0
5239       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5240           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5241            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5242 #else
5243       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5244 #endif
5245       {
5246         if (IS_ACTIVE_BOMB(element))
5247         {
5248           /* re-activate things under the bomb like gate or penguin */
5249           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5250           Back[x][y] = 0;
5251         }
5252
5253         continue;
5254       }
5255
5256       /* save walkable background elements while explosion on same tile */
5257       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5258           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5259         Back[x][y] = element;
5260
5261       /* ignite explodable elements reached by other explosion */
5262       if (element == EL_EXPLOSION)
5263         element = Store2[x][y];
5264
5265       if (AmoebaNr[x][y] &&
5266           (element == EL_AMOEBA_FULL ||
5267            element == EL_BD_AMOEBA ||
5268            element == EL_AMOEBA_GROWING))
5269       {
5270         AmoebaCnt[AmoebaNr[x][y]]--;
5271         AmoebaCnt2[AmoebaNr[x][y]]--;
5272       }
5273
5274       RemoveField(x, y);
5275
5276       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5277       {
5278         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5279
5280         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5281
5282         if (PLAYERINFO(ex, ey)->use_murphy)
5283           Store[x][y] = EL_EMPTY;
5284       }
5285
5286       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5287          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5288       else if (ELEM_IS_PLAYER(center_element))
5289         Store[x][y] = EL_EMPTY;
5290       else if (center_element == EL_YAMYAM)
5291         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5292       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5293         Store[x][y] = element_info[center_element].content.e[xx][yy];
5294 #if 1
5295       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5296          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5297          otherwise) -- FIX THIS !!! */
5298       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5299         Store[x][y] = element_info[element].content.e[1][1];
5300 #else
5301       else if (!CAN_EXPLODE(element))
5302         Store[x][y] = element_info[element].content.e[1][1];
5303 #endif
5304       else
5305         Store[x][y] = EL_EMPTY;
5306
5307       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5308           center_element == EL_AMOEBA_TO_DIAMOND)
5309         Store2[x][y] = element;
5310
5311       Feld[x][y] = EL_EXPLOSION;
5312       GfxElement[x][y] = artwork_element;
5313
5314       ExplodePhase[x][y] = 1;
5315       ExplodeDelay[x][y] = last_phase;
5316
5317       Stop[x][y] = TRUE;
5318     }
5319
5320     if (center_element == EL_YAMYAM)
5321       game.yamyam_content_nr =
5322         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5323
5324     return;
5325   }
5326
5327   if (Stop[ex][ey])
5328     return;
5329
5330   x = ex;
5331   y = ey;
5332
5333   if (phase == 1)
5334     GfxFrame[x][y] = 0;         /* restart explosion animation */
5335
5336   last_phase = ExplodeDelay[x][y];
5337
5338   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5339
5340   /* this can happen if the player leaves an explosion just in time */
5341   if (GfxElement[x][y] == EL_UNDEFINED)
5342     GfxElement[x][y] = EL_EMPTY;
5343
5344   border_element = Store2[x][y];
5345   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5346     border_element = StorePlayer[x][y];
5347
5348   if (phase == element_info[border_element].ignition_delay ||
5349       phase == last_phase)
5350   {
5351     boolean border_explosion = FALSE;
5352
5353     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5354         !PLAYER_EXPLOSION_PROTECTED(x, y))
5355     {
5356       KillPlayerUnlessExplosionProtected(x, y);
5357       border_explosion = TRUE;
5358     }
5359     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5360     {
5361       Feld[x][y] = Store2[x][y];
5362       Store2[x][y] = 0;
5363       Bang(x, y);
5364       border_explosion = TRUE;
5365     }
5366     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5367     {
5368       AmoebeUmwandeln(x, y);
5369       Store2[x][y] = 0;
5370       border_explosion = TRUE;
5371     }
5372
5373     /* if an element just explodes due to another explosion (chain-reaction),
5374        do not immediately end the new explosion when it was the last frame of
5375        the explosion (as it would be done in the following "if"-statement!) */
5376     if (border_explosion && phase == last_phase)
5377       return;
5378   }
5379
5380   if (phase == last_phase)
5381   {
5382     int element;
5383
5384     element = Feld[x][y] = Store[x][y];
5385     Store[x][y] = Store2[x][y] = 0;
5386     GfxElement[x][y] = EL_UNDEFINED;
5387
5388     /* player can escape from explosions and might therefore be still alive */
5389     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5390         element <= EL_PLAYER_IS_EXPLODING_4)
5391     {
5392       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5393       int explosion_element = EL_PLAYER_1 + player_nr;
5394       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5395       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5396
5397       if (level.use_explosion_element[player_nr])
5398         explosion_element = level.explosion_element[player_nr];
5399
5400       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5401                     element_info[explosion_element].content.e[xx][yy]);
5402     }
5403
5404     /* restore probably existing indestructible background element */
5405     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5406       element = Feld[x][y] = Back[x][y];
5407     Back[x][y] = 0;
5408
5409     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5410     GfxDir[x][y] = MV_NONE;
5411     ChangeDelay[x][y] = 0;
5412     ChangePage[x][y] = -1;
5413
5414     CustomValue[x][y] = 0;
5415
5416     InitField_WithBug2(x, y, FALSE);
5417
5418     TEST_DrawLevelField(x, y);
5419
5420     TestIfElementTouchesCustomElement(x, y);
5421
5422     if (GFX_CRUMBLED(element))
5423       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5424
5425     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5426       StorePlayer[x][y] = 0;
5427
5428     if (ELEM_IS_PLAYER(element))
5429       RelocatePlayer(x, y, element);
5430   }
5431   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5432   {
5433     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5434     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5435
5436     if (phase == delay)
5437       TEST_DrawLevelFieldCrumbled(x, y);
5438
5439     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5440     {
5441       DrawLevelElement(x, y, Back[x][y]);
5442       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5443     }
5444     else if (IS_WALKABLE_UNDER(Back[x][y]))
5445     {
5446       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5447       DrawLevelElementThruMask(x, y, Back[x][y]);
5448     }
5449     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5450       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5451   }
5452 }
5453
5454 void DynaExplode(int ex, int ey)
5455 {
5456   int i, j;
5457   int dynabomb_element = Feld[ex][ey];
5458   int dynabomb_size = 1;
5459   boolean dynabomb_xl = FALSE;
5460   struct PlayerInfo *player;
5461   static int xy[4][2] =
5462   {
5463     { 0, -1 },
5464     { -1, 0 },
5465     { +1, 0 },
5466     { 0, +1 }
5467   };
5468
5469   if (IS_ACTIVE_BOMB(dynabomb_element))
5470   {
5471     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5472     dynabomb_size = player->dynabomb_size;
5473     dynabomb_xl = player->dynabomb_xl;
5474     player->dynabombs_left++;
5475   }
5476
5477   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5478
5479   for (i = 0; i < NUM_DIRECTIONS; i++)
5480   {
5481     for (j = 1; j <= dynabomb_size; j++)
5482     {
5483       int x = ex + j * xy[i][0];
5484       int y = ey + j * xy[i][1];
5485       int element;
5486
5487       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5488         break;
5489
5490       element = Feld[x][y];
5491
5492       /* do not restart explosions of fields with active bombs */
5493       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5494         continue;
5495
5496       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5497
5498       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5499           !IS_DIGGABLE(element) && !dynabomb_xl)
5500         break;
5501     }
5502   }
5503 }
5504
5505 void Bang(int x, int y)
5506 {
5507   int element = MovingOrBlocked2Element(x, y);
5508   int explosion_type = EX_TYPE_NORMAL;
5509
5510   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5511   {
5512     struct PlayerInfo *player = PLAYERINFO(x, y);
5513
5514     element = Feld[x][y] = player->initial_element;
5515
5516     if (level.use_explosion_element[player->index_nr])
5517     {
5518       int explosion_element = level.explosion_element[player->index_nr];
5519
5520       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5521         explosion_type = EX_TYPE_CROSS;
5522       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5523         explosion_type = EX_TYPE_CENTER;
5524     }
5525   }
5526
5527   switch (element)
5528   {
5529     case EL_BUG:
5530     case EL_SPACESHIP:
5531     case EL_BD_BUTTERFLY:
5532     case EL_BD_FIREFLY:
5533     case EL_YAMYAM:
5534     case EL_DARK_YAMYAM:
5535     case EL_ROBOT:
5536     case EL_PACMAN:
5537     case EL_MOLE:
5538       RaiseScoreElement(element);
5539       break;
5540
5541     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5542     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5543     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5544     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5545     case EL_DYNABOMB_INCREASE_NUMBER:
5546     case EL_DYNABOMB_INCREASE_SIZE:
5547     case EL_DYNABOMB_INCREASE_POWER:
5548       explosion_type = EX_TYPE_DYNA;
5549       break;
5550
5551     case EL_DC_LANDMINE:
5552       explosion_type = EX_TYPE_CENTER;
5553       break;
5554
5555     case EL_PENGUIN:
5556     case EL_LAMP:
5557     case EL_LAMP_ACTIVE:
5558     case EL_AMOEBA_TO_DIAMOND:
5559       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5560         explosion_type = EX_TYPE_CENTER;
5561       break;
5562
5563     default:
5564       if (element_info[element].explosion_type == EXPLODES_CROSS)
5565         explosion_type = EX_TYPE_CROSS;
5566       else if (element_info[element].explosion_type == EXPLODES_1X1)
5567         explosion_type = EX_TYPE_CENTER;
5568       break;
5569   }
5570
5571   if (explosion_type == EX_TYPE_DYNA)
5572     DynaExplode(x, y);
5573   else
5574     Explode(x, y, EX_PHASE_START, explosion_type);
5575
5576   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5577 }
5578
5579 void SplashAcid(int x, int y)
5580 {
5581   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5582       (!IN_LEV_FIELD(x - 1, y - 2) ||
5583        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5584     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5585
5586   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5587       (!IN_LEV_FIELD(x + 1, y - 2) ||
5588        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5589     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5590
5591   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5592 }
5593
5594 static void InitBeltMovement()
5595 {
5596   static int belt_base_element[4] =
5597   {
5598     EL_CONVEYOR_BELT_1_LEFT,
5599     EL_CONVEYOR_BELT_2_LEFT,
5600     EL_CONVEYOR_BELT_3_LEFT,
5601     EL_CONVEYOR_BELT_4_LEFT
5602   };
5603   static int belt_base_active_element[4] =
5604   {
5605     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5606     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5607     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5608     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5609   };
5610
5611   int x, y, i, j;
5612
5613   /* set frame order for belt animation graphic according to belt direction */
5614   for (i = 0; i < NUM_BELTS; i++)
5615   {
5616     int belt_nr = i;
5617
5618     for (j = 0; j < NUM_BELT_PARTS; j++)
5619     {
5620       int element = belt_base_active_element[belt_nr] + j;
5621       int graphic_1 = el2img(element);
5622       int graphic_2 = el2panelimg(element);
5623
5624       if (game.belt_dir[i] == MV_LEFT)
5625       {
5626         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5627         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5628       }
5629       else
5630       {
5631         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5632         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5633       }
5634     }
5635   }
5636
5637   SCAN_PLAYFIELD(x, y)
5638   {
5639     int element = Feld[x][y];
5640
5641     for (i = 0; i < NUM_BELTS; i++)
5642     {
5643       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5644       {
5645         int e_belt_nr = getBeltNrFromBeltElement(element);
5646         int belt_nr = i;
5647
5648         if (e_belt_nr == belt_nr)
5649         {
5650           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5651
5652           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5653         }
5654       }
5655     }
5656   }
5657 }
5658
5659 static void ToggleBeltSwitch(int x, int y)
5660 {
5661   static int belt_base_element[4] =
5662   {
5663     EL_CONVEYOR_BELT_1_LEFT,
5664     EL_CONVEYOR_BELT_2_LEFT,
5665     EL_CONVEYOR_BELT_3_LEFT,
5666     EL_CONVEYOR_BELT_4_LEFT
5667   };
5668   static int belt_base_active_element[4] =
5669   {
5670     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5671     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5672     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5673     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5674   };
5675   static int belt_base_switch_element[4] =
5676   {
5677     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5678     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5679     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5680     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5681   };
5682   static int belt_move_dir[4] =
5683   {
5684     MV_LEFT,
5685     MV_NONE,
5686     MV_RIGHT,
5687     MV_NONE,
5688   };
5689
5690   int element = Feld[x][y];
5691   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5692   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5693   int belt_dir = belt_move_dir[belt_dir_nr];
5694   int xx, yy, i;
5695
5696   if (!IS_BELT_SWITCH(element))
5697     return;
5698
5699   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5700   game.belt_dir[belt_nr] = belt_dir;
5701
5702   if (belt_dir_nr == 3)
5703     belt_dir_nr = 1;
5704
5705   /* set frame order for belt animation graphic according to belt direction */
5706   for (i = 0; i < NUM_BELT_PARTS; i++)
5707   {
5708     int element = belt_base_active_element[belt_nr] + i;
5709     int graphic_1 = el2img(element);
5710     int graphic_2 = el2panelimg(element);
5711
5712     if (belt_dir == MV_LEFT)
5713     {
5714       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5715       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5716     }
5717     else
5718     {
5719       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5720       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5721     }
5722   }
5723
5724   SCAN_PLAYFIELD(xx, yy)
5725   {
5726     int element = Feld[xx][yy];
5727
5728     if (IS_BELT_SWITCH(element))
5729     {
5730       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5731
5732       if (e_belt_nr == belt_nr)
5733       {
5734         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5735         TEST_DrawLevelField(xx, yy);
5736       }
5737     }
5738     else if (IS_BELT(element) && belt_dir != MV_NONE)
5739     {
5740       int e_belt_nr = getBeltNrFromBeltElement(element);
5741
5742       if (e_belt_nr == belt_nr)
5743       {
5744         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5745
5746         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5747         TEST_DrawLevelField(xx, yy);
5748       }
5749     }
5750     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5751     {
5752       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5753
5754       if (e_belt_nr == belt_nr)
5755       {
5756         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5757
5758         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5759         TEST_DrawLevelField(xx, yy);
5760       }
5761     }
5762   }
5763 }
5764
5765 static void ToggleSwitchgateSwitch(int x, int y)
5766 {
5767   int xx, yy;
5768
5769   game.switchgate_pos = !game.switchgate_pos;
5770
5771   SCAN_PLAYFIELD(xx, yy)
5772   {
5773     int element = Feld[xx][yy];
5774
5775     if (element == EL_SWITCHGATE_SWITCH_UP)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5778       TEST_DrawLevelField(xx, yy);
5779     }
5780     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5781     {
5782       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5783       TEST_DrawLevelField(xx, yy);
5784     }
5785     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5786     {
5787       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5788       TEST_DrawLevelField(xx, yy);
5789     }
5790     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5791     {
5792       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5793       TEST_DrawLevelField(xx, yy);
5794     }
5795     else if (element == EL_SWITCHGATE_OPEN ||
5796              element == EL_SWITCHGATE_OPENING)
5797     {
5798       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5799
5800       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5801     }
5802     else if (element == EL_SWITCHGATE_CLOSED ||
5803              element == EL_SWITCHGATE_CLOSING)
5804     {
5805       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5806
5807       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5808     }
5809   }
5810 }
5811
5812 static int getInvisibleActiveFromInvisibleElement(int element)
5813 {
5814   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5815           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5816           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5817           element);
5818 }
5819
5820 static int getInvisibleFromInvisibleActiveElement(int element)
5821 {
5822   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5823           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5824           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5825           element);
5826 }
5827
5828 static void RedrawAllLightSwitchesAndInvisibleElements()
5829 {
5830   int x, y;
5831
5832   SCAN_PLAYFIELD(x, y)
5833   {
5834     int element = Feld[x][y];
5835
5836     if (element == EL_LIGHT_SWITCH &&
5837         game.light_time_left > 0)
5838     {
5839       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5840       TEST_DrawLevelField(x, y);
5841     }
5842     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5843              game.light_time_left == 0)
5844     {
5845       Feld[x][y] = EL_LIGHT_SWITCH;
5846       TEST_DrawLevelField(x, y);
5847     }
5848     else if (element == EL_EMC_DRIPPER &&
5849              game.light_time_left > 0)
5850     {
5851       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5852       TEST_DrawLevelField(x, y);
5853     }
5854     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5855              game.light_time_left == 0)
5856     {
5857       Feld[x][y] = EL_EMC_DRIPPER;
5858       TEST_DrawLevelField(x, y);
5859     }
5860     else if (element == EL_INVISIBLE_STEELWALL ||
5861              element == EL_INVISIBLE_WALL ||
5862              element == EL_INVISIBLE_SAND)
5863     {
5864       if (game.light_time_left > 0)
5865         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5866
5867       TEST_DrawLevelField(x, y);
5868
5869       /* uncrumble neighbour fields, if needed */
5870       if (element == EL_INVISIBLE_SAND)
5871         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5872     }
5873     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5874              element == EL_INVISIBLE_WALL_ACTIVE ||
5875              element == EL_INVISIBLE_SAND_ACTIVE)
5876     {
5877       if (game.light_time_left == 0)
5878         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5879
5880       TEST_DrawLevelField(x, y);
5881
5882       /* re-crumble neighbour fields, if needed */
5883       if (element == EL_INVISIBLE_SAND)
5884         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5885     }
5886   }
5887 }
5888
5889 static void RedrawAllInvisibleElementsForLenses()
5890 {
5891   int x, y;
5892
5893   SCAN_PLAYFIELD(x, y)
5894   {
5895     int element = Feld[x][y];
5896
5897     if (element == EL_EMC_DRIPPER &&
5898         game.lenses_time_left > 0)
5899     {
5900       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5901       TEST_DrawLevelField(x, y);
5902     }
5903     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5904              game.lenses_time_left == 0)
5905     {
5906       Feld[x][y] = EL_EMC_DRIPPER;
5907       TEST_DrawLevelField(x, y);
5908     }
5909     else if (element == EL_INVISIBLE_STEELWALL ||
5910              element == EL_INVISIBLE_WALL ||
5911              element == EL_INVISIBLE_SAND)
5912     {
5913       if (game.lenses_time_left > 0)
5914         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5915
5916       TEST_DrawLevelField(x, y);
5917
5918       /* uncrumble neighbour fields, if needed */
5919       if (element == EL_INVISIBLE_SAND)
5920         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5921     }
5922     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5923              element == EL_INVISIBLE_WALL_ACTIVE ||
5924              element == EL_INVISIBLE_SAND_ACTIVE)
5925     {
5926       if (game.lenses_time_left == 0)
5927         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5928
5929       TEST_DrawLevelField(x, y);
5930
5931       /* re-crumble neighbour fields, if needed */
5932       if (element == EL_INVISIBLE_SAND)
5933         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5934     }
5935   }
5936 }
5937
5938 static void RedrawAllInvisibleElementsForMagnifier()
5939 {
5940   int x, y;
5941
5942   SCAN_PLAYFIELD(x, y)
5943   {
5944     int element = Feld[x][y];
5945
5946     if (element == EL_EMC_FAKE_GRASS &&
5947         game.magnify_time_left > 0)
5948     {
5949       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5950       TEST_DrawLevelField(x, y);
5951     }
5952     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5953              game.magnify_time_left == 0)
5954     {
5955       Feld[x][y] = EL_EMC_FAKE_GRASS;
5956       TEST_DrawLevelField(x, y);
5957     }
5958     else if (IS_GATE_GRAY(element) &&
5959              game.magnify_time_left > 0)
5960     {
5961       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5962                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5963                     IS_EM_GATE_GRAY(element) ?
5964                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5965                     IS_EMC_GATE_GRAY(element) ?
5966                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5967                     IS_DC_GATE_GRAY(element) ?
5968                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5969                     element);
5970       TEST_DrawLevelField(x, y);
5971     }
5972     else if (IS_GATE_GRAY_ACTIVE(element) &&
5973              game.magnify_time_left == 0)
5974     {
5975       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5976                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5977                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5978                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5979                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5980                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5981                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5982                     EL_DC_GATE_WHITE_GRAY :
5983                     element);
5984       TEST_DrawLevelField(x, y);
5985     }
5986   }
5987 }
5988
5989 static void ToggleLightSwitch(int x, int y)
5990 {
5991   int element = Feld[x][y];
5992
5993   game.light_time_left =
5994     (element == EL_LIGHT_SWITCH ?
5995      level.time_light * FRAMES_PER_SECOND : 0);
5996
5997   RedrawAllLightSwitchesAndInvisibleElements();
5998 }
5999
6000 static void ActivateTimegateSwitch(int x, int y)
6001 {
6002   int xx, yy;
6003
6004   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6005
6006   SCAN_PLAYFIELD(xx, yy)
6007   {
6008     int element = Feld[xx][yy];
6009
6010     if (element == EL_TIMEGATE_CLOSED ||
6011         element == EL_TIMEGATE_CLOSING)
6012     {
6013       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6014       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6015     }
6016
6017     /*
6018     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6019     {
6020       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6021       TEST_DrawLevelField(xx, yy);
6022     }
6023     */
6024
6025   }
6026
6027   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6028                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6029 }
6030
6031 void Impact(int x, int y)
6032 {
6033   boolean last_line = (y == lev_fieldy - 1);
6034   boolean object_hit = FALSE;
6035   boolean impact = (last_line || object_hit);
6036   int element = Feld[x][y];
6037   int smashed = EL_STEELWALL;
6038
6039   if (!last_line)       /* check if element below was hit */
6040   {
6041     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6042       return;
6043
6044     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6045                                          MovDir[x][y + 1] != MV_DOWN ||
6046                                          MovPos[x][y + 1] <= TILEY / 2));
6047
6048     /* do not smash moving elements that left the smashed field in time */
6049     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6050         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6051       object_hit = FALSE;
6052
6053 #if USE_QUICKSAND_IMPACT_BUGFIX
6054     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6055     {
6056       RemoveMovingField(x, y + 1);
6057       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6058       Feld[x][y + 2] = EL_ROCK;
6059       TEST_DrawLevelField(x, y + 2);
6060
6061       object_hit = TRUE;
6062     }
6063
6064     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6065     {
6066       RemoveMovingField(x, y + 1);
6067       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6068       Feld[x][y + 2] = EL_ROCK;
6069       TEST_DrawLevelField(x, y + 2);
6070
6071       object_hit = TRUE;
6072     }
6073 #endif
6074
6075     if (object_hit)
6076       smashed = MovingOrBlocked2Element(x, y + 1);
6077
6078     impact = (last_line || object_hit);
6079   }
6080
6081   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6082   {
6083     SplashAcid(x, y + 1);
6084     return;
6085   }
6086
6087   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6088   /* only reset graphic animation if graphic really changes after impact */
6089   if (impact &&
6090       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6091   {
6092     ResetGfxAnimation(x, y);
6093     TEST_DrawLevelField(x, y);
6094   }
6095
6096   if (impact && CAN_EXPLODE_IMPACT(element))
6097   {
6098     Bang(x, y);
6099     return;
6100   }
6101   else if (impact && element == EL_PEARL &&
6102            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6103   {
6104     ResetGfxAnimation(x, y);
6105
6106     Feld[x][y] = EL_PEARL_BREAKING;
6107     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6108     return;
6109   }
6110   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6111   {
6112     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6113
6114     return;
6115   }
6116
6117   if (impact && element == EL_AMOEBA_DROP)
6118   {
6119     if (object_hit && IS_PLAYER(x, y + 1))
6120       KillPlayerUnlessEnemyProtected(x, y + 1);
6121     else if (object_hit && smashed == EL_PENGUIN)
6122       Bang(x, y + 1);
6123     else
6124     {
6125       Feld[x][y] = EL_AMOEBA_GROWING;
6126       Store[x][y] = EL_AMOEBA_WET;
6127
6128       ResetRandomAnimationValue(x, y);
6129     }
6130     return;
6131   }
6132
6133   if (object_hit)               /* check which object was hit */
6134   {
6135     if ((CAN_PASS_MAGIC_WALL(element) && 
6136          (smashed == EL_MAGIC_WALL ||
6137           smashed == EL_BD_MAGIC_WALL)) ||
6138         (CAN_PASS_DC_MAGIC_WALL(element) &&
6139          smashed == EL_DC_MAGIC_WALL))
6140     {
6141       int xx, yy;
6142       int activated_magic_wall =
6143         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6144          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6145          EL_DC_MAGIC_WALL_ACTIVE);
6146
6147       /* activate magic wall / mill */
6148       SCAN_PLAYFIELD(xx, yy)
6149       {
6150         if (Feld[xx][yy] == smashed)
6151           Feld[xx][yy] = activated_magic_wall;
6152       }
6153
6154       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6155       game.magic_wall_active = TRUE;
6156
6157       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6158                             SND_MAGIC_WALL_ACTIVATING :
6159                             smashed == EL_BD_MAGIC_WALL ?
6160                             SND_BD_MAGIC_WALL_ACTIVATING :
6161                             SND_DC_MAGIC_WALL_ACTIVATING));
6162     }
6163
6164     if (IS_PLAYER(x, y + 1))
6165     {
6166       if (CAN_SMASH_PLAYER(element))
6167       {
6168         KillPlayerUnlessEnemyProtected(x, y + 1);
6169         return;
6170       }
6171     }
6172     else if (smashed == EL_PENGUIN)
6173     {
6174       if (CAN_SMASH_PLAYER(element))
6175       {
6176         Bang(x, y + 1);
6177         return;
6178       }
6179     }
6180     else if (element == EL_BD_DIAMOND)
6181     {
6182       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6183       {
6184         Bang(x, y + 1);
6185         return;
6186       }
6187     }
6188     else if (((element == EL_SP_INFOTRON ||
6189                element == EL_SP_ZONK) &&
6190               (smashed == EL_SP_SNIKSNAK ||
6191                smashed == EL_SP_ELECTRON ||
6192                smashed == EL_SP_DISK_ORANGE)) ||
6193              (element == EL_SP_INFOTRON &&
6194               smashed == EL_SP_DISK_YELLOW))
6195     {
6196       Bang(x, y + 1);
6197       return;
6198     }
6199     else if (CAN_SMASH_EVERYTHING(element))
6200     {
6201       if (IS_CLASSIC_ENEMY(smashed) ||
6202           CAN_EXPLODE_SMASHED(smashed))
6203       {
6204         Bang(x, y + 1);
6205         return;
6206       }
6207       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6208       {
6209         if (smashed == EL_LAMP ||
6210             smashed == EL_LAMP_ACTIVE)
6211         {
6212           Bang(x, y + 1);
6213           return;
6214         }
6215         else if (smashed == EL_NUT)
6216         {
6217           Feld[x][y + 1] = EL_NUT_BREAKING;
6218           PlayLevelSound(x, y, SND_NUT_BREAKING);
6219           RaiseScoreElement(EL_NUT);
6220           return;
6221         }
6222         else if (smashed == EL_PEARL)
6223         {
6224           ResetGfxAnimation(x, y);
6225
6226           Feld[x][y + 1] = EL_PEARL_BREAKING;
6227           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6228           return;
6229         }
6230         else if (smashed == EL_DIAMOND)
6231         {
6232           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6233           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6234           return;
6235         }
6236         else if (IS_BELT_SWITCH(smashed))
6237         {
6238           ToggleBeltSwitch(x, y + 1);
6239         }
6240         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6241                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6242                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6243                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6244         {
6245           ToggleSwitchgateSwitch(x, y + 1);
6246         }
6247         else if (smashed == EL_LIGHT_SWITCH ||
6248                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6249         {
6250           ToggleLightSwitch(x, y + 1);
6251         }
6252         else
6253         {
6254           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6255
6256           CheckElementChangeBySide(x, y + 1, smashed, element,
6257                                    CE_SWITCHED, CH_SIDE_TOP);
6258           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6259                                             CH_SIDE_TOP);
6260         }
6261       }
6262       else
6263       {
6264         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6265       }
6266     }
6267   }
6268
6269   /* play sound of magic wall / mill */
6270   if (!last_line &&
6271       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6272        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6273        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6274   {
6275     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6276       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6277     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6278       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6279     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6280       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6281
6282     return;
6283   }
6284
6285   /* play sound of object that hits the ground */
6286   if (last_line || object_hit)
6287     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6288 }
6289
6290 inline static void TurnRoundExt(int x, int y)
6291 {
6292   static struct
6293   {
6294     int dx, dy;
6295   } move_xy[] =
6296   {
6297     {  0,  0 },
6298     { -1,  0 },
6299     { +1,  0 },
6300     {  0,  0 },
6301     {  0, -1 },
6302     {  0,  0 }, { 0, 0 }, { 0, 0 },
6303     {  0, +1 }
6304   };
6305   static struct
6306   {
6307     int left, right, back;
6308   } turn[] =
6309   {
6310     { 0,        0,              0        },
6311     { MV_DOWN,  MV_UP,          MV_RIGHT },
6312     { MV_UP,    MV_DOWN,        MV_LEFT  },
6313     { 0,        0,              0        },
6314     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6315     { 0,        0,              0        },
6316     { 0,        0,              0        },
6317     { 0,        0,              0        },
6318     { MV_RIGHT, MV_LEFT,        MV_UP    }
6319   };
6320
6321   int element = Feld[x][y];
6322   int move_pattern = element_info[element].move_pattern;
6323
6324   int old_move_dir = MovDir[x][y];
6325   int left_dir  = turn[old_move_dir].left;
6326   int right_dir = turn[old_move_dir].right;
6327   int back_dir  = turn[old_move_dir].back;
6328
6329   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6330   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6331   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6332   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6333
6334   int left_x  = x + left_dx,  left_y  = y + left_dy;
6335   int right_x = x + right_dx, right_y = y + right_dy;
6336   int move_x  = x + move_dx,  move_y  = y + move_dy;
6337
6338   int xx, yy;
6339
6340   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6341   {
6342     TestIfBadThingTouchesOtherBadThing(x, y);
6343
6344     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6345       MovDir[x][y] = right_dir;
6346     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6347       MovDir[x][y] = left_dir;
6348
6349     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6350       MovDelay[x][y] = 9;
6351     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6352       MovDelay[x][y] = 1;
6353   }
6354   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6355   {
6356     TestIfBadThingTouchesOtherBadThing(x, y);
6357
6358     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6359       MovDir[x][y] = left_dir;
6360     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6361       MovDir[x][y] = right_dir;
6362
6363     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6364       MovDelay[x][y] = 9;
6365     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6366       MovDelay[x][y] = 1;
6367   }
6368   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6369   {
6370     TestIfBadThingTouchesOtherBadThing(x, y);
6371
6372     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6373       MovDir[x][y] = left_dir;
6374     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6375       MovDir[x][y] = right_dir;
6376
6377     if (MovDir[x][y] != old_move_dir)
6378       MovDelay[x][y] = 9;
6379   }
6380   else if (element == EL_YAMYAM)
6381   {
6382     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6383     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6384
6385     if (can_turn_left && can_turn_right)
6386       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6387     else if (can_turn_left)
6388       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6389     else if (can_turn_right)
6390       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6391     else
6392       MovDir[x][y] = back_dir;
6393
6394     MovDelay[x][y] = 16 + 16 * RND(3);
6395   }
6396   else if (element == EL_DARK_YAMYAM)
6397   {
6398     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6399                                                          left_x, left_y);
6400     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6401                                                          right_x, right_y);
6402
6403     if (can_turn_left && can_turn_right)
6404       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6405     else if (can_turn_left)
6406       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6407     else if (can_turn_right)
6408       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6409     else
6410       MovDir[x][y] = back_dir;
6411
6412     MovDelay[x][y] = 16 + 16 * RND(3);
6413   }
6414   else if (element == EL_PACMAN)
6415   {
6416     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6417     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6418
6419     if (can_turn_left && can_turn_right)
6420       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6421     else if (can_turn_left)
6422       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6423     else if (can_turn_right)
6424       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6425     else
6426       MovDir[x][y] = back_dir;
6427
6428     MovDelay[x][y] = 6 + RND(40);
6429   }
6430   else if (element == EL_PIG)
6431   {
6432     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6433     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6434     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6435     boolean should_turn_left, should_turn_right, should_move_on;
6436     int rnd_value = 24;
6437     int rnd = RND(rnd_value);
6438
6439     should_turn_left = (can_turn_left &&
6440                         (!can_move_on ||
6441                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6442                                                    y + back_dy + left_dy)));
6443     should_turn_right = (can_turn_right &&
6444                          (!can_move_on ||
6445                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6446                                                     y + back_dy + right_dy)));
6447     should_move_on = (can_move_on &&
6448                       (!can_turn_left ||
6449                        !can_turn_right ||
6450                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6451                                                  y + move_dy + left_dy) ||
6452                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6453                                                  y + move_dy + right_dy)));
6454
6455     if (should_turn_left || should_turn_right || should_move_on)
6456     {
6457       if (should_turn_left && should_turn_right && should_move_on)
6458         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6459                         rnd < 2 * rnd_value / 3 ? right_dir :
6460                         old_move_dir);
6461       else if (should_turn_left && should_turn_right)
6462         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6463       else if (should_turn_left && should_move_on)
6464         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6465       else if (should_turn_right && should_move_on)
6466         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6467       else if (should_turn_left)
6468         MovDir[x][y] = left_dir;
6469       else if (should_turn_right)
6470         MovDir[x][y] = right_dir;
6471       else if (should_move_on)
6472         MovDir[x][y] = old_move_dir;
6473     }
6474     else if (can_move_on && rnd > rnd_value / 8)
6475       MovDir[x][y] = old_move_dir;
6476     else if (can_turn_left && can_turn_right)
6477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478     else if (can_turn_left && rnd > rnd_value / 8)
6479       MovDir[x][y] = left_dir;
6480     else if (can_turn_right && rnd > rnd_value/8)
6481       MovDir[x][y] = right_dir;
6482     else
6483       MovDir[x][y] = back_dir;
6484
6485     xx = x + move_xy[MovDir[x][y]].dx;
6486     yy = y + move_xy[MovDir[x][y]].dy;
6487
6488     if (!IN_LEV_FIELD(xx, yy) ||
6489         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6490       MovDir[x][y] = old_move_dir;
6491
6492     MovDelay[x][y] = 0;
6493   }
6494   else if (element == EL_DRAGON)
6495   {
6496     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6497     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6498     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6499     int rnd_value = 24;
6500     int rnd = RND(rnd_value);
6501
6502     if (can_move_on && rnd > rnd_value / 8)
6503       MovDir[x][y] = old_move_dir;
6504     else if (can_turn_left && can_turn_right)
6505       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6506     else if (can_turn_left && rnd > rnd_value / 8)
6507       MovDir[x][y] = left_dir;
6508     else if (can_turn_right && rnd > rnd_value / 8)
6509       MovDir[x][y] = right_dir;
6510     else
6511       MovDir[x][y] = back_dir;
6512
6513     xx = x + move_xy[MovDir[x][y]].dx;
6514     yy = y + move_xy[MovDir[x][y]].dy;
6515
6516     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6517       MovDir[x][y] = old_move_dir;
6518
6519     MovDelay[x][y] = 0;
6520   }
6521   else if (element == EL_MOLE)
6522   {
6523     boolean can_move_on =
6524       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6525                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6526                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6527     if (!can_move_on)
6528     {
6529       boolean can_turn_left =
6530         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6531                               IS_AMOEBOID(Feld[left_x][left_y])));
6532
6533       boolean can_turn_right =
6534         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6535                               IS_AMOEBOID(Feld[right_x][right_y])));
6536
6537       if (can_turn_left && can_turn_right)
6538         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6539       else if (can_turn_left)
6540         MovDir[x][y] = left_dir;
6541       else
6542         MovDir[x][y] = right_dir;
6543     }
6544
6545     if (MovDir[x][y] != old_move_dir)
6546       MovDelay[x][y] = 9;
6547   }
6548   else if (element == EL_BALLOON)
6549   {
6550     MovDir[x][y] = game.wind_direction;
6551     MovDelay[x][y] = 0;
6552   }
6553   else if (element == EL_SPRING)
6554   {
6555     if (MovDir[x][y] & MV_HORIZONTAL)
6556     {
6557       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6558           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6559       {
6560         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6561         ResetGfxAnimation(move_x, move_y);
6562         TEST_DrawLevelField(move_x, move_y);
6563
6564         MovDir[x][y] = back_dir;
6565       }
6566       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6567                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6568         MovDir[x][y] = MV_NONE;
6569     }
6570
6571     MovDelay[x][y] = 0;
6572   }
6573   else if (element == EL_ROBOT ||
6574            element == EL_SATELLITE ||
6575            element == EL_PENGUIN ||
6576            element == EL_EMC_ANDROID)
6577   {
6578     int attr_x = -1, attr_y = -1;
6579
6580     if (AllPlayersGone)
6581     {
6582       attr_x = ExitX;
6583       attr_y = ExitY;
6584     }
6585     else
6586     {
6587       int i;
6588
6589       for (i = 0; i < MAX_PLAYERS; i++)
6590       {
6591         struct PlayerInfo *player = &stored_player[i];
6592         int jx = player->jx, jy = player->jy;
6593
6594         if (!player->active)
6595           continue;
6596
6597         if (attr_x == -1 ||
6598             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6599         {
6600           attr_x = jx;
6601           attr_y = jy;
6602         }
6603       }
6604     }
6605
6606     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6607         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6608          game.engine_version < VERSION_IDENT(3,1,0,0)))
6609     {
6610       attr_x = ZX;
6611       attr_y = ZY;
6612     }
6613
6614     if (element == EL_PENGUIN)
6615     {
6616       int i;
6617       static int xy[4][2] =
6618       {
6619         { 0, -1 },
6620         { -1, 0 },
6621         { +1, 0 },
6622         { 0, +1 }
6623       };
6624
6625       for (i = 0; i < NUM_DIRECTIONS; i++)
6626       {
6627         int ex = x + xy[i][0];
6628         int ey = y + xy[i][1];
6629
6630         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6631                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6632                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6633                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6634         {
6635           attr_x = ex;
6636           attr_y = ey;
6637           break;
6638         }
6639       }
6640     }
6641
6642     MovDir[x][y] = MV_NONE;
6643     if (attr_x < x)
6644       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6645     else if (attr_x > x)
6646       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6647     if (attr_y < y)
6648       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6649     else if (attr_y > y)
6650       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6651
6652     if (element == EL_ROBOT)
6653     {
6654       int newx, newy;
6655
6656       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6657         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6658       Moving2Blocked(x, y, &newx, &newy);
6659
6660       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6661         MovDelay[x][y] = 8 + 8 * !RND(3);
6662       else
6663         MovDelay[x][y] = 16;
6664     }
6665     else if (element == EL_PENGUIN)
6666     {
6667       int newx, newy;
6668
6669       MovDelay[x][y] = 1;
6670
6671       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6672       {
6673         boolean first_horiz = RND(2);
6674         int new_move_dir = MovDir[x][y];
6675
6676         MovDir[x][y] =
6677           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6678         Moving2Blocked(x, y, &newx, &newy);
6679
6680         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6681           return;
6682
6683         MovDir[x][y] =
6684           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6685         Moving2Blocked(x, y, &newx, &newy);
6686
6687         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6688           return;
6689
6690         MovDir[x][y] = old_move_dir;
6691         return;
6692       }
6693     }
6694     else if (element == EL_SATELLITE)
6695     {
6696       int newx, newy;
6697
6698       MovDelay[x][y] = 1;
6699
6700       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6701       {
6702         boolean first_horiz = RND(2);
6703         int new_move_dir = MovDir[x][y];
6704
6705         MovDir[x][y] =
6706           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6707         Moving2Blocked(x, y, &newx, &newy);
6708
6709         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6710           return;
6711
6712         MovDir[x][y] =
6713           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6714         Moving2Blocked(x, y, &newx, &newy);
6715
6716         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6717           return;
6718
6719         MovDir[x][y] = old_move_dir;
6720         return;
6721       }
6722     }
6723     else if (element == EL_EMC_ANDROID)
6724     {
6725       static int check_pos[16] =
6726       {
6727         -1,             /*  0 => (invalid)          */
6728         7,              /*  1 => MV_LEFT            */
6729         3,              /*  2 => MV_RIGHT           */
6730         -1,             /*  3 => (invalid)          */
6731         1,              /*  4 =>            MV_UP   */
6732         0,              /*  5 => MV_LEFT  | MV_UP   */
6733         2,              /*  6 => MV_RIGHT | MV_UP   */
6734         -1,             /*  7 => (invalid)          */
6735         5,              /*  8 =>            MV_DOWN */
6736         6,              /*  9 => MV_LEFT  | MV_DOWN */
6737         4,              /* 10 => MV_RIGHT | MV_DOWN */
6738         -1,             /* 11 => (invalid)          */
6739         -1,             /* 12 => (invalid)          */
6740         -1,             /* 13 => (invalid)          */
6741         -1,             /* 14 => (invalid)          */
6742         -1,             /* 15 => (invalid)          */
6743       };
6744       static struct
6745       {
6746         int dx, dy;
6747         int dir;
6748       } check_xy[8] =
6749       {
6750         { -1, -1,       MV_LEFT  | MV_UP   },
6751         {  0, -1,                  MV_UP   },
6752         { +1, -1,       MV_RIGHT | MV_UP   },
6753         { +1,  0,       MV_RIGHT           },
6754         { +1, +1,       MV_RIGHT | MV_DOWN },
6755         {  0, +1,                  MV_DOWN },
6756         { -1, +1,       MV_LEFT  | MV_DOWN },
6757         { -1,  0,       MV_LEFT            },
6758       };
6759       int start_pos, check_order;
6760       boolean can_clone = FALSE;
6761       int i;
6762
6763       /* check if there is any free field around current position */
6764       for (i = 0; i < 8; i++)
6765       {
6766         int newx = x + check_xy[i].dx;
6767         int newy = y + check_xy[i].dy;
6768
6769         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6770         {
6771           can_clone = TRUE;
6772
6773           break;
6774         }
6775       }
6776
6777       if (can_clone)            /* randomly find an element to clone */
6778       {
6779         can_clone = FALSE;
6780
6781         start_pos = check_pos[RND(8)];
6782         check_order = (RND(2) ? -1 : +1);
6783
6784         for (i = 0; i < 8; i++)
6785         {
6786           int pos_raw = start_pos + i * check_order;
6787           int pos = (pos_raw + 8) % 8;
6788           int newx = x + check_xy[pos].dx;
6789           int newy = y + check_xy[pos].dy;
6790
6791           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6792           {
6793             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6794             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6795
6796             Store[x][y] = Feld[newx][newy];
6797
6798             can_clone = TRUE;
6799
6800             break;
6801           }
6802         }
6803       }
6804
6805       if (can_clone)            /* randomly find a direction to move */
6806       {
6807         can_clone = FALSE;
6808
6809         start_pos = check_pos[RND(8)];
6810         check_order = (RND(2) ? -1 : +1);
6811
6812         for (i = 0; i < 8; i++)
6813         {
6814           int pos_raw = start_pos + i * check_order;
6815           int pos = (pos_raw + 8) % 8;
6816           int newx = x + check_xy[pos].dx;
6817           int newy = y + check_xy[pos].dy;
6818           int new_move_dir = check_xy[pos].dir;
6819
6820           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6821           {
6822             MovDir[x][y] = new_move_dir;
6823             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6824
6825             can_clone = TRUE;
6826
6827             break;
6828           }
6829         }
6830       }
6831
6832       if (can_clone)            /* cloning and moving successful */
6833         return;
6834
6835       /* cannot clone -- try to move towards player */
6836
6837       start_pos = check_pos[MovDir[x][y] & 0x0f];
6838       check_order = (RND(2) ? -1 : +1);
6839
6840       for (i = 0; i < 3; i++)
6841       {
6842         /* first check start_pos, then previous/next or (next/previous) pos */
6843         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6844         int pos = (pos_raw + 8) % 8;
6845         int newx = x + check_xy[pos].dx;
6846         int newy = y + check_xy[pos].dy;
6847         int new_move_dir = check_xy[pos].dir;
6848
6849         if (IS_PLAYER(newx, newy))
6850           break;
6851
6852         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6853         {
6854           MovDir[x][y] = new_move_dir;
6855           MovDelay[x][y] = level.android_move_time * 8 + 1;
6856
6857           break;
6858         }
6859       }
6860     }
6861   }
6862   else if (move_pattern == MV_TURNING_LEFT ||
6863            move_pattern == MV_TURNING_RIGHT ||
6864            move_pattern == MV_TURNING_LEFT_RIGHT ||
6865            move_pattern == MV_TURNING_RIGHT_LEFT ||
6866            move_pattern == MV_TURNING_RANDOM ||
6867            move_pattern == MV_ALL_DIRECTIONS)
6868   {
6869     boolean can_turn_left =
6870       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6871     boolean can_turn_right =
6872       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6873
6874     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6875       return;
6876
6877     if (move_pattern == MV_TURNING_LEFT)
6878       MovDir[x][y] = left_dir;
6879     else if (move_pattern == MV_TURNING_RIGHT)
6880       MovDir[x][y] = right_dir;
6881     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6882       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6883     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6884       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6885     else if (move_pattern == MV_TURNING_RANDOM)
6886       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6887                       can_turn_right && !can_turn_left ? right_dir :
6888                       RND(2) ? left_dir : right_dir);
6889     else if (can_turn_left && can_turn_right)
6890       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6891     else if (can_turn_left)
6892       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6893     else if (can_turn_right)
6894       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6895     else
6896       MovDir[x][y] = back_dir;
6897
6898     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6899   }
6900   else if (move_pattern == MV_HORIZONTAL ||
6901            move_pattern == MV_VERTICAL)
6902   {
6903     if (move_pattern & old_move_dir)
6904       MovDir[x][y] = back_dir;
6905     else if (move_pattern == MV_HORIZONTAL)
6906       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6907     else if (move_pattern == MV_VERTICAL)
6908       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6909
6910     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6911   }
6912   else if (move_pattern & MV_ANY_DIRECTION)
6913   {
6914     MovDir[x][y] = move_pattern;
6915     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6916   }
6917   else if (move_pattern & MV_WIND_DIRECTION)
6918   {
6919     MovDir[x][y] = game.wind_direction;
6920     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6921   }
6922   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6923   {
6924     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6925       MovDir[x][y] = left_dir;
6926     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6927       MovDir[x][y] = right_dir;
6928
6929     if (MovDir[x][y] != old_move_dir)
6930       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6931   }
6932   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6933   {
6934     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6935       MovDir[x][y] = right_dir;
6936     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6937       MovDir[x][y] = left_dir;
6938
6939     if (MovDir[x][y] != old_move_dir)
6940       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6941   }
6942   else if (move_pattern == MV_TOWARDS_PLAYER ||
6943            move_pattern == MV_AWAY_FROM_PLAYER)
6944   {
6945     int attr_x = -1, attr_y = -1;
6946     int newx, newy;
6947     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6948
6949     if (AllPlayersGone)
6950     {
6951       attr_x = ExitX;
6952       attr_y = ExitY;
6953     }
6954     else
6955     {
6956       int i;
6957
6958       for (i = 0; i < MAX_PLAYERS; i++)
6959       {
6960         struct PlayerInfo *player = &stored_player[i];
6961         int jx = player->jx, jy = player->jy;
6962
6963         if (!player->active)
6964           continue;
6965
6966         if (attr_x == -1 ||
6967             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6968         {
6969           attr_x = jx;
6970           attr_y = jy;
6971         }
6972       }
6973     }
6974
6975     MovDir[x][y] = MV_NONE;
6976     if (attr_x < x)
6977       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6978     else if (attr_x > x)
6979       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6980     if (attr_y < y)
6981       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6982     else if (attr_y > y)
6983       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6984
6985     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6986
6987     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6988     {
6989       boolean first_horiz = RND(2);
6990       int new_move_dir = MovDir[x][y];
6991
6992       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6993       {
6994         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6995         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6996
6997         return;
6998       }
6999
7000       MovDir[x][y] =
7001         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7002       Moving2Blocked(x, y, &newx, &newy);
7003
7004       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7005         return;
7006
7007       MovDir[x][y] =
7008         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7009       Moving2Blocked(x, y, &newx, &newy);
7010
7011       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7012         return;
7013
7014       MovDir[x][y] = old_move_dir;
7015     }
7016   }
7017   else if (move_pattern == MV_WHEN_PUSHED ||
7018            move_pattern == MV_WHEN_DROPPED)
7019   {
7020     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7021       MovDir[x][y] = MV_NONE;
7022
7023     MovDelay[x][y] = 0;
7024   }
7025   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7026   {
7027     static int test_xy[7][2] =
7028     {
7029       { 0, -1 },
7030       { -1, 0 },
7031       { +1, 0 },
7032       { 0, +1 },
7033       { 0, -1 },
7034       { -1, 0 },
7035       { +1, 0 },
7036     };
7037     static int test_dir[7] =
7038     {
7039       MV_UP,
7040       MV_LEFT,
7041       MV_RIGHT,
7042       MV_DOWN,
7043       MV_UP,
7044       MV_LEFT,
7045       MV_RIGHT,
7046     };
7047     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7048     int move_preference = -1000000;     /* start with very low preference */
7049     int new_move_dir = MV_NONE;
7050     int start_test = RND(4);
7051     int i;
7052
7053     for (i = 0; i < NUM_DIRECTIONS; i++)
7054     {
7055       int move_dir = test_dir[start_test + i];
7056       int move_dir_preference;
7057
7058       xx = x + test_xy[start_test + i][0];
7059       yy = y + test_xy[start_test + i][1];
7060
7061       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7062           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7063       {
7064         new_move_dir = move_dir;
7065
7066         break;
7067       }
7068
7069       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7070         continue;
7071
7072       move_dir_preference = -1 * RunnerVisit[xx][yy];
7073       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7074         move_dir_preference = PlayerVisit[xx][yy];
7075
7076       if (move_dir_preference > move_preference)
7077       {
7078         /* prefer field that has not been visited for the longest time */
7079         move_preference = move_dir_preference;
7080         new_move_dir = move_dir;
7081       }
7082       else if (move_dir_preference == move_preference &&
7083                move_dir == old_move_dir)
7084       {
7085         /* prefer last direction when all directions are preferred equally */
7086         move_preference = move_dir_preference;
7087         new_move_dir = move_dir;
7088       }
7089     }
7090
7091     MovDir[x][y] = new_move_dir;
7092     if (old_move_dir != new_move_dir)
7093       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7094   }
7095 }
7096
7097 static void TurnRound(int x, int y)
7098 {
7099   int direction = MovDir[x][y];
7100
7101   TurnRoundExt(x, y);
7102
7103   GfxDir[x][y] = MovDir[x][y];
7104
7105   if (direction != MovDir[x][y])
7106     GfxFrame[x][y] = 0;
7107
7108   if (MovDelay[x][y])
7109     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7110
7111   ResetGfxFrame(x, y, FALSE);
7112 }
7113
7114 static boolean JustBeingPushed(int x, int y)
7115 {
7116   int i;
7117
7118   for (i = 0; i < MAX_PLAYERS; i++)
7119   {
7120     struct PlayerInfo *player = &stored_player[i];
7121
7122     if (player->active && player->is_pushing && player->MovPos)
7123     {
7124       int next_jx = player->jx + (player->jx - player->last_jx);
7125       int next_jy = player->jy + (player->jy - player->last_jy);
7126
7127       if (x == next_jx && y == next_jy)
7128         return TRUE;
7129     }
7130   }
7131
7132   return FALSE;
7133 }
7134
7135 void StartMoving(int x, int y)
7136 {
7137   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7138   int element = Feld[x][y];
7139
7140   if (Stop[x][y])
7141     return;
7142
7143   if (MovDelay[x][y] == 0)
7144     GfxAction[x][y] = ACTION_DEFAULT;
7145
7146   if (CAN_FALL(element) && y < lev_fieldy - 1)
7147   {
7148     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7149         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7150       if (JustBeingPushed(x, y))
7151         return;
7152
7153     if (element == EL_QUICKSAND_FULL)
7154     {
7155       if (IS_FREE(x, y + 1))
7156       {
7157         InitMovingField(x, y, MV_DOWN);
7158         started_moving = TRUE;
7159
7160         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7161 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7162         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7163           Store[x][y] = EL_ROCK;
7164 #else
7165         Store[x][y] = EL_ROCK;
7166 #endif
7167
7168         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7169       }
7170       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7171       {
7172         if (!MovDelay[x][y])
7173         {
7174           MovDelay[x][y] = TILEY + 1;
7175
7176           ResetGfxAnimation(x, y);
7177           ResetGfxAnimation(x, y + 1);
7178         }
7179
7180         if (MovDelay[x][y])
7181         {
7182           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7183           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7184
7185           MovDelay[x][y]--;
7186           if (MovDelay[x][y])
7187             return;
7188         }
7189
7190         Feld[x][y] = EL_QUICKSAND_EMPTY;
7191         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7192         Store[x][y + 1] = Store[x][y];
7193         Store[x][y] = 0;
7194
7195         PlayLevelSoundAction(x, y, ACTION_FILLING);
7196       }
7197       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7198       {
7199         if (!MovDelay[x][y])
7200         {
7201           MovDelay[x][y] = TILEY + 1;
7202
7203           ResetGfxAnimation(x, y);
7204           ResetGfxAnimation(x, y + 1);
7205         }
7206
7207         if (MovDelay[x][y])
7208         {
7209           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7210           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7211
7212           MovDelay[x][y]--;
7213           if (MovDelay[x][y])
7214             return;
7215         }
7216
7217         Feld[x][y] = EL_QUICKSAND_EMPTY;
7218         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7219         Store[x][y + 1] = Store[x][y];
7220         Store[x][y] = 0;
7221
7222         PlayLevelSoundAction(x, y, ACTION_FILLING);
7223       }
7224     }
7225     else if (element == EL_QUICKSAND_FAST_FULL)
7226     {
7227       if (IS_FREE(x, y + 1))
7228       {
7229         InitMovingField(x, y, MV_DOWN);
7230         started_moving = TRUE;
7231
7232         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7233 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7234         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7235           Store[x][y] = EL_ROCK;
7236 #else
7237         Store[x][y] = EL_ROCK;
7238 #endif
7239
7240         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7241       }
7242       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7243       {
7244         if (!MovDelay[x][y])
7245         {
7246           MovDelay[x][y] = TILEY + 1;
7247
7248           ResetGfxAnimation(x, y);
7249           ResetGfxAnimation(x, y + 1);
7250         }
7251
7252         if (MovDelay[x][y])
7253         {
7254           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7255           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7256
7257           MovDelay[x][y]--;
7258           if (MovDelay[x][y])
7259             return;
7260         }
7261
7262         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7263         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7264         Store[x][y + 1] = Store[x][y];
7265         Store[x][y] = 0;
7266
7267         PlayLevelSoundAction(x, y, ACTION_FILLING);
7268       }
7269       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7270       {
7271         if (!MovDelay[x][y])
7272         {
7273           MovDelay[x][y] = TILEY + 1;
7274
7275           ResetGfxAnimation(x, y);
7276           ResetGfxAnimation(x, y + 1);
7277         }
7278
7279         if (MovDelay[x][y])
7280         {
7281           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7282           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7283
7284           MovDelay[x][y]--;
7285           if (MovDelay[x][y])
7286             return;
7287         }
7288
7289         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7290         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7291         Store[x][y + 1] = Store[x][y];
7292         Store[x][y] = 0;
7293
7294         PlayLevelSoundAction(x, y, ACTION_FILLING);
7295       }
7296     }
7297     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7298              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7299     {
7300       InitMovingField(x, y, MV_DOWN);
7301       started_moving = TRUE;
7302
7303       Feld[x][y] = EL_QUICKSAND_FILLING;
7304       Store[x][y] = element;
7305
7306       PlayLevelSoundAction(x, y, ACTION_FILLING);
7307     }
7308     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7309              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7310     {
7311       InitMovingField(x, y, MV_DOWN);
7312       started_moving = TRUE;
7313
7314       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7315       Store[x][y] = element;
7316
7317       PlayLevelSoundAction(x, y, ACTION_FILLING);
7318     }
7319     else if (element == EL_MAGIC_WALL_FULL)
7320     {
7321       if (IS_FREE(x, y + 1))
7322       {
7323         InitMovingField(x, y, MV_DOWN);
7324         started_moving = TRUE;
7325
7326         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7327         Store[x][y] = EL_CHANGED(Store[x][y]);
7328       }
7329       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7330       {
7331         if (!MovDelay[x][y])
7332           MovDelay[x][y] = TILEY / 4 + 1;
7333
7334         if (MovDelay[x][y])
7335         {
7336           MovDelay[x][y]--;
7337           if (MovDelay[x][y])
7338             return;
7339         }
7340
7341         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7342         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7343         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7344         Store[x][y] = 0;
7345       }
7346     }
7347     else if (element == EL_BD_MAGIC_WALL_FULL)
7348     {
7349       if (IS_FREE(x, y + 1))
7350       {
7351         InitMovingField(x, y, MV_DOWN);
7352         started_moving = TRUE;
7353
7354         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7355         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7356       }
7357       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7358       {
7359         if (!MovDelay[x][y])
7360           MovDelay[x][y] = TILEY / 4 + 1;
7361
7362         if (MovDelay[x][y])
7363         {
7364           MovDelay[x][y]--;
7365           if (MovDelay[x][y])
7366             return;
7367         }
7368
7369         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7370         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7371         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7372         Store[x][y] = 0;
7373       }
7374     }
7375     else if (element == EL_DC_MAGIC_WALL_FULL)
7376     {
7377       if (IS_FREE(x, y + 1))
7378       {
7379         InitMovingField(x, y, MV_DOWN);
7380         started_moving = TRUE;
7381
7382         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7383         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7384       }
7385       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7386       {
7387         if (!MovDelay[x][y])
7388           MovDelay[x][y] = TILEY / 4 + 1;
7389
7390         if (MovDelay[x][y])
7391         {
7392           MovDelay[x][y]--;
7393           if (MovDelay[x][y])
7394             return;
7395         }
7396
7397         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7398         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7399         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7400         Store[x][y] = 0;
7401       }
7402     }
7403     else if ((CAN_PASS_MAGIC_WALL(element) &&
7404               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7405                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7406              (CAN_PASS_DC_MAGIC_WALL(element) &&
7407               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7408
7409     {
7410       InitMovingField(x, y, MV_DOWN);
7411       started_moving = TRUE;
7412
7413       Feld[x][y] =
7414         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7415          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7416          EL_DC_MAGIC_WALL_FILLING);
7417       Store[x][y] = element;
7418     }
7419     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7420     {
7421       SplashAcid(x, y + 1);
7422
7423       InitMovingField(x, y, MV_DOWN);
7424       started_moving = TRUE;
7425
7426       Store[x][y] = EL_ACID;
7427     }
7428     else if (
7429              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7430               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7431              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7432               CAN_FALL(element) && WasJustFalling[x][y] &&
7433               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7434
7435              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7436               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7437               (Feld[x][y + 1] == EL_BLOCKED)))
7438     {
7439       /* this is needed for a special case not covered by calling "Impact()"
7440          from "ContinueMoving()": if an element moves to a tile directly below
7441          another element which was just falling on that tile (which was empty
7442          in the previous frame), the falling element above would just stop
7443          instead of smashing the element below (in previous version, the above
7444          element was just checked for "moving" instead of "falling", resulting
7445          in incorrect smashes caused by horizontal movement of the above
7446          element; also, the case of the player being the element to smash was
7447          simply not covered here... :-/ ) */
7448
7449       CheckCollision[x][y] = 0;
7450       CheckImpact[x][y] = 0;
7451
7452       Impact(x, y);
7453     }
7454     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7455     {
7456       if (MovDir[x][y] == MV_NONE)
7457       {
7458         InitMovingField(x, y, MV_DOWN);
7459         started_moving = TRUE;
7460       }
7461     }
7462     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7463     {
7464       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7465         MovDir[x][y] = MV_DOWN;
7466
7467       InitMovingField(x, y, MV_DOWN);
7468       started_moving = TRUE;
7469     }
7470     else if (element == EL_AMOEBA_DROP)
7471     {
7472       Feld[x][y] = EL_AMOEBA_GROWING;
7473       Store[x][y] = EL_AMOEBA_WET;
7474     }
7475     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7476               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7477              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7478              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7479     {
7480       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7481                                 (IS_FREE(x - 1, y + 1) ||
7482                                  Feld[x - 1][y + 1] == EL_ACID));
7483       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7484                                 (IS_FREE(x + 1, y + 1) ||
7485                                  Feld[x + 1][y + 1] == EL_ACID));
7486       boolean can_fall_any  = (can_fall_left || can_fall_right);
7487       boolean can_fall_both = (can_fall_left && can_fall_right);
7488       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7489
7490       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7491       {
7492         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7493           can_fall_right = FALSE;
7494         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7495           can_fall_left = FALSE;
7496         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7497           can_fall_right = FALSE;
7498         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7499           can_fall_left = FALSE;
7500
7501         can_fall_any  = (can_fall_left || can_fall_right);
7502         can_fall_both = FALSE;
7503       }
7504
7505       if (can_fall_both)
7506       {
7507         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7508           can_fall_right = FALSE;       /* slip down on left side */
7509         else
7510           can_fall_left = !(can_fall_right = RND(2));
7511
7512         can_fall_both = FALSE;
7513       }
7514
7515       if (can_fall_any)
7516       {
7517         /* if not determined otherwise, prefer left side for slipping down */
7518         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7519         started_moving = TRUE;
7520       }
7521     }
7522     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7523     {
7524       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7525       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7526       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7527       int belt_dir = game.belt_dir[belt_nr];
7528
7529       if ((belt_dir == MV_LEFT  && left_is_free) ||
7530           (belt_dir == MV_RIGHT && right_is_free))
7531       {
7532         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7533
7534         InitMovingField(x, y, belt_dir);
7535         started_moving = TRUE;
7536
7537         Pushed[x][y] = TRUE;
7538         Pushed[nextx][y] = TRUE;
7539
7540         GfxAction[x][y] = ACTION_DEFAULT;
7541       }
7542       else
7543       {
7544         MovDir[x][y] = 0;       /* if element was moving, stop it */
7545       }
7546     }
7547   }
7548
7549   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7550   if (CAN_MOVE(element) && !started_moving)
7551   {
7552     int move_pattern = element_info[element].move_pattern;
7553     int newx, newy;
7554
7555     Moving2Blocked(x, y, &newx, &newy);
7556
7557     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7558       return;
7559
7560     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7561         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7562     {
7563       WasJustMoving[x][y] = 0;
7564       CheckCollision[x][y] = 0;
7565
7566       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7567
7568       if (Feld[x][y] != element)        /* element has changed */
7569         return;
7570     }
7571
7572     if (!MovDelay[x][y])        /* start new movement phase */
7573     {
7574       /* all objects that can change their move direction after each step
7575          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7576
7577       if (element != EL_YAMYAM &&
7578           element != EL_DARK_YAMYAM &&
7579           element != EL_PACMAN &&
7580           !(move_pattern & MV_ANY_DIRECTION) &&
7581           move_pattern != MV_TURNING_LEFT &&
7582           move_pattern != MV_TURNING_RIGHT &&
7583           move_pattern != MV_TURNING_LEFT_RIGHT &&
7584           move_pattern != MV_TURNING_RIGHT_LEFT &&
7585           move_pattern != MV_TURNING_RANDOM)
7586       {
7587         TurnRound(x, y);
7588
7589         if (MovDelay[x][y] && (element == EL_BUG ||
7590                                element == EL_SPACESHIP ||
7591                                element == EL_SP_SNIKSNAK ||
7592                                element == EL_SP_ELECTRON ||
7593                                element == EL_MOLE))
7594           TEST_DrawLevelField(x, y);
7595       }
7596     }
7597
7598     if (MovDelay[x][y])         /* wait some time before next movement */
7599     {
7600       MovDelay[x][y]--;
7601
7602       if (element == EL_ROBOT ||
7603           element == EL_YAMYAM ||
7604           element == EL_DARK_YAMYAM)
7605       {
7606         DrawLevelElementAnimationIfNeeded(x, y, element);
7607         PlayLevelSoundAction(x, y, ACTION_WAITING);
7608       }
7609       else if (element == EL_SP_ELECTRON)
7610         DrawLevelElementAnimationIfNeeded(x, y, element);
7611       else if (element == EL_DRAGON)
7612       {
7613         int i;
7614         int dir = MovDir[x][y];
7615         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7616         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7617         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7618                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7619                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7620                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7621         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7622
7623         GfxAction[x][y] = ACTION_ATTACKING;
7624
7625         if (IS_PLAYER(x, y))
7626           DrawPlayerField(x, y);
7627         else
7628           TEST_DrawLevelField(x, y);
7629
7630         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7631
7632         for (i = 1; i <= 3; i++)
7633         {
7634           int xx = x + i * dx;
7635           int yy = y + i * dy;
7636           int sx = SCREENX(xx);
7637           int sy = SCREENY(yy);
7638           int flame_graphic = graphic + (i - 1);
7639
7640           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7641             break;
7642
7643           if (MovDelay[x][y])
7644           {
7645             int flamed = MovingOrBlocked2Element(xx, yy);
7646
7647             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7648               Bang(xx, yy);
7649             else
7650               RemoveMovingField(xx, yy);
7651
7652             ChangeDelay[xx][yy] = 0;
7653
7654             Feld[xx][yy] = EL_FLAMES;
7655
7656             if (IN_SCR_FIELD(sx, sy))
7657             {
7658               TEST_DrawLevelFieldCrumbled(xx, yy);
7659               DrawGraphic(sx, sy, flame_graphic, frame);
7660             }
7661           }
7662           else
7663           {
7664             if (Feld[xx][yy] == EL_FLAMES)
7665               Feld[xx][yy] = EL_EMPTY;
7666             TEST_DrawLevelField(xx, yy);
7667           }
7668         }
7669       }
7670
7671       if (MovDelay[x][y])       /* element still has to wait some time */
7672       {
7673         PlayLevelSoundAction(x, y, ACTION_WAITING);
7674
7675         return;
7676       }
7677     }
7678
7679     /* now make next step */
7680
7681     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7682
7683     if (DONT_COLLIDE_WITH(element) &&
7684         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7685         !PLAYER_ENEMY_PROTECTED(newx, newy))
7686     {
7687       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7688
7689       return;
7690     }
7691
7692     else if (CAN_MOVE_INTO_ACID(element) &&
7693              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7694              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7695              (MovDir[x][y] == MV_DOWN ||
7696               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7697     {
7698       SplashAcid(newx, newy);
7699       Store[x][y] = EL_ACID;
7700     }
7701     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7702     {
7703       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7704           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7705           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7706           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7707       {
7708         RemoveField(x, y);
7709         TEST_DrawLevelField(x, y);
7710
7711         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7712         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7713           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7714
7715         local_player->friends_still_needed--;
7716         if (!local_player->friends_still_needed &&
7717             !local_player->GameOver && AllPlayersGone)
7718           PlayerWins(local_player);
7719
7720         return;
7721       }
7722       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7723       {
7724         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7725           TEST_DrawLevelField(newx, newy);
7726         else
7727           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7728       }
7729       else if (!IS_FREE(newx, newy))
7730       {
7731         GfxAction[x][y] = ACTION_WAITING;
7732
7733         if (IS_PLAYER(x, y))
7734           DrawPlayerField(x, y);
7735         else
7736           TEST_DrawLevelField(x, y);
7737
7738         return;
7739       }
7740     }
7741     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7742     {
7743       if (IS_FOOD_PIG(Feld[newx][newy]))
7744       {
7745         if (IS_MOVING(newx, newy))
7746           RemoveMovingField(newx, newy);
7747         else
7748         {
7749           Feld[newx][newy] = EL_EMPTY;
7750           TEST_DrawLevelField(newx, newy);
7751         }
7752
7753         PlayLevelSound(x, y, SND_PIG_DIGGING);
7754       }
7755       else if (!IS_FREE(newx, newy))
7756       {
7757         if (IS_PLAYER(x, y))
7758           DrawPlayerField(x, y);
7759         else
7760           TEST_DrawLevelField(x, y);
7761
7762         return;
7763       }
7764     }
7765     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7766     {
7767       if (Store[x][y] != EL_EMPTY)
7768       {
7769         boolean can_clone = FALSE;
7770         int xx, yy;
7771
7772         /* check if element to clone is still there */
7773         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7774         {
7775           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7776           {
7777             can_clone = TRUE;
7778
7779             break;
7780           }
7781         }
7782
7783         /* cannot clone or target field not free anymore -- do not clone */
7784         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7785           Store[x][y] = EL_EMPTY;
7786       }
7787
7788       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7789       {
7790         if (IS_MV_DIAGONAL(MovDir[x][y]))
7791         {
7792           int diagonal_move_dir = MovDir[x][y];
7793           int stored = Store[x][y];
7794           int change_delay = 8;
7795           int graphic;
7796
7797           /* android is moving diagonally */
7798
7799           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7800
7801           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7802           GfxElement[x][y] = EL_EMC_ANDROID;
7803           GfxAction[x][y] = ACTION_SHRINKING;
7804           GfxDir[x][y] = diagonal_move_dir;
7805           ChangeDelay[x][y] = change_delay;
7806
7807           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7808                                    GfxDir[x][y]);
7809
7810           DrawLevelGraphicAnimation(x, y, graphic);
7811           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7812
7813           if (Feld[newx][newy] == EL_ACID)
7814           {
7815             SplashAcid(newx, newy);
7816
7817             return;
7818           }
7819
7820           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7821
7822           Store[newx][newy] = EL_EMC_ANDROID;
7823           GfxElement[newx][newy] = EL_EMC_ANDROID;
7824           GfxAction[newx][newy] = ACTION_GROWING;
7825           GfxDir[newx][newy] = diagonal_move_dir;
7826           ChangeDelay[newx][newy] = change_delay;
7827
7828           graphic = el_act_dir2img(GfxElement[newx][newy],
7829                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7830
7831           DrawLevelGraphicAnimation(newx, newy, graphic);
7832           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7833
7834           return;
7835         }
7836         else
7837         {
7838           Feld[newx][newy] = EL_EMPTY;
7839           TEST_DrawLevelField(newx, newy);
7840
7841           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7842         }
7843       }
7844       else if (!IS_FREE(newx, newy))
7845       {
7846         return;
7847       }
7848     }
7849     else if (IS_CUSTOM_ELEMENT(element) &&
7850              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7851     {
7852       if (!DigFieldByCE(newx, newy, element))
7853         return;
7854
7855       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7856       {
7857         RunnerVisit[x][y] = FrameCounter;
7858         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7859       }
7860     }
7861     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7862     {
7863       if (!IS_FREE(newx, newy))
7864       {
7865         if (IS_PLAYER(x, y))
7866           DrawPlayerField(x, y);
7867         else
7868           TEST_DrawLevelField(x, y);
7869
7870         return;
7871       }
7872       else
7873       {
7874         boolean wanna_flame = !RND(10);
7875         int dx = newx - x, dy = newy - y;
7876         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7877         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7878         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7879                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7880         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7881                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7882
7883         if ((wanna_flame ||
7884              IS_CLASSIC_ENEMY(element1) ||
7885              IS_CLASSIC_ENEMY(element2)) &&
7886             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7887             element1 != EL_FLAMES && element2 != EL_FLAMES)
7888         {
7889           ResetGfxAnimation(x, y);
7890           GfxAction[x][y] = ACTION_ATTACKING;
7891
7892           if (IS_PLAYER(x, y))
7893             DrawPlayerField(x, y);
7894           else
7895             TEST_DrawLevelField(x, y);
7896
7897           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7898
7899           MovDelay[x][y] = 50;
7900
7901           Feld[newx][newy] = EL_FLAMES;
7902           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7903             Feld[newx1][newy1] = EL_FLAMES;
7904           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7905             Feld[newx2][newy2] = EL_FLAMES;
7906
7907           return;
7908         }
7909       }
7910     }
7911     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7912              Feld[newx][newy] == EL_DIAMOND)
7913     {
7914       if (IS_MOVING(newx, newy))
7915         RemoveMovingField(newx, newy);
7916       else
7917       {
7918         Feld[newx][newy] = EL_EMPTY;
7919         TEST_DrawLevelField(newx, newy);
7920       }
7921
7922       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7923     }
7924     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7925              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7926     {
7927       if (AmoebaNr[newx][newy])
7928       {
7929         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7930         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7931             Feld[newx][newy] == EL_BD_AMOEBA)
7932           AmoebaCnt[AmoebaNr[newx][newy]]--;
7933       }
7934
7935       if (IS_MOVING(newx, newy))
7936       {
7937         RemoveMovingField(newx, newy);
7938       }
7939       else
7940       {
7941         Feld[newx][newy] = EL_EMPTY;
7942         TEST_DrawLevelField(newx, newy);
7943       }
7944
7945       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7946     }
7947     else if ((element == EL_PACMAN || element == EL_MOLE)
7948              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7949     {
7950       if (AmoebaNr[newx][newy])
7951       {
7952         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7953         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7954             Feld[newx][newy] == EL_BD_AMOEBA)
7955           AmoebaCnt[AmoebaNr[newx][newy]]--;
7956       }
7957
7958       if (element == EL_MOLE)
7959       {
7960         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7961         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7962
7963         ResetGfxAnimation(x, y);
7964         GfxAction[x][y] = ACTION_DIGGING;
7965         TEST_DrawLevelField(x, y);
7966
7967         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7968
7969         return;                         /* wait for shrinking amoeba */
7970       }
7971       else      /* element == EL_PACMAN */
7972       {
7973         Feld[newx][newy] = EL_EMPTY;
7974         TEST_DrawLevelField(newx, newy);
7975         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7976       }
7977     }
7978     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7979              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7980               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7981     {
7982       /* wait for shrinking amoeba to completely disappear */
7983       return;
7984     }
7985     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7986     {
7987       /* object was running against a wall */
7988
7989       TurnRound(x, y);
7990
7991       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7992         DrawLevelElementAnimation(x, y, element);
7993
7994       if (DONT_TOUCH(element))
7995         TestIfBadThingTouchesPlayer(x, y);
7996
7997       return;
7998     }
7999
8000     InitMovingField(x, y, MovDir[x][y]);
8001
8002     PlayLevelSoundAction(x, y, ACTION_MOVING);
8003   }
8004
8005   if (MovDir[x][y])
8006     ContinueMoving(x, y);
8007 }
8008
8009 void ContinueMoving(int x, int y)
8010 {
8011   int element = Feld[x][y];
8012   struct ElementInfo *ei = &element_info[element];
8013   int direction = MovDir[x][y];
8014   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8015   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8016   int newx = x + dx, newy = y + dy;
8017   int stored = Store[x][y];
8018   int stored_new = Store[newx][newy];
8019   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8020   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8021   boolean last_line = (newy == lev_fieldy - 1);
8022
8023   MovPos[x][y] += getElementMoveStepsize(x, y);
8024
8025   if (pushed_by_player) /* special case: moving object pushed by player */
8026     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8027
8028   if (ABS(MovPos[x][y]) < TILEX)
8029   {
8030     TEST_DrawLevelField(x, y);
8031
8032     return;     /* element is still moving */
8033   }
8034
8035   /* element reached destination field */
8036
8037   Feld[x][y] = EL_EMPTY;
8038   Feld[newx][newy] = element;
8039   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8040
8041   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8042   {
8043     element = Feld[newx][newy] = EL_ACID;
8044   }
8045   else if (element == EL_MOLE)
8046   {
8047     Feld[x][y] = EL_SAND;
8048
8049     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8050   }
8051   else if (element == EL_QUICKSAND_FILLING)
8052   {
8053     element = Feld[newx][newy] = get_next_element(element);
8054     Store[newx][newy] = Store[x][y];
8055   }
8056   else if (element == EL_QUICKSAND_EMPTYING)
8057   {
8058     Feld[x][y] = get_next_element(element);
8059     element = Feld[newx][newy] = Store[x][y];
8060   }
8061   else if (element == EL_QUICKSAND_FAST_FILLING)
8062   {
8063     element = Feld[newx][newy] = get_next_element(element);
8064     Store[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     element = Feld[newx][newy] = Store[x][y];
8070   }
8071   else if (element == EL_MAGIC_WALL_FILLING)
8072   {
8073     element = Feld[newx][newy] = get_next_element(element);
8074     if (!game.magic_wall_active)
8075       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8076     Store[newx][newy] = Store[x][y];
8077   }
8078   else if (element == EL_MAGIC_WALL_EMPTYING)
8079   {
8080     Feld[x][y] = get_next_element(element);
8081     if (!game.magic_wall_active)
8082       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8083     element = Feld[newx][newy] = Store[x][y];
8084
8085     InitField(newx, newy, FALSE);
8086   }
8087   else if (element == EL_BD_MAGIC_WALL_FILLING)
8088   {
8089     element = Feld[newx][newy] = get_next_element(element);
8090     if (!game.magic_wall_active)
8091       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8092     Store[newx][newy] = Store[x][y];
8093   }
8094   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8095   {
8096     Feld[x][y] = get_next_element(element);
8097     if (!game.magic_wall_active)
8098       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8099     element = Feld[newx][newy] = Store[x][y];
8100
8101     InitField(newx, newy, FALSE);
8102   }
8103   else if (element == EL_DC_MAGIC_WALL_FILLING)
8104   {
8105     element = Feld[newx][newy] = get_next_element(element);
8106     if (!game.magic_wall_active)
8107       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8108     Store[newx][newy] = Store[x][y];
8109   }
8110   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8111   {
8112     Feld[x][y] = get_next_element(element);
8113     if (!game.magic_wall_active)
8114       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8115     element = Feld[newx][newy] = Store[x][y];
8116
8117     InitField(newx, newy, FALSE);
8118   }
8119   else if (element == EL_AMOEBA_DROPPING)
8120   {
8121     Feld[x][y] = get_next_element(element);
8122     element = Feld[newx][newy] = Store[x][y];
8123   }
8124   else if (element == EL_SOKOBAN_OBJECT)
8125   {
8126     if (Back[x][y])
8127       Feld[x][y] = Back[x][y];
8128
8129     if (Back[newx][newy])
8130       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8131
8132     Back[x][y] = Back[newx][newy] = 0;
8133   }
8134
8135   Store[x][y] = EL_EMPTY;
8136   MovPos[x][y] = 0;
8137   MovDir[x][y] = 0;
8138   MovDelay[x][y] = 0;
8139
8140   MovDelay[newx][newy] = 0;
8141
8142   if (CAN_CHANGE_OR_HAS_ACTION(element))
8143   {
8144     /* copy element change control values to new field */
8145     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8146     ChangePage[newx][newy]  = ChangePage[x][y];
8147     ChangeCount[newx][newy] = ChangeCount[x][y];
8148     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8149   }
8150
8151   CustomValue[newx][newy] = CustomValue[x][y];
8152
8153   ChangeDelay[x][y] = 0;
8154   ChangePage[x][y] = -1;
8155   ChangeCount[x][y] = 0;
8156   ChangeEvent[x][y] = -1;
8157
8158   CustomValue[x][y] = 0;
8159
8160   /* copy animation control values to new field */
8161   GfxFrame[newx][newy]  = GfxFrame[x][y];
8162   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8163   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8164   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8165
8166   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8167
8168   /* some elements can leave other elements behind after moving */
8169   if (ei->move_leave_element != EL_EMPTY &&
8170       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8171       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8172   {
8173     int move_leave_element = ei->move_leave_element;
8174
8175     /* this makes it possible to leave the removed element again */
8176     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8177       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8178
8179     Feld[x][y] = move_leave_element;
8180
8181     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8182       MovDir[x][y] = direction;
8183
8184     InitField(x, y, FALSE);
8185
8186     if (GFX_CRUMBLED(Feld[x][y]))
8187       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8188
8189     if (ELEM_IS_PLAYER(move_leave_element))
8190       RelocatePlayer(x, y, move_leave_element);
8191   }
8192
8193   /* do this after checking for left-behind element */
8194   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8195
8196   if (!CAN_MOVE(element) ||
8197       (CAN_FALL(element) && direction == MV_DOWN &&
8198        (element == EL_SPRING ||
8199         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8200         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8201     GfxDir[x][y] = MovDir[newx][newy] = 0;
8202
8203   TEST_DrawLevelField(x, y);
8204   TEST_DrawLevelField(newx, newy);
8205
8206   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8207
8208   /* prevent pushed element from moving on in pushed direction */
8209   if (pushed_by_player && CAN_MOVE(element) &&
8210       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8211       !(element_info[element].move_pattern & direction))
8212     TurnRound(newx, newy);
8213
8214   /* prevent elements on conveyor belt from moving on in last direction */
8215   if (pushed_by_conveyor && CAN_FALL(element) &&
8216       direction & MV_HORIZONTAL)
8217     MovDir[newx][newy] = 0;
8218
8219   if (!pushed_by_player)
8220   {
8221     int nextx = newx + dx, nexty = newy + dy;
8222     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8223
8224     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8225
8226     if (CAN_FALL(element) && direction == MV_DOWN)
8227       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8228
8229     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8230       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8231
8232     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8233       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8234   }
8235
8236   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8237   {
8238     TestIfBadThingTouchesPlayer(newx, newy);
8239     TestIfBadThingTouchesFriend(newx, newy);
8240
8241     if (!IS_CUSTOM_ELEMENT(element))
8242       TestIfBadThingTouchesOtherBadThing(newx, newy);
8243   }
8244   else if (element == EL_PENGUIN)
8245     TestIfFriendTouchesBadThing(newx, newy);
8246
8247   if (DONT_GET_HIT_BY(element))
8248   {
8249     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8250   }
8251
8252   /* give the player one last chance (one more frame) to move away */
8253   if (CAN_FALL(element) && direction == MV_DOWN &&
8254       (last_line || (!IS_FREE(x, newy + 1) &&
8255                      (!IS_PLAYER(x, newy + 1) ||
8256                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8257     Impact(x, newy);
8258
8259   if (pushed_by_player && !game.use_change_when_pushing_bug)
8260   {
8261     int push_side = MV_DIR_OPPOSITE(direction);
8262     struct PlayerInfo *player = PLAYERINFO(x, y);
8263
8264     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8265                                player->index_bit, push_side);
8266     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8267                                         player->index_bit, push_side);
8268   }
8269
8270   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8271     MovDelay[newx][newy] = 1;
8272
8273   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8274
8275   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8276   TestIfElementHitsCustomElement(newx, newy, direction);
8277   TestIfPlayerTouchesCustomElement(newx, newy);
8278   TestIfElementTouchesCustomElement(newx, newy);
8279
8280   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8281       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8282     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8283                              MV_DIR_OPPOSITE(direction));
8284 }
8285
8286 int AmoebeNachbarNr(int ax, int ay)
8287 {
8288   int i;
8289   int element = Feld[ax][ay];
8290   int group_nr = 0;
8291   static int xy[4][2] =
8292   {
8293     { 0, -1 },
8294     { -1, 0 },
8295     { +1, 0 },
8296     { 0, +1 }
8297   };
8298
8299   for (i = 0; i < NUM_DIRECTIONS; i++)
8300   {
8301     int x = ax + xy[i][0];
8302     int y = ay + xy[i][1];
8303
8304     if (!IN_LEV_FIELD(x, y))
8305       continue;
8306
8307     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8308       group_nr = AmoebaNr[x][y];
8309   }
8310
8311   return group_nr;
8312 }
8313
8314 void AmoebenVereinigen(int ax, int ay)
8315 {
8316   int i, x, y, xx, yy;
8317   int new_group_nr = AmoebaNr[ax][ay];
8318   static int xy[4][2] =
8319   {
8320     { 0, -1 },
8321     { -1, 0 },
8322     { +1, 0 },
8323     { 0, +1 }
8324   };
8325
8326   if (new_group_nr == 0)
8327     return;
8328
8329   for (i = 0; i < NUM_DIRECTIONS; i++)
8330   {
8331     x = ax + xy[i][0];
8332     y = ay + xy[i][1];
8333
8334     if (!IN_LEV_FIELD(x, y))
8335       continue;
8336
8337     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8338          Feld[x][y] == EL_BD_AMOEBA ||
8339          Feld[x][y] == EL_AMOEBA_DEAD) &&
8340         AmoebaNr[x][y] != new_group_nr)
8341     {
8342       int old_group_nr = AmoebaNr[x][y];
8343
8344       if (old_group_nr == 0)
8345         return;
8346
8347       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8348       AmoebaCnt[old_group_nr] = 0;
8349       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8350       AmoebaCnt2[old_group_nr] = 0;
8351
8352       SCAN_PLAYFIELD(xx, yy)
8353       {
8354         if (AmoebaNr[xx][yy] == old_group_nr)
8355           AmoebaNr[xx][yy] = new_group_nr;
8356       }
8357     }
8358   }
8359 }
8360
8361 void AmoebeUmwandeln(int ax, int ay)
8362 {
8363   int i, x, y;
8364
8365   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8366   {
8367     int group_nr = AmoebaNr[ax][ay];
8368
8369 #ifdef DEBUG
8370     if (group_nr == 0)
8371     {
8372       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8373       printf("AmoebeUmwandeln(): This should never happen!\n");
8374       return;
8375     }
8376 #endif
8377
8378     SCAN_PLAYFIELD(x, y)
8379     {
8380       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8381       {
8382         AmoebaNr[x][y] = 0;
8383         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8384       }
8385     }
8386
8387     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8388                             SND_AMOEBA_TURNING_TO_GEM :
8389                             SND_AMOEBA_TURNING_TO_ROCK));
8390     Bang(ax, ay);
8391   }
8392   else
8393   {
8394     static int xy[4][2] =
8395     {
8396       { 0, -1 },
8397       { -1, 0 },
8398       { +1, 0 },
8399       { 0, +1 }
8400     };
8401
8402     for (i = 0; i < NUM_DIRECTIONS; i++)
8403     {
8404       x = ax + xy[i][0];
8405       y = ay + xy[i][1];
8406
8407       if (!IN_LEV_FIELD(x, y))
8408         continue;
8409
8410       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8411       {
8412         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8413                               SND_AMOEBA_TURNING_TO_GEM :
8414                               SND_AMOEBA_TURNING_TO_ROCK));
8415         Bang(x, y);
8416       }
8417     }
8418   }
8419 }
8420
8421 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8422 {
8423   int x, y;
8424   int group_nr = AmoebaNr[ax][ay];
8425   boolean done = FALSE;
8426
8427 #ifdef DEBUG
8428   if (group_nr == 0)
8429   {
8430     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8431     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8432     return;
8433   }
8434 #endif
8435
8436   SCAN_PLAYFIELD(x, y)
8437   {
8438     if (AmoebaNr[x][y] == group_nr &&
8439         (Feld[x][y] == EL_AMOEBA_DEAD ||
8440          Feld[x][y] == EL_BD_AMOEBA ||
8441          Feld[x][y] == EL_AMOEBA_GROWING))
8442     {
8443       AmoebaNr[x][y] = 0;
8444       Feld[x][y] = new_element;
8445       InitField(x, y, FALSE);
8446       TEST_DrawLevelField(x, y);
8447       done = TRUE;
8448     }
8449   }
8450
8451   if (done)
8452     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8453                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8454                             SND_BD_AMOEBA_TURNING_TO_GEM));
8455 }
8456
8457 void AmoebeWaechst(int x, int y)
8458 {
8459   static unsigned int sound_delay = 0;
8460   static unsigned int sound_delay_value = 0;
8461
8462   if (!MovDelay[x][y])          /* start new growing cycle */
8463   {
8464     MovDelay[x][y] = 7;
8465
8466     if (DelayReached(&sound_delay, sound_delay_value))
8467     {
8468       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8469       sound_delay_value = 30;
8470     }
8471   }
8472
8473   if (MovDelay[x][y])           /* wait some time before growing bigger */
8474   {
8475     MovDelay[x][y]--;
8476     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8477     {
8478       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8479                                            6 - MovDelay[x][y]);
8480
8481       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8482     }
8483
8484     if (!MovDelay[x][y])
8485     {
8486       Feld[x][y] = Store[x][y];
8487       Store[x][y] = 0;
8488       TEST_DrawLevelField(x, y);
8489     }
8490   }
8491 }
8492
8493 void AmoebaDisappearing(int x, int y)
8494 {
8495   static unsigned int sound_delay = 0;
8496   static unsigned int sound_delay_value = 0;
8497
8498   if (!MovDelay[x][y])          /* start new shrinking cycle */
8499   {
8500     MovDelay[x][y] = 7;
8501
8502     if (DelayReached(&sound_delay, sound_delay_value))
8503       sound_delay_value = 30;
8504   }
8505
8506   if (MovDelay[x][y])           /* wait some time before shrinking */
8507   {
8508     MovDelay[x][y]--;
8509     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8510     {
8511       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8512                                            6 - MovDelay[x][y]);
8513
8514       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8515     }
8516
8517     if (!MovDelay[x][y])
8518     {
8519       Feld[x][y] = EL_EMPTY;
8520       TEST_DrawLevelField(x, y);
8521
8522       /* don't let mole enter this field in this cycle;
8523          (give priority to objects falling to this field from above) */
8524       Stop[x][y] = TRUE;
8525     }
8526   }
8527 }
8528
8529 void AmoebeAbleger(int ax, int ay)
8530 {
8531   int i;
8532   int element = Feld[ax][ay];
8533   int graphic = el2img(element);
8534   int newax = ax, neway = ay;
8535   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8536   static int xy[4][2] =
8537   {
8538     { 0, -1 },
8539     { -1, 0 },
8540     { +1, 0 },
8541     { 0, +1 }
8542   };
8543
8544   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8545   {
8546     Feld[ax][ay] = EL_AMOEBA_DEAD;
8547     TEST_DrawLevelField(ax, ay);
8548     return;
8549   }
8550
8551   if (IS_ANIMATED(graphic))
8552     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8553
8554   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8555     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8556
8557   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8558   {
8559     MovDelay[ax][ay]--;
8560     if (MovDelay[ax][ay])
8561       return;
8562   }
8563
8564   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8565   {
8566     int start = RND(4);
8567     int x = ax + xy[start][0];
8568     int y = ay + xy[start][1];
8569
8570     if (!IN_LEV_FIELD(x, y))
8571       return;
8572
8573     if (IS_FREE(x, y) ||
8574         CAN_GROW_INTO(Feld[x][y]) ||
8575         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8576         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8577     {
8578       newax = x;
8579       neway = y;
8580     }
8581
8582     if (newax == ax && neway == ay)
8583       return;
8584   }
8585   else                          /* normal or "filled" (BD style) amoeba */
8586   {
8587     int start = RND(4);
8588     boolean waiting_for_player = FALSE;
8589
8590     for (i = 0; i < NUM_DIRECTIONS; i++)
8591     {
8592       int j = (start + i) % 4;
8593       int x = ax + xy[j][0];
8594       int y = ay + xy[j][1];
8595
8596       if (!IN_LEV_FIELD(x, y))
8597         continue;
8598
8599       if (IS_FREE(x, y) ||
8600           CAN_GROW_INTO(Feld[x][y]) ||
8601           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8602           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8603       {
8604         newax = x;
8605         neway = y;
8606         break;
8607       }
8608       else if (IS_PLAYER(x, y))
8609         waiting_for_player = TRUE;
8610     }
8611
8612     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8613     {
8614       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8615       {
8616         Feld[ax][ay] = EL_AMOEBA_DEAD;
8617         TEST_DrawLevelField(ax, ay);
8618         AmoebaCnt[AmoebaNr[ax][ay]]--;
8619
8620         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8621         {
8622           if (element == EL_AMOEBA_FULL)
8623             AmoebeUmwandeln(ax, ay);
8624           else if (element == EL_BD_AMOEBA)
8625             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8626         }
8627       }
8628       return;
8629     }
8630     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8631     {
8632       /* amoeba gets larger by growing in some direction */
8633
8634       int new_group_nr = AmoebaNr[ax][ay];
8635
8636 #ifdef DEBUG
8637   if (new_group_nr == 0)
8638   {
8639     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8640     printf("AmoebeAbleger(): This should never happen!\n");
8641     return;
8642   }
8643 #endif
8644
8645       AmoebaNr[newax][neway] = new_group_nr;
8646       AmoebaCnt[new_group_nr]++;
8647       AmoebaCnt2[new_group_nr]++;
8648
8649       /* if amoeba touches other amoeba(s) after growing, unify them */
8650       AmoebenVereinigen(newax, neway);
8651
8652       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8653       {
8654         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8655         return;
8656       }
8657     }
8658   }
8659
8660   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8661       (neway == lev_fieldy - 1 && newax != ax))
8662   {
8663     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8664     Store[newax][neway] = element;
8665   }
8666   else if (neway == ay || element == EL_EMC_DRIPPER)
8667   {
8668     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8669
8670     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8671   }
8672   else
8673   {
8674     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8675     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8676     Store[ax][ay] = EL_AMOEBA_DROP;
8677     ContinueMoving(ax, ay);
8678     return;
8679   }
8680
8681   TEST_DrawLevelField(newax, neway);
8682 }
8683
8684 void Life(int ax, int ay)
8685 {
8686   int x1, y1, x2, y2;
8687   int life_time = 40;
8688   int element = Feld[ax][ay];
8689   int graphic = el2img(element);
8690   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8691                          level.biomaze);
8692   boolean changed = FALSE;
8693
8694   if (IS_ANIMATED(graphic))
8695     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8696
8697   if (Stop[ax][ay])
8698     return;
8699
8700   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8701     MovDelay[ax][ay] = life_time;
8702
8703   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8704   {
8705     MovDelay[ax][ay]--;
8706     if (MovDelay[ax][ay])
8707       return;
8708   }
8709
8710   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8711   {
8712     int xx = ax+x1, yy = ay+y1;
8713     int nachbarn = 0;
8714
8715     if (!IN_LEV_FIELD(xx, yy))
8716       continue;
8717
8718     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8719     {
8720       int x = xx+x2, y = yy+y2;
8721
8722       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8723         continue;
8724
8725       if (((Feld[x][y] == element ||
8726             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8727            !Stop[x][y]) ||
8728           (IS_FREE(x, y) && Stop[x][y]))
8729         nachbarn++;
8730     }
8731
8732     if (xx == ax && yy == ay)           /* field in the middle */
8733     {
8734       if (nachbarn < life_parameter[0] ||
8735           nachbarn > life_parameter[1])
8736       {
8737         Feld[xx][yy] = EL_EMPTY;
8738         if (!Stop[xx][yy])
8739           TEST_DrawLevelField(xx, yy);
8740         Stop[xx][yy] = TRUE;
8741         changed = TRUE;
8742       }
8743     }
8744     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8745     {                                   /* free border field */
8746       if (nachbarn >= life_parameter[2] &&
8747           nachbarn <= life_parameter[3])
8748       {
8749         Feld[xx][yy] = element;
8750         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8751         if (!Stop[xx][yy])
8752           TEST_DrawLevelField(xx, yy);
8753         Stop[xx][yy] = TRUE;
8754         changed = TRUE;
8755       }
8756     }
8757   }
8758
8759   if (changed)
8760     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8761                    SND_GAME_OF_LIFE_GROWING);
8762 }
8763
8764 static void InitRobotWheel(int x, int y)
8765 {
8766   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8767 }
8768
8769 static void RunRobotWheel(int x, int y)
8770 {
8771   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8772 }
8773
8774 static void StopRobotWheel(int x, int y)
8775 {
8776   if (ZX == x && ZY == y)
8777   {
8778     ZX = ZY = -1;
8779
8780     game.robot_wheel_active = FALSE;
8781   }
8782 }
8783
8784 static void InitTimegateWheel(int x, int y)
8785 {
8786   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8787 }
8788
8789 static void RunTimegateWheel(int x, int y)
8790 {
8791   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8792 }
8793
8794 static void InitMagicBallDelay(int x, int y)
8795 {
8796   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8797 }
8798
8799 static void ActivateMagicBall(int bx, int by)
8800 {
8801   int x, y;
8802
8803   if (level.ball_random)
8804   {
8805     int pos_border = RND(8);    /* select one of the eight border elements */
8806     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8807     int xx = pos_content % 3;
8808     int yy = pos_content / 3;
8809
8810     x = bx - 1 + xx;
8811     y = by - 1 + yy;
8812
8813     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8814       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8815   }
8816   else
8817   {
8818     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8819     {
8820       int xx = x - bx + 1;
8821       int yy = y - by + 1;
8822
8823       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8824         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8825     }
8826   }
8827
8828   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8829 }
8830
8831 void CheckExit(int x, int y)
8832 {
8833   if (local_player->gems_still_needed > 0 ||
8834       local_player->sokobanfields_still_needed > 0 ||
8835       local_player->lights_still_needed > 0)
8836   {
8837     int element = Feld[x][y];
8838     int graphic = el2img(element);
8839
8840     if (IS_ANIMATED(graphic))
8841       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8842
8843     return;
8844   }
8845
8846   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8847     return;
8848
8849   Feld[x][y] = EL_EXIT_OPENING;
8850
8851   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8852 }
8853
8854 void CheckExitEM(int x, int y)
8855 {
8856   if (local_player->gems_still_needed > 0 ||
8857       local_player->sokobanfields_still_needed > 0 ||
8858       local_player->lights_still_needed > 0)
8859   {
8860     int element = Feld[x][y];
8861     int graphic = el2img(element);
8862
8863     if (IS_ANIMATED(graphic))
8864       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8865
8866     return;
8867   }
8868
8869   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8870     return;
8871
8872   Feld[x][y] = EL_EM_EXIT_OPENING;
8873
8874   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8875 }
8876
8877 void CheckExitSteel(int x, int y)
8878 {
8879   if (local_player->gems_still_needed > 0 ||
8880       local_player->sokobanfields_still_needed > 0 ||
8881       local_player->lights_still_needed > 0)
8882   {
8883     int element = Feld[x][y];
8884     int graphic = el2img(element);
8885
8886     if (IS_ANIMATED(graphic))
8887       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8888
8889     return;
8890   }
8891
8892   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8893     return;
8894
8895   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8896
8897   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8898 }
8899
8900 void CheckExitSteelEM(int x, int y)
8901 {
8902   if (local_player->gems_still_needed > 0 ||
8903       local_player->sokobanfields_still_needed > 0 ||
8904       local_player->lights_still_needed > 0)
8905   {
8906     int element = Feld[x][y];
8907     int graphic = el2img(element);
8908
8909     if (IS_ANIMATED(graphic))
8910       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8911
8912     return;
8913   }
8914
8915   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8916     return;
8917
8918   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8919
8920   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8921 }
8922
8923 void CheckExitSP(int x, int y)
8924 {
8925   if (local_player->gems_still_needed > 0)
8926   {
8927     int element = Feld[x][y];
8928     int graphic = el2img(element);
8929
8930     if (IS_ANIMATED(graphic))
8931       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8932
8933     return;
8934   }
8935
8936   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8937     return;
8938
8939   Feld[x][y] = EL_SP_EXIT_OPENING;
8940
8941   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8942 }
8943
8944 static void CloseAllOpenTimegates()
8945 {
8946   int x, y;
8947
8948   SCAN_PLAYFIELD(x, y)
8949   {
8950     int element = Feld[x][y];
8951
8952     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8953     {
8954       Feld[x][y] = EL_TIMEGATE_CLOSING;
8955
8956       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8957     }
8958   }
8959 }
8960
8961 void DrawTwinkleOnField(int x, int y)
8962 {
8963   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8964     return;
8965
8966   if (Feld[x][y] == EL_BD_DIAMOND)
8967     return;
8968
8969   if (MovDelay[x][y] == 0)      /* next animation frame */
8970     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8971
8972   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8973   {
8974     MovDelay[x][y]--;
8975
8976     DrawLevelElementAnimation(x, y, Feld[x][y]);
8977
8978     if (MovDelay[x][y] != 0)
8979     {
8980       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8981                                            10 - MovDelay[x][y]);
8982
8983       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8984     }
8985   }
8986 }
8987
8988 void MauerWaechst(int x, int y)
8989 {
8990   int delay = 6;
8991
8992   if (!MovDelay[x][y])          /* next animation frame */
8993     MovDelay[x][y] = 3 * delay;
8994
8995   if (MovDelay[x][y])           /* wait some time before next frame */
8996   {
8997     MovDelay[x][y]--;
8998
8999     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9000     {
9001       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9002       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9003
9004       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9005     }
9006
9007     if (!MovDelay[x][y])
9008     {
9009       if (MovDir[x][y] == MV_LEFT)
9010       {
9011         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9012           TEST_DrawLevelField(x - 1, y);
9013       }
9014       else if (MovDir[x][y] == MV_RIGHT)
9015       {
9016         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9017           TEST_DrawLevelField(x + 1, y);
9018       }
9019       else if (MovDir[x][y] == MV_UP)
9020       {
9021         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9022           TEST_DrawLevelField(x, y - 1);
9023       }
9024       else
9025       {
9026         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9027           TEST_DrawLevelField(x, y + 1);
9028       }
9029
9030       Feld[x][y] = Store[x][y];
9031       Store[x][y] = 0;
9032       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9033       TEST_DrawLevelField(x, y);
9034     }
9035   }
9036 }
9037
9038 void MauerAbleger(int ax, int ay)
9039 {
9040   int element = Feld[ax][ay];
9041   int graphic = el2img(element);
9042   boolean oben_frei = FALSE, unten_frei = FALSE;
9043   boolean links_frei = FALSE, rechts_frei = FALSE;
9044   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9045   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9046   boolean new_wall = FALSE;
9047
9048   if (IS_ANIMATED(graphic))
9049     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9050
9051   if (!MovDelay[ax][ay])        /* start building new wall */
9052     MovDelay[ax][ay] = 6;
9053
9054   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9055   {
9056     MovDelay[ax][ay]--;
9057     if (MovDelay[ax][ay])
9058       return;
9059   }
9060
9061   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9062     oben_frei = TRUE;
9063   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9064     unten_frei = TRUE;
9065   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9066     links_frei = TRUE;
9067   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9068     rechts_frei = TRUE;
9069
9070   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9071       element == EL_EXPANDABLE_WALL_ANY)
9072   {
9073     if (oben_frei)
9074     {
9075       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9076       Store[ax][ay-1] = element;
9077       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9078       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9079         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9080                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9081       new_wall = TRUE;
9082     }
9083     if (unten_frei)
9084     {
9085       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9086       Store[ax][ay+1] = element;
9087       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9088       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9089         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9090                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9091       new_wall = TRUE;
9092     }
9093   }
9094
9095   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9096       element == EL_EXPANDABLE_WALL_ANY ||
9097       element == EL_EXPANDABLE_WALL ||
9098       element == EL_BD_EXPANDABLE_WALL)
9099   {
9100     if (links_frei)
9101     {
9102       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9103       Store[ax-1][ay] = element;
9104       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9105       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9106         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9107                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9108       new_wall = TRUE;
9109     }
9110
9111     if (rechts_frei)
9112     {
9113       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9114       Store[ax+1][ay] = element;
9115       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9116       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9117         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9118                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9119       new_wall = TRUE;
9120     }
9121   }
9122
9123   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9124     TEST_DrawLevelField(ax, ay);
9125
9126   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9127     oben_massiv = TRUE;
9128   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9129     unten_massiv = TRUE;
9130   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9131     links_massiv = TRUE;
9132   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9133     rechts_massiv = TRUE;
9134
9135   if (((oben_massiv && unten_massiv) ||
9136        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9137        element == EL_EXPANDABLE_WALL) &&
9138       ((links_massiv && rechts_massiv) ||
9139        element == EL_EXPANDABLE_WALL_VERTICAL))
9140     Feld[ax][ay] = EL_WALL;
9141
9142   if (new_wall)
9143     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9144 }
9145
9146 void MauerAblegerStahl(int ax, int ay)
9147 {
9148   int element = Feld[ax][ay];
9149   int graphic = el2img(element);
9150   boolean oben_frei = FALSE, unten_frei = FALSE;
9151   boolean links_frei = FALSE, rechts_frei = FALSE;
9152   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9153   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9154   boolean new_wall = FALSE;
9155
9156   if (IS_ANIMATED(graphic))
9157     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9158
9159   if (!MovDelay[ax][ay])        /* start building new wall */
9160     MovDelay[ax][ay] = 6;
9161
9162   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9163   {
9164     MovDelay[ax][ay]--;
9165     if (MovDelay[ax][ay])
9166       return;
9167   }
9168
9169   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9170     oben_frei = TRUE;
9171   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9172     unten_frei = TRUE;
9173   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9174     links_frei = TRUE;
9175   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9176     rechts_frei = TRUE;
9177
9178   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9179       element == EL_EXPANDABLE_STEELWALL_ANY)
9180   {
9181     if (oben_frei)
9182     {
9183       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9184       Store[ax][ay-1] = element;
9185       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9186       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9187         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9188                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9189       new_wall = TRUE;
9190     }
9191     if (unten_frei)
9192     {
9193       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9194       Store[ax][ay+1] = element;
9195       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9196       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9197         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9198                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9199       new_wall = TRUE;
9200     }
9201   }
9202
9203   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9204       element == EL_EXPANDABLE_STEELWALL_ANY)
9205   {
9206     if (links_frei)
9207     {
9208       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9209       Store[ax-1][ay] = element;
9210       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9211       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9212         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9213                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9214       new_wall = TRUE;
9215     }
9216
9217     if (rechts_frei)
9218     {
9219       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9220       Store[ax+1][ay] = element;
9221       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9222       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9223         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9224                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9225       new_wall = TRUE;
9226     }
9227   }
9228
9229   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9230     oben_massiv = TRUE;
9231   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9232     unten_massiv = TRUE;
9233   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9234     links_massiv = TRUE;
9235   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9236     rechts_massiv = TRUE;
9237
9238   if (((oben_massiv && unten_massiv) ||
9239        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9240       ((links_massiv && rechts_massiv) ||
9241        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9242     Feld[ax][ay] = EL_STEELWALL;
9243
9244   if (new_wall)
9245     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9246 }
9247
9248 void CheckForDragon(int x, int y)
9249 {
9250   int i, j;
9251   boolean dragon_found = FALSE;
9252   static int xy[4][2] =
9253   {
9254     { 0, -1 },
9255     { -1, 0 },
9256     { +1, 0 },
9257     { 0, +1 }
9258   };
9259
9260   for (i = 0; i < NUM_DIRECTIONS; i++)
9261   {
9262     for (j = 0; j < 4; j++)
9263     {
9264       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9265
9266       if (IN_LEV_FIELD(xx, yy) &&
9267           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9268       {
9269         if (Feld[xx][yy] == EL_DRAGON)
9270           dragon_found = TRUE;
9271       }
9272       else
9273         break;
9274     }
9275   }
9276
9277   if (!dragon_found)
9278   {
9279     for (i = 0; i < NUM_DIRECTIONS; i++)
9280     {
9281       for (j = 0; j < 3; j++)
9282       {
9283         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9284   
9285         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9286         {
9287           Feld[xx][yy] = EL_EMPTY;
9288           TEST_DrawLevelField(xx, yy);
9289         }
9290         else
9291           break;
9292       }
9293     }
9294   }
9295 }
9296
9297 static void InitBuggyBase(int x, int y)
9298 {
9299   int element = Feld[x][y];
9300   int activating_delay = FRAMES_PER_SECOND / 4;
9301
9302   ChangeDelay[x][y] =
9303     (element == EL_SP_BUGGY_BASE ?
9304      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9305      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9306      activating_delay :
9307      element == EL_SP_BUGGY_BASE_ACTIVE ?
9308      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9309 }
9310
9311 static void WarnBuggyBase(int x, int y)
9312 {
9313   int i;
9314   static int xy[4][2] =
9315   {
9316     { 0, -1 },
9317     { -1, 0 },
9318     { +1, 0 },
9319     { 0, +1 }
9320   };
9321
9322   for (i = 0; i < NUM_DIRECTIONS; i++)
9323   {
9324     int xx = x + xy[i][0];
9325     int yy = y + xy[i][1];
9326
9327     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9328     {
9329       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9330
9331       break;
9332     }
9333   }
9334 }
9335
9336 static void InitTrap(int x, int y)
9337 {
9338   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9339 }
9340
9341 static void ActivateTrap(int x, int y)
9342 {
9343   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9344 }
9345
9346 static void ChangeActiveTrap(int x, int y)
9347 {
9348   int graphic = IMG_TRAP_ACTIVE;
9349
9350   /* if new animation frame was drawn, correct crumbled sand border */
9351   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9352     TEST_DrawLevelFieldCrumbled(x, y);
9353 }
9354
9355 static int getSpecialActionElement(int element, int number, int base_element)
9356 {
9357   return (element != EL_EMPTY ? element :
9358           number != -1 ? base_element + number - 1 :
9359           EL_EMPTY);
9360 }
9361
9362 static int getModifiedActionNumber(int value_old, int operator, int operand,
9363                                    int value_min, int value_max)
9364 {
9365   int value_new = (operator == CA_MODE_SET      ? operand :
9366                    operator == CA_MODE_ADD      ? value_old + operand :
9367                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9368                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9369                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9370                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9371                    value_old);
9372
9373   return (value_new < value_min ? value_min :
9374           value_new > value_max ? value_max :
9375           value_new);
9376 }
9377
9378 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9379 {
9380   struct ElementInfo *ei = &element_info[element];
9381   struct ElementChangeInfo *change = &ei->change_page[page];
9382   int target_element = change->target_element;
9383   int action_type = change->action_type;
9384   int action_mode = change->action_mode;
9385   int action_arg = change->action_arg;
9386   int action_element = change->action_element;
9387   int i;
9388
9389   if (!change->has_action)
9390     return;
9391
9392   /* ---------- determine action paramater values -------------------------- */
9393
9394   int level_time_value =
9395     (level.time > 0 ? TimeLeft :
9396      TimePlayed);
9397
9398   int action_arg_element_raw =
9399     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9400      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9401      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9402      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9403      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9404      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9405      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9406      EL_EMPTY);
9407   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9408
9409   int action_arg_direction =
9410     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9411      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9412      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9413      change->actual_trigger_side :
9414      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9415      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9416      MV_NONE);
9417
9418   int action_arg_number_min =
9419     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9420      CA_ARG_MIN);
9421
9422   int action_arg_number_max =
9423     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9424      action_type == CA_SET_LEVEL_GEMS ? 999 :
9425      action_type == CA_SET_LEVEL_TIME ? 9999 :
9426      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9427      action_type == CA_SET_CE_VALUE ? 9999 :
9428      action_type == CA_SET_CE_SCORE ? 9999 :
9429      CA_ARG_MAX);
9430
9431   int action_arg_number_reset =
9432     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9433      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9434      action_type == CA_SET_LEVEL_TIME ? level.time :
9435      action_type == CA_SET_LEVEL_SCORE ? 0 :
9436      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9437      action_type == CA_SET_CE_SCORE ? 0 :
9438      0);
9439
9440   int action_arg_number =
9441     (action_arg <= CA_ARG_MAX ? action_arg :
9442      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9443      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9444      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9445      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9446      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9447      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9448      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9449      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9450      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9451      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9452      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9453      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9454      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9455      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9456      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9457      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9458      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9459      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9460      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9461      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9462      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9463      -1);
9464
9465   int action_arg_number_old =
9466     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9467      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9468      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9469      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9470      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9471      0);
9472
9473   int action_arg_number_new =
9474     getModifiedActionNumber(action_arg_number_old,
9475                             action_mode, action_arg_number,
9476                             action_arg_number_min, action_arg_number_max);
9477
9478   int trigger_player_bits =
9479     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9480      change->actual_trigger_player_bits : change->trigger_player);
9481
9482   int action_arg_player_bits =
9483     (action_arg >= CA_ARG_PLAYER_1 &&
9484      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9485      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9486      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9487      PLAYER_BITS_ANY);
9488
9489   /* ---------- execute action  -------------------------------------------- */
9490
9491   switch (action_type)
9492   {
9493     case CA_NO_ACTION:
9494     {
9495       return;
9496     }
9497
9498     /* ---------- level actions  ------------------------------------------- */
9499
9500     case CA_RESTART_LEVEL:
9501     {
9502       game.restart_level = TRUE;
9503
9504       break;
9505     }
9506
9507     case CA_SHOW_ENVELOPE:
9508     {
9509       int element = getSpecialActionElement(action_arg_element,
9510                                             action_arg_number, EL_ENVELOPE_1);
9511
9512       if (IS_ENVELOPE(element))
9513         local_player->show_envelope = element;
9514
9515       break;
9516     }
9517
9518     case CA_SET_LEVEL_TIME:
9519     {
9520       if (level.time > 0)       /* only modify limited time value */
9521       {
9522         TimeLeft = action_arg_number_new;
9523
9524         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9525
9526         DisplayGameControlValues();
9527
9528         if (!TimeLeft && setup.time_limit)
9529           for (i = 0; i < MAX_PLAYERS; i++)
9530             KillPlayer(&stored_player[i]);
9531       }
9532
9533       break;
9534     }
9535
9536     case CA_SET_LEVEL_SCORE:
9537     {
9538       local_player->score = action_arg_number_new;
9539
9540       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9541
9542       DisplayGameControlValues();
9543
9544       break;
9545     }
9546
9547     case CA_SET_LEVEL_GEMS:
9548     {
9549       local_player->gems_still_needed = action_arg_number_new;
9550
9551       game_panel_controls[GAME_PANEL_GEMS].value =
9552         local_player->gems_still_needed;
9553
9554       DisplayGameControlValues();
9555
9556       break;
9557     }
9558
9559     case CA_SET_LEVEL_WIND:
9560     {
9561       game.wind_direction = action_arg_direction;
9562
9563       break;
9564     }
9565
9566     case CA_SET_LEVEL_RANDOM_SEED:
9567     {
9568       /* ensure that setting a new random seed while playing is predictable */
9569       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9570
9571       break;
9572     }
9573
9574     /* ---------- player actions  ------------------------------------------ */
9575
9576     case CA_MOVE_PLAYER:
9577     {
9578       /* automatically move to the next field in specified direction */
9579       for (i = 0; i < MAX_PLAYERS; i++)
9580         if (trigger_player_bits & (1 << i))
9581           stored_player[i].programmed_action = action_arg_direction;
9582
9583       break;
9584     }
9585
9586     case CA_EXIT_PLAYER:
9587     {
9588       for (i = 0; i < MAX_PLAYERS; i++)
9589         if (action_arg_player_bits & (1 << i))
9590           PlayerWins(&stored_player[i]);
9591
9592       break;
9593     }
9594
9595     case CA_KILL_PLAYER:
9596     {
9597       for (i = 0; i < MAX_PLAYERS; i++)
9598         if (action_arg_player_bits & (1 << i))
9599           KillPlayer(&stored_player[i]);
9600
9601       break;
9602     }
9603
9604     case CA_SET_PLAYER_KEYS:
9605     {
9606       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9607       int element = getSpecialActionElement(action_arg_element,
9608                                             action_arg_number, EL_KEY_1);
9609
9610       if (IS_KEY(element))
9611       {
9612         for (i = 0; i < MAX_PLAYERS; i++)
9613         {
9614           if (trigger_player_bits & (1 << i))
9615           {
9616             stored_player[i].key[KEY_NR(element)] = key_state;
9617
9618             DrawGameDoorValues();
9619           }
9620         }
9621       }
9622
9623       break;
9624     }
9625
9626     case CA_SET_PLAYER_SPEED:
9627     {
9628       for (i = 0; i < MAX_PLAYERS; i++)
9629       {
9630         if (trigger_player_bits & (1 << i))
9631         {
9632           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9633
9634           if (action_arg == CA_ARG_SPEED_FASTER &&
9635               stored_player[i].cannot_move)
9636           {
9637             action_arg_number = STEPSIZE_VERY_SLOW;
9638           }
9639           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9640                    action_arg == CA_ARG_SPEED_FASTER)
9641           {
9642             action_arg_number = 2;
9643             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9644                            CA_MODE_MULTIPLY);
9645           }
9646           else if (action_arg == CA_ARG_NUMBER_RESET)
9647           {
9648             action_arg_number = level.initial_player_stepsize[i];
9649           }
9650
9651           move_stepsize =
9652             getModifiedActionNumber(move_stepsize,
9653                                     action_mode,
9654                                     action_arg_number,
9655                                     action_arg_number_min,
9656                                     action_arg_number_max);
9657
9658           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9659         }
9660       }
9661
9662       break;
9663     }
9664
9665     case CA_SET_PLAYER_SHIELD:
9666     {
9667       for (i = 0; i < MAX_PLAYERS; i++)
9668       {
9669         if (trigger_player_bits & (1 << i))
9670         {
9671           if (action_arg == CA_ARG_SHIELD_OFF)
9672           {
9673             stored_player[i].shield_normal_time_left = 0;
9674             stored_player[i].shield_deadly_time_left = 0;
9675           }
9676           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9677           {
9678             stored_player[i].shield_normal_time_left = 999999;
9679           }
9680           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9681           {
9682             stored_player[i].shield_normal_time_left = 999999;
9683             stored_player[i].shield_deadly_time_left = 999999;
9684           }
9685         }
9686       }
9687
9688       break;
9689     }
9690
9691     case CA_SET_PLAYER_GRAVITY:
9692     {
9693       for (i = 0; i < MAX_PLAYERS; i++)
9694       {
9695         if (trigger_player_bits & (1 << i))
9696         {
9697           stored_player[i].gravity =
9698             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9699              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9700              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9701              stored_player[i].gravity);
9702         }
9703       }
9704
9705       break;
9706     }
9707
9708     case CA_SET_PLAYER_ARTWORK:
9709     {
9710       for (i = 0; i < MAX_PLAYERS; i++)
9711       {
9712         if (trigger_player_bits & (1 << i))
9713         {
9714           int artwork_element = action_arg_element;
9715
9716           if (action_arg == CA_ARG_ELEMENT_RESET)
9717             artwork_element =
9718               (level.use_artwork_element[i] ? level.artwork_element[i] :
9719                stored_player[i].element_nr);
9720
9721           if (stored_player[i].artwork_element != artwork_element)
9722             stored_player[i].Frame = 0;
9723
9724           stored_player[i].artwork_element = artwork_element;
9725
9726           SetPlayerWaiting(&stored_player[i], FALSE);
9727
9728           /* set number of special actions for bored and sleeping animation */
9729           stored_player[i].num_special_action_bored =
9730             get_num_special_action(artwork_element,
9731                                    ACTION_BORING_1, ACTION_BORING_LAST);
9732           stored_player[i].num_special_action_sleeping =
9733             get_num_special_action(artwork_element,
9734                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9735         }
9736       }
9737
9738       break;
9739     }
9740
9741     case CA_SET_PLAYER_INVENTORY:
9742     {
9743       for (i = 0; i < MAX_PLAYERS; i++)
9744       {
9745         struct PlayerInfo *player = &stored_player[i];
9746         int j, k;
9747
9748         if (trigger_player_bits & (1 << i))
9749         {
9750           int inventory_element = action_arg_element;
9751
9752           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9753               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9754               action_arg == CA_ARG_ELEMENT_ACTION)
9755           {
9756             int element = inventory_element;
9757             int collect_count = element_info[element].collect_count_initial;
9758
9759             if (!IS_CUSTOM_ELEMENT(element))
9760               collect_count = 1;
9761
9762             if (collect_count == 0)
9763               player->inventory_infinite_element = element;
9764             else
9765               for (k = 0; k < collect_count; k++)
9766                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9767                   player->inventory_element[player->inventory_size++] =
9768                     element;
9769           }
9770           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9771                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9772                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9773           {
9774             if (player->inventory_infinite_element != EL_UNDEFINED &&
9775                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9776                                      action_arg_element_raw))
9777               player->inventory_infinite_element = EL_UNDEFINED;
9778
9779             for (k = 0, j = 0; j < player->inventory_size; j++)
9780             {
9781               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9782                                         action_arg_element_raw))
9783                 player->inventory_element[k++] = player->inventory_element[j];
9784             }
9785
9786             player->inventory_size = k;
9787           }
9788           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9789           {
9790             if (player->inventory_size > 0)
9791             {
9792               for (j = 0; j < player->inventory_size - 1; j++)
9793                 player->inventory_element[j] = player->inventory_element[j + 1];
9794
9795               player->inventory_size--;
9796             }
9797           }
9798           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9799           {
9800             if (player->inventory_size > 0)
9801               player->inventory_size--;
9802           }
9803           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9804           {
9805             player->inventory_infinite_element = EL_UNDEFINED;
9806             player->inventory_size = 0;
9807           }
9808           else if (action_arg == CA_ARG_INVENTORY_RESET)
9809           {
9810             player->inventory_infinite_element = EL_UNDEFINED;
9811             player->inventory_size = 0;
9812
9813             if (level.use_initial_inventory[i])
9814             {
9815               for (j = 0; j < level.initial_inventory_size[i]; j++)
9816               {
9817                 int element = level.initial_inventory_content[i][j];
9818                 int collect_count = element_info[element].collect_count_initial;
9819
9820                 if (!IS_CUSTOM_ELEMENT(element))
9821                   collect_count = 1;
9822
9823                 if (collect_count == 0)
9824                   player->inventory_infinite_element = element;
9825                 else
9826                   for (k = 0; k < collect_count; k++)
9827                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9828                       player->inventory_element[player->inventory_size++] =
9829                         element;
9830               }
9831             }
9832           }
9833         }
9834       }
9835
9836       break;
9837     }
9838
9839     /* ---------- CE actions  ---------------------------------------------- */
9840
9841     case CA_SET_CE_VALUE:
9842     {
9843       int last_ce_value = CustomValue[x][y];
9844
9845       CustomValue[x][y] = action_arg_number_new;
9846
9847       if (CustomValue[x][y] != last_ce_value)
9848       {
9849         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9850         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9851
9852         if (CustomValue[x][y] == 0)
9853         {
9854           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9855           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9856         }
9857       }
9858
9859       break;
9860     }
9861
9862     case CA_SET_CE_SCORE:
9863     {
9864       int last_ce_score = ei->collect_score;
9865
9866       ei->collect_score = action_arg_number_new;
9867
9868       if (ei->collect_score != last_ce_score)
9869       {
9870         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9871         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9872
9873         if (ei->collect_score == 0)
9874         {
9875           int xx, yy;
9876
9877           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9878           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9879
9880           /*
9881             This is a very special case that seems to be a mixture between
9882             CheckElementChange() and CheckTriggeredElementChange(): while
9883             the first one only affects single elements that are triggered
9884             directly, the second one affects multiple elements in the playfield
9885             that are triggered indirectly by another element. This is a third
9886             case: Changing the CE score always affects multiple identical CEs,
9887             so every affected CE must be checked, not only the single CE for
9888             which the CE score was changed in the first place (as every instance
9889             of that CE shares the same CE score, and therefore also can change)!
9890           */
9891           SCAN_PLAYFIELD(xx, yy)
9892           {
9893             if (Feld[xx][yy] == element)
9894               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9895                                  CE_SCORE_GETS_ZERO);
9896           }
9897         }
9898       }
9899
9900       break;
9901     }
9902
9903     case CA_SET_CE_ARTWORK:
9904     {
9905       int artwork_element = action_arg_element;
9906       boolean reset_frame = FALSE;
9907       int xx, yy;
9908
9909       if (action_arg == CA_ARG_ELEMENT_RESET)
9910         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9911                            element);
9912
9913       if (ei->gfx_element != artwork_element)
9914         reset_frame = TRUE;
9915
9916       ei->gfx_element = artwork_element;
9917
9918       SCAN_PLAYFIELD(xx, yy)
9919       {
9920         if (Feld[xx][yy] == element)
9921         {
9922           if (reset_frame)
9923           {
9924             ResetGfxAnimation(xx, yy);
9925             ResetRandomAnimationValue(xx, yy);
9926           }
9927
9928           TEST_DrawLevelField(xx, yy);
9929         }
9930       }
9931
9932       break;
9933     }
9934
9935     /* ---------- engine actions  ------------------------------------------ */
9936
9937     case CA_SET_ENGINE_SCAN_MODE:
9938     {
9939       InitPlayfieldScanMode(action_arg);
9940
9941       break;
9942     }
9943
9944     default:
9945       break;
9946   }
9947 }
9948
9949 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9950 {
9951   int old_element = Feld[x][y];
9952   int new_element = GetElementFromGroupElement(element);
9953   int previous_move_direction = MovDir[x][y];
9954   int last_ce_value = CustomValue[x][y];
9955   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9956   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9957   boolean add_player_onto_element = (new_element_is_player &&
9958                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9959                                      IS_WALKABLE(old_element));
9960
9961   if (!add_player_onto_element)
9962   {
9963     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9964       RemoveMovingField(x, y);
9965     else
9966       RemoveField(x, y);
9967
9968     Feld[x][y] = new_element;
9969
9970     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9971       MovDir[x][y] = previous_move_direction;
9972
9973     if (element_info[new_element].use_last_ce_value)
9974       CustomValue[x][y] = last_ce_value;
9975
9976     InitField_WithBug1(x, y, FALSE);
9977
9978     new_element = Feld[x][y];   /* element may have changed */
9979
9980     ResetGfxAnimation(x, y);
9981     ResetRandomAnimationValue(x, y);
9982
9983     TEST_DrawLevelField(x, y);
9984
9985     if (GFX_CRUMBLED(new_element))
9986       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9987   }
9988
9989   /* check if element under the player changes from accessible to unaccessible
9990      (needed for special case of dropping element which then changes) */
9991   /* (must be checked after creating new element for walkable group elements) */
9992   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9993       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9994   {
9995     Bang(x, y);
9996
9997     return;
9998   }
9999
10000   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10001   if (new_element_is_player)
10002     RelocatePlayer(x, y, new_element);
10003
10004   if (is_change)
10005     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10006
10007   TestIfBadThingTouchesPlayer(x, y);
10008   TestIfPlayerTouchesCustomElement(x, y);
10009   TestIfElementTouchesCustomElement(x, y);
10010 }
10011
10012 static void CreateField(int x, int y, int element)
10013 {
10014   CreateFieldExt(x, y, element, FALSE);
10015 }
10016
10017 static void CreateElementFromChange(int x, int y, int element)
10018 {
10019   element = GET_VALID_RUNTIME_ELEMENT(element);
10020
10021   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10022   {
10023     int old_element = Feld[x][y];
10024
10025     /* prevent changed element from moving in same engine frame
10026        unless both old and new element can either fall or move */
10027     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10028         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10029       Stop[x][y] = TRUE;
10030   }
10031
10032   CreateFieldExt(x, y, element, TRUE);
10033 }
10034
10035 static boolean ChangeElement(int x, int y, int element, int page)
10036 {
10037   struct ElementInfo *ei = &element_info[element];
10038   struct ElementChangeInfo *change = &ei->change_page[page];
10039   int ce_value = CustomValue[x][y];
10040   int ce_score = ei->collect_score;
10041   int target_element;
10042   int old_element = Feld[x][y];
10043
10044   /* always use default change event to prevent running into a loop */
10045   if (ChangeEvent[x][y] == -1)
10046     ChangeEvent[x][y] = CE_DELAY;
10047
10048   if (ChangeEvent[x][y] == CE_DELAY)
10049   {
10050     /* reset actual trigger element, trigger player and action element */
10051     change->actual_trigger_element = EL_EMPTY;
10052     change->actual_trigger_player = EL_EMPTY;
10053     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10054     change->actual_trigger_side = CH_SIDE_NONE;
10055     change->actual_trigger_ce_value = 0;
10056     change->actual_trigger_ce_score = 0;
10057   }
10058
10059   /* do not change elements more than a specified maximum number of changes */
10060   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10061     return FALSE;
10062
10063   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10064
10065   if (change->explode)
10066   {
10067     Bang(x, y);
10068
10069     return TRUE;
10070   }
10071
10072   if (change->use_target_content)
10073   {
10074     boolean complete_replace = TRUE;
10075     boolean can_replace[3][3];
10076     int xx, yy;
10077
10078     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10079     {
10080       boolean is_empty;
10081       boolean is_walkable;
10082       boolean is_diggable;
10083       boolean is_collectible;
10084       boolean is_removable;
10085       boolean is_destructible;
10086       int ex = x + xx - 1;
10087       int ey = y + yy - 1;
10088       int content_element = change->target_content.e[xx][yy];
10089       int e;
10090
10091       can_replace[xx][yy] = TRUE;
10092
10093       if (ex == x && ey == y)   /* do not check changing element itself */
10094         continue;
10095
10096       if (content_element == EL_EMPTY_SPACE)
10097       {
10098         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10099
10100         continue;
10101       }
10102
10103       if (!IN_LEV_FIELD(ex, ey))
10104       {
10105         can_replace[xx][yy] = FALSE;
10106         complete_replace = FALSE;
10107
10108         continue;
10109       }
10110
10111       e = Feld[ex][ey];
10112
10113       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10114         e = MovingOrBlocked2Element(ex, ey);
10115
10116       is_empty = (IS_FREE(ex, ey) ||
10117                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10118
10119       is_walkable     = (is_empty || IS_WALKABLE(e));
10120       is_diggable     = (is_empty || IS_DIGGABLE(e));
10121       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10122       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10123       is_removable    = (is_diggable || is_collectible);
10124
10125       can_replace[xx][yy] =
10126         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10127           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10128           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10129           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10130           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10131           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10132          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10133
10134       if (!can_replace[xx][yy])
10135         complete_replace = FALSE;
10136     }
10137
10138     if (!change->only_if_complete || complete_replace)
10139     {
10140       boolean something_has_changed = FALSE;
10141
10142       if (change->only_if_complete && change->use_random_replace &&
10143           RND(100) < change->random_percentage)
10144         return FALSE;
10145
10146       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10147       {
10148         int ex = x + xx - 1;
10149         int ey = y + yy - 1;
10150         int content_element;
10151
10152         if (can_replace[xx][yy] && (!change->use_random_replace ||
10153                                     RND(100) < change->random_percentage))
10154         {
10155           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10156             RemoveMovingField(ex, ey);
10157
10158           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10159
10160           content_element = change->target_content.e[xx][yy];
10161           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10162                                               ce_value, ce_score);
10163
10164           CreateElementFromChange(ex, ey, target_element);
10165
10166           something_has_changed = TRUE;
10167
10168           /* for symmetry reasons, freeze newly created border elements */
10169           if (ex != x || ey != y)
10170             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10171         }
10172       }
10173
10174       if (something_has_changed)
10175       {
10176         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10177         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10178       }
10179     }
10180   }
10181   else
10182   {
10183     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10184                                         ce_value, ce_score);
10185
10186     if (element == EL_DIAGONAL_GROWING ||
10187         element == EL_DIAGONAL_SHRINKING)
10188     {
10189       target_element = Store[x][y];
10190
10191       Store[x][y] = EL_EMPTY;
10192     }
10193
10194     CreateElementFromChange(x, y, target_element);
10195
10196     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10197     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10198   }
10199
10200   /* this uses direct change before indirect change */
10201   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10202
10203   return TRUE;
10204 }
10205
10206 static void HandleElementChange(int x, int y, int page)
10207 {
10208   int element = MovingOrBlocked2Element(x, y);
10209   struct ElementInfo *ei = &element_info[element];
10210   struct ElementChangeInfo *change = &ei->change_page[page];
10211   boolean handle_action_before_change = FALSE;
10212
10213 #ifdef DEBUG
10214   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10215       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10216   {
10217     printf("\n\n");
10218     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10219            x, y, element, element_info[element].token_name);
10220     printf("HandleElementChange(): This should never happen!\n");
10221     printf("\n\n");
10222   }
10223 #endif
10224
10225   /* this can happen with classic bombs on walkable, changing elements */
10226   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10227   {
10228     return;
10229   }
10230
10231   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10232   {
10233     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10234
10235     if (change->can_change)
10236     {
10237       /* !!! not clear why graphic animation should be reset at all here !!! */
10238       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10239       /* when a custom element is about to change (for example by change delay),
10240          do not reset graphic animation when the custom element is moving */
10241       if (!IS_MOVING(x, y))
10242       {
10243         ResetGfxAnimation(x, y);
10244         ResetRandomAnimationValue(x, y);
10245       }
10246
10247       if (change->pre_change_function)
10248         change->pre_change_function(x, y);
10249     }
10250   }
10251
10252   ChangeDelay[x][y]--;
10253
10254   if (ChangeDelay[x][y] != 0)           /* continue element change */
10255   {
10256     if (change->can_change)
10257     {
10258       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10259
10260       if (IS_ANIMATED(graphic))
10261         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10262
10263       if (change->change_function)
10264         change->change_function(x, y);
10265     }
10266   }
10267   else                                  /* finish element change */
10268   {
10269     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10270     {
10271       page = ChangePage[x][y];
10272       ChangePage[x][y] = -1;
10273
10274       change = &ei->change_page[page];
10275     }
10276
10277     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10278     {
10279       ChangeDelay[x][y] = 1;            /* try change after next move step */
10280       ChangePage[x][y] = page;          /* remember page to use for change */
10281
10282       return;
10283     }
10284
10285     /* special case: set new level random seed before changing element */
10286     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10287       handle_action_before_change = TRUE;
10288
10289     if (change->has_action && handle_action_before_change)
10290       ExecuteCustomElementAction(x, y, element, page);
10291
10292     if (change->can_change)
10293     {
10294       if (ChangeElement(x, y, element, page))
10295       {
10296         if (change->post_change_function)
10297           change->post_change_function(x, y);
10298       }
10299     }
10300
10301     if (change->has_action && !handle_action_before_change)
10302       ExecuteCustomElementAction(x, y, element, page);
10303   }
10304 }
10305
10306 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10307                                               int trigger_element,
10308                                               int trigger_event,
10309                                               int trigger_player,
10310                                               int trigger_side,
10311                                               int trigger_page)
10312 {
10313   boolean change_done_any = FALSE;
10314   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10315   int i;
10316
10317   if (!(trigger_events[trigger_element][trigger_event]))
10318     return FALSE;
10319
10320   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10321
10322   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10323   {
10324     int element = EL_CUSTOM_START + i;
10325     boolean change_done = FALSE;
10326     int p;
10327
10328     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10329         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10330       continue;
10331
10332     for (p = 0; p < element_info[element].num_change_pages; p++)
10333     {
10334       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10335
10336       if (change->can_change_or_has_action &&
10337           change->has_event[trigger_event] &&
10338           change->trigger_side & trigger_side &&
10339           change->trigger_player & trigger_player &&
10340           change->trigger_page & trigger_page_bits &&
10341           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10342       {
10343         change->actual_trigger_element = trigger_element;
10344         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10345         change->actual_trigger_player_bits = trigger_player;
10346         change->actual_trigger_side = trigger_side;
10347         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10348         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10349
10350         if ((change->can_change && !change_done) || change->has_action)
10351         {
10352           int x, y;
10353
10354           SCAN_PLAYFIELD(x, y)
10355           {
10356             if (Feld[x][y] == element)
10357             {
10358               if (change->can_change && !change_done)
10359               {
10360                 /* if element already changed in this frame, not only prevent
10361                    another element change (checked in ChangeElement()), but
10362                    also prevent additional element actions for this element */
10363
10364                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10365                     !level.use_action_after_change_bug)
10366                   continue;
10367
10368                 ChangeDelay[x][y] = 1;
10369                 ChangeEvent[x][y] = trigger_event;
10370
10371                 HandleElementChange(x, y, p);
10372               }
10373               else if (change->has_action)
10374               {
10375                 /* if element already changed in this frame, not only prevent
10376                    another element change (checked in ChangeElement()), but
10377                    also prevent additional element actions for this element */
10378
10379                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10380                     !level.use_action_after_change_bug)
10381                   continue;
10382
10383                 ExecuteCustomElementAction(x, y, element, p);
10384                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10385               }
10386             }
10387           }
10388
10389           if (change->can_change)
10390           {
10391             change_done = TRUE;
10392             change_done_any = TRUE;
10393           }
10394         }
10395       }
10396     }
10397   }
10398
10399   RECURSION_LOOP_DETECTION_END();
10400
10401   return change_done_any;
10402 }
10403
10404 static boolean CheckElementChangeExt(int x, int y,
10405                                      int element,
10406                                      int trigger_element,
10407                                      int trigger_event,
10408                                      int trigger_player,
10409                                      int trigger_side)
10410 {
10411   boolean change_done = FALSE;
10412   int p;
10413
10414   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10415       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10416     return FALSE;
10417
10418   if (Feld[x][y] == EL_BLOCKED)
10419   {
10420     Blocked2Moving(x, y, &x, &y);
10421     element = Feld[x][y];
10422   }
10423
10424   /* check if element has already changed or is about to change after moving */
10425   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10426        Feld[x][y] != element) ||
10427
10428       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10429        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10430         ChangePage[x][y] != -1)))
10431     return FALSE;
10432
10433   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10434
10435   for (p = 0; p < element_info[element].num_change_pages; p++)
10436   {
10437     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10438
10439     /* check trigger element for all events where the element that is checked
10440        for changing interacts with a directly adjacent element -- this is
10441        different to element changes that affect other elements to change on the
10442        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10443     boolean check_trigger_element =
10444       (trigger_event == CE_TOUCHING_X ||
10445        trigger_event == CE_HITTING_X ||
10446        trigger_event == CE_HIT_BY_X ||
10447        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10448
10449     if (change->can_change_or_has_action &&
10450         change->has_event[trigger_event] &&
10451         change->trigger_side & trigger_side &&
10452         change->trigger_player & trigger_player &&
10453         (!check_trigger_element ||
10454          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10455     {
10456       change->actual_trigger_element = trigger_element;
10457       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10458       change->actual_trigger_player_bits = trigger_player;
10459       change->actual_trigger_side = trigger_side;
10460       change->actual_trigger_ce_value = CustomValue[x][y];
10461       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10462
10463       /* special case: trigger element not at (x,y) position for some events */
10464       if (check_trigger_element)
10465       {
10466         static struct
10467         {
10468           int dx, dy;
10469         } move_xy[] =
10470           {
10471             {  0,  0 },
10472             { -1,  0 },
10473             { +1,  0 },
10474             {  0,  0 },
10475             {  0, -1 },
10476             {  0,  0 }, { 0, 0 }, { 0, 0 },
10477             {  0, +1 }
10478           };
10479
10480         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10481         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10482
10483         change->actual_trigger_ce_value = CustomValue[xx][yy];
10484         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10485       }
10486
10487       if (change->can_change && !change_done)
10488       {
10489         ChangeDelay[x][y] = 1;
10490         ChangeEvent[x][y] = trigger_event;
10491
10492         HandleElementChange(x, y, p);
10493
10494         change_done = TRUE;
10495       }
10496       else if (change->has_action)
10497       {
10498         ExecuteCustomElementAction(x, y, element, p);
10499         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10500       }
10501     }
10502   }
10503
10504   RECURSION_LOOP_DETECTION_END();
10505
10506   return change_done;
10507 }
10508
10509 static void PlayPlayerSound(struct PlayerInfo *player)
10510 {
10511   int jx = player->jx, jy = player->jy;
10512   int sound_element = player->artwork_element;
10513   int last_action = player->last_action_waiting;
10514   int action = player->action_waiting;
10515
10516   if (player->is_waiting)
10517   {
10518     if (action != last_action)
10519       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10520     else
10521       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10522   }
10523   else
10524   {
10525     if (action != last_action)
10526       StopSound(element_info[sound_element].sound[last_action]);
10527
10528     if (last_action == ACTION_SLEEPING)
10529       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10530   }
10531 }
10532
10533 static void PlayAllPlayersSound()
10534 {
10535   int i;
10536
10537   for (i = 0; i < MAX_PLAYERS; i++)
10538     if (stored_player[i].active)
10539       PlayPlayerSound(&stored_player[i]);
10540 }
10541
10542 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10543 {
10544   boolean last_waiting = player->is_waiting;
10545   int move_dir = player->MovDir;
10546
10547   player->dir_waiting = move_dir;
10548   player->last_action_waiting = player->action_waiting;
10549
10550   if (is_waiting)
10551   {
10552     if (!last_waiting)          /* not waiting -> waiting */
10553     {
10554       player->is_waiting = TRUE;
10555
10556       player->frame_counter_bored =
10557         FrameCounter +
10558         game.player_boring_delay_fixed +
10559         GetSimpleRandom(game.player_boring_delay_random);
10560       player->frame_counter_sleeping =
10561         FrameCounter +
10562         game.player_sleeping_delay_fixed +
10563         GetSimpleRandom(game.player_sleeping_delay_random);
10564
10565       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10566     }
10567
10568     if (game.player_sleeping_delay_fixed +
10569         game.player_sleeping_delay_random > 0 &&
10570         player->anim_delay_counter == 0 &&
10571         player->post_delay_counter == 0 &&
10572         FrameCounter >= player->frame_counter_sleeping)
10573       player->is_sleeping = TRUE;
10574     else if (game.player_boring_delay_fixed +
10575              game.player_boring_delay_random > 0 &&
10576              FrameCounter >= player->frame_counter_bored)
10577       player->is_bored = TRUE;
10578
10579     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10580                               player->is_bored ? ACTION_BORING :
10581                               ACTION_WAITING);
10582
10583     if (player->is_sleeping && player->use_murphy)
10584     {
10585       /* special case for sleeping Murphy when leaning against non-free tile */
10586
10587       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10588           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10589            !IS_MOVING(player->jx - 1, player->jy)))
10590         move_dir = MV_LEFT;
10591       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10592                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10593                 !IS_MOVING(player->jx + 1, player->jy)))
10594         move_dir = MV_RIGHT;
10595       else
10596         player->is_sleeping = FALSE;
10597
10598       player->dir_waiting = move_dir;
10599     }
10600
10601     if (player->is_sleeping)
10602     {
10603       if (player->num_special_action_sleeping > 0)
10604       {
10605         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10606         {
10607           int last_special_action = player->special_action_sleeping;
10608           int num_special_action = player->num_special_action_sleeping;
10609           int special_action =
10610             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10611              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10612              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10613              last_special_action + 1 : ACTION_SLEEPING);
10614           int special_graphic =
10615             el_act_dir2img(player->artwork_element, special_action, move_dir);
10616
10617           player->anim_delay_counter =
10618             graphic_info[special_graphic].anim_delay_fixed +
10619             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10620           player->post_delay_counter =
10621             graphic_info[special_graphic].post_delay_fixed +
10622             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10623
10624           player->special_action_sleeping = special_action;
10625         }
10626
10627         if (player->anim_delay_counter > 0)
10628         {
10629           player->action_waiting = player->special_action_sleeping;
10630           player->anim_delay_counter--;
10631         }
10632         else if (player->post_delay_counter > 0)
10633         {
10634           player->post_delay_counter--;
10635         }
10636       }
10637     }
10638     else if (player->is_bored)
10639     {
10640       if (player->num_special_action_bored > 0)
10641       {
10642         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10643         {
10644           int special_action =
10645             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10646           int special_graphic =
10647             el_act_dir2img(player->artwork_element, special_action, move_dir);
10648
10649           player->anim_delay_counter =
10650             graphic_info[special_graphic].anim_delay_fixed +
10651             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10652           player->post_delay_counter =
10653             graphic_info[special_graphic].post_delay_fixed +
10654             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10655
10656           player->special_action_bored = special_action;
10657         }
10658
10659         if (player->anim_delay_counter > 0)
10660         {
10661           player->action_waiting = player->special_action_bored;
10662           player->anim_delay_counter--;
10663         }
10664         else if (player->post_delay_counter > 0)
10665         {
10666           player->post_delay_counter--;
10667         }
10668       }
10669     }
10670   }
10671   else if (last_waiting)        /* waiting -> not waiting */
10672   {
10673     player->is_waiting = FALSE;
10674     player->is_bored = FALSE;
10675     player->is_sleeping = FALSE;
10676
10677     player->frame_counter_bored = -1;
10678     player->frame_counter_sleeping = -1;
10679
10680     player->anim_delay_counter = 0;
10681     player->post_delay_counter = 0;
10682
10683     player->dir_waiting = player->MovDir;
10684     player->action_waiting = ACTION_DEFAULT;
10685
10686     player->special_action_bored = ACTION_DEFAULT;
10687     player->special_action_sleeping = ACTION_DEFAULT;
10688   }
10689 }
10690
10691 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10692 {
10693   static boolean player_was_moving = FALSE;
10694   static boolean player_was_snapping = FALSE;
10695   static boolean player_was_dropping = FALSE;
10696
10697   if ((!player->is_moving  && player_was_moving) ||
10698       (player->MovPos == 0 && player_was_moving) ||
10699       (player->is_snapping && !player_was_snapping) ||
10700       (player->is_dropping && !player_was_dropping))
10701   {
10702     if (!SaveEngineSnapshotToList())
10703       return;
10704
10705     player_was_moving = FALSE;
10706     player_was_snapping = TRUE;
10707     player_was_dropping = TRUE;
10708   }
10709   else
10710   {
10711     if (player->is_moving)
10712       player_was_moving = TRUE;
10713
10714     if (!player->is_snapping)
10715       player_was_snapping = FALSE;
10716
10717     if (!player->is_dropping)
10718       player_was_dropping = FALSE;
10719   }
10720 }
10721
10722 static void CheckSingleStepMode(struct PlayerInfo *player)
10723 {
10724   if (tape.single_step && tape.recording && !tape.pausing)
10725   {
10726     /* as it is called "single step mode", just return to pause mode when the
10727        player stopped moving after one tile (or never starts moving at all) */
10728     if (!player->is_moving && !player->is_pushing)
10729     {
10730       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731       SnapField(player, 0, 0);                  /* stop snapping */
10732     }
10733   }
10734
10735   CheckSaveEngineSnapshot(player);
10736 }
10737
10738 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10739 {
10740   int left      = player_action & JOY_LEFT;
10741   int right     = player_action & JOY_RIGHT;
10742   int up        = player_action & JOY_UP;
10743   int down      = player_action & JOY_DOWN;
10744   int button1   = player_action & JOY_BUTTON_1;
10745   int button2   = player_action & JOY_BUTTON_2;
10746   int dx        = (left ? -1 : right ? 1 : 0);
10747   int dy        = (up   ? -1 : down  ? 1 : 0);
10748
10749   if (!player->active || tape.pausing)
10750     return 0;
10751
10752   if (player_action)
10753   {
10754     if (button1)
10755       SnapField(player, dx, dy);
10756     else
10757     {
10758       if (button2)
10759         DropElement(player);
10760
10761       MovePlayer(player, dx, dy);
10762     }
10763
10764     CheckSingleStepMode(player);
10765
10766     SetPlayerWaiting(player, FALSE);
10767
10768     return player_action;
10769   }
10770   else
10771   {
10772     /* no actions for this player (no input at player's configured device) */
10773
10774     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10775     SnapField(player, 0, 0);
10776     CheckGravityMovementWhenNotMoving(player);
10777
10778     if (player->MovPos == 0)
10779       SetPlayerWaiting(player, TRUE);
10780
10781     if (player->MovPos == 0)    /* needed for tape.playing */
10782       player->is_moving = FALSE;
10783
10784     player->is_dropping = FALSE;
10785     player->is_dropping_pressed = FALSE;
10786     player->drop_pressed_delay = 0;
10787
10788     CheckSingleStepMode(player);
10789
10790     return 0;
10791   }
10792 }
10793
10794 static void CheckLevelTime()
10795 {
10796   int i;
10797
10798   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10799   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10800   {
10801     if (level.native_em_level->lev->home == 0)  /* all players at home */
10802     {
10803       PlayerWins(local_player);
10804
10805       AllPlayersGone = TRUE;
10806
10807       level.native_em_level->lev->home = -1;
10808     }
10809
10810     if (level.native_em_level->ply[0]->alive == 0 &&
10811         level.native_em_level->ply[1]->alive == 0 &&
10812         level.native_em_level->ply[2]->alive == 0 &&
10813         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10814       AllPlayersGone = TRUE;
10815   }
10816   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10817   {
10818     if (game_sp.LevelSolved &&
10819         !game_sp.GameOver)                              /* game won */
10820     {
10821       PlayerWins(local_player);
10822
10823       game_sp.GameOver = TRUE;
10824
10825       AllPlayersGone = TRUE;
10826     }
10827
10828     if (game_sp.GameOver)                               /* game lost */
10829       AllPlayersGone = TRUE;
10830   }
10831
10832   if (TimeFrames >= FRAMES_PER_SECOND)
10833   {
10834     TimeFrames = 0;
10835     TapeTime++;
10836
10837     for (i = 0; i < MAX_PLAYERS; i++)
10838     {
10839       struct PlayerInfo *player = &stored_player[i];
10840
10841       if (SHIELD_ON(player))
10842       {
10843         player->shield_normal_time_left--;
10844
10845         if (player->shield_deadly_time_left > 0)
10846           player->shield_deadly_time_left--;
10847       }
10848     }
10849
10850     if (!local_player->LevelSolved && !level.use_step_counter)
10851     {
10852       TimePlayed++;
10853
10854       if (TimeLeft > 0)
10855       {
10856         TimeLeft--;
10857
10858         if (TimeLeft <= 10 && setup.time_limit)
10859           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10860
10861         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10862            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10863
10864         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10865
10866         if (!TimeLeft && setup.time_limit)
10867         {
10868           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10869             level.native_em_level->lev->killed_out_of_time = TRUE;
10870           else
10871             for (i = 0; i < MAX_PLAYERS; i++)
10872               KillPlayer(&stored_player[i]);
10873         }
10874       }
10875       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10876       {
10877         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10878       }
10879
10880       level.native_em_level->lev->time =
10881         (game.no_time_limit ? TimePlayed : TimeLeft);
10882     }
10883
10884     if (tape.recording || tape.playing)
10885       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10886   }
10887
10888   if (tape.recording || tape.playing)
10889     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10890
10891   UpdateAndDisplayGameControlValues();
10892 }
10893
10894 void AdvanceFrameAndPlayerCounters(int player_nr)
10895 {
10896   int i;
10897
10898   /* advance frame counters (global frame counter and time frame counter) */
10899   FrameCounter++;
10900   TimeFrames++;
10901
10902   /* advance player counters (counters for move delay, move animation etc.) */
10903   for (i = 0; i < MAX_PLAYERS; i++)
10904   {
10905     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10906     int move_delay_value = stored_player[i].move_delay_value;
10907     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10908
10909     if (!advance_player_counters)       /* not all players may be affected */
10910       continue;
10911
10912     if (move_frames == 0)       /* less than one move per game frame */
10913     {
10914       int stepsize = TILEX / move_delay_value;
10915       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10916       int count = (stored_player[i].is_moving ?
10917                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10918
10919       if (count % delay == 0)
10920         move_frames = 1;
10921     }
10922
10923     stored_player[i].Frame += move_frames;
10924
10925     if (stored_player[i].MovPos != 0)
10926       stored_player[i].StepFrame += move_frames;
10927
10928     if (stored_player[i].move_delay > 0)
10929       stored_player[i].move_delay--;
10930
10931     /* due to bugs in previous versions, counter must count up, not down */
10932     if (stored_player[i].push_delay != -1)
10933       stored_player[i].push_delay++;
10934
10935     if (stored_player[i].drop_delay > 0)
10936       stored_player[i].drop_delay--;
10937
10938     if (stored_player[i].is_dropping_pressed)
10939       stored_player[i].drop_pressed_delay++;
10940   }
10941 }
10942
10943 void StartGameActions(boolean init_network_game, boolean record_tape,
10944                       int random_seed)
10945 {
10946   unsigned int new_random_seed = InitRND(random_seed);
10947
10948   if (record_tape)
10949     TapeStartRecording(new_random_seed);
10950
10951 #if defined(NETWORK_AVALIABLE)
10952   if (init_network_game)
10953   {
10954     SendToServer_StartPlaying();
10955
10956     return;
10957   }
10958 #endif
10959
10960   InitGame();
10961 }
10962
10963 void GameActions()
10964 {
10965   static unsigned int game_frame_delay = 0;
10966   unsigned int game_frame_delay_value;
10967   byte *recorded_player_action;
10968   byte summarized_player_action = 0;
10969   byte tape_action[MAX_PLAYERS];
10970   int i;
10971
10972   for (i = 0; i < MAX_PLAYERS; i++)
10973   {
10974     struct PlayerInfo *player = &stored_player[i];
10975
10976     // allow engine snapshot if movement attempt was stopped
10977     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10978         (player->action & KEY_MOTION) == 0)
10979       game.snapshot.changed_action = TRUE;
10980
10981     // allow engine snapshot in case of snapping/dropping attempt
10982     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10983         (player->action & KEY_BUTTON) != 0)
10984       game.snapshot.changed_action = TRUE;
10985
10986     game.snapshot.last_action[i] = player->action;
10987   }
10988
10989   /* detect endless loops, caused by custom element programming */
10990   if (recursion_loop_detected && recursion_loop_depth == 0)
10991   {
10992     char *message = getStringCat3("Internal Error! Element ",
10993                                   EL_NAME(recursion_loop_element),
10994                                   " caused endless loop! Quit the game?");
10995
10996     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10997           EL_NAME(recursion_loop_element));
10998
10999     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11000
11001     recursion_loop_detected = FALSE;    /* if game should be continued */
11002
11003     free(message);
11004
11005     return;
11006   }
11007
11008   if (game.restart_level)
11009     StartGameActions(options.network, setup.autorecord, level.random_seed);
11010
11011   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11012   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11013   {
11014     if (level.native_em_level->lev->home == 0)  /* all players at home */
11015     {
11016       PlayerWins(local_player);
11017
11018       AllPlayersGone = TRUE;
11019
11020       level.native_em_level->lev->home = -1;
11021     }
11022
11023     if (level.native_em_level->ply[0]->alive == 0 &&
11024         level.native_em_level->ply[1]->alive == 0 &&
11025         level.native_em_level->ply[2]->alive == 0 &&
11026         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11027       AllPlayersGone = TRUE;
11028   }
11029   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11030   {
11031     if (game_sp.LevelSolved &&
11032         !game_sp.GameOver)                              /* game won */
11033     {
11034       PlayerWins(local_player);
11035
11036       game_sp.GameOver = TRUE;
11037
11038       AllPlayersGone = TRUE;
11039     }
11040
11041     if (game_sp.GameOver)                               /* game lost */
11042       AllPlayersGone = TRUE;
11043   }
11044
11045   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11046     GameWon();
11047
11048   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11049     TapeStop();
11050
11051   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11052     return;
11053
11054   game_frame_delay_value =
11055     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11056
11057   if (tape.playing && tape.warp_forward && !tape.pausing)
11058     game_frame_delay_value = 0;
11059
11060   /* ---------- main game synchronization point ---------- */
11061
11062   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11063
11064   if (network_playing && !network_player_action_received)
11065   {
11066     /* try to get network player actions in time */
11067
11068 #if defined(NETWORK_AVALIABLE)
11069     /* last chance to get network player actions without main loop delay */
11070     HandleNetworking();
11071 #endif
11072
11073     /* game was quit by network peer */
11074     if (game_status != GAME_MODE_PLAYING)
11075       return;
11076
11077     if (!network_player_action_received)
11078       return;           /* failed to get network player actions in time */
11079
11080     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11081   }
11082
11083   if (tape.pausing)
11084     return;
11085
11086   /* at this point we know that we really continue executing the game */
11087
11088   network_player_action_received = FALSE;
11089
11090   /* when playing tape, read previously recorded player input from tape data */
11091   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11092
11093   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11094   if (tape.pausing)
11095     return;
11096
11097   if (tape.set_centered_player)
11098   {
11099     game.centered_player_nr_next = tape.centered_player_nr_next;
11100     game.set_centered_player = TRUE;
11101   }
11102
11103   for (i = 0; i < MAX_PLAYERS; i++)
11104   {
11105     summarized_player_action |= stored_player[i].action;
11106
11107     if (!network_playing && (game.team_mode || tape.playing))
11108       stored_player[i].effective_action = stored_player[i].action;
11109   }
11110
11111 #if defined(NETWORK_AVALIABLE)
11112   if (network_playing)
11113     SendToServer_MovePlayer(summarized_player_action);
11114 #endif
11115
11116   if (!options.network && !game.team_mode)
11117     local_player->effective_action = summarized_player_action;
11118
11119   if (tape.recording &&
11120       setup.team_mode &&
11121       setup.input_on_focus &&
11122       game.centered_player_nr != -1)
11123   {
11124     for (i = 0; i < MAX_PLAYERS; i++)
11125       stored_player[i].effective_action =
11126         (i == game.centered_player_nr ? summarized_player_action : 0);
11127   }
11128
11129   if (recorded_player_action != NULL)
11130     for (i = 0; i < MAX_PLAYERS; i++)
11131       stored_player[i].effective_action = recorded_player_action[i];
11132
11133   for (i = 0; i < MAX_PLAYERS; i++)
11134   {
11135     tape_action[i] = stored_player[i].effective_action;
11136
11137     /* (this may happen in the RND game engine if a player was not present on
11138        the playfield on level start, but appeared later from a custom element */
11139     if (setup.team_mode &&
11140         tape.recording &&
11141         tape_action[i] &&
11142         !tape.player_participates[i])
11143       tape.player_participates[i] = TRUE;
11144   }
11145
11146   /* only record actions from input devices, but not programmed actions */
11147   if (tape.recording)
11148     TapeRecordAction(tape_action);
11149
11150 #if USE_NEW_PLAYER_ASSIGNMENTS
11151   // !!! also map player actions in single player mode !!!
11152   // if (game.team_mode)
11153   {
11154     byte mapped_action[MAX_PLAYERS];
11155
11156 #if DEBUG_PLAYER_ACTIONS
11157     printf(":::");
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       printf(" %d, ", stored_player[i].effective_action);
11160 #endif
11161
11162     for (i = 0; i < MAX_PLAYERS; i++)
11163       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11164
11165     for (i = 0; i < MAX_PLAYERS; i++)
11166       stored_player[i].effective_action = mapped_action[i];
11167
11168 #if DEBUG_PLAYER_ACTIONS
11169     printf(" =>");
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       printf(" %d, ", stored_player[i].effective_action);
11172     printf("\n");
11173 #endif
11174   }
11175 #if DEBUG_PLAYER_ACTIONS
11176   else
11177   {
11178     printf(":::");
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180       printf(" %d, ", stored_player[i].effective_action);
11181     printf("\n");
11182   }
11183 #endif
11184 #endif
11185
11186   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11187   {
11188     GameActions_EM_Main();
11189   }
11190   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11191   {
11192     GameActions_SP_Main();
11193   }
11194   else
11195   {
11196     GameActions_RND_Main();
11197   }
11198
11199   BlitScreenToBitmap(backbuffer);
11200
11201   CheckLevelTime();
11202
11203   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11204
11205   if (options.debug)                    /* calculate frames per second */
11206   {
11207     static unsigned int fps_counter = 0;
11208     static int fps_frames = 0;
11209     unsigned int fps_delay_ms = Counter() - fps_counter;
11210
11211     fps_frames++;
11212
11213     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11214     {
11215       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11216
11217       fps_frames = 0;
11218       fps_counter = Counter();
11219     }
11220
11221     redraw_mask |= REDRAW_FPS;
11222   }
11223 }
11224
11225 void GameActions_EM_Main()
11226 {
11227   byte effective_action[MAX_PLAYERS];
11228   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11229   int i;
11230
11231   for (i = 0; i < MAX_PLAYERS; i++)
11232     effective_action[i] = stored_player[i].effective_action;
11233
11234   GameActions_EM(effective_action, warp_mode);
11235 }
11236
11237 void GameActions_SP_Main()
11238 {
11239   byte effective_action[MAX_PLAYERS];
11240   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11241   int i;
11242
11243   for (i = 0; i < MAX_PLAYERS; i++)
11244     effective_action[i] = stored_player[i].effective_action;
11245
11246   GameActions_SP(effective_action, warp_mode);
11247 }
11248
11249 void GameActions_RND_Main()
11250 {
11251   GameActions_RND();
11252 }
11253
11254 void GameActions_RND()
11255 {
11256   int magic_wall_x = 0, magic_wall_y = 0;
11257   int i, x, y, element, graphic;
11258
11259   InitPlayfieldScanModeVars();
11260
11261   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11262   {
11263     SCAN_PLAYFIELD(x, y)
11264     {
11265       ChangeCount[x][y] = 0;
11266       ChangeEvent[x][y] = -1;
11267     }
11268   }
11269
11270   if (game.set_centered_player)
11271   {
11272     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11273
11274     /* switching to "all players" only possible if all players fit to screen */
11275     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11276     {
11277       game.centered_player_nr_next = game.centered_player_nr;
11278       game.set_centered_player = FALSE;
11279     }
11280
11281     /* do not switch focus to non-existing (or non-active) player */
11282     if (game.centered_player_nr_next >= 0 &&
11283         !stored_player[game.centered_player_nr_next].active)
11284     {
11285       game.centered_player_nr_next = game.centered_player_nr;
11286       game.set_centered_player = FALSE;
11287     }
11288   }
11289
11290   if (game.set_centered_player &&
11291       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11292   {
11293     int sx, sy;
11294
11295     if (game.centered_player_nr_next == -1)
11296     {
11297       setScreenCenteredToAllPlayers(&sx, &sy);
11298     }
11299     else
11300     {
11301       sx = stored_player[game.centered_player_nr_next].jx;
11302       sy = stored_player[game.centered_player_nr_next].jy;
11303     }
11304
11305     game.centered_player_nr = game.centered_player_nr_next;
11306     game.set_centered_player = FALSE;
11307
11308     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11309     DrawGameDoorValues();
11310   }
11311
11312   for (i = 0; i < MAX_PLAYERS; i++)
11313   {
11314     int actual_player_action = stored_player[i].effective_action;
11315
11316 #if 1
11317     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11318        - rnd_equinox_tetrachloride 048
11319        - rnd_equinox_tetrachloride_ii 096
11320        - rnd_emanuel_schmieg 002
11321        - doctor_sloan_ww 001, 020
11322     */
11323     if (stored_player[i].MovPos == 0)
11324       CheckGravityMovement(&stored_player[i]);
11325 #endif
11326
11327     /* overwrite programmed action with tape action */
11328     if (stored_player[i].programmed_action)
11329       actual_player_action = stored_player[i].programmed_action;
11330
11331     PlayerActions(&stored_player[i], actual_player_action);
11332
11333     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11334   }
11335
11336   ScrollScreen(NULL, SCROLL_GO_ON);
11337
11338   /* for backwards compatibility, the following code emulates a fixed bug that
11339      occured when pushing elements (causing elements that just made their last
11340      pushing step to already (if possible) make their first falling step in the
11341      same game frame, which is bad); this code is also needed to use the famous
11342      "spring push bug" which is used in older levels and might be wanted to be
11343      used also in newer levels, but in this case the buggy pushing code is only
11344      affecting the "spring" element and no other elements */
11345
11346   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11347   {
11348     for (i = 0; i < MAX_PLAYERS; i++)
11349     {
11350       struct PlayerInfo *player = &stored_player[i];
11351       int x = player->jx;
11352       int y = player->jy;
11353
11354       if (player->active && player->is_pushing && player->is_moving &&
11355           IS_MOVING(x, y) &&
11356           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11357            Feld[x][y] == EL_SPRING))
11358       {
11359         ContinueMoving(x, y);
11360
11361         /* continue moving after pushing (this is actually a bug) */
11362         if (!IS_MOVING(x, y))
11363           Stop[x][y] = FALSE;
11364       }
11365     }
11366   }
11367
11368   SCAN_PLAYFIELD(x, y)
11369   {
11370     ChangeCount[x][y] = 0;
11371     ChangeEvent[x][y] = -1;
11372
11373     /* this must be handled before main playfield loop */
11374     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11375     {
11376       MovDelay[x][y]--;
11377       if (MovDelay[x][y] <= 0)
11378         RemoveField(x, y);
11379     }
11380
11381     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11382     {
11383       MovDelay[x][y]--;
11384       if (MovDelay[x][y] <= 0)
11385       {
11386         RemoveField(x, y);
11387         TEST_DrawLevelField(x, y);
11388
11389         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11390       }
11391     }
11392
11393 #if DEBUG
11394     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11395     {
11396       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11397       printf("GameActions(): This should never happen!\n");
11398
11399       ChangePage[x][y] = -1;
11400     }
11401 #endif
11402
11403     Stop[x][y] = FALSE;
11404     if (WasJustMoving[x][y] > 0)
11405       WasJustMoving[x][y]--;
11406     if (WasJustFalling[x][y] > 0)
11407       WasJustFalling[x][y]--;
11408     if (CheckCollision[x][y] > 0)
11409       CheckCollision[x][y]--;
11410     if (CheckImpact[x][y] > 0)
11411       CheckImpact[x][y]--;
11412
11413     GfxFrame[x][y]++;
11414
11415     /* reset finished pushing action (not done in ContinueMoving() to allow
11416        continuous pushing animation for elements with zero push delay) */
11417     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11418     {
11419       ResetGfxAnimation(x, y);
11420       TEST_DrawLevelField(x, y);
11421     }
11422
11423 #if DEBUG
11424     if (IS_BLOCKED(x, y))
11425     {
11426       int oldx, oldy;
11427
11428       Blocked2Moving(x, y, &oldx, &oldy);
11429       if (!IS_MOVING(oldx, oldy))
11430       {
11431         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11432         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11433         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11434         printf("GameActions(): This should never happen!\n");
11435       }
11436     }
11437 #endif
11438   }
11439
11440   SCAN_PLAYFIELD(x, y)
11441   {
11442     element = Feld[x][y];
11443     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11444
11445     ResetGfxFrame(x, y, TRUE);
11446
11447     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11448         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11449       ResetRandomAnimationValue(x, y);
11450
11451     SetRandomAnimationValue(x, y);
11452
11453     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11454
11455     if (IS_INACTIVE(element))
11456     {
11457       if (IS_ANIMATED(graphic))
11458         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11459
11460       continue;
11461     }
11462
11463     /* this may take place after moving, so 'element' may have changed */
11464     if (IS_CHANGING(x, y) &&
11465         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11466     {
11467       int page = element_info[element].event_page_nr[CE_DELAY];
11468
11469       HandleElementChange(x, y, page);
11470
11471       element = Feld[x][y];
11472       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11473     }
11474
11475     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11476     {
11477       StartMoving(x, y);
11478
11479       element = Feld[x][y];
11480       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11481
11482       if (IS_ANIMATED(graphic) &&
11483           !IS_MOVING(x, y) &&
11484           !Stop[x][y])
11485         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11486
11487       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11488         TEST_DrawTwinkleOnField(x, y);
11489     }
11490     else if ((element == EL_ACID ||
11491               element == EL_EXIT_OPEN ||
11492               element == EL_EM_EXIT_OPEN ||
11493               element == EL_SP_EXIT_OPEN ||
11494               element == EL_STEEL_EXIT_OPEN ||
11495               element == EL_EM_STEEL_EXIT_OPEN ||
11496               element == EL_SP_TERMINAL ||
11497               element == EL_SP_TERMINAL_ACTIVE ||
11498               element == EL_EXTRA_TIME ||
11499               element == EL_SHIELD_NORMAL ||
11500               element == EL_SHIELD_DEADLY) &&
11501              IS_ANIMATED(graphic))
11502       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11503     else if (IS_MOVING(x, y))
11504       ContinueMoving(x, y);
11505     else if (IS_ACTIVE_BOMB(element))
11506       CheckDynamite(x, y);
11507     else if (element == EL_AMOEBA_GROWING)
11508       AmoebeWaechst(x, y);
11509     else if (element == EL_AMOEBA_SHRINKING)
11510       AmoebaDisappearing(x, y);
11511
11512 #if !USE_NEW_AMOEBA_CODE
11513     else if (IS_AMOEBALIVE(element))
11514       AmoebeAbleger(x, y);
11515 #endif
11516
11517     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11518       Life(x, y);
11519     else if (element == EL_EXIT_CLOSED)
11520       CheckExit(x, y);
11521     else if (element == EL_EM_EXIT_CLOSED)
11522       CheckExitEM(x, y);
11523     else if (element == EL_STEEL_EXIT_CLOSED)
11524       CheckExitSteel(x, y);
11525     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11526       CheckExitSteelEM(x, y);
11527     else if (element == EL_SP_EXIT_CLOSED)
11528       CheckExitSP(x, y);
11529     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11530              element == EL_EXPANDABLE_STEELWALL_GROWING)
11531       MauerWaechst(x, y);
11532     else if (element == EL_EXPANDABLE_WALL ||
11533              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11534              element == EL_EXPANDABLE_WALL_VERTICAL ||
11535              element == EL_EXPANDABLE_WALL_ANY ||
11536              element == EL_BD_EXPANDABLE_WALL)
11537       MauerAbleger(x, y);
11538     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11539              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11540              element == EL_EXPANDABLE_STEELWALL_ANY)
11541       MauerAblegerStahl(x, y);
11542     else if (element == EL_FLAMES)
11543       CheckForDragon(x, y);
11544     else if (element == EL_EXPLOSION)
11545       ; /* drawing of correct explosion animation is handled separately */
11546     else if (element == EL_ELEMENT_SNAPPING ||
11547              element == EL_DIAGONAL_SHRINKING ||
11548              element == EL_DIAGONAL_GROWING)
11549     {
11550       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11551
11552       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11553     }
11554     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11555       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11556
11557     if (IS_BELT_ACTIVE(element))
11558       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11559
11560     if (game.magic_wall_active)
11561     {
11562       int jx = local_player->jx, jy = local_player->jy;
11563
11564       /* play the element sound at the position nearest to the player */
11565       if ((element == EL_MAGIC_WALL_FULL ||
11566            element == EL_MAGIC_WALL_ACTIVE ||
11567            element == EL_MAGIC_WALL_EMPTYING ||
11568            element == EL_BD_MAGIC_WALL_FULL ||
11569            element == EL_BD_MAGIC_WALL_ACTIVE ||
11570            element == EL_BD_MAGIC_WALL_EMPTYING ||
11571            element == EL_DC_MAGIC_WALL_FULL ||
11572            element == EL_DC_MAGIC_WALL_ACTIVE ||
11573            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11574           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11575       {
11576         magic_wall_x = x;
11577         magic_wall_y = y;
11578       }
11579     }
11580   }
11581
11582 #if USE_NEW_AMOEBA_CODE
11583   /* new experimental amoeba growth stuff */
11584   if (!(FrameCounter % 8))
11585   {
11586     static unsigned int random = 1684108901;
11587
11588     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11589     {
11590       x = RND(lev_fieldx);
11591       y = RND(lev_fieldy);
11592       element = Feld[x][y];
11593
11594       if (!IS_PLAYER(x,y) &&
11595           (element == EL_EMPTY ||
11596            CAN_GROW_INTO(element) ||
11597            element == EL_QUICKSAND_EMPTY ||
11598            element == EL_QUICKSAND_FAST_EMPTY ||
11599            element == EL_ACID_SPLASH_LEFT ||
11600            element == EL_ACID_SPLASH_RIGHT))
11601       {
11602         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11603             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11604             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11605             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11606           Feld[x][y] = EL_AMOEBA_DROP;
11607       }
11608
11609       random = random * 129 + 1;
11610     }
11611   }
11612 #endif
11613
11614   game.explosions_delayed = FALSE;
11615
11616   SCAN_PLAYFIELD(x, y)
11617   {
11618     element = Feld[x][y];
11619
11620     if (ExplodeField[x][y])
11621       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11622     else if (element == EL_EXPLOSION)
11623       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11624
11625     ExplodeField[x][y] = EX_TYPE_NONE;
11626   }
11627
11628   game.explosions_delayed = TRUE;
11629
11630   if (game.magic_wall_active)
11631   {
11632     if (!(game.magic_wall_time_left % 4))
11633     {
11634       int element = Feld[magic_wall_x][magic_wall_y];
11635
11636       if (element == EL_BD_MAGIC_WALL_FULL ||
11637           element == EL_BD_MAGIC_WALL_ACTIVE ||
11638           element == EL_BD_MAGIC_WALL_EMPTYING)
11639         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11640       else if (element == EL_DC_MAGIC_WALL_FULL ||
11641                element == EL_DC_MAGIC_WALL_ACTIVE ||
11642                element == EL_DC_MAGIC_WALL_EMPTYING)
11643         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11644       else
11645         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11646     }
11647
11648     if (game.magic_wall_time_left > 0)
11649     {
11650       game.magic_wall_time_left--;
11651
11652       if (!game.magic_wall_time_left)
11653       {
11654         SCAN_PLAYFIELD(x, y)
11655         {
11656           element = Feld[x][y];
11657
11658           if (element == EL_MAGIC_WALL_ACTIVE ||
11659               element == EL_MAGIC_WALL_FULL)
11660           {
11661             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11662             TEST_DrawLevelField(x, y);
11663           }
11664           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11665                    element == EL_BD_MAGIC_WALL_FULL)
11666           {
11667             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11668             TEST_DrawLevelField(x, y);
11669           }
11670           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11671                    element == EL_DC_MAGIC_WALL_FULL)
11672           {
11673             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11674             TEST_DrawLevelField(x, y);
11675           }
11676         }
11677
11678         game.magic_wall_active = FALSE;
11679       }
11680     }
11681   }
11682
11683   if (game.light_time_left > 0)
11684   {
11685     game.light_time_left--;
11686
11687     if (game.light_time_left == 0)
11688       RedrawAllLightSwitchesAndInvisibleElements();
11689   }
11690
11691   if (game.timegate_time_left > 0)
11692   {
11693     game.timegate_time_left--;
11694
11695     if (game.timegate_time_left == 0)
11696       CloseAllOpenTimegates();
11697   }
11698
11699   if (game.lenses_time_left > 0)
11700   {
11701     game.lenses_time_left--;
11702
11703     if (game.lenses_time_left == 0)
11704       RedrawAllInvisibleElementsForLenses();
11705   }
11706
11707   if (game.magnify_time_left > 0)
11708   {
11709     game.magnify_time_left--;
11710
11711     if (game.magnify_time_left == 0)
11712       RedrawAllInvisibleElementsForMagnifier();
11713   }
11714
11715   for (i = 0; i < MAX_PLAYERS; i++)
11716   {
11717     struct PlayerInfo *player = &stored_player[i];
11718
11719     if (SHIELD_ON(player))
11720     {
11721       if (player->shield_deadly_time_left)
11722         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11723       else if (player->shield_normal_time_left)
11724         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11725     }
11726   }
11727
11728 #if USE_DELAYED_GFX_REDRAW
11729   SCAN_PLAYFIELD(x, y)
11730   {
11731     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11732     {
11733       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11734          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11735
11736       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11737         DrawLevelField(x, y);
11738
11739       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11740         DrawLevelFieldCrumbled(x, y);
11741
11742       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11743         DrawLevelFieldCrumbledNeighbours(x, y);
11744
11745       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11746         DrawTwinkleOnField(x, y);
11747     }
11748
11749     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11750   }
11751 #endif
11752
11753   DrawAllPlayers();
11754   PlayAllPlayersSound();
11755
11756   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11757   {
11758     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11759
11760     local_player->show_envelope = 0;
11761   }
11762
11763   /* use random number generator in every frame to make it less predictable */
11764   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11765     RND(1);
11766 }
11767
11768 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11769 {
11770   int min_x = x, min_y = y, max_x = x, max_y = y;
11771   int i;
11772
11773   for (i = 0; i < MAX_PLAYERS; i++)
11774   {
11775     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11776
11777     if (!stored_player[i].active || &stored_player[i] == player)
11778       continue;
11779
11780     min_x = MIN(min_x, jx);
11781     min_y = MIN(min_y, jy);
11782     max_x = MAX(max_x, jx);
11783     max_y = MAX(max_y, jy);
11784   }
11785
11786   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11787 }
11788
11789 static boolean AllPlayersInVisibleScreen()
11790 {
11791   int i;
11792
11793   for (i = 0; i < MAX_PLAYERS; i++)
11794   {
11795     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11796
11797     if (!stored_player[i].active)
11798       continue;
11799
11800     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11801       return FALSE;
11802   }
11803
11804   return TRUE;
11805 }
11806
11807 void ScrollLevel(int dx, int dy)
11808 {
11809   int scroll_offset = 2 * TILEX_VAR;
11810   int x, y;
11811
11812   BlitBitmap(drawto_field, drawto_field,
11813              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11814              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11815              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11816              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11817              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11818              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11819
11820   if (dx != 0)
11821   {
11822     x = (dx == 1 ? BX1 : BX2);
11823     for (y = BY1; y <= BY2; y++)
11824       DrawScreenField(x, y);
11825   }
11826
11827   if (dy != 0)
11828   {
11829     y = (dy == 1 ? BY1 : BY2);
11830     for (x = BX1; x <= BX2; x++)
11831       DrawScreenField(x, y);
11832   }
11833
11834   redraw_mask |= REDRAW_FIELD;
11835 }
11836
11837 static boolean canFallDown(struct PlayerInfo *player)
11838 {
11839   int jx = player->jx, jy = player->jy;
11840
11841   return (IN_LEV_FIELD(jx, jy + 1) &&
11842           (IS_FREE(jx, jy + 1) ||
11843            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11844           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11845           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11846 }
11847
11848 static boolean canPassField(int x, int y, int move_dir)
11849 {
11850   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11851   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11852   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11853   int nextx = x + dx;
11854   int nexty = y + dy;
11855   int element = Feld[x][y];
11856
11857   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11858           !CAN_MOVE(element) &&
11859           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11860           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11861           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11862 }
11863
11864 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11865 {
11866   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11867   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11868   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11869   int newx = x + dx;
11870   int newy = y + dy;
11871
11872   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11873           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11874           (IS_DIGGABLE(Feld[newx][newy]) ||
11875            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11876            canPassField(newx, newy, move_dir)));
11877 }
11878
11879 static void CheckGravityMovement(struct PlayerInfo *player)
11880 {
11881   if (player->gravity && !player->programmed_action)
11882   {
11883     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11884     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11885     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11886     int jx = player->jx, jy = player->jy;
11887     boolean player_is_moving_to_valid_field =
11888       (!player_is_snapping &&
11889        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11890         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11891     boolean player_can_fall_down = canFallDown(player);
11892
11893     if (player_can_fall_down &&
11894         !player_is_moving_to_valid_field)
11895       player->programmed_action = MV_DOWN;
11896   }
11897 }
11898
11899 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11900 {
11901   return CheckGravityMovement(player);
11902
11903   if (player->gravity && !player->programmed_action)
11904   {
11905     int jx = player->jx, jy = player->jy;
11906     boolean field_under_player_is_free =
11907       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11908     boolean player_is_standing_on_valid_field =
11909       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11910        (IS_WALKABLE(Feld[jx][jy]) &&
11911         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11912
11913     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11914       player->programmed_action = MV_DOWN;
11915   }
11916 }
11917
11918 /*
11919   MovePlayerOneStep()
11920   -----------------------------------------------------------------------------
11921   dx, dy:               direction (non-diagonal) to try to move the player to
11922   real_dx, real_dy:     direction as read from input device (can be diagonal)
11923 */
11924
11925 boolean MovePlayerOneStep(struct PlayerInfo *player,
11926                           int dx, int dy, int real_dx, int real_dy)
11927 {
11928   int jx = player->jx, jy = player->jy;
11929   int new_jx = jx + dx, new_jy = jy + dy;
11930   int can_move;
11931   boolean player_can_move = !player->cannot_move;
11932
11933   if (!player->active || (!dx && !dy))
11934     return MP_NO_ACTION;
11935
11936   player->MovDir = (dx < 0 ? MV_LEFT :
11937                     dx > 0 ? MV_RIGHT :
11938                     dy < 0 ? MV_UP :
11939                     dy > 0 ? MV_DOWN :  MV_NONE);
11940
11941   if (!IN_LEV_FIELD(new_jx, new_jy))
11942     return MP_NO_ACTION;
11943
11944   if (!player_can_move)
11945   {
11946     if (player->MovPos == 0)
11947     {
11948       player->is_moving = FALSE;
11949       player->is_digging = FALSE;
11950       player->is_collecting = FALSE;
11951       player->is_snapping = FALSE;
11952       player->is_pushing = FALSE;
11953     }
11954   }
11955
11956   if (!options.network && game.centered_player_nr == -1 &&
11957       !AllPlayersInSight(player, new_jx, new_jy))
11958     return MP_NO_ACTION;
11959
11960   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11961   if (can_move != MP_MOVING)
11962     return can_move;
11963
11964   /* check if DigField() has caused relocation of the player */
11965   if (player->jx != jx || player->jy != jy)
11966     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11967
11968   StorePlayer[jx][jy] = 0;
11969   player->last_jx = jx;
11970   player->last_jy = jy;
11971   player->jx = new_jx;
11972   player->jy = new_jy;
11973   StorePlayer[new_jx][new_jy] = player->element_nr;
11974
11975   if (player->move_delay_value_next != -1)
11976   {
11977     player->move_delay_value = player->move_delay_value_next;
11978     player->move_delay_value_next = -1;
11979   }
11980
11981   player->MovPos =
11982     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11983
11984   player->step_counter++;
11985
11986   PlayerVisit[jx][jy] = FrameCounter;
11987
11988   player->is_moving = TRUE;
11989
11990 #if 1
11991   /* should better be called in MovePlayer(), but this breaks some tapes */
11992   ScrollPlayer(player, SCROLL_INIT);
11993 #endif
11994
11995   return MP_MOVING;
11996 }
11997
11998 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11999 {
12000   int jx = player->jx, jy = player->jy;
12001   int old_jx = jx, old_jy = jy;
12002   int moved = MP_NO_ACTION;
12003
12004   if (!player->active)
12005     return FALSE;
12006
12007   if (!dx && !dy)
12008   {
12009     if (player->MovPos == 0)
12010     {
12011       player->is_moving = FALSE;
12012       player->is_digging = FALSE;
12013       player->is_collecting = FALSE;
12014       player->is_snapping = FALSE;
12015       player->is_pushing = FALSE;
12016     }
12017
12018     return FALSE;
12019   }
12020
12021   if (player->move_delay > 0)
12022     return FALSE;
12023
12024   player->move_delay = -1;              /* set to "uninitialized" value */
12025
12026   /* store if player is automatically moved to next field */
12027   player->is_auto_moving = (player->programmed_action != MV_NONE);
12028
12029   /* remove the last programmed player action */
12030   player->programmed_action = 0;
12031
12032   if (player->MovPos)
12033   {
12034     /* should only happen if pre-1.2 tape recordings are played */
12035     /* this is only for backward compatibility */
12036
12037     int original_move_delay_value = player->move_delay_value;
12038
12039 #if DEBUG
12040     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12041            tape.counter);
12042 #endif
12043
12044     /* scroll remaining steps with finest movement resolution */
12045     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12046
12047     while (player->MovPos)
12048     {
12049       ScrollPlayer(player, SCROLL_GO_ON);
12050       ScrollScreen(NULL, SCROLL_GO_ON);
12051
12052       AdvanceFrameAndPlayerCounters(player->index_nr);
12053
12054       DrawAllPlayers();
12055       BackToFront();
12056     }
12057
12058     player->move_delay_value = original_move_delay_value;
12059   }
12060
12061   player->is_active = FALSE;
12062
12063   if (player->last_move_dir & MV_HORIZONTAL)
12064   {
12065     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12066       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12067   }
12068   else
12069   {
12070     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12071       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12072   }
12073
12074   if (!moved && !player->is_active)
12075   {
12076     player->is_moving = FALSE;
12077     player->is_digging = FALSE;
12078     player->is_collecting = FALSE;
12079     player->is_snapping = FALSE;
12080     player->is_pushing = FALSE;
12081   }
12082
12083   jx = player->jx;
12084   jy = player->jy;
12085
12086   if (moved & MP_MOVING && !ScreenMovPos &&
12087       (player->index_nr == game.centered_player_nr ||
12088        game.centered_player_nr == -1))
12089   {
12090     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12091     int offset = game.scroll_delay_value;
12092
12093     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12094     {
12095       /* actual player has left the screen -- scroll in that direction */
12096       if (jx != old_jx)         /* player has moved horizontally */
12097         scroll_x += (jx - old_jx);
12098       else                      /* player has moved vertically */
12099         scroll_y += (jy - old_jy);
12100     }
12101     else
12102     {
12103       if (jx != old_jx)         /* player has moved horizontally */
12104       {
12105         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12106             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12107           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12108
12109         /* don't scroll over playfield boundaries */
12110         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12111           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12112
12113         /* don't scroll more than one field at a time */
12114         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12115
12116         /* don't scroll against the player's moving direction */
12117         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12118             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12119           scroll_x = old_scroll_x;
12120       }
12121       else                      /* player has moved vertically */
12122       {
12123         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12124             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12125           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12126
12127         /* don't scroll over playfield boundaries */
12128         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12129           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12130
12131         /* don't scroll more than one field at a time */
12132         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12133
12134         /* don't scroll against the player's moving direction */
12135         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12136             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12137           scroll_y = old_scroll_y;
12138       }
12139     }
12140
12141     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12142     {
12143       if (!options.network && game.centered_player_nr == -1 &&
12144           !AllPlayersInVisibleScreen())
12145       {
12146         scroll_x = old_scroll_x;
12147         scroll_y = old_scroll_y;
12148       }
12149       else
12150       {
12151         ScrollScreen(player, SCROLL_INIT);
12152         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12153       }
12154     }
12155   }
12156
12157   player->StepFrame = 0;
12158
12159   if (moved & MP_MOVING)
12160   {
12161     if (old_jx != jx && old_jy == jy)
12162       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12163     else if (old_jx == jx && old_jy != jy)
12164       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12165
12166     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12167
12168     player->last_move_dir = player->MovDir;
12169     player->is_moving = TRUE;
12170     player->is_snapping = FALSE;
12171     player->is_switching = FALSE;
12172     player->is_dropping = FALSE;
12173     player->is_dropping_pressed = FALSE;
12174     player->drop_pressed_delay = 0;
12175
12176 #if 0
12177     /* should better be called here than above, but this breaks some tapes */
12178     ScrollPlayer(player, SCROLL_INIT);
12179 #endif
12180   }
12181   else
12182   {
12183     CheckGravityMovementWhenNotMoving(player);
12184
12185     player->is_moving = FALSE;
12186
12187     /* at this point, the player is allowed to move, but cannot move right now
12188        (e.g. because of something blocking the way) -- ensure that the player
12189        is also allowed to move in the next frame (in old versions before 3.1.1,
12190        the player was forced to wait again for eight frames before next try) */
12191
12192     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12193       player->move_delay = 0;   /* allow direct movement in the next frame */
12194   }
12195
12196   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12197     player->move_delay = player->move_delay_value;
12198
12199   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12200   {
12201     TestIfPlayerTouchesBadThing(jx, jy);
12202     TestIfPlayerTouchesCustomElement(jx, jy);
12203   }
12204
12205   if (!player->active)
12206     RemovePlayer(player);
12207
12208   return moved;
12209 }
12210
12211 void ScrollPlayer(struct PlayerInfo *player, int mode)
12212 {
12213   int jx = player->jx, jy = player->jy;
12214   int last_jx = player->last_jx, last_jy = player->last_jy;
12215   int move_stepsize = TILEX / player->move_delay_value;
12216
12217   if (!player->active)
12218     return;
12219
12220   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12221     return;
12222
12223   if (mode == SCROLL_INIT)
12224   {
12225     player->actual_frame_counter = FrameCounter;
12226     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12227
12228     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12229         Feld[last_jx][last_jy] == EL_EMPTY)
12230     {
12231       int last_field_block_delay = 0;   /* start with no blocking at all */
12232       int block_delay_adjustment = player->block_delay_adjustment;
12233
12234       /* if player blocks last field, add delay for exactly one move */
12235       if (player->block_last_field)
12236       {
12237         last_field_block_delay += player->move_delay_value;
12238
12239         /* when blocking enabled, prevent moving up despite gravity */
12240         if (player->gravity && player->MovDir == MV_UP)
12241           block_delay_adjustment = -1;
12242       }
12243
12244       /* add block delay adjustment (also possible when not blocking) */
12245       last_field_block_delay += block_delay_adjustment;
12246
12247       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12248       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12249     }
12250
12251     if (player->MovPos != 0)    /* player has not yet reached destination */
12252       return;
12253   }
12254   else if (!FrameReached(&player->actual_frame_counter, 1))
12255     return;
12256
12257   if (player->MovPos != 0)
12258   {
12259     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12260     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12261
12262     /* before DrawPlayer() to draw correct player graphic for this case */
12263     if (player->MovPos == 0)
12264       CheckGravityMovement(player);
12265   }
12266
12267   if (player->MovPos == 0)      /* player reached destination field */
12268   {
12269     if (player->move_delay_reset_counter > 0)
12270     {
12271       player->move_delay_reset_counter--;
12272
12273       if (player->move_delay_reset_counter == 0)
12274       {
12275         /* continue with normal speed after quickly moving through gate */
12276         HALVE_PLAYER_SPEED(player);
12277
12278         /* be able to make the next move without delay */
12279         player->move_delay = 0;
12280       }
12281     }
12282
12283     player->last_jx = jx;
12284     player->last_jy = jy;
12285
12286     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12287         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12288         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12289         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12290         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12291         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12292         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12293         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12294     {
12295       DrawPlayer(player);       /* needed here only to cleanup last field */
12296       RemovePlayer(player);
12297
12298       if (local_player->friends_still_needed == 0 ||
12299           IS_SP_ELEMENT(Feld[jx][jy]))
12300         PlayerWins(player);
12301     }
12302
12303     /* this breaks one level: "machine", level 000 */
12304     {
12305       int move_direction = player->MovDir;
12306       int enter_side = MV_DIR_OPPOSITE(move_direction);
12307       int leave_side = move_direction;
12308       int old_jx = last_jx;
12309       int old_jy = last_jy;
12310       int old_element = Feld[old_jx][old_jy];
12311       int new_element = Feld[jx][jy];
12312
12313       if (IS_CUSTOM_ELEMENT(old_element))
12314         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12315                                    CE_LEFT_BY_PLAYER,
12316                                    player->index_bit, leave_side);
12317
12318       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12319                                           CE_PLAYER_LEAVES_X,
12320                                           player->index_bit, leave_side);
12321
12322       if (IS_CUSTOM_ELEMENT(new_element))
12323         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12324                                    player->index_bit, enter_side);
12325
12326       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12327                                           CE_PLAYER_ENTERS_X,
12328                                           player->index_bit, enter_side);
12329
12330       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12331                                         CE_MOVE_OF_X, move_direction);
12332     }
12333
12334     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12335     {
12336       TestIfPlayerTouchesBadThing(jx, jy);
12337       TestIfPlayerTouchesCustomElement(jx, jy);
12338
12339       /* needed because pushed element has not yet reached its destination,
12340          so it would trigger a change event at its previous field location */
12341       if (!player->is_pushing)
12342         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12343
12344       if (!player->active)
12345         RemovePlayer(player);
12346     }
12347
12348     if (!local_player->LevelSolved && level.use_step_counter)
12349     {
12350       int i;
12351
12352       TimePlayed++;
12353
12354       if (TimeLeft > 0)
12355       {
12356         TimeLeft--;
12357
12358         if (TimeLeft <= 10 && setup.time_limit)
12359           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12360
12361         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12362
12363         DisplayGameControlValues();
12364
12365         if (!TimeLeft && setup.time_limit)
12366           for (i = 0; i < MAX_PLAYERS; i++)
12367             KillPlayer(&stored_player[i]);
12368       }
12369       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12370       {
12371         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12372
12373         DisplayGameControlValues();
12374       }
12375     }
12376
12377     if (tape.single_step && tape.recording && !tape.pausing &&
12378         !player->programmed_action)
12379       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12380
12381     if (!player->programmed_action)
12382       CheckSaveEngineSnapshot(player);
12383   }
12384 }
12385
12386 void ScrollScreen(struct PlayerInfo *player, int mode)
12387 {
12388   static unsigned int screen_frame_counter = 0;
12389
12390   if (mode == SCROLL_INIT)
12391   {
12392     /* set scrolling step size according to actual player's moving speed */
12393     ScrollStepSize = TILEX / player->move_delay_value;
12394
12395     screen_frame_counter = FrameCounter;
12396     ScreenMovDir = player->MovDir;
12397     ScreenMovPos = player->MovPos;
12398     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12399     return;
12400   }
12401   else if (!FrameReached(&screen_frame_counter, 1))
12402     return;
12403
12404   if (ScreenMovPos)
12405   {
12406     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12407     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12408     redraw_mask |= REDRAW_FIELD;
12409   }
12410   else
12411     ScreenMovDir = MV_NONE;
12412 }
12413
12414 void TestIfPlayerTouchesCustomElement(int x, int y)
12415 {
12416   static int xy[4][2] =
12417   {
12418     { 0, -1 },
12419     { -1, 0 },
12420     { +1, 0 },
12421     { 0, +1 }
12422   };
12423   static int trigger_sides[4][2] =
12424   {
12425     /* center side       border side */
12426     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12427     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12428     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12429     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12430   };
12431   static int touch_dir[4] =
12432   {
12433     MV_LEFT | MV_RIGHT,
12434     MV_UP   | MV_DOWN,
12435     MV_UP   | MV_DOWN,
12436     MV_LEFT | MV_RIGHT
12437   };
12438   int center_element = Feld[x][y];      /* should always be non-moving! */
12439   int i;
12440
12441   for (i = 0; i < NUM_DIRECTIONS; i++)
12442   {
12443     int xx = x + xy[i][0];
12444     int yy = y + xy[i][1];
12445     int center_side = trigger_sides[i][0];
12446     int border_side = trigger_sides[i][1];
12447     int border_element;
12448
12449     if (!IN_LEV_FIELD(xx, yy))
12450       continue;
12451
12452     if (IS_PLAYER(x, y))                /* player found at center element */
12453     {
12454       struct PlayerInfo *player = PLAYERINFO(x, y);
12455
12456       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12457         border_element = Feld[xx][yy];          /* may be moving! */
12458       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12459         border_element = Feld[xx][yy];
12460       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12461         border_element = MovingOrBlocked2Element(xx, yy);
12462       else
12463         continue;               /* center and border element do not touch */
12464
12465       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12466                                  player->index_bit, border_side);
12467       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12468                                           CE_PLAYER_TOUCHES_X,
12469                                           player->index_bit, border_side);
12470
12471       {
12472         /* use player element that is initially defined in the level playfield,
12473            not the player element that corresponds to the runtime player number
12474            (example: a level that contains EL_PLAYER_3 as the only player would
12475            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12476         int player_element = PLAYERINFO(x, y)->initial_element;
12477
12478         CheckElementChangeBySide(xx, yy, border_element, player_element,
12479                                  CE_TOUCHING_X, border_side);
12480       }
12481     }
12482     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12483     {
12484       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12485
12486       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12487       {
12488         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12489           continue;             /* center and border element do not touch */
12490       }
12491
12492       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12493                                  player->index_bit, center_side);
12494       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12495                                           CE_PLAYER_TOUCHES_X,
12496                                           player->index_bit, center_side);
12497
12498       {
12499         /* use player element that is initially defined in the level playfield,
12500            not the player element that corresponds to the runtime player number
12501            (example: a level that contains EL_PLAYER_3 as the only player would
12502            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12503         int player_element = PLAYERINFO(xx, yy)->initial_element;
12504
12505         CheckElementChangeBySide(x, y, center_element, player_element,
12506                                  CE_TOUCHING_X, center_side);
12507       }
12508
12509       break;
12510     }
12511   }
12512 }
12513
12514 void TestIfElementTouchesCustomElement(int x, int y)
12515 {
12516   static int xy[4][2] =
12517   {
12518     { 0, -1 },
12519     { -1, 0 },
12520     { +1, 0 },
12521     { 0, +1 }
12522   };
12523   static int trigger_sides[4][2] =
12524   {
12525     /* center side      border side */
12526     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12527     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12528     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12529     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12530   };
12531   static int touch_dir[4] =
12532   {
12533     MV_LEFT | MV_RIGHT,
12534     MV_UP   | MV_DOWN,
12535     MV_UP   | MV_DOWN,
12536     MV_LEFT | MV_RIGHT
12537   };
12538   boolean change_center_element = FALSE;
12539   int center_element = Feld[x][y];      /* should always be non-moving! */
12540   int border_element_old[NUM_DIRECTIONS];
12541   int i;
12542
12543   for (i = 0; i < NUM_DIRECTIONS; i++)
12544   {
12545     int xx = x + xy[i][0];
12546     int yy = y + xy[i][1];
12547     int border_element;
12548
12549     border_element_old[i] = -1;
12550
12551     if (!IN_LEV_FIELD(xx, yy))
12552       continue;
12553
12554     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12555       border_element = Feld[xx][yy];    /* may be moving! */
12556     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12557       border_element = Feld[xx][yy];
12558     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12559       border_element = MovingOrBlocked2Element(xx, yy);
12560     else
12561       continue;                 /* center and border element do not touch */
12562
12563     border_element_old[i] = border_element;
12564   }
12565
12566   for (i = 0; i < NUM_DIRECTIONS; i++)
12567   {
12568     int xx = x + xy[i][0];
12569     int yy = y + xy[i][1];
12570     int center_side = trigger_sides[i][0];
12571     int border_element = border_element_old[i];
12572
12573     if (border_element == -1)
12574       continue;
12575
12576     /* check for change of border element */
12577     CheckElementChangeBySide(xx, yy, border_element, center_element,
12578                              CE_TOUCHING_X, center_side);
12579
12580     /* (center element cannot be player, so we dont have to check this here) */
12581   }
12582
12583   for (i = 0; i < NUM_DIRECTIONS; i++)
12584   {
12585     int xx = x + xy[i][0];
12586     int yy = y + xy[i][1];
12587     int border_side = trigger_sides[i][1];
12588     int border_element = border_element_old[i];
12589
12590     if (border_element == -1)
12591       continue;
12592
12593     /* check for change of center element (but change it only once) */
12594     if (!change_center_element)
12595       change_center_element =
12596         CheckElementChangeBySide(x, y, center_element, border_element,
12597                                  CE_TOUCHING_X, border_side);
12598
12599     if (IS_PLAYER(xx, yy))
12600     {
12601       /* use player element that is initially defined in the level playfield,
12602          not the player element that corresponds to the runtime player number
12603          (example: a level that contains EL_PLAYER_3 as the only player would
12604          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12605       int player_element = PLAYERINFO(xx, yy)->initial_element;
12606
12607       CheckElementChangeBySide(x, y, center_element, player_element,
12608                                CE_TOUCHING_X, border_side);
12609     }
12610   }
12611 }
12612
12613 void TestIfElementHitsCustomElement(int x, int y, int direction)
12614 {
12615   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12616   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12617   int hitx = x + dx, hity = y + dy;
12618   int hitting_element = Feld[x][y];
12619   int touched_element;
12620
12621   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12622     return;
12623
12624   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12625                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12626
12627   if (IN_LEV_FIELD(hitx, hity))
12628   {
12629     int opposite_direction = MV_DIR_OPPOSITE(direction);
12630     int hitting_side = direction;
12631     int touched_side = opposite_direction;
12632     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12633                           MovDir[hitx][hity] != direction ||
12634                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12635
12636     object_hit = TRUE;
12637
12638     if (object_hit)
12639     {
12640       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12641                                CE_HITTING_X, touched_side);
12642
12643       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12644                                CE_HIT_BY_X, hitting_side);
12645
12646       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12647                                CE_HIT_BY_SOMETHING, opposite_direction);
12648
12649       if (IS_PLAYER(hitx, hity))
12650       {
12651         /* use player element that is initially defined in the level playfield,
12652            not the player element that corresponds to the runtime player number
12653            (example: a level that contains EL_PLAYER_3 as the only player would
12654            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12655         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12656
12657         CheckElementChangeBySide(x, y, hitting_element, player_element,
12658                                  CE_HITTING_X, touched_side);
12659       }
12660     }
12661   }
12662
12663   /* "hitting something" is also true when hitting the playfield border */
12664   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12665                            CE_HITTING_SOMETHING, direction);
12666 }
12667
12668 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12669 {
12670   int i, kill_x = -1, kill_y = -1;
12671
12672   int bad_element = -1;
12673   static int test_xy[4][2] =
12674   {
12675     { 0, -1 },
12676     { -1, 0 },
12677     { +1, 0 },
12678     { 0, +1 }
12679   };
12680   static int test_dir[4] =
12681   {
12682     MV_UP,
12683     MV_LEFT,
12684     MV_RIGHT,
12685     MV_DOWN
12686   };
12687
12688   for (i = 0; i < NUM_DIRECTIONS; i++)
12689   {
12690     int test_x, test_y, test_move_dir, test_element;
12691
12692     test_x = good_x + test_xy[i][0];
12693     test_y = good_y + test_xy[i][1];
12694
12695     if (!IN_LEV_FIELD(test_x, test_y))
12696       continue;
12697
12698     test_move_dir =
12699       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12700
12701     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12702
12703     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12704        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12705     */
12706     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12707         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12708     {
12709       kill_x = test_x;
12710       kill_y = test_y;
12711       bad_element = test_element;
12712
12713       break;
12714     }
12715   }
12716
12717   if (kill_x != -1 || kill_y != -1)
12718   {
12719     if (IS_PLAYER(good_x, good_y))
12720     {
12721       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12722
12723       if (player->shield_deadly_time_left > 0 &&
12724           !IS_INDESTRUCTIBLE(bad_element))
12725         Bang(kill_x, kill_y);
12726       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12727         KillPlayer(player);
12728     }
12729     else
12730       Bang(good_x, good_y);
12731   }
12732 }
12733
12734 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12735 {
12736   int i, kill_x = -1, kill_y = -1;
12737   int bad_element = Feld[bad_x][bad_y];
12738   static int test_xy[4][2] =
12739   {
12740     { 0, -1 },
12741     { -1, 0 },
12742     { +1, 0 },
12743     { 0, +1 }
12744   };
12745   static int touch_dir[4] =
12746   {
12747     MV_LEFT | MV_RIGHT,
12748     MV_UP   | MV_DOWN,
12749     MV_UP   | MV_DOWN,
12750     MV_LEFT | MV_RIGHT
12751   };
12752   static int test_dir[4] =
12753   {
12754     MV_UP,
12755     MV_LEFT,
12756     MV_RIGHT,
12757     MV_DOWN
12758   };
12759
12760   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12761     return;
12762
12763   for (i = 0; i < NUM_DIRECTIONS; i++)
12764   {
12765     int test_x, test_y, test_move_dir, test_element;
12766
12767     test_x = bad_x + test_xy[i][0];
12768     test_y = bad_y + test_xy[i][1];
12769
12770     if (!IN_LEV_FIELD(test_x, test_y))
12771       continue;
12772
12773     test_move_dir =
12774       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12775
12776     test_element = Feld[test_x][test_y];
12777
12778     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12779        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12780     */
12781     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12782         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12783     {
12784       /* good thing is player or penguin that does not move away */
12785       if (IS_PLAYER(test_x, test_y))
12786       {
12787         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12788
12789         if (bad_element == EL_ROBOT && player->is_moving)
12790           continue;     /* robot does not kill player if he is moving */
12791
12792         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12793         {
12794           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12795             continue;           /* center and border element do not touch */
12796         }
12797
12798         kill_x = test_x;
12799         kill_y = test_y;
12800
12801         break;
12802       }
12803       else if (test_element == EL_PENGUIN)
12804       {
12805         kill_x = test_x;
12806         kill_y = test_y;
12807
12808         break;
12809       }
12810     }
12811   }
12812
12813   if (kill_x != -1 || kill_y != -1)
12814   {
12815     if (IS_PLAYER(kill_x, kill_y))
12816     {
12817       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12818
12819       if (player->shield_deadly_time_left > 0 &&
12820           !IS_INDESTRUCTIBLE(bad_element))
12821         Bang(bad_x, bad_y);
12822       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12823         KillPlayer(player);
12824     }
12825     else
12826       Bang(kill_x, kill_y);
12827   }
12828 }
12829
12830 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12831 {
12832   int bad_element = Feld[bad_x][bad_y];
12833   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12834   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12835   int test_x = bad_x + dx, test_y = bad_y + dy;
12836   int test_move_dir, test_element;
12837   int kill_x = -1, kill_y = -1;
12838
12839   if (!IN_LEV_FIELD(test_x, test_y))
12840     return;
12841
12842   test_move_dir =
12843     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12844
12845   test_element = Feld[test_x][test_y];
12846
12847   if (test_move_dir != bad_move_dir)
12848   {
12849     /* good thing can be player or penguin that does not move away */
12850     if (IS_PLAYER(test_x, test_y))
12851     {
12852       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12853
12854       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12855          player as being hit when he is moving towards the bad thing, because
12856          the "get hit by" condition would be lost after the player stops) */
12857       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12858         return;         /* player moves away from bad thing */
12859
12860       kill_x = test_x;
12861       kill_y = test_y;
12862     }
12863     else if (test_element == EL_PENGUIN)
12864     {
12865       kill_x = test_x;
12866       kill_y = test_y;
12867     }
12868   }
12869
12870   if (kill_x != -1 || kill_y != -1)
12871   {
12872     if (IS_PLAYER(kill_x, kill_y))
12873     {
12874       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12875
12876       if (player->shield_deadly_time_left > 0 &&
12877           !IS_INDESTRUCTIBLE(bad_element))
12878         Bang(bad_x, bad_y);
12879       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12880         KillPlayer(player);
12881     }
12882     else
12883       Bang(kill_x, kill_y);
12884   }
12885 }
12886
12887 void TestIfPlayerTouchesBadThing(int x, int y)
12888 {
12889   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12890 }
12891
12892 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12893 {
12894   TestIfGoodThingHitsBadThing(x, y, move_dir);
12895 }
12896
12897 void TestIfBadThingTouchesPlayer(int x, int y)
12898 {
12899   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12900 }
12901
12902 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12903 {
12904   TestIfBadThingHitsGoodThing(x, y, move_dir);
12905 }
12906
12907 void TestIfFriendTouchesBadThing(int x, int y)
12908 {
12909   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12910 }
12911
12912 void TestIfBadThingTouchesFriend(int x, int y)
12913 {
12914   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12915 }
12916
12917 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12918 {
12919   int i, kill_x = bad_x, kill_y = bad_y;
12920   static int xy[4][2] =
12921   {
12922     { 0, -1 },
12923     { -1, 0 },
12924     { +1, 0 },
12925     { 0, +1 }
12926   };
12927
12928   for (i = 0; i < NUM_DIRECTIONS; i++)
12929   {
12930     int x, y, element;
12931
12932     x = bad_x + xy[i][0];
12933     y = bad_y + xy[i][1];
12934     if (!IN_LEV_FIELD(x, y))
12935       continue;
12936
12937     element = Feld[x][y];
12938     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12939         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12940     {
12941       kill_x = x;
12942       kill_y = y;
12943       break;
12944     }
12945   }
12946
12947   if (kill_x != bad_x || kill_y != bad_y)
12948     Bang(bad_x, bad_y);
12949 }
12950
12951 void KillPlayer(struct PlayerInfo *player)
12952 {
12953   int jx = player->jx, jy = player->jy;
12954
12955   if (!player->active)
12956     return;
12957
12958 #if 0
12959   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12960          player->killed, player->active, player->reanimated);
12961 #endif
12962
12963   /* the following code was introduced to prevent an infinite loop when calling
12964      -> Bang()
12965      -> CheckTriggeredElementChangeExt()
12966      -> ExecuteCustomElementAction()
12967      -> KillPlayer()
12968      -> (infinitely repeating the above sequence of function calls)
12969      which occurs when killing the player while having a CE with the setting
12970      "kill player X when explosion of <player X>"; the solution using a new
12971      field "player->killed" was chosen for backwards compatibility, although
12972      clever use of the fields "player->active" etc. would probably also work */
12973 #if 1
12974   if (player->killed)
12975     return;
12976 #endif
12977
12978   player->killed = TRUE;
12979
12980   /* remove accessible field at the player's position */
12981   Feld[jx][jy] = EL_EMPTY;
12982
12983   /* deactivate shield (else Bang()/Explode() would not work right) */
12984   player->shield_normal_time_left = 0;
12985   player->shield_deadly_time_left = 0;
12986
12987 #if 0
12988   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12989          player->killed, player->active, player->reanimated);
12990 #endif
12991
12992   Bang(jx, jy);
12993
12994 #if 0
12995   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12996          player->killed, player->active, player->reanimated);
12997 #endif
12998
12999   if (player->reanimated)       /* killed player may have been reanimated */
13000     player->killed = player->reanimated = FALSE;
13001   else
13002     BuryPlayer(player);
13003 }
13004
13005 static void KillPlayerUnlessEnemyProtected(int x, int y)
13006 {
13007   if (!PLAYER_ENEMY_PROTECTED(x, y))
13008     KillPlayer(PLAYERINFO(x, y));
13009 }
13010
13011 static void KillPlayerUnlessExplosionProtected(int x, int y)
13012 {
13013   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13014     KillPlayer(PLAYERINFO(x, y));
13015 }
13016
13017 void BuryPlayer(struct PlayerInfo *player)
13018 {
13019   int jx = player->jx, jy = player->jy;
13020
13021   if (!player->active)
13022     return;
13023
13024   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13025   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13026
13027   player->GameOver = TRUE;
13028   RemovePlayer(player);
13029 }
13030
13031 void RemovePlayer(struct PlayerInfo *player)
13032 {
13033   int jx = player->jx, jy = player->jy;
13034   int i, found = FALSE;
13035
13036   player->present = FALSE;
13037   player->active = FALSE;
13038
13039   if (!ExplodeField[jx][jy])
13040     StorePlayer[jx][jy] = 0;
13041
13042   if (player->is_moving)
13043     TEST_DrawLevelField(player->last_jx, player->last_jy);
13044
13045   for (i = 0; i < MAX_PLAYERS; i++)
13046     if (stored_player[i].active)
13047       found = TRUE;
13048
13049   if (!found)
13050     AllPlayersGone = TRUE;
13051
13052   ExitX = ZX = jx;
13053   ExitY = ZY = jy;
13054 }
13055
13056 static void setFieldForSnapping(int x, int y, int element, int direction)
13057 {
13058   struct ElementInfo *ei = &element_info[element];
13059   int direction_bit = MV_DIR_TO_BIT(direction);
13060   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13061   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13062                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13063
13064   Feld[x][y] = EL_ELEMENT_SNAPPING;
13065   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13066
13067   ResetGfxAnimation(x, y);
13068
13069   GfxElement[x][y] = element;
13070   GfxAction[x][y] = action;
13071   GfxDir[x][y] = direction;
13072   GfxFrame[x][y] = -1;
13073 }
13074
13075 /*
13076   =============================================================================
13077   checkDiagonalPushing()
13078   -----------------------------------------------------------------------------
13079   check if diagonal input device direction results in pushing of object
13080   (by checking if the alternative direction is walkable, diggable, ...)
13081   =============================================================================
13082 */
13083
13084 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13085                                     int x, int y, int real_dx, int real_dy)
13086 {
13087   int jx, jy, dx, dy, xx, yy;
13088
13089   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13090     return TRUE;
13091
13092   /* diagonal direction: check alternative direction */
13093   jx = player->jx;
13094   jy = player->jy;
13095   dx = x - jx;
13096   dy = y - jy;
13097   xx = jx + (dx == 0 ? real_dx : 0);
13098   yy = jy + (dy == 0 ? real_dy : 0);
13099
13100   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13101 }
13102
13103 /*
13104   =============================================================================
13105   DigField()
13106   -----------------------------------------------------------------------------
13107   x, y:                 field next to player (non-diagonal) to try to dig to
13108   real_dx, real_dy:     direction as read from input device (can be diagonal)
13109   =============================================================================
13110 */
13111
13112 static int DigField(struct PlayerInfo *player,
13113                     int oldx, int oldy, int x, int y,
13114                     int real_dx, int real_dy, int mode)
13115 {
13116   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13117   boolean player_was_pushing = player->is_pushing;
13118   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13119   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13120   int jx = oldx, jy = oldy;
13121   int dx = x - jx, dy = y - jy;
13122   int nextx = x + dx, nexty = y + dy;
13123   int move_direction = (dx == -1 ? MV_LEFT  :
13124                         dx == +1 ? MV_RIGHT :
13125                         dy == -1 ? MV_UP    :
13126                         dy == +1 ? MV_DOWN  : MV_NONE);
13127   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13128   int dig_side = MV_DIR_OPPOSITE(move_direction);
13129   int old_element = Feld[jx][jy];
13130   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13131   int collect_count;
13132
13133   if (is_player)                /* function can also be called by EL_PENGUIN */
13134   {
13135     if (player->MovPos == 0)
13136     {
13137       player->is_digging = FALSE;
13138       player->is_collecting = FALSE;
13139     }
13140
13141     if (player->MovPos == 0)    /* last pushing move finished */
13142       player->is_pushing = FALSE;
13143
13144     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13145     {
13146       player->is_switching = FALSE;
13147       player->push_delay = -1;
13148
13149       return MP_NO_ACTION;
13150     }
13151   }
13152
13153   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13154     old_element = Back[jx][jy];
13155
13156   /* in case of element dropped at player position, check background */
13157   else if (Back[jx][jy] != EL_EMPTY &&
13158            game.engine_version >= VERSION_IDENT(2,2,0,0))
13159     old_element = Back[jx][jy];
13160
13161   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13162     return MP_NO_ACTION;        /* field has no opening in this direction */
13163
13164   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13165     return MP_NO_ACTION;        /* field has no opening in this direction */
13166
13167   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13168   {
13169     SplashAcid(x, y);
13170
13171     Feld[jx][jy] = player->artwork_element;
13172     InitMovingField(jx, jy, MV_DOWN);
13173     Store[jx][jy] = EL_ACID;
13174     ContinueMoving(jx, jy);
13175     BuryPlayer(player);
13176
13177     return MP_DONT_RUN_INTO;
13178   }
13179
13180   if (player_can_move && DONT_RUN_INTO(element))
13181   {
13182     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13183
13184     return MP_DONT_RUN_INTO;
13185   }
13186
13187   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13188     return MP_NO_ACTION;
13189
13190   collect_count = element_info[element].collect_count_initial;
13191
13192   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13193     return MP_NO_ACTION;
13194
13195   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13196     player_can_move = player_can_move_or_snap;
13197
13198   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13199       game.engine_version >= VERSION_IDENT(2,2,0,0))
13200   {
13201     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13202                                player->index_bit, dig_side);
13203     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13204                                         player->index_bit, dig_side);
13205
13206     if (element == EL_DC_LANDMINE)
13207       Bang(x, y);
13208
13209     if (Feld[x][y] != element)          /* field changed by snapping */
13210       return MP_ACTION;
13211
13212     return MP_NO_ACTION;
13213   }
13214
13215   if (player->gravity && is_player && !player->is_auto_moving &&
13216       canFallDown(player) && move_direction != MV_DOWN &&
13217       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13218     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13219
13220   if (player_can_move &&
13221       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13222   {
13223     int sound_element = SND_ELEMENT(element);
13224     int sound_action = ACTION_WALKING;
13225
13226     if (IS_RND_GATE(element))
13227     {
13228       if (!player->key[RND_GATE_NR(element)])
13229         return MP_NO_ACTION;
13230     }
13231     else if (IS_RND_GATE_GRAY(element))
13232     {
13233       if (!player->key[RND_GATE_GRAY_NR(element)])
13234         return MP_NO_ACTION;
13235     }
13236     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13237     {
13238       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13239         return MP_NO_ACTION;
13240     }
13241     else if (element == EL_EXIT_OPEN ||
13242              element == EL_EM_EXIT_OPEN ||
13243              element == EL_EM_EXIT_OPENING ||
13244              element == EL_STEEL_EXIT_OPEN ||
13245              element == EL_EM_STEEL_EXIT_OPEN ||
13246              element == EL_EM_STEEL_EXIT_OPENING ||
13247              element == EL_SP_EXIT_OPEN ||
13248              element == EL_SP_EXIT_OPENING)
13249     {
13250       sound_action = ACTION_PASSING;    /* player is passing exit */
13251     }
13252     else if (element == EL_EMPTY)
13253     {
13254       sound_action = ACTION_MOVING;             /* nothing to walk on */
13255     }
13256
13257     /* play sound from background or player, whatever is available */
13258     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13259       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13260     else
13261       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13262   }
13263   else if (player_can_move &&
13264            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13265   {
13266     if (!ACCESS_FROM(element, opposite_direction))
13267       return MP_NO_ACTION;      /* field not accessible from this direction */
13268
13269     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13270       return MP_NO_ACTION;
13271
13272     if (IS_EM_GATE(element))
13273     {
13274       if (!player->key[EM_GATE_NR(element)])
13275         return MP_NO_ACTION;
13276     }
13277     else if (IS_EM_GATE_GRAY(element))
13278     {
13279       if (!player->key[EM_GATE_GRAY_NR(element)])
13280         return MP_NO_ACTION;
13281     }
13282     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13283     {
13284       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13285         return MP_NO_ACTION;
13286     }
13287     else if (IS_EMC_GATE(element))
13288     {
13289       if (!player->key[EMC_GATE_NR(element)])
13290         return MP_NO_ACTION;
13291     }
13292     else if (IS_EMC_GATE_GRAY(element))
13293     {
13294       if (!player->key[EMC_GATE_GRAY_NR(element)])
13295         return MP_NO_ACTION;
13296     }
13297     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13298     {
13299       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13300         return MP_NO_ACTION;
13301     }
13302     else if (element == EL_DC_GATE_WHITE ||
13303              element == EL_DC_GATE_WHITE_GRAY ||
13304              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13305     {
13306       if (player->num_white_keys == 0)
13307         return MP_NO_ACTION;
13308
13309       player->num_white_keys--;
13310     }
13311     else if (IS_SP_PORT(element))
13312     {
13313       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13314           element == EL_SP_GRAVITY_PORT_RIGHT ||
13315           element == EL_SP_GRAVITY_PORT_UP ||
13316           element == EL_SP_GRAVITY_PORT_DOWN)
13317         player->gravity = !player->gravity;
13318       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13319                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13320                element == EL_SP_GRAVITY_ON_PORT_UP ||
13321                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13322         player->gravity = TRUE;
13323       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13324                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13325                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13326                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13327         player->gravity = FALSE;
13328     }
13329
13330     /* automatically move to the next field with double speed */
13331     player->programmed_action = move_direction;
13332
13333     if (player->move_delay_reset_counter == 0)
13334     {
13335       player->move_delay_reset_counter = 2;     /* two double speed steps */
13336
13337       DOUBLE_PLAYER_SPEED(player);
13338     }
13339
13340     PlayLevelSoundAction(x, y, ACTION_PASSING);
13341   }
13342   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13343   {
13344     RemoveField(x, y);
13345
13346     if (mode != DF_SNAP)
13347     {
13348       GfxElement[x][y] = GFX_ELEMENT(element);
13349       player->is_digging = TRUE;
13350     }
13351
13352     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13353
13354     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13355                                         player->index_bit, dig_side);
13356
13357     if (mode == DF_SNAP)
13358     {
13359       if (level.block_snap_field)
13360         setFieldForSnapping(x, y, element, move_direction);
13361       else
13362         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13363
13364       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13365                                           player->index_bit, dig_side);
13366     }
13367   }
13368   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13369   {
13370     RemoveField(x, y);
13371
13372     if (is_player && mode != DF_SNAP)
13373     {
13374       GfxElement[x][y] = element;
13375       player->is_collecting = TRUE;
13376     }
13377
13378     if (element == EL_SPEED_PILL)
13379     {
13380       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13381     }
13382     else if (element == EL_EXTRA_TIME && level.time > 0)
13383     {
13384       TimeLeft += level.extra_time;
13385
13386       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13387
13388       DisplayGameControlValues();
13389     }
13390     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13391     {
13392       player->shield_normal_time_left += level.shield_normal_time;
13393       if (element == EL_SHIELD_DEADLY)
13394         player->shield_deadly_time_left += level.shield_deadly_time;
13395     }
13396     else if (element == EL_DYNAMITE ||
13397              element == EL_EM_DYNAMITE ||
13398              element == EL_SP_DISK_RED)
13399     {
13400       if (player->inventory_size < MAX_INVENTORY_SIZE)
13401         player->inventory_element[player->inventory_size++] = element;
13402
13403       DrawGameDoorValues();
13404     }
13405     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13406     {
13407       player->dynabomb_count++;
13408       player->dynabombs_left++;
13409     }
13410     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13411     {
13412       player->dynabomb_size++;
13413     }
13414     else if (element == EL_DYNABOMB_INCREASE_POWER)
13415     {
13416       player->dynabomb_xl = TRUE;
13417     }
13418     else if (IS_KEY(element))
13419     {
13420       player->key[KEY_NR(element)] = TRUE;
13421
13422       DrawGameDoorValues();
13423     }
13424     else if (element == EL_DC_KEY_WHITE)
13425     {
13426       player->num_white_keys++;
13427
13428       /* display white keys? */
13429       /* DrawGameDoorValues(); */
13430     }
13431     else if (IS_ENVELOPE(element))
13432     {
13433       player->show_envelope = element;
13434     }
13435     else if (element == EL_EMC_LENSES)
13436     {
13437       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13438
13439       RedrawAllInvisibleElementsForLenses();
13440     }
13441     else if (element == EL_EMC_MAGNIFIER)
13442     {
13443       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13444
13445       RedrawAllInvisibleElementsForMagnifier();
13446     }
13447     else if (IS_DROPPABLE(element) ||
13448              IS_THROWABLE(element))     /* can be collected and dropped */
13449     {
13450       int i;
13451
13452       if (collect_count == 0)
13453         player->inventory_infinite_element = element;
13454       else
13455         for (i = 0; i < collect_count; i++)
13456           if (player->inventory_size < MAX_INVENTORY_SIZE)
13457             player->inventory_element[player->inventory_size++] = element;
13458
13459       DrawGameDoorValues();
13460     }
13461     else if (collect_count > 0)
13462     {
13463       local_player->gems_still_needed -= collect_count;
13464       if (local_player->gems_still_needed < 0)
13465         local_player->gems_still_needed = 0;
13466
13467       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13468
13469       DisplayGameControlValues();
13470     }
13471
13472     RaiseScoreElement(element);
13473     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13474
13475     if (is_player)
13476       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13477                                           player->index_bit, dig_side);
13478
13479     if (mode == DF_SNAP)
13480     {
13481       if (level.block_snap_field)
13482         setFieldForSnapping(x, y, element, move_direction);
13483       else
13484         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13485
13486       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13487                                           player->index_bit, dig_side);
13488     }
13489   }
13490   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13491   {
13492     if (mode == DF_SNAP && element != EL_BD_ROCK)
13493       return MP_NO_ACTION;
13494
13495     if (CAN_FALL(element) && dy)
13496       return MP_NO_ACTION;
13497
13498     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13499         !(element == EL_SPRING && level.use_spring_bug))
13500       return MP_NO_ACTION;
13501
13502     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13503         ((move_direction & MV_VERTICAL &&
13504           ((element_info[element].move_pattern & MV_LEFT &&
13505             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13506            (element_info[element].move_pattern & MV_RIGHT &&
13507             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13508          (move_direction & MV_HORIZONTAL &&
13509           ((element_info[element].move_pattern & MV_UP &&
13510             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13511            (element_info[element].move_pattern & MV_DOWN &&
13512             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13513       return MP_NO_ACTION;
13514
13515     /* do not push elements already moving away faster than player */
13516     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13517         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13518       return MP_NO_ACTION;
13519
13520     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13521     {
13522       if (player->push_delay_value == -1 || !player_was_pushing)
13523         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13524     }
13525     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13526     {
13527       if (player->push_delay_value == -1)
13528         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13529     }
13530     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13531     {
13532       if (!player->is_pushing)
13533         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13534     }
13535
13536     player->is_pushing = TRUE;
13537     player->is_active = TRUE;
13538
13539     if (!(IN_LEV_FIELD(nextx, nexty) &&
13540           (IS_FREE(nextx, nexty) ||
13541            (IS_SB_ELEMENT(element) &&
13542             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13543            (IS_CUSTOM_ELEMENT(element) &&
13544             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13545       return MP_NO_ACTION;
13546
13547     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13548       return MP_NO_ACTION;
13549
13550     if (player->push_delay == -1)       /* new pushing; restart delay */
13551       player->push_delay = 0;
13552
13553     if (player->push_delay < player->push_delay_value &&
13554         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13555         element != EL_SPRING && element != EL_BALLOON)
13556     {
13557       /* make sure that there is no move delay before next try to push */
13558       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13559         player->move_delay = 0;
13560
13561       return MP_NO_ACTION;
13562     }
13563
13564     if (IS_CUSTOM_ELEMENT(element) &&
13565         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13566     {
13567       if (!DigFieldByCE(nextx, nexty, element))
13568         return MP_NO_ACTION;
13569     }
13570
13571     if (IS_SB_ELEMENT(element))
13572     {
13573       if (element == EL_SOKOBAN_FIELD_FULL)
13574       {
13575         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13576         local_player->sokobanfields_still_needed++;
13577       }
13578
13579       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13580       {
13581         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13582         local_player->sokobanfields_still_needed--;
13583       }
13584
13585       Feld[x][y] = EL_SOKOBAN_OBJECT;
13586
13587       if (Back[x][y] == Back[nextx][nexty])
13588         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13589       else if (Back[x][y] != 0)
13590         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13591                                     ACTION_EMPTYING);
13592       else
13593         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13594                                     ACTION_FILLING);
13595
13596       if (local_player->sokobanfields_still_needed == 0 &&
13597           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13598       {
13599         PlayerWins(player);
13600
13601         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13602       }
13603     }
13604     else
13605       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13606
13607     InitMovingField(x, y, move_direction);
13608     GfxAction[x][y] = ACTION_PUSHING;
13609
13610     if (mode == DF_SNAP)
13611       ContinueMoving(x, y);
13612     else
13613       MovPos[x][y] = (dx != 0 ? dx : dy);
13614
13615     Pushed[x][y] = TRUE;
13616     Pushed[nextx][nexty] = TRUE;
13617
13618     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13619       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13620     else
13621       player->push_delay_value = -1;    /* get new value later */
13622
13623     /* check for element change _after_ element has been pushed */
13624     if (game.use_change_when_pushing_bug)
13625     {
13626       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13627                                  player->index_bit, dig_side);
13628       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13629                                           player->index_bit, dig_side);
13630     }
13631   }
13632   else if (IS_SWITCHABLE(element))
13633   {
13634     if (PLAYER_SWITCHING(player, x, y))
13635     {
13636       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13637                                           player->index_bit, dig_side);
13638
13639       return MP_ACTION;
13640     }
13641
13642     player->is_switching = TRUE;
13643     player->switch_x = x;
13644     player->switch_y = y;
13645
13646     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13647
13648     if (element == EL_ROBOT_WHEEL)
13649     {
13650       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13651       ZX = x;
13652       ZY = y;
13653
13654       game.robot_wheel_active = TRUE;
13655
13656       TEST_DrawLevelField(x, y);
13657     }
13658     else if (element == EL_SP_TERMINAL)
13659     {
13660       int xx, yy;
13661
13662       SCAN_PLAYFIELD(xx, yy)
13663       {
13664         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13665           Bang(xx, yy);
13666         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13667           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13668       }
13669     }
13670     else if (IS_BELT_SWITCH(element))
13671     {
13672       ToggleBeltSwitch(x, y);
13673     }
13674     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13675              element == EL_SWITCHGATE_SWITCH_DOWN ||
13676              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13677              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13678     {
13679       ToggleSwitchgateSwitch(x, y);
13680     }
13681     else if (element == EL_LIGHT_SWITCH ||
13682              element == EL_LIGHT_SWITCH_ACTIVE)
13683     {
13684       ToggleLightSwitch(x, y);
13685     }
13686     else if (element == EL_TIMEGATE_SWITCH ||
13687              element == EL_DC_TIMEGATE_SWITCH)
13688     {
13689       ActivateTimegateSwitch(x, y);
13690     }
13691     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13692              element == EL_BALLOON_SWITCH_RIGHT ||
13693              element == EL_BALLOON_SWITCH_UP    ||
13694              element == EL_BALLOON_SWITCH_DOWN  ||
13695              element == EL_BALLOON_SWITCH_NONE  ||
13696              element == EL_BALLOON_SWITCH_ANY)
13697     {
13698       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13699                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13700                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13701                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13702                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13703                              move_direction);
13704     }
13705     else if (element == EL_LAMP)
13706     {
13707       Feld[x][y] = EL_LAMP_ACTIVE;
13708       local_player->lights_still_needed--;
13709
13710       ResetGfxAnimation(x, y);
13711       TEST_DrawLevelField(x, y);
13712     }
13713     else if (element == EL_TIME_ORB_FULL)
13714     {
13715       Feld[x][y] = EL_TIME_ORB_EMPTY;
13716
13717       if (level.time > 0 || level.use_time_orb_bug)
13718       {
13719         TimeLeft += level.time_orb_time;
13720         game.no_time_limit = FALSE;
13721
13722         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13723
13724         DisplayGameControlValues();
13725       }
13726
13727       ResetGfxAnimation(x, y);
13728       TEST_DrawLevelField(x, y);
13729     }
13730     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13731              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13732     {
13733       int xx, yy;
13734
13735       game.ball_state = !game.ball_state;
13736
13737       SCAN_PLAYFIELD(xx, yy)
13738       {
13739         int e = Feld[xx][yy];
13740
13741         if (game.ball_state)
13742         {
13743           if (e == EL_EMC_MAGIC_BALL)
13744             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13745           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13746             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13747         }
13748         else
13749         {
13750           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13751             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13752           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13753             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13754         }
13755       }
13756     }
13757
13758     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13759                                         player->index_bit, dig_side);
13760
13761     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13762                                         player->index_bit, dig_side);
13763
13764     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13765                                         player->index_bit, dig_side);
13766
13767     return MP_ACTION;
13768   }
13769   else
13770   {
13771     if (!PLAYER_SWITCHING(player, x, y))
13772     {
13773       player->is_switching = TRUE;
13774       player->switch_x = x;
13775       player->switch_y = y;
13776
13777       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13778                                  player->index_bit, dig_side);
13779       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13780                                           player->index_bit, dig_side);
13781
13782       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13783                                  player->index_bit, dig_side);
13784       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13785                                           player->index_bit, dig_side);
13786     }
13787
13788     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13789                                player->index_bit, dig_side);
13790     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13791                                         player->index_bit, dig_side);
13792
13793     return MP_NO_ACTION;
13794   }
13795
13796   player->push_delay = -1;
13797
13798   if (is_player)                /* function can also be called by EL_PENGUIN */
13799   {
13800     if (Feld[x][y] != element)          /* really digged/collected something */
13801     {
13802       player->is_collecting = !player->is_digging;
13803       player->is_active = TRUE;
13804     }
13805   }
13806
13807   return MP_MOVING;
13808 }
13809
13810 static boolean DigFieldByCE(int x, int y, int digging_element)
13811 {
13812   int element = Feld[x][y];
13813
13814   if (!IS_FREE(x, y))
13815   {
13816     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13817                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13818                   ACTION_BREAKING);
13819
13820     /* no element can dig solid indestructible elements */
13821     if (IS_INDESTRUCTIBLE(element) &&
13822         !IS_DIGGABLE(element) &&
13823         !IS_COLLECTIBLE(element))
13824       return FALSE;
13825
13826     if (AmoebaNr[x][y] &&
13827         (element == EL_AMOEBA_FULL ||
13828          element == EL_BD_AMOEBA ||
13829          element == EL_AMOEBA_GROWING))
13830     {
13831       AmoebaCnt[AmoebaNr[x][y]]--;
13832       AmoebaCnt2[AmoebaNr[x][y]]--;
13833     }
13834
13835     if (IS_MOVING(x, y))
13836       RemoveMovingField(x, y);
13837     else
13838     {
13839       RemoveField(x, y);
13840       TEST_DrawLevelField(x, y);
13841     }
13842
13843     /* if digged element was about to explode, prevent the explosion */
13844     ExplodeField[x][y] = EX_TYPE_NONE;
13845
13846     PlayLevelSoundAction(x, y, action);
13847   }
13848
13849   Store[x][y] = EL_EMPTY;
13850
13851   /* this makes it possible to leave the removed element again */
13852   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13853     Store[x][y] = element;
13854
13855   return TRUE;
13856 }
13857
13858 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13859 {
13860   int jx = player->jx, jy = player->jy;
13861   int x = jx + dx, y = jy + dy;
13862   int snap_direction = (dx == -1 ? MV_LEFT  :
13863                         dx == +1 ? MV_RIGHT :
13864                         dy == -1 ? MV_UP    :
13865                         dy == +1 ? MV_DOWN  : MV_NONE);
13866   boolean can_continue_snapping = (level.continuous_snapping &&
13867                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13868
13869   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13870     return FALSE;
13871
13872   if (!player->active || !IN_LEV_FIELD(x, y))
13873     return FALSE;
13874
13875   if (dx && dy)
13876     return FALSE;
13877
13878   if (!dx && !dy)
13879   {
13880     if (player->MovPos == 0)
13881       player->is_pushing = FALSE;
13882
13883     player->is_snapping = FALSE;
13884
13885     if (player->MovPos == 0)
13886     {
13887       player->is_moving = FALSE;
13888       player->is_digging = FALSE;
13889       player->is_collecting = FALSE;
13890     }
13891
13892     return FALSE;
13893   }
13894
13895   /* prevent snapping with already pressed snap key when not allowed */
13896   if (player->is_snapping && !can_continue_snapping)
13897     return FALSE;
13898
13899   player->MovDir = snap_direction;
13900
13901   if (player->MovPos == 0)
13902   {
13903     player->is_moving = FALSE;
13904     player->is_digging = FALSE;
13905     player->is_collecting = FALSE;
13906   }
13907
13908   player->is_dropping = FALSE;
13909   player->is_dropping_pressed = FALSE;
13910   player->drop_pressed_delay = 0;
13911
13912   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13913     return FALSE;
13914
13915   player->is_snapping = TRUE;
13916   player->is_active = TRUE;
13917
13918   if (player->MovPos == 0)
13919   {
13920     player->is_moving = FALSE;
13921     player->is_digging = FALSE;
13922     player->is_collecting = FALSE;
13923   }
13924
13925   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13926     TEST_DrawLevelField(player->last_jx, player->last_jy);
13927
13928   TEST_DrawLevelField(x, y);
13929
13930   return TRUE;
13931 }
13932
13933 static boolean DropElement(struct PlayerInfo *player)
13934 {
13935   int old_element, new_element;
13936   int dropx = player->jx, dropy = player->jy;
13937   int drop_direction = player->MovDir;
13938   int drop_side = drop_direction;
13939   int drop_element = get_next_dropped_element(player);
13940
13941   player->is_dropping_pressed = TRUE;
13942
13943   /* do not drop an element on top of another element; when holding drop key
13944      pressed without moving, dropped element must move away before the next
13945      element can be dropped (this is especially important if the next element
13946      is dynamite, which can be placed on background for historical reasons) */
13947   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13948     return MP_ACTION;
13949
13950   if (IS_THROWABLE(drop_element))
13951   {
13952     dropx += GET_DX_FROM_DIR(drop_direction);
13953     dropy += GET_DY_FROM_DIR(drop_direction);
13954
13955     if (!IN_LEV_FIELD(dropx, dropy))
13956       return FALSE;
13957   }
13958
13959   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13960   new_element = drop_element;           /* default: no change when dropping */
13961
13962   /* check if player is active, not moving and ready to drop */
13963   if (!player->active || player->MovPos || player->drop_delay > 0)
13964     return FALSE;
13965
13966   /* check if player has anything that can be dropped */
13967   if (new_element == EL_UNDEFINED)
13968     return FALSE;
13969
13970   /* check if drop key was pressed long enough for EM style dynamite */
13971   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13972     return FALSE;
13973
13974   /* check if anything can be dropped at the current position */
13975   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13976     return FALSE;
13977
13978   /* collected custom elements can only be dropped on empty fields */
13979   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13980     return FALSE;
13981
13982   if (old_element != EL_EMPTY)
13983     Back[dropx][dropy] = old_element;   /* store old element on this field */
13984
13985   ResetGfxAnimation(dropx, dropy);
13986   ResetRandomAnimationValue(dropx, dropy);
13987
13988   if (player->inventory_size > 0 ||
13989       player->inventory_infinite_element != EL_UNDEFINED)
13990   {
13991     if (player->inventory_size > 0)
13992     {
13993       player->inventory_size--;
13994
13995       DrawGameDoorValues();
13996
13997       if (new_element == EL_DYNAMITE)
13998         new_element = EL_DYNAMITE_ACTIVE;
13999       else if (new_element == EL_EM_DYNAMITE)
14000         new_element = EL_EM_DYNAMITE_ACTIVE;
14001       else if (new_element == EL_SP_DISK_RED)
14002         new_element = EL_SP_DISK_RED_ACTIVE;
14003     }
14004
14005     Feld[dropx][dropy] = new_element;
14006
14007     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14008       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14009                           el2img(Feld[dropx][dropy]), 0);
14010
14011     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14012
14013     /* needed if previous element just changed to "empty" in the last frame */
14014     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14015
14016     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14017                                player->index_bit, drop_side);
14018     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14019                                         CE_PLAYER_DROPS_X,
14020                                         player->index_bit, drop_side);
14021
14022     TestIfElementTouchesCustomElement(dropx, dropy);
14023   }
14024   else          /* player is dropping a dyna bomb */
14025   {
14026     player->dynabombs_left--;
14027
14028     Feld[dropx][dropy] = new_element;
14029
14030     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14031       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14032                           el2img(Feld[dropx][dropy]), 0);
14033
14034     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14035   }
14036
14037   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14038     InitField_WithBug1(dropx, dropy, FALSE);
14039
14040   new_element = Feld[dropx][dropy];     /* element might have changed */
14041
14042   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14043       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14044   {
14045     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14046       MovDir[dropx][dropy] = drop_direction;
14047
14048     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14049
14050     /* do not cause impact style collision by dropping elements that can fall */
14051     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14052   }
14053
14054   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14055   player->is_dropping = TRUE;
14056
14057   player->drop_pressed_delay = 0;
14058   player->is_dropping_pressed = FALSE;
14059
14060   player->drop_x = dropx;
14061   player->drop_y = dropy;
14062
14063   return TRUE;
14064 }
14065
14066 /* ------------------------------------------------------------------------- */
14067 /* game sound playing functions                                              */
14068 /* ------------------------------------------------------------------------- */
14069
14070 static int *loop_sound_frame = NULL;
14071 static int *loop_sound_volume = NULL;
14072
14073 void InitPlayLevelSound()
14074 {
14075   int num_sounds = getSoundListSize();
14076
14077   checked_free(loop_sound_frame);
14078   checked_free(loop_sound_volume);
14079
14080   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14081   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14082 }
14083
14084 static void PlayLevelSound(int x, int y, int nr)
14085 {
14086   int sx = SCREENX(x), sy = SCREENY(y);
14087   int volume, stereo_position;
14088   int max_distance = 8;
14089   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14090
14091   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14092       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14093     return;
14094
14095   if (!IN_LEV_FIELD(x, y) ||
14096       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14097       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14098     return;
14099
14100   volume = SOUND_MAX_VOLUME;
14101
14102   if (!IN_SCR_FIELD(sx, sy))
14103   {
14104     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14105     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14106
14107     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14108   }
14109
14110   stereo_position = (SOUND_MAX_LEFT +
14111                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14112                      (SCR_FIELDX + 2 * max_distance));
14113
14114   if (IS_LOOP_SOUND(nr))
14115   {
14116     /* This assures that quieter loop sounds do not overwrite louder ones,
14117        while restarting sound volume comparison with each new game frame. */
14118
14119     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14120       return;
14121
14122     loop_sound_volume[nr] = volume;
14123     loop_sound_frame[nr] = FrameCounter;
14124   }
14125
14126   PlaySoundExt(nr, volume, stereo_position, type);
14127 }
14128
14129 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14130 {
14131   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14132                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14133                  y < LEVELY(BY1) ? LEVELY(BY1) :
14134                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14135                  sound_action);
14136 }
14137
14138 static void PlayLevelSoundAction(int x, int y, int action)
14139 {
14140   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14141 }
14142
14143 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14144 {
14145   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14146
14147   if (sound_effect != SND_UNDEFINED)
14148     PlayLevelSound(x, y, sound_effect);
14149 }
14150
14151 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14152                                               int action)
14153 {
14154   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14155
14156   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14157     PlayLevelSound(x, y, sound_effect);
14158 }
14159
14160 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14161 {
14162   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14163
14164   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14165     PlayLevelSound(x, y, sound_effect);
14166 }
14167
14168 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14169 {
14170   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14171
14172   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14173     StopSound(sound_effect);
14174 }
14175
14176 static void PlayLevelMusic()
14177 {
14178   if (levelset.music[level_nr] != MUS_UNDEFINED)
14179     PlayMusic(levelset.music[level_nr]);        /* from config file */
14180   else
14181     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14182 }
14183
14184 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14185 {
14186   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14187   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14188   int x = xx - 1 - offset;
14189   int y = yy - 1 - offset;
14190
14191   switch (sample)
14192   {
14193     case SAMPLE_blank:
14194       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14195       break;
14196
14197     case SAMPLE_roll:
14198       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14199       break;
14200
14201     case SAMPLE_stone:
14202       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14203       break;
14204
14205     case SAMPLE_nut:
14206       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14207       break;
14208
14209     case SAMPLE_crack:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14211       break;
14212
14213     case SAMPLE_bug:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14215       break;
14216
14217     case SAMPLE_tank:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14219       break;
14220
14221     case SAMPLE_android_clone:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14223       break;
14224
14225     case SAMPLE_android_move:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14227       break;
14228
14229     case SAMPLE_spring:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14231       break;
14232
14233     case SAMPLE_slurp:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14235       break;
14236
14237     case SAMPLE_eater:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14239       break;
14240
14241     case SAMPLE_eater_eat:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14243       break;
14244
14245     case SAMPLE_alien:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14247       break;
14248
14249     case SAMPLE_collect:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14251       break;
14252
14253     case SAMPLE_diamond:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14255       break;
14256
14257     case SAMPLE_squash:
14258       /* !!! CHECK THIS !!! */
14259 #if 1
14260       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14261 #else
14262       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14263 #endif
14264       break;
14265
14266     case SAMPLE_wonderfall:
14267       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14268       break;
14269
14270     case SAMPLE_drip:
14271       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14272       break;
14273
14274     case SAMPLE_push:
14275       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14276       break;
14277
14278     case SAMPLE_dirt:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14280       break;
14281
14282     case SAMPLE_acid:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14284       break;
14285
14286     case SAMPLE_ball:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14288       break;
14289
14290     case SAMPLE_grow:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14292       break;
14293
14294     case SAMPLE_wonder:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14296       break;
14297
14298     case SAMPLE_door:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14300       break;
14301
14302     case SAMPLE_exit_open:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14304       break;
14305
14306     case SAMPLE_exit_leave:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14308       break;
14309
14310     case SAMPLE_dynamite:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14312       break;
14313
14314     case SAMPLE_tick:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14316       break;
14317
14318     case SAMPLE_press:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14320       break;
14321
14322     case SAMPLE_wheel:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14324       break;
14325
14326     case SAMPLE_boom:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14328       break;
14329
14330     case SAMPLE_die:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14332       break;
14333
14334     case SAMPLE_time:
14335       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14336       break;
14337
14338     default:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14340       break;
14341   }
14342 }
14343
14344 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14345 {
14346   int element = map_element_SP_to_RND(element_sp);
14347   int action = map_action_SP_to_RND(action_sp);
14348   int offset = (setup.sp_show_border_elements ? 0 : 1);
14349   int x = xx - offset;
14350   int y = yy - offset;
14351
14352   PlayLevelSoundElementAction(x, y, element, action);
14353 }
14354
14355 void RaiseScore(int value)
14356 {
14357   local_player->score += value;
14358
14359   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14360
14361   DisplayGameControlValues();
14362 }
14363
14364 void RaiseScoreElement(int element)
14365 {
14366   switch (element)
14367   {
14368     case EL_EMERALD:
14369     case EL_BD_DIAMOND:
14370     case EL_EMERALD_YELLOW:
14371     case EL_EMERALD_RED:
14372     case EL_EMERALD_PURPLE:
14373     case EL_SP_INFOTRON:
14374       RaiseScore(level.score[SC_EMERALD]);
14375       break;
14376     case EL_DIAMOND:
14377       RaiseScore(level.score[SC_DIAMOND]);
14378       break;
14379     case EL_CRYSTAL:
14380       RaiseScore(level.score[SC_CRYSTAL]);
14381       break;
14382     case EL_PEARL:
14383       RaiseScore(level.score[SC_PEARL]);
14384       break;
14385     case EL_BUG:
14386     case EL_BD_BUTTERFLY:
14387     case EL_SP_ELECTRON:
14388       RaiseScore(level.score[SC_BUG]);
14389       break;
14390     case EL_SPACESHIP:
14391     case EL_BD_FIREFLY:
14392     case EL_SP_SNIKSNAK:
14393       RaiseScore(level.score[SC_SPACESHIP]);
14394       break;
14395     case EL_YAMYAM:
14396     case EL_DARK_YAMYAM:
14397       RaiseScore(level.score[SC_YAMYAM]);
14398       break;
14399     case EL_ROBOT:
14400       RaiseScore(level.score[SC_ROBOT]);
14401       break;
14402     case EL_PACMAN:
14403       RaiseScore(level.score[SC_PACMAN]);
14404       break;
14405     case EL_NUT:
14406       RaiseScore(level.score[SC_NUT]);
14407       break;
14408     case EL_DYNAMITE:
14409     case EL_EM_DYNAMITE:
14410     case EL_SP_DISK_RED:
14411     case EL_DYNABOMB_INCREASE_NUMBER:
14412     case EL_DYNABOMB_INCREASE_SIZE:
14413     case EL_DYNABOMB_INCREASE_POWER:
14414       RaiseScore(level.score[SC_DYNAMITE]);
14415       break;
14416     case EL_SHIELD_NORMAL:
14417     case EL_SHIELD_DEADLY:
14418       RaiseScore(level.score[SC_SHIELD]);
14419       break;
14420     case EL_EXTRA_TIME:
14421       RaiseScore(level.extra_time_score);
14422       break;
14423     case EL_KEY_1:
14424     case EL_KEY_2:
14425     case EL_KEY_3:
14426     case EL_KEY_4:
14427     case EL_EM_KEY_1:
14428     case EL_EM_KEY_2:
14429     case EL_EM_KEY_3:
14430     case EL_EM_KEY_4:
14431     case EL_EMC_KEY_5:
14432     case EL_EMC_KEY_6:
14433     case EL_EMC_KEY_7:
14434     case EL_EMC_KEY_8:
14435     case EL_DC_KEY_WHITE:
14436       RaiseScore(level.score[SC_KEY]);
14437       break;
14438     default:
14439       RaiseScore(element_info[element].collect_score);
14440       break;
14441   }
14442 }
14443
14444 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14445 {
14446   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14447   {
14448     /* closing door required in case of envelope style request dialogs */
14449     if (!skip_request)
14450       CloseDoor(DOOR_CLOSE_1);
14451
14452 #if defined(NETWORK_AVALIABLE)
14453     if (options.network)
14454       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14455     else
14456 #endif
14457     {
14458       if (quick_quit)
14459       {
14460         FadeSkipNextFadeIn();
14461
14462         game_status = GAME_MODE_MAIN;
14463
14464         DrawAndFadeInMainMenu(REDRAW_FIELD);
14465       }
14466       else
14467       {
14468         game_status = GAME_MODE_MAIN;
14469
14470         DrawAndFadeInMainMenu(REDRAW_FIELD);
14471       }
14472     }
14473   }
14474   else          /* continue playing the game */
14475   {
14476     if (tape.playing && tape.deactivate_display)
14477       TapeDeactivateDisplayOff(TRUE);
14478
14479     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14480
14481     if (tape.playing && tape.deactivate_display)
14482       TapeDeactivateDisplayOn();
14483   }
14484 }
14485
14486 void RequestQuitGame(boolean ask_if_really_quit)
14487 {
14488   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14489   boolean skip_request = AllPlayersGone || quick_quit;
14490
14491   RequestQuitGameExt(skip_request, quick_quit,
14492                      "Do you really want to quit the game?");
14493 }
14494
14495
14496 /* ------------------------------------------------------------------------- */
14497 /* random generator functions                                                */
14498 /* ------------------------------------------------------------------------- */
14499
14500 unsigned int InitEngineRandom_RND(int seed)
14501 {
14502   game.num_random_calls = 0;
14503
14504   return InitEngineRandom(seed);
14505 }
14506
14507 unsigned int RND(int max)
14508 {
14509   if (max > 0)
14510   {
14511     game.num_random_calls++;
14512
14513     return GetEngineRandom(max);
14514   }
14515
14516   return 0;
14517 }
14518
14519
14520 /* ------------------------------------------------------------------------- */
14521 /* game engine snapshot handling functions                                   */
14522 /* ------------------------------------------------------------------------- */
14523
14524 struct EngineSnapshotInfo
14525 {
14526   /* runtime values for custom element collect score */
14527   int collect_score[NUM_CUSTOM_ELEMENTS];
14528
14529   /* runtime values for group element choice position */
14530   int choice_pos[NUM_GROUP_ELEMENTS];
14531
14532   /* runtime values for belt position animations */
14533   int belt_graphic[4][NUM_BELT_PARTS];
14534   int belt_anim_mode[4][NUM_BELT_PARTS];
14535 };
14536
14537 static struct EngineSnapshotInfo engine_snapshot_rnd;
14538 static char *snapshot_level_identifier = NULL;
14539 static int snapshot_level_nr = -1;
14540
14541 static void SaveEngineSnapshotValues_RND()
14542 {
14543   static int belt_base_active_element[4] =
14544   {
14545     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14546     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14547     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14548     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14549   };
14550   int i, j;
14551
14552   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14553   {
14554     int element = EL_CUSTOM_START + i;
14555
14556     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14557   }
14558
14559   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14560   {
14561     int element = EL_GROUP_START + i;
14562
14563     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14564   }
14565
14566   for (i = 0; i < 4; i++)
14567   {
14568     for (j = 0; j < NUM_BELT_PARTS; j++)
14569     {
14570       int element = belt_base_active_element[i] + j;
14571       int graphic = el2img(element);
14572       int anim_mode = graphic_info[graphic].anim_mode;
14573
14574       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14575       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14576     }
14577   }
14578 }
14579
14580 static void LoadEngineSnapshotValues_RND()
14581 {
14582   unsigned int num_random_calls = game.num_random_calls;
14583   int i, j;
14584
14585   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14586   {
14587     int element = EL_CUSTOM_START + i;
14588
14589     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14590   }
14591
14592   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14593   {
14594     int element = EL_GROUP_START + i;
14595
14596     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14597   }
14598
14599   for (i = 0; i < 4; i++)
14600   {
14601     for (j = 0; j < NUM_BELT_PARTS; j++)
14602     {
14603       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14604       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14605
14606       graphic_info[graphic].anim_mode = anim_mode;
14607     }
14608   }
14609
14610   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14611   {
14612     InitRND(tape.random_seed);
14613     for (i = 0; i < num_random_calls; i++)
14614       RND(1);
14615   }
14616
14617   if (game.num_random_calls != num_random_calls)
14618   {
14619     Error(ERR_INFO, "number of random calls out of sync");
14620     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14621     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14622     Error(ERR_EXIT, "this should not happen -- please debug");
14623   }
14624 }
14625
14626 void FreeEngineSnapshotSingle()
14627 {
14628   FreeSnapshotSingle();
14629
14630   setString(&snapshot_level_identifier, NULL);
14631   snapshot_level_nr = -1;
14632 }
14633
14634 void FreeEngineSnapshotList()
14635 {
14636   FreeSnapshotList();
14637 }
14638
14639 ListNode *SaveEngineSnapshotBuffers()
14640 {
14641   ListNode *buffers = NULL;
14642
14643   /* copy some special values to a structure better suited for the snapshot */
14644
14645   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14646     SaveEngineSnapshotValues_RND();
14647   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14648     SaveEngineSnapshotValues_EM();
14649   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14650     SaveEngineSnapshotValues_SP(&buffers);
14651
14652   /* save values stored in special snapshot structure */
14653
14654   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14655     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14656   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14657     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14658   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14659     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14660
14661   /* save further RND engine values */
14662
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14666
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14671
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14677
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14683
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14685
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14688
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14707
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14710
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14714
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14717
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14723
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14726
14727 #if 0
14728   ListNode *node = engine_snapshot_list_rnd;
14729   int num_bytes = 0;
14730
14731   while (node != NULL)
14732   {
14733     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14734
14735     node = node->next;
14736   }
14737
14738   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14739 #endif
14740
14741   return buffers;
14742 }
14743
14744 void SaveEngineSnapshotSingle()
14745 {
14746   ListNode *buffers = SaveEngineSnapshotBuffers();
14747
14748   /* finally save all snapshot buffers to single snapshot */
14749   SaveSnapshotSingle(buffers);
14750
14751   /* save level identification information */
14752   setString(&snapshot_level_identifier, leveldir_current->identifier);
14753   snapshot_level_nr = level_nr;
14754 }
14755
14756 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14757 {
14758   boolean save_snapshot =
14759     (initial_snapshot ||
14760      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14761      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14762       game.snapshot.changed_action));
14763
14764   game.snapshot.changed_action = FALSE;
14765
14766   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14767       tape.quick_resume ||
14768       !save_snapshot)
14769     return FALSE;
14770
14771   ListNode *buffers = SaveEngineSnapshotBuffers();
14772
14773   /* finally save all snapshot buffers to snapshot list */
14774   SaveSnapshotToList(buffers);
14775
14776   return TRUE;
14777 }
14778
14779 boolean SaveEngineSnapshotToList()
14780 {
14781   return SaveEngineSnapshotToListExt(FALSE);
14782 }
14783
14784 void SaveEngineSnapshotToListInitial()
14785 {
14786   FreeEngineSnapshotList();
14787
14788   SaveEngineSnapshotToListExt(TRUE);
14789 }
14790
14791 void LoadEngineSnapshotValues()
14792 {
14793   /* restore special values from snapshot structure */
14794
14795   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14796     LoadEngineSnapshotValues_RND();
14797   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14798     LoadEngineSnapshotValues_EM();
14799   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14800     LoadEngineSnapshotValues_SP();
14801 }
14802
14803 void LoadEngineSnapshotSingle()
14804 {
14805   LoadSnapshotSingle();
14806
14807   LoadEngineSnapshotValues();
14808 }
14809
14810 void LoadEngineSnapshot_Undo(int steps)
14811 {
14812   LoadSnapshotFromList_Older(steps);
14813
14814   LoadEngineSnapshotValues();
14815 }
14816
14817 void LoadEngineSnapshot_Redo(int steps)
14818 {
14819   LoadSnapshotFromList_Newer(steps);
14820
14821   LoadEngineSnapshotValues();
14822 }
14823
14824 boolean CheckEngineSnapshotSingle()
14825 {
14826   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14827           snapshot_level_nr == level_nr);
14828 }
14829
14830 boolean CheckEngineSnapshotList()
14831 {
14832   return CheckSnapshotList();
14833 }
14834
14835
14836 /* ---------- new game button stuff ---------------------------------------- */
14837
14838 static struct
14839 {
14840   int graphic;
14841   struct XY *pos;
14842   int gadget_id;
14843   char *infotext;
14844 } gamebutton_info[NUM_GAME_BUTTONS] =
14845 {
14846   {
14847     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14848     GAME_CTRL_ID_STOP,                  "stop game"
14849   },
14850   {
14851     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14852     GAME_CTRL_ID_PAUSE,                 "pause game"
14853   },
14854   {
14855     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14856     GAME_CTRL_ID_PLAY,                  "play game"
14857   },
14858   {
14859     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14860     GAME_CTRL_ID_UNDO,                  "undo step"
14861   },
14862   {
14863     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14864     GAME_CTRL_ID_REDO,                  "redo step"
14865   },
14866   {
14867     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14868     GAME_CTRL_ID_SAVE,                  "save game"
14869   },
14870   {
14871     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14872     GAME_CTRL_ID_PAUSE2,                "pause game"
14873   },
14874   {
14875     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14876     GAME_CTRL_ID_LOAD,                  "load game"
14877   },
14878   {
14879     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14880     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14881   },
14882   {
14883     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14884     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14885   },
14886   {
14887     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14888     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14889   }
14890 };
14891
14892 void CreateGameButtons()
14893 {
14894   int i;
14895
14896   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14897   {
14898     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14899     struct XY *pos = gamebutton_info[i].pos;
14900     struct GadgetInfo *gi;
14901     int button_type;
14902     boolean checked;
14903     unsigned int event_mask;
14904     int base_x = (tape.show_game_buttons ? VX : DX);
14905     int base_y = (tape.show_game_buttons ? VY : DY);
14906     int gd_x   = gfx->src_x;
14907     int gd_y   = gfx->src_y;
14908     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14909     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14910     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14911     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14912     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14913     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14914     int id = i;
14915
14916     if (gfx->bitmap == NULL)
14917     {
14918       game_gadget[id] = NULL;
14919
14920       continue;
14921     }
14922
14923     if (id == GAME_CTRL_ID_STOP ||
14924         id == GAME_CTRL_ID_PLAY ||
14925         id == GAME_CTRL_ID_SAVE ||
14926         id == GAME_CTRL_ID_LOAD)
14927     {
14928       button_type = GD_TYPE_NORMAL_BUTTON;
14929       checked = FALSE;
14930       event_mask = GD_EVENT_RELEASED;
14931     }
14932     else if (id == GAME_CTRL_ID_UNDO ||
14933              id == GAME_CTRL_ID_REDO)
14934     {
14935       button_type = GD_TYPE_NORMAL_BUTTON;
14936       checked = FALSE;
14937       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14938     }
14939     else
14940     {
14941       button_type = GD_TYPE_CHECK_BUTTON;
14942       checked =
14943         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14944          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14945          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14946       event_mask = GD_EVENT_PRESSED;
14947     }
14948
14949     gi = CreateGadget(GDI_CUSTOM_ID, id,
14950                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14951                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14952                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14953                       GDI_WIDTH, gfx->width,
14954                       GDI_HEIGHT, gfx->height,
14955                       GDI_TYPE, button_type,
14956                       GDI_STATE, GD_BUTTON_UNPRESSED,
14957                       GDI_CHECKED, checked,
14958                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14959                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14960                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14961                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14962                       GDI_DIRECT_DRAW, FALSE,
14963                       GDI_EVENT_MASK, event_mask,
14964                       GDI_CALLBACK_ACTION, HandleGameButtons,
14965                       GDI_END);
14966
14967     if (gi == NULL)
14968       Error(ERR_EXIT, "cannot create gadget");
14969
14970     game_gadget[id] = gi;
14971   }
14972 }
14973
14974 void FreeGameButtons()
14975 {
14976   int i;
14977
14978   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14979     FreeGadget(game_gadget[i]);
14980 }
14981
14982 static void MapGameButtonsAtSamePosition(int id)
14983 {
14984   int i;
14985
14986   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14987     if (i != id &&
14988         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14989         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14990       MapGadget(game_gadget[i]);
14991 }
14992
14993 static void UnmapGameButtonsAtSamePosition(int id)
14994 {
14995   int i;
14996
14997   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14998     if (i != id &&
14999         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15000         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15001       UnmapGadget(game_gadget[i]);
15002 }
15003
15004 void MapUndoRedoButtons()
15005 {
15006   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15007   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15008
15009   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15010   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15011 }
15012
15013 void UnmapUndoRedoButtons()
15014 {
15015   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15016   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15017
15018   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15019   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15020 }
15021
15022 void MapGameButtons()
15023 {
15024   int i;
15025
15026   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15027     if (i != GAME_CTRL_ID_UNDO &&
15028         i != GAME_CTRL_ID_REDO)
15029       MapGadget(game_gadget[i]);
15030
15031   if (setup.show_snapshot_buttons)
15032   {
15033     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15034     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15035     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15036   }
15037   else
15038   {
15039     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15040     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15041     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15042   }
15043
15044   RedrawGameButtons();
15045 }
15046
15047 void UnmapGameButtons()
15048 {
15049   int i;
15050
15051   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15052     UnmapGadget(game_gadget[i]);
15053 }
15054
15055 void RedrawGameButtons()
15056 {
15057   int i;
15058
15059   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15060     RedrawGadget(game_gadget[i]);
15061
15062   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15063   redraw_mask &= ~REDRAW_ALL;
15064 }
15065
15066 void GameUndoRedoExt()
15067 {
15068   ClearPlayerAction();
15069
15070   tape.pausing = TRUE;
15071
15072   RedrawPlayfield();
15073   UpdateAndDisplayGameControlValues();
15074
15075   DrawCompleteVideoDisplay();
15076   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15077   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15078   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15079                     VIDEO_STATE_1STEP_OFF), 0);
15080
15081   BackToFront();
15082 }
15083
15084 void GameUndo(int steps)
15085 {
15086   if (!CheckEngineSnapshotList())
15087     return;
15088
15089   LoadEngineSnapshot_Undo(steps);
15090
15091   GameUndoRedoExt();
15092 }
15093
15094 void GameRedo(int steps)
15095 {
15096   if (!CheckEngineSnapshotList())
15097     return;
15098
15099   LoadEngineSnapshot_Redo(steps);
15100
15101   GameUndoRedoExt();
15102 }
15103
15104 static void HandleGameButtonsExt(int id, int button)
15105 {
15106   int steps = BUTTON_STEPSIZE(button);
15107   boolean handle_game_buttons =
15108     (game_status == GAME_MODE_PLAYING ||
15109      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15110
15111   if (!handle_game_buttons)
15112     return;
15113
15114   switch (id)
15115   {
15116     case GAME_CTRL_ID_STOP:
15117       if (game_status == GAME_MODE_MAIN)
15118         break;
15119
15120       if (tape.playing)
15121         TapeStop();
15122       else
15123         RequestQuitGame(TRUE);
15124
15125       break;
15126
15127     case GAME_CTRL_ID_PAUSE:
15128     case GAME_CTRL_ID_PAUSE2:
15129       if (options.network && game_status == GAME_MODE_PLAYING)
15130       {
15131 #if defined(NETWORK_AVALIABLE)
15132         if (tape.pausing)
15133           SendToServer_ContinuePlaying();
15134         else
15135           SendToServer_PausePlaying();
15136 #endif
15137       }
15138       else
15139         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15140       break;
15141
15142     case GAME_CTRL_ID_PLAY:
15143       if (game_status == GAME_MODE_MAIN)
15144       {
15145         StartGameActions(options.network, setup.autorecord, level.random_seed);
15146       }
15147       else if (tape.pausing)
15148       {
15149 #if defined(NETWORK_AVALIABLE)
15150         if (options.network)
15151           SendToServer_ContinuePlaying();
15152         else
15153 #endif
15154           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15155       }
15156       break;
15157
15158     case GAME_CTRL_ID_UNDO:
15159       GameUndo(steps);
15160       break;
15161
15162     case GAME_CTRL_ID_REDO:
15163       GameRedo(steps);
15164       break;
15165
15166     case GAME_CTRL_ID_SAVE:
15167       TapeQuickSave();
15168       break;
15169
15170     case GAME_CTRL_ID_LOAD:
15171       TapeQuickLoad();
15172       break;
15173
15174     case SOUND_CTRL_ID_MUSIC:
15175       if (setup.sound_music)
15176       { 
15177         setup.sound_music = FALSE;
15178
15179         FadeMusic();
15180       }
15181       else if (audio.music_available)
15182       { 
15183         setup.sound = setup.sound_music = TRUE;
15184
15185         SetAudioMode(setup.sound);
15186
15187         PlayLevelMusic();
15188       }
15189       break;
15190
15191     case SOUND_CTRL_ID_LOOPS:
15192       if (setup.sound_loops)
15193         setup.sound_loops = FALSE;
15194       else if (audio.loops_available)
15195       {
15196         setup.sound = setup.sound_loops = TRUE;
15197
15198         SetAudioMode(setup.sound);
15199       }
15200       break;
15201
15202     case SOUND_CTRL_ID_SIMPLE:
15203       if (setup.sound_simple)
15204         setup.sound_simple = FALSE;
15205       else if (audio.sound_available)
15206       {
15207         setup.sound = setup.sound_simple = TRUE;
15208
15209         SetAudioMode(setup.sound);
15210       }
15211       break;
15212
15213     default:
15214       break;
15215   }
15216 }
15217
15218 static void HandleGameButtons(struct GadgetInfo *gi)
15219 {
15220   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15221 }
15222
15223 void HandleSoundButtonKeys(Key key)
15224 {
15225
15226   if (key == setup.shortcut.sound_simple)
15227     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15228   else if (key == setup.shortcut.sound_loops)
15229     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15230   else if (key == setup.shortcut.sound_music)
15231     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15232 }