fixed fading door areas when using different masked global borders
[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 inline static 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 inline static 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   int fade_mask = REDRAW_FIELD;
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   // required here to update video display before fading (FIX THIS)
3095   DrawMaskedBorder(REDRAW_DOOR_2);
3096
3097   game_status = GAME_MODE_PLAYING;
3098
3099   if (!game.restart_level)
3100     CloseDoor(DOOR_CLOSE_1);
3101
3102   /* needed if different viewport properties defined for playing */
3103   ChangeViewportPropertiesIfNeeded();
3104
3105   if (level_editor_test_game)
3106     FadeSkipNextFadeIn();
3107   else
3108     FadeSetEnterScreen();
3109
3110   if (CheckIfGlobalBorderHasChanged())
3111     fade_mask = REDRAW_ALL;
3112
3113   FadeOut(fade_mask);
3114
3115   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3116
3117   ClearField();
3118
3119   DrawCompleteVideoDisplay();
3120
3121   InitGameEngine();
3122   InitGameControlValues();
3123
3124   /* don't play tapes over network */
3125   network_playing = (options.network && !tape.playing);
3126
3127   for (i = 0; i < MAX_PLAYERS; i++)
3128   {
3129     struct PlayerInfo *player = &stored_player[i];
3130
3131     player->index_nr = i;
3132     player->index_bit = (1 << i);
3133     player->element_nr = EL_PLAYER_1 + i;
3134
3135     player->present = FALSE;
3136     player->active = FALSE;
3137     player->mapped = FALSE;
3138
3139     player->killed = FALSE;
3140     player->reanimated = FALSE;
3141
3142     player->action = 0;
3143     player->effective_action = 0;
3144     player->programmed_action = 0;
3145
3146     player->score = 0;
3147     player->score_final = 0;
3148
3149     player->gems_still_needed = level.gems_needed;
3150     player->sokobanfields_still_needed = 0;
3151     player->lights_still_needed = 0;
3152     player->friends_still_needed = 0;
3153
3154     for (j = 0; j < MAX_NUM_KEYS; j++)
3155       player->key[j] = FALSE;
3156
3157     player->num_white_keys = 0;
3158
3159     player->dynabomb_count = 0;
3160     player->dynabomb_size = 1;
3161     player->dynabombs_left = 0;
3162     player->dynabomb_xl = FALSE;
3163
3164     player->MovDir = initial_move_dir;
3165     player->MovPos = 0;
3166     player->GfxPos = 0;
3167     player->GfxDir = initial_move_dir;
3168     player->GfxAction = ACTION_DEFAULT;
3169     player->Frame = 0;
3170     player->StepFrame = 0;
3171
3172     player->initial_element = player->element_nr;
3173     player->artwork_element =
3174       (level.use_artwork_element[i] ? level.artwork_element[i] :
3175        player->element_nr);
3176     player->use_murphy = FALSE;
3177
3178     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3179     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3180
3181     player->gravity = level.initial_player_gravity[i];
3182
3183     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3184
3185     player->actual_frame_counter = 0;
3186
3187     player->step_counter = 0;
3188
3189     player->last_move_dir = initial_move_dir;
3190
3191     player->is_active = FALSE;
3192
3193     player->is_waiting = FALSE;
3194     player->is_moving = FALSE;
3195     player->is_auto_moving = FALSE;
3196     player->is_digging = FALSE;
3197     player->is_snapping = FALSE;
3198     player->is_collecting = FALSE;
3199     player->is_pushing = FALSE;
3200     player->is_switching = FALSE;
3201     player->is_dropping = FALSE;
3202     player->is_dropping_pressed = FALSE;
3203
3204     player->is_bored = FALSE;
3205     player->is_sleeping = FALSE;
3206
3207     player->frame_counter_bored = -1;
3208     player->frame_counter_sleeping = -1;
3209
3210     player->anim_delay_counter = 0;
3211     player->post_delay_counter = 0;
3212
3213     player->dir_waiting = initial_move_dir;
3214     player->action_waiting = ACTION_DEFAULT;
3215     player->last_action_waiting = ACTION_DEFAULT;
3216     player->special_action_bored = ACTION_DEFAULT;
3217     player->special_action_sleeping = ACTION_DEFAULT;
3218
3219     player->switch_x = -1;
3220     player->switch_y = -1;
3221
3222     player->drop_x = -1;
3223     player->drop_y = -1;
3224
3225     player->show_envelope = 0;
3226
3227     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3228
3229     player->push_delay       = -1;      /* initialized when pushing starts */
3230     player->push_delay_value = game.initial_push_delay_value;
3231
3232     player->drop_delay = 0;
3233     player->drop_pressed_delay = 0;
3234
3235     player->last_jx = -1;
3236     player->last_jy = -1;
3237     player->jx = -1;
3238     player->jy = -1;
3239
3240     player->shield_normal_time_left = 0;
3241     player->shield_deadly_time_left = 0;
3242
3243     player->inventory_infinite_element = EL_UNDEFINED;
3244     player->inventory_size = 0;
3245
3246     if (level.use_initial_inventory[i])
3247     {
3248       for (j = 0; j < level.initial_inventory_size[i]; j++)
3249       {
3250         int element = level.initial_inventory_content[i][j];
3251         int collect_count = element_info[element].collect_count_initial;
3252         int k;
3253
3254         if (!IS_CUSTOM_ELEMENT(element))
3255           collect_count = 1;
3256
3257         if (collect_count == 0)
3258           player->inventory_infinite_element = element;
3259         else
3260           for (k = 0; k < collect_count; k++)
3261             if (player->inventory_size < MAX_INVENTORY_SIZE)
3262               player->inventory_element[player->inventory_size++] = element;
3263       }
3264     }
3265
3266     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3267     SnapField(player, 0, 0);
3268
3269     player->LevelSolved = FALSE;
3270     player->GameOver = FALSE;
3271
3272     player->LevelSolved_GameWon = FALSE;
3273     player->LevelSolved_GameEnd = FALSE;
3274     player->LevelSolved_PanelOff = FALSE;
3275     player->LevelSolved_SaveTape = FALSE;
3276     player->LevelSolved_SaveScore = FALSE;
3277     player->LevelSolved_CountingTime = 0;
3278     player->LevelSolved_CountingScore = 0;
3279
3280     map_player_action[i] = i;
3281   }
3282
3283   network_player_action_received = FALSE;
3284
3285 #if defined(NETWORK_AVALIABLE)
3286   /* initial null action */
3287   if (network_playing)
3288     SendToServer_MovePlayer(MV_NONE);
3289 #endif
3290
3291   ZX = ZY = -1;
3292   ExitX = ExitY = -1;
3293
3294   FrameCounter = 0;
3295   TimeFrames = 0;
3296   TimePlayed = 0;
3297   TimeLeft = level.time;
3298   TapeTime = 0;
3299
3300   ScreenMovDir = MV_NONE;
3301   ScreenMovPos = 0;
3302   ScreenGfxPos = 0;
3303
3304   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3305
3306   AllPlayersGone = FALSE;
3307
3308   game.no_time_limit = (level.time == 0);
3309
3310   game.yamyam_content_nr = 0;
3311   game.robot_wheel_active = FALSE;
3312   game.magic_wall_active = FALSE;
3313   game.magic_wall_time_left = 0;
3314   game.light_time_left = 0;
3315   game.timegate_time_left = 0;
3316   game.switchgate_pos = 0;
3317   game.wind_direction = level.wind_direction_initial;
3318
3319   game.lenses_time_left = 0;
3320   game.magnify_time_left = 0;
3321
3322   game.ball_state = level.ball_state_initial;
3323   game.ball_content_nr = 0;
3324
3325   game.envelope_active = FALSE;
3326
3327   /* set focus to local player for network games, else to all players */
3328   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3329   game.centered_player_nr_next = game.centered_player_nr;
3330   game.set_centered_player = FALSE;
3331
3332   if (network_playing && tape.recording)
3333   {
3334     /* store client dependent player focus when recording network games */
3335     tape.centered_player_nr_next = game.centered_player_nr_next;
3336     tape.set_centered_player = TRUE;
3337   }
3338
3339   for (i = 0; i < NUM_BELTS; i++)
3340   {
3341     game.belt_dir[i] = MV_NONE;
3342     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3343   }
3344
3345   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3346     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3347
3348 #if DEBUG_INIT_PLAYER
3349   if (options.debug)
3350   {
3351     printf("Player status at level initialization:\n");
3352   }
3353 #endif
3354
3355   SCAN_PLAYFIELD(x, y)
3356   {
3357     Feld[x][y] = level.field[x][y];
3358     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3359     ChangeDelay[x][y] = 0;
3360     ChangePage[x][y] = -1;
3361     CustomValue[x][y] = 0;              /* initialized in InitField() */
3362     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3363     AmoebaNr[x][y] = 0;
3364     WasJustMoving[x][y] = 0;
3365     WasJustFalling[x][y] = 0;
3366     CheckCollision[x][y] = 0;
3367     CheckImpact[x][y] = 0;
3368     Stop[x][y] = FALSE;
3369     Pushed[x][y] = FALSE;
3370
3371     ChangeCount[x][y] = 0;
3372     ChangeEvent[x][y] = -1;
3373
3374     ExplodePhase[x][y] = 0;
3375     ExplodeDelay[x][y] = 0;
3376     ExplodeField[x][y] = EX_TYPE_NONE;
3377
3378     RunnerVisit[x][y] = 0;
3379     PlayerVisit[x][y] = 0;
3380
3381     GfxFrame[x][y] = 0;
3382     GfxRandom[x][y] = INIT_GFX_RANDOM();
3383     GfxElement[x][y] = EL_UNDEFINED;
3384     GfxAction[x][y] = ACTION_DEFAULT;
3385     GfxDir[x][y] = MV_NONE;
3386     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3387   }
3388
3389   SCAN_PLAYFIELD(x, y)
3390   {
3391     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3392       emulate_bd = FALSE;
3393     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3394       emulate_sb = FALSE;
3395     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3396       emulate_sp = FALSE;
3397
3398     InitField(x, y, TRUE);
3399
3400     ResetGfxAnimation(x, y);
3401   }
3402
3403   InitBeltMovement();
3404
3405   for (i = 0; i < MAX_PLAYERS; i++)
3406   {
3407     struct PlayerInfo *player = &stored_player[i];
3408
3409     /* set number of special actions for bored and sleeping animation */
3410     player->num_special_action_bored =
3411       get_num_special_action(player->artwork_element,
3412                              ACTION_BORING_1, ACTION_BORING_LAST);
3413     player->num_special_action_sleeping =
3414       get_num_special_action(player->artwork_element,
3415                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3416   }
3417
3418   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3419                     emulate_sb ? EMU_SOKOBAN :
3420                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3421
3422   /* initialize type of slippery elements */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (!IS_CUSTOM_ELEMENT(i))
3426     {
3427       /* default: elements slip down either to the left or right randomly */
3428       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3429
3430       /* SP style elements prefer to slip down on the left side */
3431       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3432         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3433
3434       /* BD style elements prefer to slip down on the left side */
3435       if (game.emulation == EMU_BOULDERDASH)
3436         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3437     }
3438   }
3439
3440   /* initialize explosion and ignition delay */
3441   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442   {
3443     if (!IS_CUSTOM_ELEMENT(i))
3444     {
3445       int num_phase = 8;
3446       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3447                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3448                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3449       int last_phase = (num_phase + 1) * delay;
3450       int half_phase = (num_phase / 2) * delay;
3451
3452       element_info[i].explosion_delay = last_phase - 1;
3453       element_info[i].ignition_delay = half_phase;
3454
3455       if (i == EL_BLACK_ORB)
3456         element_info[i].ignition_delay = 1;
3457     }
3458   }
3459
3460   /* correct non-moving belts to start moving left */
3461   for (i = 0; i < NUM_BELTS; i++)
3462     if (game.belt_dir[i] == MV_NONE)
3463       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3464
3465 #if USE_NEW_PLAYER_ASSIGNMENTS
3466   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3467   /* choose default local player */
3468   local_player = &stored_player[0];
3469
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     stored_player[i].connected = FALSE;
3472
3473   local_player->connected = TRUE;
3474   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3475
3476   if (tape.playing)
3477   {
3478     for (i = 0; i < MAX_PLAYERS; i++)
3479       stored_player[i].connected = tape.player_participates[i];
3480   }
3481   else if (game.team_mode && !options.network)
3482   {
3483     /* try to guess locally connected team mode players (needed for correct
3484        assignment of player figures from level to locally playing players) */
3485
3486     for (i = 0; i < MAX_PLAYERS; i++)
3487       if (setup.input[i].use_joystick ||
3488           setup.input[i].key.left != KSYM_UNDEFINED)
3489         stored_player[i].connected = TRUE;
3490   }
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494   {
3495     printf("Player status after level initialization:\n");
3496
3497     for (i = 0; i < MAX_PLAYERS; i++)
3498     {
3499       struct PlayerInfo *player = &stored_player[i];
3500
3501       printf("- player %d: present == %d, connected == %d, active == %d",
3502              i + 1,
3503              player->present,
3504              player->connected,
3505              player->active);
3506
3507       if (local_player == player)
3508         printf(" (local player)");
3509
3510       printf("\n");
3511     }
3512   }
3513 #endif
3514
3515 #if DEBUG_INIT_PLAYER
3516   if (options.debug)
3517     printf("Reassigning players ...\n");
3518 #endif
3519
3520   /* check if any connected player was not found in playfield */
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     if (player->connected && !player->present)
3526     {
3527       struct PlayerInfo *field_player = NULL;
3528
3529 #if DEBUG_INIT_PLAYER
3530       if (options.debug)
3531         printf("- looking for field player for player %d ...\n", i + 1);
3532 #endif
3533
3534       /* assign first free player found that is present in the playfield */
3535
3536       /* first try: look for unmapped playfield player that is not connected */
3537       for (j = 0; j < MAX_PLAYERS; j++)
3538         if (field_player == NULL &&
3539             stored_player[j].present &&
3540             !stored_player[j].mapped &&
3541             !stored_player[j].connected)
3542           field_player = &stored_player[j];
3543
3544       /* second try: look for *any* unmapped playfield player */
3545       for (j = 0; j < MAX_PLAYERS; j++)
3546         if (field_player == NULL &&
3547             stored_player[j].present &&
3548             !stored_player[j].mapped)
3549           field_player = &stored_player[j];
3550
3551       if (field_player != NULL)
3552       {
3553         int jx = field_player->jx, jy = field_player->jy;
3554
3555 #if DEBUG_INIT_PLAYER
3556         if (options.debug)
3557           printf("- found player %d\n", field_player->index_nr + 1);
3558 #endif
3559
3560         player->present = FALSE;
3561         player->active = FALSE;
3562
3563         field_player->present = TRUE;
3564         field_player->active = TRUE;
3565
3566         /*
3567         player->initial_element = field_player->initial_element;
3568         player->artwork_element = field_player->artwork_element;
3569
3570         player->block_last_field       = field_player->block_last_field;
3571         player->block_delay_adjustment = field_player->block_delay_adjustment;
3572         */
3573
3574         StorePlayer[jx][jy] = field_player->element_nr;
3575
3576         field_player->jx = field_player->last_jx = jx;
3577         field_player->jy = field_player->last_jy = jy;
3578
3579         if (local_player == player)
3580           local_player = field_player;
3581
3582         map_player_action[field_player->index_nr] = i;
3583
3584         field_player->mapped = TRUE;
3585
3586 #if DEBUG_INIT_PLAYER
3587         if (options.debug)
3588           printf("- map_player_action[%d] == %d\n",
3589                  field_player->index_nr + 1, i + 1);
3590 #endif
3591       }
3592     }
3593
3594     if (player->connected && player->present)
3595       player->mapped = TRUE;
3596   }
3597
3598 #if DEBUG_INIT_PLAYER
3599   if (options.debug)
3600   {
3601     printf("Player status after player assignment (first stage):\n");
3602
3603     for (i = 0; i < MAX_PLAYERS; i++)
3604     {
3605       struct PlayerInfo *player = &stored_player[i];
3606
3607       printf("- player %d: present == %d, connected == %d, active == %d",
3608              i + 1,
3609              player->present,
3610              player->connected,
3611              player->active);
3612
3613       if (local_player == player)
3614         printf(" (local player)");
3615
3616       printf("\n");
3617     }
3618   }
3619 #endif
3620
3621 #else
3622
3623   /* check if any connected player was not found in playfield */
3624   for (i = 0; i < MAX_PLAYERS; i++)
3625   {
3626     struct PlayerInfo *player = &stored_player[i];
3627
3628     if (player->connected && !player->present)
3629     {
3630       for (j = 0; j < MAX_PLAYERS; j++)
3631       {
3632         struct PlayerInfo *field_player = &stored_player[j];
3633         int jx = field_player->jx, jy = field_player->jy;
3634
3635         /* assign first free player found that is present in the playfield */
3636         if (field_player->present && !field_player->connected)
3637         {
3638           player->present = TRUE;
3639           player->active = TRUE;
3640
3641           field_player->present = FALSE;
3642           field_player->active = FALSE;
3643
3644           player->initial_element = field_player->initial_element;
3645           player->artwork_element = field_player->artwork_element;
3646
3647           player->block_last_field       = field_player->block_last_field;
3648           player->block_delay_adjustment = field_player->block_delay_adjustment;
3649
3650           StorePlayer[jx][jy] = player->element_nr;
3651
3652           player->jx = player->last_jx = jx;
3653           player->jy = player->last_jy = jy;
3654
3655           break;
3656         }
3657       }
3658     }
3659   }
3660 #endif
3661
3662 #if 0
3663   printf("::: local_player->present == %d\n", local_player->present);
3664 #endif
3665
3666   if (tape.playing)
3667   {
3668     /* when playing a tape, eliminate all players who do not participate */
3669
3670 #if USE_NEW_PLAYER_ASSIGNMENTS
3671
3672     if (!game.team_mode)
3673     {
3674       for (i = 0; i < MAX_PLAYERS; i++)
3675       {
3676         if (stored_player[i].active &&
3677             !tape.player_participates[map_player_action[i]])
3678         {
3679           struct PlayerInfo *player = &stored_player[i];
3680           int jx = player->jx, jy = player->jy;
3681
3682 #if DEBUG_INIT_PLAYER
3683           if (options.debug)
3684             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3685 #endif
3686
3687           player->active = FALSE;
3688           StorePlayer[jx][jy] = 0;
3689           Feld[jx][jy] = EL_EMPTY;
3690         }
3691       }
3692     }
3693
3694 #else
3695
3696     for (i = 0; i < MAX_PLAYERS; i++)
3697     {
3698       if (stored_player[i].active &&
3699           !tape.player_participates[i])
3700       {
3701         struct PlayerInfo *player = &stored_player[i];
3702         int jx = player->jx, jy = player->jy;
3703
3704         player->active = FALSE;
3705         StorePlayer[jx][jy] = 0;
3706         Feld[jx][jy] = EL_EMPTY;
3707       }
3708     }
3709 #endif
3710   }
3711   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3712   {
3713     /* when in single player mode, eliminate all but the first active player */
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716     {
3717       if (stored_player[i].active)
3718       {
3719         for (j = i + 1; j < MAX_PLAYERS; j++)
3720         {
3721           if (stored_player[j].active)
3722           {
3723             struct PlayerInfo *player = &stored_player[j];
3724             int jx = player->jx, jy = player->jy;
3725
3726             player->active = FALSE;
3727             player->present = FALSE;
3728
3729             StorePlayer[jx][jy] = 0;
3730             Feld[jx][jy] = EL_EMPTY;
3731           }
3732         }
3733       }
3734     }
3735   }
3736
3737   /* when recording the game, store which players take part in the game */
3738   if (tape.recording)
3739   {
3740 #if USE_NEW_PLAYER_ASSIGNMENTS
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742       if (stored_player[i].connected)
3743         tape.player_participates[i] = TRUE;
3744 #else
3745     for (i = 0; i < MAX_PLAYERS; i++)
3746       if (stored_player[i].active)
3747         tape.player_participates[i] = TRUE;
3748 #endif
3749   }
3750
3751 #if DEBUG_INIT_PLAYER
3752   if (options.debug)
3753   {
3754     printf("Player status after player assignment (final stage):\n");
3755
3756     for (i = 0; i < MAX_PLAYERS; i++)
3757     {
3758       struct PlayerInfo *player = &stored_player[i];
3759
3760       printf("- player %d: present == %d, connected == %d, active == %d",
3761              i + 1,
3762              player->present,
3763              player->connected,
3764              player->active);
3765
3766       if (local_player == player)
3767         printf(" (local player)");
3768
3769       printf("\n");
3770     }
3771   }
3772 #endif
3773
3774   if (BorderElement == EL_EMPTY)
3775   {
3776     SBX_Left = 0;
3777     SBX_Right = lev_fieldx - SCR_FIELDX;
3778     SBY_Upper = 0;
3779     SBY_Lower = lev_fieldy - SCR_FIELDY;
3780   }
3781   else
3782   {
3783     SBX_Left = -1;
3784     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3785     SBY_Upper = -1;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3787   }
3788
3789   if (full_lev_fieldx <= SCR_FIELDX)
3790     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3791   if (full_lev_fieldy <= SCR_FIELDY)
3792     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3793
3794   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3795     SBX_Left--;
3796   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3797     SBY_Upper--;
3798
3799   /* if local player not found, look for custom element that might create
3800      the player (make some assumptions about the right custom element) */
3801   if (!local_player->present)
3802   {
3803     int start_x = 0, start_y = 0;
3804     int found_rating = 0;
3805     int found_element = EL_UNDEFINED;
3806     int player_nr = local_player->index_nr;
3807
3808     SCAN_PLAYFIELD(x, y)
3809     {
3810       int element = Feld[x][y];
3811       int content;
3812       int xx, yy;
3813       boolean is_player;
3814
3815       if (level.use_start_element[player_nr] &&
3816           level.start_element[player_nr] == element &&
3817           found_rating < 4)
3818       {
3819         start_x = x;
3820         start_y = y;
3821
3822         found_rating = 4;
3823         found_element = element;
3824       }
3825
3826       if (!IS_CUSTOM_ELEMENT(element))
3827         continue;
3828
3829       if (CAN_CHANGE(element))
3830       {
3831         for (i = 0; i < element_info[element].num_change_pages; i++)
3832         {
3833           /* check for player created from custom element as single target */
3834           content = element_info[element].change_page[i].target_element;
3835           is_player = ELEM_IS_PLAYER(content);
3836
3837           if (is_player && (found_rating < 3 ||
3838                             (found_rating == 3 && element < found_element)))
3839           {
3840             start_x = x;
3841             start_y = y;
3842
3843             found_rating = 3;
3844             found_element = element;
3845           }
3846         }
3847       }
3848
3849       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3850       {
3851         /* check for player created from custom element as explosion content */
3852         content = element_info[element].content.e[xx][yy];
3853         is_player = ELEM_IS_PLAYER(content);
3854
3855         if (is_player && (found_rating < 2 ||
3856                           (found_rating == 2 && element < found_element)))
3857         {
3858           start_x = x + xx - 1;
3859           start_y = y + yy - 1;
3860
3861           found_rating = 2;
3862           found_element = element;
3863         }
3864
3865         if (!CAN_CHANGE(element))
3866           continue;
3867
3868         for (i = 0; i < element_info[element].num_change_pages; i++)
3869         {
3870           /* check for player created from custom element as extended target */
3871           content =
3872             element_info[element].change_page[i].target_content.e[xx][yy];
3873
3874           is_player = ELEM_IS_PLAYER(content);
3875
3876           if (is_player && (found_rating < 1 ||
3877                             (found_rating == 1 && element < found_element)))
3878           {
3879             start_x = x + xx - 1;
3880             start_y = y + yy - 1;
3881
3882             found_rating = 1;
3883             found_element = element;
3884           }
3885         }
3886       }
3887     }
3888
3889     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3891                 start_x - MIDPOSX);
3892
3893     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 start_y - MIDPOSY);
3896   }
3897   else
3898   {
3899     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3901                 local_player->jx - MIDPOSX);
3902
3903     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 local_player->jy - MIDPOSY);
3906   }
3907
3908   /* !!! FIX THIS (START) !!! */
3909   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3910   {
3911     InitGameEngine_EM();
3912   }
3913   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3914   {
3915     InitGameEngine_SP();
3916   }
3917   else
3918   {
3919     DrawLevel(REDRAW_FIELD);
3920     DrawAllPlayers();
3921
3922     /* after drawing the level, correct some elements */
3923     if (game.timegate_time_left == 0)
3924       CloseAllOpenTimegates();
3925   }
3926
3927   /* blit playfield from scroll buffer to normal back buffer for fading in */
3928   BlitScreenToBitmap(backbuffer);
3929   /* !!! FIX THIS (END) !!! */
3930
3931   DrawMaskedBorder(fade_mask);
3932
3933   FadeIn(fade_mask);
3934
3935 #if 1
3936   // full screen redraw is required at this point in the following cases:
3937   // - special editor door undrawn when game was started from level editor
3938   // - drawing area (playfield) was changed and has to be removed completely
3939   redraw_mask = REDRAW_ALL;
3940   BackToFront();
3941 #endif
3942
3943   if (!game.restart_level)
3944   {
3945     /* copy default game door content to main double buffer */
3946
3947     /* !!! CHECK AGAIN !!! */
3948     SetPanelBackground();
3949     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3950     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3951   }
3952
3953   SetPanelBackground();
3954   SetDrawBackgroundMask(REDRAW_DOOR_1);
3955
3956   UpdateAndDisplayGameControlValues();
3957
3958   if (!game.restart_level)
3959   {
3960     UnmapGameButtons();
3961     UnmapTapeButtons();
3962
3963     FreeGameButtons();
3964     CreateGameButtons();
3965
3966     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3967     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3968     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3969
3970     MapGameButtons();
3971     MapTapeButtons();
3972
3973     /* copy actual game door content to door double buffer for OpenDoor() */
3974     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3975
3976     OpenDoor(DOOR_OPEN_ALL);
3977
3978     PlaySound(SND_GAME_STARTING);
3979
3980     if (setup.sound_music)
3981       PlayLevelMusic();
3982
3983     KeyboardAutoRepeatOffUnlessAutoplay();
3984
3985 #if DEBUG_INIT_PLAYER
3986     if (options.debug)
3987     {
3988       printf("Player status (final):\n");
3989
3990       for (i = 0; i < MAX_PLAYERS; i++)
3991       {
3992         struct PlayerInfo *player = &stored_player[i];
3993
3994         printf("- player %d: present == %d, connected == %d, active == %d",
3995                i + 1,
3996                player->present,
3997                player->connected,
3998                player->active);
3999
4000         if (local_player == player)
4001           printf(" (local player)");
4002
4003         printf("\n");
4004       }
4005     }
4006 #endif
4007   }
4008
4009   UnmapAllGadgets();
4010
4011   MapGameButtons();
4012   MapTapeButtons();
4013
4014   if (!game.restart_level && !tape.playing)
4015   {
4016     LevelStats_incPlayed(level_nr);
4017
4018     SaveLevelSetup_SeriesInfo();
4019   }
4020
4021   game.restart_level = FALSE;
4022
4023   SaveEngineSnapshotToListInitial();
4024 }
4025
4026 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4027 {
4028   /* this is used for non-R'n'D game engines to update certain engine values */
4029
4030   /* needed to determine if sounds are played within the visible screen area */
4031   scroll_x = actual_scroll_x;
4032   scroll_y = actual_scroll_y;
4033 }
4034
4035 void InitMovDir(int x, int y)
4036 {
4037   int i, element = Feld[x][y];
4038   static int xy[4][2] =
4039   {
4040     {  0, +1 },
4041     { +1,  0 },
4042     {  0, -1 },
4043     { -1,  0 }
4044   };
4045   static int direction[3][4] =
4046   {
4047     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4048     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4049     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4050   };
4051
4052   switch (element)
4053   {
4054     case EL_BUG_RIGHT:
4055     case EL_BUG_UP:
4056     case EL_BUG_LEFT:
4057     case EL_BUG_DOWN:
4058       Feld[x][y] = EL_BUG;
4059       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4060       break;
4061
4062     case EL_SPACESHIP_RIGHT:
4063     case EL_SPACESHIP_UP:
4064     case EL_SPACESHIP_LEFT:
4065     case EL_SPACESHIP_DOWN:
4066       Feld[x][y] = EL_SPACESHIP;
4067       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4068       break;
4069
4070     case EL_BD_BUTTERFLY_RIGHT:
4071     case EL_BD_BUTTERFLY_UP:
4072     case EL_BD_BUTTERFLY_LEFT:
4073     case EL_BD_BUTTERFLY_DOWN:
4074       Feld[x][y] = EL_BD_BUTTERFLY;
4075       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4076       break;
4077
4078     case EL_BD_FIREFLY_RIGHT:
4079     case EL_BD_FIREFLY_UP:
4080     case EL_BD_FIREFLY_LEFT:
4081     case EL_BD_FIREFLY_DOWN:
4082       Feld[x][y] = EL_BD_FIREFLY;
4083       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4084       break;
4085
4086     case EL_PACMAN_RIGHT:
4087     case EL_PACMAN_UP:
4088     case EL_PACMAN_LEFT:
4089     case EL_PACMAN_DOWN:
4090       Feld[x][y] = EL_PACMAN;
4091       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4092       break;
4093
4094     case EL_YAMYAM_LEFT:
4095     case EL_YAMYAM_RIGHT:
4096     case EL_YAMYAM_UP:
4097     case EL_YAMYAM_DOWN:
4098       Feld[x][y] = EL_YAMYAM;
4099       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4100       break;
4101
4102     case EL_SP_SNIKSNAK:
4103       MovDir[x][y] = MV_UP;
4104       break;
4105
4106     case EL_SP_ELECTRON:
4107       MovDir[x][y] = MV_LEFT;
4108       break;
4109
4110     case EL_MOLE_LEFT:
4111     case EL_MOLE_RIGHT:
4112     case EL_MOLE_UP:
4113     case EL_MOLE_DOWN:
4114       Feld[x][y] = EL_MOLE;
4115       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4116       break;
4117
4118     default:
4119       if (IS_CUSTOM_ELEMENT(element))
4120       {
4121         struct ElementInfo *ei = &element_info[element];
4122         int move_direction_initial = ei->move_direction_initial;
4123         int move_pattern = ei->move_pattern;
4124
4125         if (move_direction_initial == MV_START_PREVIOUS)
4126         {
4127           if (MovDir[x][y] != MV_NONE)
4128             return;
4129
4130           move_direction_initial = MV_START_AUTOMATIC;
4131         }
4132
4133         if (move_direction_initial == MV_START_RANDOM)
4134           MovDir[x][y] = 1 << RND(4);
4135         else if (move_direction_initial & MV_ANY_DIRECTION)
4136           MovDir[x][y] = move_direction_initial;
4137         else if (move_pattern == MV_ALL_DIRECTIONS ||
4138                  move_pattern == MV_TURNING_LEFT ||
4139                  move_pattern == MV_TURNING_RIGHT ||
4140                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4141                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4142                  move_pattern == MV_TURNING_RANDOM)
4143           MovDir[x][y] = 1 << RND(4);
4144         else if (move_pattern == MV_HORIZONTAL)
4145           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4146         else if (move_pattern == MV_VERTICAL)
4147           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4148         else if (move_pattern & MV_ANY_DIRECTION)
4149           MovDir[x][y] = element_info[element].move_pattern;
4150         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4151                  move_pattern == MV_ALONG_RIGHT_SIDE)
4152         {
4153           /* use random direction as default start direction */
4154           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4155             MovDir[x][y] = 1 << RND(4);
4156
4157           for (i = 0; i < NUM_DIRECTIONS; i++)
4158           {
4159             int x1 = x + xy[i][0];
4160             int y1 = y + xy[i][1];
4161
4162             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4163             {
4164               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4165                 MovDir[x][y] = direction[0][i];
4166               else
4167                 MovDir[x][y] = direction[1][i];
4168
4169               break;
4170             }
4171           }
4172         }                
4173       }
4174       else
4175       {
4176         MovDir[x][y] = 1 << RND(4);
4177
4178         if (element != EL_BUG &&
4179             element != EL_SPACESHIP &&
4180             element != EL_BD_BUTTERFLY &&
4181             element != EL_BD_FIREFLY)
4182           break;
4183
4184         for (i = 0; i < NUM_DIRECTIONS; i++)
4185         {
4186           int x1 = x + xy[i][0];
4187           int y1 = y + xy[i][1];
4188
4189           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4190           {
4191             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4192             {
4193               MovDir[x][y] = direction[0][i];
4194               break;
4195             }
4196             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4197                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4198             {
4199               MovDir[x][y] = direction[1][i];
4200               break;
4201             }
4202           }
4203         }
4204       }
4205       break;
4206   }
4207
4208   GfxDir[x][y] = MovDir[x][y];
4209 }
4210
4211 void InitAmoebaNr(int x, int y)
4212 {
4213   int i;
4214   int group_nr = AmoebeNachbarNr(x, y);
4215
4216   if (group_nr == 0)
4217   {
4218     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4219     {
4220       if (AmoebaCnt[i] == 0)
4221       {
4222         group_nr = i;
4223         break;
4224       }
4225     }
4226   }
4227
4228   AmoebaNr[x][y] = group_nr;
4229   AmoebaCnt[group_nr]++;
4230   AmoebaCnt2[group_nr]++;
4231 }
4232
4233 static void PlayerWins(struct PlayerInfo *player)
4234 {
4235   player->LevelSolved = TRUE;
4236   player->GameOver = TRUE;
4237
4238   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4239                          level.native_em_level->lev->score : player->score);
4240
4241   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4242                                       TimeLeft);
4243   player->LevelSolved_CountingScore = player->score_final;
4244 }
4245
4246 void GameWon()
4247 {
4248   static int time, time_final;
4249   static int score, score_final;
4250   static int game_over_delay_1 = 0;
4251   static int game_over_delay_2 = 0;
4252   int game_over_delay_value_1 = 50;
4253   int game_over_delay_value_2 = 50;
4254
4255   if (!local_player->LevelSolved_GameWon)
4256   {
4257     int i;
4258
4259     /* do not start end game actions before the player stops moving (to exit) */
4260     if (local_player->MovPos)
4261       return;
4262
4263     local_player->LevelSolved_GameWon = TRUE;
4264     local_player->LevelSolved_SaveTape = tape.recording;
4265     local_player->LevelSolved_SaveScore = !tape.playing;
4266
4267     if (!tape.playing)
4268     {
4269       LevelStats_incSolved(level_nr);
4270
4271       SaveLevelSetup_SeriesInfo();
4272     }
4273
4274     if (tape.auto_play)         /* tape might already be stopped here */
4275       tape.auto_play_level_solved = TRUE;
4276
4277     TapeStop();
4278
4279     game_over_delay_1 = game_over_delay_value_1;
4280     game_over_delay_2 = game_over_delay_value_2;
4281
4282     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4283     score = score_final = local_player->score_final;
4284
4285     if (TimeLeft > 0)
4286     {
4287       time_final = 0;
4288       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4289     }
4290     else if (game.no_time_limit && TimePlayed < 999)
4291     {
4292       time_final = 999;
4293       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4294     }
4295
4296     local_player->score_final = score_final;
4297
4298     if (level_editor_test_game)
4299     {
4300       time = time_final;
4301       score = score_final;
4302
4303       local_player->LevelSolved_CountingTime = time;
4304       local_player->LevelSolved_CountingScore = score;
4305
4306       game_panel_controls[GAME_PANEL_TIME].value = time;
4307       game_panel_controls[GAME_PANEL_SCORE].value = score;
4308
4309       DisplayGameControlValues();
4310     }
4311
4312     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4313     {
4314       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4315       {
4316         /* close exit door after last player */
4317         if ((AllPlayersGone &&
4318              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4319               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4320               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4321             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4322             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4323         {
4324           int element = Feld[ExitX][ExitY];
4325
4326           Feld[ExitX][ExitY] =
4327             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4328              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4329              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4330              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4331              EL_EM_STEEL_EXIT_CLOSING);
4332
4333           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4334         }
4335
4336         /* player disappears */
4337         DrawLevelField(ExitX, ExitY);
4338       }
4339
4340       for (i = 0; i < MAX_PLAYERS; i++)
4341       {
4342         struct PlayerInfo *player = &stored_player[i];
4343
4344         if (player->present)
4345         {
4346           RemovePlayer(player);
4347
4348           /* player disappears */
4349           DrawLevelField(player->jx, player->jy);
4350         }
4351       }
4352     }
4353
4354     PlaySound(SND_GAME_WINNING);
4355   }
4356
4357   if (game_over_delay_1 > 0)
4358   {
4359     game_over_delay_1--;
4360
4361     return;
4362   }
4363
4364   if (time != time_final)
4365   {
4366     int time_to_go = ABS(time_final - time);
4367     int time_count_dir = (time < time_final ? +1 : -1);
4368     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4369
4370     time  += time_count_steps * time_count_dir;
4371     score += time_count_steps * level.score[SC_TIME_BONUS];
4372
4373     local_player->LevelSolved_CountingTime = time;
4374     local_player->LevelSolved_CountingScore = score;
4375
4376     game_panel_controls[GAME_PANEL_TIME].value = time;
4377     game_panel_controls[GAME_PANEL_SCORE].value = score;
4378
4379     DisplayGameControlValues();
4380
4381     if (time == time_final)
4382       StopSound(SND_GAME_LEVELTIME_BONUS);
4383     else if (setup.sound_loops)
4384       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4385     else
4386       PlaySound(SND_GAME_LEVELTIME_BONUS);
4387
4388     return;
4389   }
4390
4391   local_player->LevelSolved_PanelOff = TRUE;
4392
4393   if (game_over_delay_2 > 0)
4394   {
4395     game_over_delay_2--;
4396
4397     return;
4398   }
4399
4400   GameEnd();
4401 }
4402
4403 void GameEnd()
4404 {
4405   int hi_pos;
4406   boolean raise_level = FALSE;
4407
4408   local_player->LevelSolved_GameEnd = TRUE;
4409
4410   if (!global.use_envelope_request)
4411     CloseDoor(DOOR_CLOSE_1);
4412
4413   if (local_player->LevelSolved_SaveTape)
4414   {
4415     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4416   }
4417
4418   CloseDoor(DOOR_CLOSE_ALL);
4419
4420   if (level_editor_test_game)
4421   {
4422     game_status = GAME_MODE_MAIN;
4423
4424     DrawMainMenu();
4425
4426     return;
4427   }
4428
4429   if (!local_player->LevelSolved_SaveScore)
4430   {
4431     FadeOut(REDRAW_FIELD);
4432
4433     game_status = GAME_MODE_MAIN;
4434
4435     DrawMainMenu();
4436
4437     return;
4438   }
4439
4440   if (level_nr == leveldir_current->handicap_level)
4441   {
4442     leveldir_current->handicap_level++;
4443
4444     SaveLevelSetup_SeriesInfo();
4445   }
4446
4447   if (level_nr < leveldir_current->last_level)
4448     raise_level = TRUE;                 /* advance to next level */
4449
4450   if ((hi_pos = NewHiScore()) >= 0) 
4451   {
4452     game_status = GAME_MODE_SCORES;
4453
4454     DrawHallOfFame(hi_pos);
4455
4456     if (raise_level)
4457     {
4458       level_nr++;
4459       TapeErase();
4460     }
4461   }
4462   else
4463   {
4464     FadeOut(REDRAW_FIELD);
4465
4466     game_status = GAME_MODE_MAIN;
4467
4468     if (raise_level)
4469     {
4470       level_nr++;
4471       TapeErase();
4472     }
4473
4474     DrawMainMenu();
4475   }
4476 }
4477
4478 int NewHiScore()
4479 {
4480   int k, l;
4481   int position = -1;
4482
4483   LoadScore(level_nr);
4484
4485   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4486       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4487     return -1;
4488
4489   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4490   {
4491     if (local_player->score_final > highscore[k].Score)
4492     {
4493       /* player has made it to the hall of fame */
4494
4495       if (k < MAX_SCORE_ENTRIES - 1)
4496       {
4497         int m = MAX_SCORE_ENTRIES - 1;
4498
4499 #ifdef ONE_PER_NAME
4500         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4501           if (strEqual(setup.player_name, highscore[l].Name))
4502             m = l;
4503         if (m == k)     /* player's new highscore overwrites his old one */
4504           goto put_into_list;
4505 #endif
4506
4507         for (l = m; l > k; l--)
4508         {
4509           strcpy(highscore[l].Name, highscore[l - 1].Name);
4510           highscore[l].Score = highscore[l - 1].Score;
4511         }
4512       }
4513
4514 #ifdef ONE_PER_NAME
4515       put_into_list:
4516 #endif
4517       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4518       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4519       highscore[k].Score = local_player->score_final; 
4520       position = k;
4521       break;
4522     }
4523
4524 #ifdef ONE_PER_NAME
4525     else if (!strncmp(setup.player_name, highscore[k].Name,
4526                       MAX_PLAYER_NAME_LEN))
4527       break;    /* player already there with a higher score */
4528 #endif
4529
4530   }
4531
4532   if (position >= 0) 
4533     SaveScore(level_nr);
4534
4535   return position;
4536 }
4537
4538 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4539 {
4540   int element = Feld[x][y];
4541   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4542   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4543   int horiz_move = (dx != 0);
4544   int sign = (horiz_move ? dx : dy);
4545   int step = sign * element_info[element].move_stepsize;
4546
4547   /* special values for move stepsize for spring and things on conveyor belt */
4548   if (horiz_move)
4549   {
4550     if (CAN_FALL(element) &&
4551         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4552       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4553     else if (element == EL_SPRING)
4554       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4555   }
4556
4557   return step;
4558 }
4559
4560 inline static int getElementMoveStepsize(int x, int y)
4561 {
4562   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4563 }
4564
4565 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4566 {
4567   if (player->GfxAction != action || player->GfxDir != dir)
4568   {
4569     player->GfxAction = action;
4570     player->GfxDir = dir;
4571     player->Frame = 0;
4572     player->StepFrame = 0;
4573   }
4574 }
4575
4576 static void ResetGfxFrame(int x, int y, boolean redraw)
4577 {
4578   int element = Feld[x][y];
4579   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4580   int last_gfx_frame = GfxFrame[x][y];
4581
4582   if (graphic_info[graphic].anim_global_sync)
4583     GfxFrame[x][y] = FrameCounter;
4584   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4585     GfxFrame[x][y] = CustomValue[x][y];
4586   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4587     GfxFrame[x][y] = element_info[element].collect_score;
4588   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4589     GfxFrame[x][y] = ChangeDelay[x][y];
4590
4591   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4592     DrawLevelGraphicAnimation(x, y, graphic);
4593 }
4594
4595 static void ResetGfxAnimation(int x, int y)
4596 {
4597   GfxAction[x][y] = ACTION_DEFAULT;
4598   GfxDir[x][y] = MovDir[x][y];
4599   GfxFrame[x][y] = 0;
4600
4601   ResetGfxFrame(x, y, FALSE);
4602 }
4603
4604 static void ResetRandomAnimationValue(int x, int y)
4605 {
4606   GfxRandom[x][y] = INIT_GFX_RANDOM();
4607 }
4608
4609 void InitMovingField(int x, int y, int direction)
4610 {
4611   int element = Feld[x][y];
4612   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4613   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4614   int newx = x + dx;
4615   int newy = y + dy;
4616   boolean is_moving_before, is_moving_after;
4617
4618   /* check if element was/is moving or being moved before/after mode change */
4619   is_moving_before = (WasJustMoving[x][y] != 0);
4620   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4621
4622   /* reset animation only for moving elements which change direction of moving
4623      or which just started or stopped moving
4624      (else CEs with property "can move" / "not moving" are reset each frame) */
4625   if (is_moving_before != is_moving_after ||
4626       direction != MovDir[x][y])
4627     ResetGfxAnimation(x, y);
4628
4629   MovDir[x][y] = direction;
4630   GfxDir[x][y] = direction;
4631
4632   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4633                      direction == MV_DOWN && CAN_FALL(element) ?
4634                      ACTION_FALLING : ACTION_MOVING);
4635
4636   /* this is needed for CEs with property "can move" / "not moving" */
4637
4638   if (is_moving_after)
4639   {
4640     if (Feld[newx][newy] == EL_EMPTY)
4641       Feld[newx][newy] = EL_BLOCKED;
4642
4643     MovDir[newx][newy] = MovDir[x][y];
4644
4645     CustomValue[newx][newy] = CustomValue[x][y];
4646
4647     GfxFrame[newx][newy] = GfxFrame[x][y];
4648     GfxRandom[newx][newy] = GfxRandom[x][y];
4649     GfxAction[newx][newy] = GfxAction[x][y];
4650     GfxDir[newx][newy] = GfxDir[x][y];
4651   }
4652 }
4653
4654 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4655 {
4656   int direction = MovDir[x][y];
4657   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4658   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4659
4660   *goes_to_x = newx;
4661   *goes_to_y = newy;
4662 }
4663
4664 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4665 {
4666   int oldx = x, oldy = y;
4667   int direction = MovDir[x][y];
4668
4669   if (direction == MV_LEFT)
4670     oldx++;
4671   else if (direction == MV_RIGHT)
4672     oldx--;
4673   else if (direction == MV_UP)
4674     oldy++;
4675   else if (direction == MV_DOWN)
4676     oldy--;
4677
4678   *comes_from_x = oldx;
4679   *comes_from_y = oldy;
4680 }
4681
4682 int MovingOrBlocked2Element(int x, int y)
4683 {
4684   int element = Feld[x][y];
4685
4686   if (element == EL_BLOCKED)
4687   {
4688     int oldx, oldy;
4689
4690     Blocked2Moving(x, y, &oldx, &oldy);
4691     return Feld[oldx][oldy];
4692   }
4693   else
4694     return element;
4695 }
4696
4697 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4698 {
4699   /* like MovingOrBlocked2Element(), but if element is moving
4700      and (x,y) is the field the moving element is just leaving,
4701      return EL_BLOCKED instead of the element value */
4702   int element = Feld[x][y];
4703
4704   if (IS_MOVING(x, y))
4705   {
4706     if (element == EL_BLOCKED)
4707     {
4708       int oldx, oldy;
4709
4710       Blocked2Moving(x, y, &oldx, &oldy);
4711       return Feld[oldx][oldy];
4712     }
4713     else
4714       return EL_BLOCKED;
4715   }
4716   else
4717     return element;
4718 }
4719
4720 static void RemoveField(int x, int y)
4721 {
4722   Feld[x][y] = EL_EMPTY;
4723
4724   MovPos[x][y] = 0;
4725   MovDir[x][y] = 0;
4726   MovDelay[x][y] = 0;
4727
4728   CustomValue[x][y] = 0;
4729
4730   AmoebaNr[x][y] = 0;
4731   ChangeDelay[x][y] = 0;
4732   ChangePage[x][y] = -1;
4733   Pushed[x][y] = FALSE;
4734
4735   GfxElement[x][y] = EL_UNDEFINED;
4736   GfxAction[x][y] = ACTION_DEFAULT;
4737   GfxDir[x][y] = MV_NONE;
4738 }
4739
4740 void RemoveMovingField(int x, int y)
4741 {
4742   int oldx = x, oldy = y, newx = x, newy = y;
4743   int element = Feld[x][y];
4744   int next_element = EL_UNDEFINED;
4745
4746   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4747     return;
4748
4749   if (IS_MOVING(x, y))
4750   {
4751     Moving2Blocked(x, y, &newx, &newy);
4752
4753     if (Feld[newx][newy] != EL_BLOCKED)
4754     {
4755       /* element is moving, but target field is not free (blocked), but
4756          already occupied by something different (example: acid pool);
4757          in this case, only remove the moving field, but not the target */
4758
4759       RemoveField(oldx, oldy);
4760
4761       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4762
4763       TEST_DrawLevelField(oldx, oldy);
4764
4765       return;
4766     }
4767   }
4768   else if (element == EL_BLOCKED)
4769   {
4770     Blocked2Moving(x, y, &oldx, &oldy);
4771     if (!IS_MOVING(oldx, oldy))
4772       return;
4773   }
4774
4775   if (element == EL_BLOCKED &&
4776       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4777        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4778        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4779        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4780        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4781        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4782     next_element = get_next_element(Feld[oldx][oldy]);
4783
4784   RemoveField(oldx, oldy);
4785   RemoveField(newx, newy);
4786
4787   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4788
4789   if (next_element != EL_UNDEFINED)
4790     Feld[oldx][oldy] = next_element;
4791
4792   TEST_DrawLevelField(oldx, oldy);
4793   TEST_DrawLevelField(newx, newy);
4794 }
4795
4796 void DrawDynamite(int x, int y)
4797 {
4798   int sx = SCREENX(x), sy = SCREENY(y);
4799   int graphic = el2img(Feld[x][y]);
4800   int frame;
4801
4802   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4803     return;
4804
4805   if (IS_WALKABLE_INSIDE(Back[x][y]))
4806     return;
4807
4808   if (Back[x][y])
4809     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4810   else if (Store[x][y])
4811     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4812
4813   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4814
4815   if (Back[x][y] || Store[x][y])
4816     DrawGraphicThruMask(sx, sy, graphic, frame);
4817   else
4818     DrawGraphic(sx, sy, graphic, frame);
4819 }
4820
4821 void CheckDynamite(int x, int y)
4822 {
4823   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4824   {
4825     MovDelay[x][y]--;
4826
4827     if (MovDelay[x][y] != 0)
4828     {
4829       DrawDynamite(x, y);
4830       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4831
4832       return;
4833     }
4834   }
4835
4836   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4837
4838   Bang(x, y);
4839 }
4840
4841 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4842 {
4843   boolean num_checked_players = 0;
4844   int i;
4845
4846   for (i = 0; i < MAX_PLAYERS; i++)
4847   {
4848     if (stored_player[i].active)
4849     {
4850       int sx = stored_player[i].jx;
4851       int sy = stored_player[i].jy;
4852
4853       if (num_checked_players == 0)
4854       {
4855         *sx1 = *sx2 = sx;
4856         *sy1 = *sy2 = sy;
4857       }
4858       else
4859       {
4860         *sx1 = MIN(*sx1, sx);
4861         *sy1 = MIN(*sy1, sy);
4862         *sx2 = MAX(*sx2, sx);
4863         *sy2 = MAX(*sy2, sy);
4864       }
4865
4866       num_checked_players++;
4867     }
4868   }
4869 }
4870
4871 static boolean checkIfAllPlayersFitToScreen_RND()
4872 {
4873   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4874
4875   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4876
4877   return (sx2 - sx1 < SCR_FIELDX &&
4878           sy2 - sy1 < SCR_FIELDY);
4879 }
4880
4881 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4882 {
4883   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4884
4885   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4886
4887   *sx = (sx1 + sx2) / 2;
4888   *sy = (sy1 + sy2) / 2;
4889 }
4890
4891 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4892                         boolean center_screen, boolean quick_relocation)
4893 {
4894   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4895   boolean no_delay = (tape.warp_forward);
4896   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4897   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4898
4899   if (quick_relocation)
4900   {
4901     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4902     {
4903       if (!level.shifted_relocation || center_screen)
4904       {
4905         /* quick relocation (without scrolling), with centering of screen */
4906
4907         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4908                     x > SBX_Right + MIDPOSX ? SBX_Right :
4909                     x - MIDPOSX);
4910
4911         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4912                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4913                     y - MIDPOSY);
4914       }
4915       else
4916       {
4917         /* quick relocation (without scrolling), but do not center screen */
4918
4919         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4920                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4921                                old_x - MIDPOSX);
4922
4923         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4924                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4925                                old_y - MIDPOSY);
4926
4927         int offset_x = x + (scroll_x - center_scroll_x);
4928         int offset_y = y + (scroll_y - center_scroll_y);
4929
4930         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4931                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4932                     offset_x - MIDPOSX);
4933
4934         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4935                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4936                     offset_y - MIDPOSY);
4937       }
4938     }
4939     else
4940     {
4941       if (!level.shifted_relocation || center_screen)
4942       {
4943         /* quick relocation (without scrolling), with centering of screen */
4944
4945         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4946                     x > SBX_Right + MIDPOSX ? SBX_Right :
4947                     x - MIDPOSX);
4948
4949         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4950                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4951                     y - MIDPOSY);
4952       }
4953       else
4954       {
4955         /* quick relocation (without scrolling), but do not center screen */
4956
4957         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4958                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4959                                old_x - MIDPOSX);
4960
4961         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4962                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4963                                old_y - MIDPOSY);
4964
4965         int offset_x = x + (scroll_x - center_scroll_x);
4966         int offset_y = y + (scroll_y - center_scroll_y);
4967
4968         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4969                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4970                     offset_x - MIDPOSX);
4971
4972         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4973                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4974                     offset_y - MIDPOSY);
4975       }
4976     }
4977
4978     RedrawPlayfield();
4979   }
4980   else
4981   {
4982     int scroll_xx, scroll_yy;
4983
4984     if (!level.shifted_relocation || center_screen)
4985     {
4986       /* visible relocation (with scrolling), with centering of screen */
4987
4988       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4989                    x > SBX_Right + MIDPOSX ? SBX_Right :
4990                    x - MIDPOSX);
4991
4992       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4993                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4994                    y - MIDPOSY);
4995     }
4996     else
4997     {
4998       /* visible relocation (with scrolling), but do not center screen */
4999
5000       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5001                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5002                              old_x - MIDPOSX);
5003
5004       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5005                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5006                              old_y - MIDPOSY);
5007
5008       int offset_x = x + (scroll_x - center_scroll_x);
5009       int offset_y = y + (scroll_y - center_scroll_y);
5010
5011       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5012                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5013                    offset_x - MIDPOSX);
5014
5015       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5016                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5017                    offset_y - MIDPOSY);
5018     }
5019
5020
5021     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5022
5023     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5024     {
5025       int dx = 0, dy = 0;
5026       int fx = FX, fy = FY;
5027
5028       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5029       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5030
5031       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5032         break;
5033
5034       scroll_x -= dx;
5035       scroll_y -= dy;
5036
5037       fx += dx * TILEX / 2;
5038       fy += dy * TILEY / 2;
5039
5040       ScrollLevel(dx, dy);
5041       DrawAllPlayers();
5042
5043       /* scroll in two steps of half tile size to make things smoother */
5044       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5045       Delay(wait_delay_value);
5046
5047       /* scroll second step to align at full tile size */
5048       BackToFront();
5049       Delay(wait_delay_value);
5050     }
5051
5052     DrawAllPlayers();
5053     BackToFront();
5054     Delay(wait_delay_value);
5055   }
5056 }
5057
5058 void RelocatePlayer(int jx, int jy, int el_player_raw)
5059 {
5060   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5061   int player_nr = GET_PLAYER_NR(el_player);
5062   struct PlayerInfo *player = &stored_player[player_nr];
5063   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5064   boolean no_delay = (tape.warp_forward);
5065   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5066   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5067   int old_jx = player->jx;
5068   int old_jy = player->jy;
5069   int old_element = Feld[old_jx][old_jy];
5070   int element = Feld[jx][jy];
5071   boolean player_relocated = (old_jx != jx || old_jy != jy);
5072
5073   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5074   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5075   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5076   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5077   int leave_side_horiz = move_dir_horiz;
5078   int leave_side_vert  = move_dir_vert;
5079   int enter_side = enter_side_horiz | enter_side_vert;
5080   int leave_side = leave_side_horiz | leave_side_vert;
5081
5082   if (player->GameOver)         /* do not reanimate dead player */
5083     return;
5084
5085   if (!player_relocated)        /* no need to relocate the player */
5086     return;
5087
5088   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5089   {
5090     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5091     DrawLevelField(jx, jy);
5092   }
5093
5094   if (player->present)
5095   {
5096     while (player->MovPos)
5097     {
5098       ScrollPlayer(player, SCROLL_GO_ON);
5099       ScrollScreen(NULL, SCROLL_GO_ON);
5100
5101       AdvanceFrameAndPlayerCounters(player->index_nr);
5102
5103       DrawPlayer(player);
5104
5105       BackToFront();
5106       Delay(wait_delay_value);
5107     }
5108
5109     DrawPlayer(player);         /* needed here only to cleanup last field */
5110     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5111
5112     player->is_moving = FALSE;
5113   }
5114
5115   if (IS_CUSTOM_ELEMENT(old_element))
5116     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5117                                CE_LEFT_BY_PLAYER,
5118                                player->index_bit, leave_side);
5119
5120   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5121                                       CE_PLAYER_LEAVES_X,
5122                                       player->index_bit, leave_side);
5123
5124   Feld[jx][jy] = el_player;
5125   InitPlayerField(jx, jy, el_player, TRUE);
5126
5127   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5128      possible that the relocation target field did not contain a player element,
5129      but a walkable element, to which the new player was relocated -- in this
5130      case, restore that (already initialized!) element on the player field */
5131   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5132   {
5133     Feld[jx][jy] = element;     /* restore previously existing element */
5134   }
5135
5136   /* only visually relocate centered player */
5137   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5138                      FALSE, level.instant_relocation);
5139
5140   TestIfPlayerTouchesBadThing(jx, jy);
5141   TestIfPlayerTouchesCustomElement(jx, jy);
5142
5143   if (IS_CUSTOM_ELEMENT(element))
5144     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5145                                player->index_bit, enter_side);
5146
5147   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5148                                       player->index_bit, enter_side);
5149
5150   if (player->is_switching)
5151   {
5152     /* ensure that relocation while still switching an element does not cause
5153        a new element to be treated as also switched directly after relocation
5154        (this is important for teleporter switches that teleport the player to
5155        a place where another teleporter switch is in the same direction, which
5156        would then incorrectly be treated as immediately switched before the
5157        direction key that caused the switch was released) */
5158
5159     player->switch_x += jx - old_jx;
5160     player->switch_y += jy - old_jy;
5161   }
5162 }
5163
5164 void Explode(int ex, int ey, int phase, int mode)
5165 {
5166   int x, y;
5167   int last_phase;
5168   int border_element;
5169
5170   /* !!! eliminate this variable !!! */
5171   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5172
5173   if (game.explosions_delayed)
5174   {
5175     ExplodeField[ex][ey] = mode;
5176     return;
5177   }
5178
5179   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5180   {
5181     int center_element = Feld[ex][ey];
5182     int artwork_element, explosion_element;     /* set these values later */
5183
5184     /* remove things displayed in background while burning dynamite */
5185     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5186       Back[ex][ey] = 0;
5187
5188     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5189     {
5190       /* put moving element to center field (and let it explode there) */
5191       center_element = MovingOrBlocked2Element(ex, ey);
5192       RemoveMovingField(ex, ey);
5193       Feld[ex][ey] = center_element;
5194     }
5195
5196     /* now "center_element" is finally determined -- set related values now */
5197     artwork_element = center_element;           /* for custom player artwork */
5198     explosion_element = center_element;         /* for custom player artwork */
5199
5200     if (IS_PLAYER(ex, ey))
5201     {
5202       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5203
5204       artwork_element = stored_player[player_nr].artwork_element;
5205
5206       if (level.use_explosion_element[player_nr])
5207       {
5208         explosion_element = level.explosion_element[player_nr];
5209         artwork_element = explosion_element;
5210       }
5211     }
5212
5213     if (mode == EX_TYPE_NORMAL ||
5214         mode == EX_TYPE_CENTER ||
5215         mode == EX_TYPE_CROSS)
5216       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5217
5218     last_phase = element_info[explosion_element].explosion_delay + 1;
5219
5220     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5221     {
5222       int xx = x - ex + 1;
5223       int yy = y - ey + 1;
5224       int element;
5225
5226       if (!IN_LEV_FIELD(x, y) ||
5227           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5228           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5229         continue;
5230
5231       element = Feld[x][y];
5232
5233       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5234       {
5235         element = MovingOrBlocked2Element(x, y);
5236
5237         if (!IS_EXPLOSION_PROOF(element))
5238           RemoveMovingField(x, y);
5239       }
5240
5241       /* indestructible elements can only explode in center (but not flames) */
5242       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5243                                            mode == EX_TYPE_BORDER)) ||
5244           element == EL_FLAMES)
5245         continue;
5246
5247       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5248          behaviour, for example when touching a yamyam that explodes to rocks
5249          with active deadly shield, a rock is created under the player !!! */
5250       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5251 #if 0
5252       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5253           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5254            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5255 #else
5256       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5257 #endif
5258       {
5259         if (IS_ACTIVE_BOMB(element))
5260         {
5261           /* re-activate things under the bomb like gate or penguin */
5262           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5263           Back[x][y] = 0;
5264         }
5265
5266         continue;
5267       }
5268
5269       /* save walkable background elements while explosion on same tile */
5270       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5271           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5272         Back[x][y] = element;
5273
5274       /* ignite explodable elements reached by other explosion */
5275       if (element == EL_EXPLOSION)
5276         element = Store2[x][y];
5277
5278       if (AmoebaNr[x][y] &&
5279           (element == EL_AMOEBA_FULL ||
5280            element == EL_BD_AMOEBA ||
5281            element == EL_AMOEBA_GROWING))
5282       {
5283         AmoebaCnt[AmoebaNr[x][y]]--;
5284         AmoebaCnt2[AmoebaNr[x][y]]--;
5285       }
5286
5287       RemoveField(x, y);
5288
5289       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5290       {
5291         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5292
5293         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5294
5295         if (PLAYERINFO(ex, ey)->use_murphy)
5296           Store[x][y] = EL_EMPTY;
5297       }
5298
5299       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5300          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5301       else if (ELEM_IS_PLAYER(center_element))
5302         Store[x][y] = EL_EMPTY;
5303       else if (center_element == EL_YAMYAM)
5304         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5305       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5306         Store[x][y] = element_info[center_element].content.e[xx][yy];
5307 #if 1
5308       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5309          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5310          otherwise) -- FIX THIS !!! */
5311       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5312         Store[x][y] = element_info[element].content.e[1][1];
5313 #else
5314       else if (!CAN_EXPLODE(element))
5315         Store[x][y] = element_info[element].content.e[1][1];
5316 #endif
5317       else
5318         Store[x][y] = EL_EMPTY;
5319
5320       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5321           center_element == EL_AMOEBA_TO_DIAMOND)
5322         Store2[x][y] = element;
5323
5324       Feld[x][y] = EL_EXPLOSION;
5325       GfxElement[x][y] = artwork_element;
5326
5327       ExplodePhase[x][y] = 1;
5328       ExplodeDelay[x][y] = last_phase;
5329
5330       Stop[x][y] = TRUE;
5331     }
5332
5333     if (center_element == EL_YAMYAM)
5334       game.yamyam_content_nr =
5335         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5336
5337     return;
5338   }
5339
5340   if (Stop[ex][ey])
5341     return;
5342
5343   x = ex;
5344   y = ey;
5345
5346   if (phase == 1)
5347     GfxFrame[x][y] = 0;         /* restart explosion animation */
5348
5349   last_phase = ExplodeDelay[x][y];
5350
5351   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5352
5353   /* this can happen if the player leaves an explosion just in time */
5354   if (GfxElement[x][y] == EL_UNDEFINED)
5355     GfxElement[x][y] = EL_EMPTY;
5356
5357   border_element = Store2[x][y];
5358   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5359     border_element = StorePlayer[x][y];
5360
5361   if (phase == element_info[border_element].ignition_delay ||
5362       phase == last_phase)
5363   {
5364     boolean border_explosion = FALSE;
5365
5366     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5367         !PLAYER_EXPLOSION_PROTECTED(x, y))
5368     {
5369       KillPlayerUnlessExplosionProtected(x, y);
5370       border_explosion = TRUE;
5371     }
5372     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5373     {
5374       Feld[x][y] = Store2[x][y];
5375       Store2[x][y] = 0;
5376       Bang(x, y);
5377       border_explosion = TRUE;
5378     }
5379     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5380     {
5381       AmoebeUmwandeln(x, y);
5382       Store2[x][y] = 0;
5383       border_explosion = TRUE;
5384     }
5385
5386     /* if an element just explodes due to another explosion (chain-reaction),
5387        do not immediately end the new explosion when it was the last frame of
5388        the explosion (as it would be done in the following "if"-statement!) */
5389     if (border_explosion && phase == last_phase)
5390       return;
5391   }
5392
5393   if (phase == last_phase)
5394   {
5395     int element;
5396
5397     element = Feld[x][y] = Store[x][y];
5398     Store[x][y] = Store2[x][y] = 0;
5399     GfxElement[x][y] = EL_UNDEFINED;
5400
5401     /* player can escape from explosions and might therefore be still alive */
5402     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5403         element <= EL_PLAYER_IS_EXPLODING_4)
5404     {
5405       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5406       int explosion_element = EL_PLAYER_1 + player_nr;
5407       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5408       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5409
5410       if (level.use_explosion_element[player_nr])
5411         explosion_element = level.explosion_element[player_nr];
5412
5413       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5414                     element_info[explosion_element].content.e[xx][yy]);
5415     }
5416
5417     /* restore probably existing indestructible background element */
5418     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5419       element = Feld[x][y] = Back[x][y];
5420     Back[x][y] = 0;
5421
5422     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5423     GfxDir[x][y] = MV_NONE;
5424     ChangeDelay[x][y] = 0;
5425     ChangePage[x][y] = -1;
5426
5427     CustomValue[x][y] = 0;
5428
5429     InitField_WithBug2(x, y, FALSE);
5430
5431     TEST_DrawLevelField(x, y);
5432
5433     TestIfElementTouchesCustomElement(x, y);
5434
5435     if (GFX_CRUMBLED(element))
5436       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5437
5438     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5439       StorePlayer[x][y] = 0;
5440
5441     if (ELEM_IS_PLAYER(element))
5442       RelocatePlayer(x, y, element);
5443   }
5444   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5445   {
5446     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5447     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5448
5449     if (phase == delay)
5450       TEST_DrawLevelFieldCrumbled(x, y);
5451
5452     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5453     {
5454       DrawLevelElement(x, y, Back[x][y]);
5455       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5456     }
5457     else if (IS_WALKABLE_UNDER(Back[x][y]))
5458     {
5459       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5460       DrawLevelElementThruMask(x, y, Back[x][y]);
5461     }
5462     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5463       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5464   }
5465 }
5466
5467 void DynaExplode(int ex, int ey)
5468 {
5469   int i, j;
5470   int dynabomb_element = Feld[ex][ey];
5471   int dynabomb_size = 1;
5472   boolean dynabomb_xl = FALSE;
5473   struct PlayerInfo *player;
5474   static int xy[4][2] =
5475   {
5476     { 0, -1 },
5477     { -1, 0 },
5478     { +1, 0 },
5479     { 0, +1 }
5480   };
5481
5482   if (IS_ACTIVE_BOMB(dynabomb_element))
5483   {
5484     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5485     dynabomb_size = player->dynabomb_size;
5486     dynabomb_xl = player->dynabomb_xl;
5487     player->dynabombs_left++;
5488   }
5489
5490   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5491
5492   for (i = 0; i < NUM_DIRECTIONS; i++)
5493   {
5494     for (j = 1; j <= dynabomb_size; j++)
5495     {
5496       int x = ex + j * xy[i][0];
5497       int y = ey + j * xy[i][1];
5498       int element;
5499
5500       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5501         break;
5502
5503       element = Feld[x][y];
5504
5505       /* do not restart explosions of fields with active bombs */
5506       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5507         continue;
5508
5509       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5510
5511       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5512           !IS_DIGGABLE(element) && !dynabomb_xl)
5513         break;
5514     }
5515   }
5516 }
5517
5518 void Bang(int x, int y)
5519 {
5520   int element = MovingOrBlocked2Element(x, y);
5521   int explosion_type = EX_TYPE_NORMAL;
5522
5523   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5524   {
5525     struct PlayerInfo *player = PLAYERINFO(x, y);
5526
5527     element = Feld[x][y] = player->initial_element;
5528
5529     if (level.use_explosion_element[player->index_nr])
5530     {
5531       int explosion_element = level.explosion_element[player->index_nr];
5532
5533       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5534         explosion_type = EX_TYPE_CROSS;
5535       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5536         explosion_type = EX_TYPE_CENTER;
5537     }
5538   }
5539
5540   switch (element)
5541   {
5542     case EL_BUG:
5543     case EL_SPACESHIP:
5544     case EL_BD_BUTTERFLY:
5545     case EL_BD_FIREFLY:
5546     case EL_YAMYAM:
5547     case EL_DARK_YAMYAM:
5548     case EL_ROBOT:
5549     case EL_PACMAN:
5550     case EL_MOLE:
5551       RaiseScoreElement(element);
5552       break;
5553
5554     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5555     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5556     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5557     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5558     case EL_DYNABOMB_INCREASE_NUMBER:
5559     case EL_DYNABOMB_INCREASE_SIZE:
5560     case EL_DYNABOMB_INCREASE_POWER:
5561       explosion_type = EX_TYPE_DYNA;
5562       break;
5563
5564     case EL_DC_LANDMINE:
5565       explosion_type = EX_TYPE_CENTER;
5566       break;
5567
5568     case EL_PENGUIN:
5569     case EL_LAMP:
5570     case EL_LAMP_ACTIVE:
5571     case EL_AMOEBA_TO_DIAMOND:
5572       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5573         explosion_type = EX_TYPE_CENTER;
5574       break;
5575
5576     default:
5577       if (element_info[element].explosion_type == EXPLODES_CROSS)
5578         explosion_type = EX_TYPE_CROSS;
5579       else if (element_info[element].explosion_type == EXPLODES_1X1)
5580         explosion_type = EX_TYPE_CENTER;
5581       break;
5582   }
5583
5584   if (explosion_type == EX_TYPE_DYNA)
5585     DynaExplode(x, y);
5586   else
5587     Explode(x, y, EX_PHASE_START, explosion_type);
5588
5589   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5590 }
5591
5592 void SplashAcid(int x, int y)
5593 {
5594   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5595       (!IN_LEV_FIELD(x - 1, y - 2) ||
5596        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5597     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5598
5599   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5600       (!IN_LEV_FIELD(x + 1, y - 2) ||
5601        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5602     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5603
5604   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5605 }
5606
5607 static void InitBeltMovement()
5608 {
5609   static int belt_base_element[4] =
5610   {
5611     EL_CONVEYOR_BELT_1_LEFT,
5612     EL_CONVEYOR_BELT_2_LEFT,
5613     EL_CONVEYOR_BELT_3_LEFT,
5614     EL_CONVEYOR_BELT_4_LEFT
5615   };
5616   static int belt_base_active_element[4] =
5617   {
5618     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5619     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5620     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5621     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5622   };
5623
5624   int x, y, i, j;
5625
5626   /* set frame order for belt animation graphic according to belt direction */
5627   for (i = 0; i < NUM_BELTS; i++)
5628   {
5629     int belt_nr = i;
5630
5631     for (j = 0; j < NUM_BELT_PARTS; j++)
5632     {
5633       int element = belt_base_active_element[belt_nr] + j;
5634       int graphic_1 = el2img(element);
5635       int graphic_2 = el2panelimg(element);
5636
5637       if (game.belt_dir[i] == MV_LEFT)
5638       {
5639         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5640         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5641       }
5642       else
5643       {
5644         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5645         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5646       }
5647     }
5648   }
5649
5650   SCAN_PLAYFIELD(x, y)
5651   {
5652     int element = Feld[x][y];
5653
5654     for (i = 0; i < NUM_BELTS; i++)
5655     {
5656       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5657       {
5658         int e_belt_nr = getBeltNrFromBeltElement(element);
5659         int belt_nr = i;
5660
5661         if (e_belt_nr == belt_nr)
5662         {
5663           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5664
5665           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5666         }
5667       }
5668     }
5669   }
5670 }
5671
5672 static void ToggleBeltSwitch(int x, int y)
5673 {
5674   static int belt_base_element[4] =
5675   {
5676     EL_CONVEYOR_BELT_1_LEFT,
5677     EL_CONVEYOR_BELT_2_LEFT,
5678     EL_CONVEYOR_BELT_3_LEFT,
5679     EL_CONVEYOR_BELT_4_LEFT
5680   };
5681   static int belt_base_active_element[4] =
5682   {
5683     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5684     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5685     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5686     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5687   };
5688   static int belt_base_switch_element[4] =
5689   {
5690     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5691     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5692     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5693     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5694   };
5695   static int belt_move_dir[4] =
5696   {
5697     MV_LEFT,
5698     MV_NONE,
5699     MV_RIGHT,
5700     MV_NONE,
5701   };
5702
5703   int element = Feld[x][y];
5704   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5705   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5706   int belt_dir = belt_move_dir[belt_dir_nr];
5707   int xx, yy, i;
5708
5709   if (!IS_BELT_SWITCH(element))
5710     return;
5711
5712   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5713   game.belt_dir[belt_nr] = belt_dir;
5714
5715   if (belt_dir_nr == 3)
5716     belt_dir_nr = 1;
5717
5718   /* set frame order for belt animation graphic according to belt direction */
5719   for (i = 0; i < NUM_BELT_PARTS; i++)
5720   {
5721     int element = belt_base_active_element[belt_nr] + i;
5722     int graphic_1 = el2img(element);
5723     int graphic_2 = el2panelimg(element);
5724
5725     if (belt_dir == MV_LEFT)
5726     {
5727       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5728       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5729     }
5730     else
5731     {
5732       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5733       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5734     }
5735   }
5736
5737   SCAN_PLAYFIELD(xx, yy)
5738   {
5739     int element = Feld[xx][yy];
5740
5741     if (IS_BELT_SWITCH(element))
5742     {
5743       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5744
5745       if (e_belt_nr == belt_nr)
5746       {
5747         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5748         TEST_DrawLevelField(xx, yy);
5749       }
5750     }
5751     else if (IS_BELT(element) && belt_dir != MV_NONE)
5752     {
5753       int e_belt_nr = getBeltNrFromBeltElement(element);
5754
5755       if (e_belt_nr == belt_nr)
5756       {
5757         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5758
5759         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5760         TEST_DrawLevelField(xx, yy);
5761       }
5762     }
5763     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5764     {
5765       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5766
5767       if (e_belt_nr == belt_nr)
5768       {
5769         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5770
5771         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5772         TEST_DrawLevelField(xx, yy);
5773       }
5774     }
5775   }
5776 }
5777
5778 static void ToggleSwitchgateSwitch(int x, int y)
5779 {
5780   int xx, yy;
5781
5782   game.switchgate_pos = !game.switchgate_pos;
5783
5784   SCAN_PLAYFIELD(xx, yy)
5785   {
5786     int element = Feld[xx][yy];
5787
5788     if (element == EL_SWITCHGATE_SWITCH_UP)
5789     {
5790       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5791       TEST_DrawLevelField(xx, yy);
5792     }
5793     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5794     {
5795       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5796       TEST_DrawLevelField(xx, yy);
5797     }
5798     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5799     {
5800       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5801       TEST_DrawLevelField(xx, yy);
5802     }
5803     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5804     {
5805       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5806       TEST_DrawLevelField(xx, yy);
5807     }
5808     else if (element == EL_SWITCHGATE_OPEN ||
5809              element == EL_SWITCHGATE_OPENING)
5810     {
5811       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5812
5813       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5814     }
5815     else if (element == EL_SWITCHGATE_CLOSED ||
5816              element == EL_SWITCHGATE_CLOSING)
5817     {
5818       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5819
5820       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5821     }
5822   }
5823 }
5824
5825 static int getInvisibleActiveFromInvisibleElement(int element)
5826 {
5827   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5828           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5829           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5830           element);
5831 }
5832
5833 static int getInvisibleFromInvisibleActiveElement(int element)
5834 {
5835   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5836           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5837           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5838           element);
5839 }
5840
5841 static void RedrawAllLightSwitchesAndInvisibleElements()
5842 {
5843   int x, y;
5844
5845   SCAN_PLAYFIELD(x, y)
5846   {
5847     int element = Feld[x][y];
5848
5849     if (element == EL_LIGHT_SWITCH &&
5850         game.light_time_left > 0)
5851     {
5852       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5853       TEST_DrawLevelField(x, y);
5854     }
5855     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5856              game.light_time_left == 0)
5857     {
5858       Feld[x][y] = EL_LIGHT_SWITCH;
5859       TEST_DrawLevelField(x, y);
5860     }
5861     else if (element == EL_EMC_DRIPPER &&
5862              game.light_time_left > 0)
5863     {
5864       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5865       TEST_DrawLevelField(x, y);
5866     }
5867     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5868              game.light_time_left == 0)
5869     {
5870       Feld[x][y] = EL_EMC_DRIPPER;
5871       TEST_DrawLevelField(x, y);
5872     }
5873     else if (element == EL_INVISIBLE_STEELWALL ||
5874              element == EL_INVISIBLE_WALL ||
5875              element == EL_INVISIBLE_SAND)
5876     {
5877       if (game.light_time_left > 0)
5878         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5879
5880       TEST_DrawLevelField(x, y);
5881
5882       /* uncrumble neighbour fields, if needed */
5883       if (element == EL_INVISIBLE_SAND)
5884         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5885     }
5886     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5887              element == EL_INVISIBLE_WALL_ACTIVE ||
5888              element == EL_INVISIBLE_SAND_ACTIVE)
5889     {
5890       if (game.light_time_left == 0)
5891         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5892
5893       TEST_DrawLevelField(x, y);
5894
5895       /* re-crumble neighbour fields, if needed */
5896       if (element == EL_INVISIBLE_SAND)
5897         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5898     }
5899   }
5900 }
5901
5902 static void RedrawAllInvisibleElementsForLenses()
5903 {
5904   int x, y;
5905
5906   SCAN_PLAYFIELD(x, y)
5907   {
5908     int element = Feld[x][y];
5909
5910     if (element == EL_EMC_DRIPPER &&
5911         game.lenses_time_left > 0)
5912     {
5913       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5914       TEST_DrawLevelField(x, y);
5915     }
5916     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5917              game.lenses_time_left == 0)
5918     {
5919       Feld[x][y] = EL_EMC_DRIPPER;
5920       TEST_DrawLevelField(x, y);
5921     }
5922     else if (element == EL_INVISIBLE_STEELWALL ||
5923              element == EL_INVISIBLE_WALL ||
5924              element == EL_INVISIBLE_SAND)
5925     {
5926       if (game.lenses_time_left > 0)
5927         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5928
5929       TEST_DrawLevelField(x, y);
5930
5931       /* uncrumble neighbour fields, if needed */
5932       if (element == EL_INVISIBLE_SAND)
5933         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5934     }
5935     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5936              element == EL_INVISIBLE_WALL_ACTIVE ||
5937              element == EL_INVISIBLE_SAND_ACTIVE)
5938     {
5939       if (game.lenses_time_left == 0)
5940         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5941
5942       TEST_DrawLevelField(x, y);
5943
5944       /* re-crumble neighbour fields, if needed */
5945       if (element == EL_INVISIBLE_SAND)
5946         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5947     }
5948   }
5949 }
5950
5951 static void RedrawAllInvisibleElementsForMagnifier()
5952 {
5953   int x, y;
5954
5955   SCAN_PLAYFIELD(x, y)
5956   {
5957     int element = Feld[x][y];
5958
5959     if (element == EL_EMC_FAKE_GRASS &&
5960         game.magnify_time_left > 0)
5961     {
5962       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5963       TEST_DrawLevelField(x, y);
5964     }
5965     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5966              game.magnify_time_left == 0)
5967     {
5968       Feld[x][y] = EL_EMC_FAKE_GRASS;
5969       TEST_DrawLevelField(x, y);
5970     }
5971     else if (IS_GATE_GRAY(element) &&
5972              game.magnify_time_left > 0)
5973     {
5974       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5975                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5976                     IS_EM_GATE_GRAY(element) ?
5977                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5978                     IS_EMC_GATE_GRAY(element) ?
5979                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5980                     IS_DC_GATE_GRAY(element) ?
5981                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5982                     element);
5983       TEST_DrawLevelField(x, y);
5984     }
5985     else if (IS_GATE_GRAY_ACTIVE(element) &&
5986              game.magnify_time_left == 0)
5987     {
5988       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5989                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5990                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5991                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5992                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5993                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5994                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5995                     EL_DC_GATE_WHITE_GRAY :
5996                     element);
5997       TEST_DrawLevelField(x, y);
5998     }
5999   }
6000 }
6001
6002 static void ToggleLightSwitch(int x, int y)
6003 {
6004   int element = Feld[x][y];
6005
6006   game.light_time_left =
6007     (element == EL_LIGHT_SWITCH ?
6008      level.time_light * FRAMES_PER_SECOND : 0);
6009
6010   RedrawAllLightSwitchesAndInvisibleElements();
6011 }
6012
6013 static void ActivateTimegateSwitch(int x, int y)
6014 {
6015   int xx, yy;
6016
6017   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6018
6019   SCAN_PLAYFIELD(xx, yy)
6020   {
6021     int element = Feld[xx][yy];
6022
6023     if (element == EL_TIMEGATE_CLOSED ||
6024         element == EL_TIMEGATE_CLOSING)
6025     {
6026       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6027       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6028     }
6029
6030     /*
6031     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6032     {
6033       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6034       TEST_DrawLevelField(xx, yy);
6035     }
6036     */
6037
6038   }
6039
6040   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6041                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6042 }
6043
6044 void Impact(int x, int y)
6045 {
6046   boolean last_line = (y == lev_fieldy - 1);
6047   boolean object_hit = FALSE;
6048   boolean impact = (last_line || object_hit);
6049   int element = Feld[x][y];
6050   int smashed = EL_STEELWALL;
6051
6052   if (!last_line)       /* check if element below was hit */
6053   {
6054     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6055       return;
6056
6057     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6058                                          MovDir[x][y + 1] != MV_DOWN ||
6059                                          MovPos[x][y + 1] <= TILEY / 2));
6060
6061     /* do not smash moving elements that left the smashed field in time */
6062     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6063         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6064       object_hit = FALSE;
6065
6066 #if USE_QUICKSAND_IMPACT_BUGFIX
6067     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6068     {
6069       RemoveMovingField(x, y + 1);
6070       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6071       Feld[x][y + 2] = EL_ROCK;
6072       TEST_DrawLevelField(x, y + 2);
6073
6074       object_hit = TRUE;
6075     }
6076
6077     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6078     {
6079       RemoveMovingField(x, y + 1);
6080       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6081       Feld[x][y + 2] = EL_ROCK;
6082       TEST_DrawLevelField(x, y + 2);
6083
6084       object_hit = TRUE;
6085     }
6086 #endif
6087
6088     if (object_hit)
6089       smashed = MovingOrBlocked2Element(x, y + 1);
6090
6091     impact = (last_line || object_hit);
6092   }
6093
6094   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6095   {
6096     SplashAcid(x, y + 1);
6097     return;
6098   }
6099
6100   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6101   /* only reset graphic animation if graphic really changes after impact */
6102   if (impact &&
6103       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6104   {
6105     ResetGfxAnimation(x, y);
6106     TEST_DrawLevelField(x, y);
6107   }
6108
6109   if (impact && CAN_EXPLODE_IMPACT(element))
6110   {
6111     Bang(x, y);
6112     return;
6113   }
6114   else if (impact && element == EL_PEARL &&
6115            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6116   {
6117     ResetGfxAnimation(x, y);
6118
6119     Feld[x][y] = EL_PEARL_BREAKING;
6120     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6121     return;
6122   }
6123   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6124   {
6125     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6126
6127     return;
6128   }
6129
6130   if (impact && element == EL_AMOEBA_DROP)
6131   {
6132     if (object_hit && IS_PLAYER(x, y + 1))
6133       KillPlayerUnlessEnemyProtected(x, y + 1);
6134     else if (object_hit && smashed == EL_PENGUIN)
6135       Bang(x, y + 1);
6136     else
6137     {
6138       Feld[x][y] = EL_AMOEBA_GROWING;
6139       Store[x][y] = EL_AMOEBA_WET;
6140
6141       ResetRandomAnimationValue(x, y);
6142     }
6143     return;
6144   }
6145
6146   if (object_hit)               /* check which object was hit */
6147   {
6148     if ((CAN_PASS_MAGIC_WALL(element) && 
6149          (smashed == EL_MAGIC_WALL ||
6150           smashed == EL_BD_MAGIC_WALL)) ||
6151         (CAN_PASS_DC_MAGIC_WALL(element) &&
6152          smashed == EL_DC_MAGIC_WALL))
6153     {
6154       int xx, yy;
6155       int activated_magic_wall =
6156         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6157          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6158          EL_DC_MAGIC_WALL_ACTIVE);
6159
6160       /* activate magic wall / mill */
6161       SCAN_PLAYFIELD(xx, yy)
6162       {
6163         if (Feld[xx][yy] == smashed)
6164           Feld[xx][yy] = activated_magic_wall;
6165       }
6166
6167       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6168       game.magic_wall_active = TRUE;
6169
6170       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6171                             SND_MAGIC_WALL_ACTIVATING :
6172                             smashed == EL_BD_MAGIC_WALL ?
6173                             SND_BD_MAGIC_WALL_ACTIVATING :
6174                             SND_DC_MAGIC_WALL_ACTIVATING));
6175     }
6176
6177     if (IS_PLAYER(x, y + 1))
6178     {
6179       if (CAN_SMASH_PLAYER(element))
6180       {
6181         KillPlayerUnlessEnemyProtected(x, y + 1);
6182         return;
6183       }
6184     }
6185     else if (smashed == EL_PENGUIN)
6186     {
6187       if (CAN_SMASH_PLAYER(element))
6188       {
6189         Bang(x, y + 1);
6190         return;
6191       }
6192     }
6193     else if (element == EL_BD_DIAMOND)
6194     {
6195       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6196       {
6197         Bang(x, y + 1);
6198         return;
6199       }
6200     }
6201     else if (((element == EL_SP_INFOTRON ||
6202                element == EL_SP_ZONK) &&
6203               (smashed == EL_SP_SNIKSNAK ||
6204                smashed == EL_SP_ELECTRON ||
6205                smashed == EL_SP_DISK_ORANGE)) ||
6206              (element == EL_SP_INFOTRON &&
6207               smashed == EL_SP_DISK_YELLOW))
6208     {
6209       Bang(x, y + 1);
6210       return;
6211     }
6212     else if (CAN_SMASH_EVERYTHING(element))
6213     {
6214       if (IS_CLASSIC_ENEMY(smashed) ||
6215           CAN_EXPLODE_SMASHED(smashed))
6216       {
6217         Bang(x, y + 1);
6218         return;
6219       }
6220       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6221       {
6222         if (smashed == EL_LAMP ||
6223             smashed == EL_LAMP_ACTIVE)
6224         {
6225           Bang(x, y + 1);
6226           return;
6227         }
6228         else if (smashed == EL_NUT)
6229         {
6230           Feld[x][y + 1] = EL_NUT_BREAKING;
6231           PlayLevelSound(x, y, SND_NUT_BREAKING);
6232           RaiseScoreElement(EL_NUT);
6233           return;
6234         }
6235         else if (smashed == EL_PEARL)
6236         {
6237           ResetGfxAnimation(x, y);
6238
6239           Feld[x][y + 1] = EL_PEARL_BREAKING;
6240           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6241           return;
6242         }
6243         else if (smashed == EL_DIAMOND)
6244         {
6245           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6246           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6247           return;
6248         }
6249         else if (IS_BELT_SWITCH(smashed))
6250         {
6251           ToggleBeltSwitch(x, y + 1);
6252         }
6253         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6254                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6255                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6256                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6257         {
6258           ToggleSwitchgateSwitch(x, y + 1);
6259         }
6260         else if (smashed == EL_LIGHT_SWITCH ||
6261                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6262         {
6263           ToggleLightSwitch(x, y + 1);
6264         }
6265         else
6266         {
6267           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6268
6269           CheckElementChangeBySide(x, y + 1, smashed, element,
6270                                    CE_SWITCHED, CH_SIDE_TOP);
6271           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6272                                             CH_SIDE_TOP);
6273         }
6274       }
6275       else
6276       {
6277         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6278       }
6279     }
6280   }
6281
6282   /* play sound of magic wall / mill */
6283   if (!last_line &&
6284       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6285        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6286        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6287   {
6288     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6289       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6290     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6291       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6292     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6293       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6294
6295     return;
6296   }
6297
6298   /* play sound of object that hits the ground */
6299   if (last_line || object_hit)
6300     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6301 }
6302
6303 inline static void TurnRoundExt(int x, int y)
6304 {
6305   static struct
6306   {
6307     int dx, dy;
6308   } move_xy[] =
6309   {
6310     {  0,  0 },
6311     { -1,  0 },
6312     { +1,  0 },
6313     {  0,  0 },
6314     {  0, -1 },
6315     {  0,  0 }, { 0, 0 }, { 0, 0 },
6316     {  0, +1 }
6317   };
6318   static struct
6319   {
6320     int left, right, back;
6321   } turn[] =
6322   {
6323     { 0,        0,              0        },
6324     { MV_DOWN,  MV_UP,          MV_RIGHT },
6325     { MV_UP,    MV_DOWN,        MV_LEFT  },
6326     { 0,        0,              0        },
6327     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6328     { 0,        0,              0        },
6329     { 0,        0,              0        },
6330     { 0,        0,              0        },
6331     { MV_RIGHT, MV_LEFT,        MV_UP    }
6332   };
6333
6334   int element = Feld[x][y];
6335   int move_pattern = element_info[element].move_pattern;
6336
6337   int old_move_dir = MovDir[x][y];
6338   int left_dir  = turn[old_move_dir].left;
6339   int right_dir = turn[old_move_dir].right;
6340   int back_dir  = turn[old_move_dir].back;
6341
6342   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6343   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6344   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6345   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6346
6347   int left_x  = x + left_dx,  left_y  = y + left_dy;
6348   int right_x = x + right_dx, right_y = y + right_dy;
6349   int move_x  = x + move_dx,  move_y  = y + move_dy;
6350
6351   int xx, yy;
6352
6353   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6354   {
6355     TestIfBadThingTouchesOtherBadThing(x, y);
6356
6357     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6358       MovDir[x][y] = right_dir;
6359     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6360       MovDir[x][y] = left_dir;
6361
6362     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6363       MovDelay[x][y] = 9;
6364     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6365       MovDelay[x][y] = 1;
6366   }
6367   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6368   {
6369     TestIfBadThingTouchesOtherBadThing(x, y);
6370
6371     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6372       MovDir[x][y] = left_dir;
6373     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6374       MovDir[x][y] = right_dir;
6375
6376     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6377       MovDelay[x][y] = 9;
6378     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6379       MovDelay[x][y] = 1;
6380   }
6381   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6382   {
6383     TestIfBadThingTouchesOtherBadThing(x, y);
6384
6385     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6386       MovDir[x][y] = left_dir;
6387     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6388       MovDir[x][y] = right_dir;
6389
6390     if (MovDir[x][y] != old_move_dir)
6391       MovDelay[x][y] = 9;
6392   }
6393   else if (element == EL_YAMYAM)
6394   {
6395     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6396     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6397
6398     if (can_turn_left && can_turn_right)
6399       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6400     else if (can_turn_left)
6401       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6402     else if (can_turn_right)
6403       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6404     else
6405       MovDir[x][y] = back_dir;
6406
6407     MovDelay[x][y] = 16 + 16 * RND(3);
6408   }
6409   else if (element == EL_DARK_YAMYAM)
6410   {
6411     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6412                                                          left_x, left_y);
6413     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6414                                                          right_x, right_y);
6415
6416     if (can_turn_left && can_turn_right)
6417       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6418     else if (can_turn_left)
6419       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6420     else if (can_turn_right)
6421       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6422     else
6423       MovDir[x][y] = back_dir;
6424
6425     MovDelay[x][y] = 16 + 16 * RND(3);
6426   }
6427   else if (element == EL_PACMAN)
6428   {
6429     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6430     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6431
6432     if (can_turn_left && can_turn_right)
6433       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6434     else if (can_turn_left)
6435       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6436     else if (can_turn_right)
6437       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6438     else
6439       MovDir[x][y] = back_dir;
6440
6441     MovDelay[x][y] = 6 + RND(40);
6442   }
6443   else if (element == EL_PIG)
6444   {
6445     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6446     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6447     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6448     boolean should_turn_left, should_turn_right, should_move_on;
6449     int rnd_value = 24;
6450     int rnd = RND(rnd_value);
6451
6452     should_turn_left = (can_turn_left &&
6453                         (!can_move_on ||
6454                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6455                                                    y + back_dy + left_dy)));
6456     should_turn_right = (can_turn_right &&
6457                          (!can_move_on ||
6458                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6459                                                     y + back_dy + right_dy)));
6460     should_move_on = (can_move_on &&
6461                       (!can_turn_left ||
6462                        !can_turn_right ||
6463                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6464                                                  y + move_dy + left_dy) ||
6465                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6466                                                  y + move_dy + right_dy)));
6467
6468     if (should_turn_left || should_turn_right || should_move_on)
6469     {
6470       if (should_turn_left && should_turn_right && should_move_on)
6471         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6472                         rnd < 2 * rnd_value / 3 ? right_dir :
6473                         old_move_dir);
6474       else if (should_turn_left && should_turn_right)
6475         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6476       else if (should_turn_left && should_move_on)
6477         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6478       else if (should_turn_right && should_move_on)
6479         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6480       else if (should_turn_left)
6481         MovDir[x][y] = left_dir;
6482       else if (should_turn_right)
6483         MovDir[x][y] = right_dir;
6484       else if (should_move_on)
6485         MovDir[x][y] = old_move_dir;
6486     }
6487     else if (can_move_on && rnd > rnd_value / 8)
6488       MovDir[x][y] = old_move_dir;
6489     else if (can_turn_left && can_turn_right)
6490       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6491     else if (can_turn_left && rnd > rnd_value / 8)
6492       MovDir[x][y] = left_dir;
6493     else if (can_turn_right && rnd > rnd_value/8)
6494       MovDir[x][y] = right_dir;
6495     else
6496       MovDir[x][y] = back_dir;
6497
6498     xx = x + move_xy[MovDir[x][y]].dx;
6499     yy = y + move_xy[MovDir[x][y]].dy;
6500
6501     if (!IN_LEV_FIELD(xx, yy) ||
6502         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6503       MovDir[x][y] = old_move_dir;
6504
6505     MovDelay[x][y] = 0;
6506   }
6507   else if (element == EL_DRAGON)
6508   {
6509     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6510     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6511     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6512     int rnd_value = 24;
6513     int rnd = RND(rnd_value);
6514
6515     if (can_move_on && rnd > rnd_value / 8)
6516       MovDir[x][y] = old_move_dir;
6517     else if (can_turn_left && can_turn_right)
6518       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6519     else if (can_turn_left && rnd > rnd_value / 8)
6520       MovDir[x][y] = left_dir;
6521     else if (can_turn_right && rnd > rnd_value / 8)
6522       MovDir[x][y] = right_dir;
6523     else
6524       MovDir[x][y] = back_dir;
6525
6526     xx = x + move_xy[MovDir[x][y]].dx;
6527     yy = y + move_xy[MovDir[x][y]].dy;
6528
6529     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6530       MovDir[x][y] = old_move_dir;
6531
6532     MovDelay[x][y] = 0;
6533   }
6534   else if (element == EL_MOLE)
6535   {
6536     boolean can_move_on =
6537       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6538                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6539                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6540     if (!can_move_on)
6541     {
6542       boolean can_turn_left =
6543         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6544                               IS_AMOEBOID(Feld[left_x][left_y])));
6545
6546       boolean can_turn_right =
6547         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6548                               IS_AMOEBOID(Feld[right_x][right_y])));
6549
6550       if (can_turn_left && can_turn_right)
6551         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6552       else if (can_turn_left)
6553         MovDir[x][y] = left_dir;
6554       else
6555         MovDir[x][y] = right_dir;
6556     }
6557
6558     if (MovDir[x][y] != old_move_dir)
6559       MovDelay[x][y] = 9;
6560   }
6561   else if (element == EL_BALLOON)
6562   {
6563     MovDir[x][y] = game.wind_direction;
6564     MovDelay[x][y] = 0;
6565   }
6566   else if (element == EL_SPRING)
6567   {
6568     if (MovDir[x][y] & MV_HORIZONTAL)
6569     {
6570       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6571           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6572       {
6573         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6574         ResetGfxAnimation(move_x, move_y);
6575         TEST_DrawLevelField(move_x, move_y);
6576
6577         MovDir[x][y] = back_dir;
6578       }
6579       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6580                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6581         MovDir[x][y] = MV_NONE;
6582     }
6583
6584     MovDelay[x][y] = 0;
6585   }
6586   else if (element == EL_ROBOT ||
6587            element == EL_SATELLITE ||
6588            element == EL_PENGUIN ||
6589            element == EL_EMC_ANDROID)
6590   {
6591     int attr_x = -1, attr_y = -1;
6592
6593     if (AllPlayersGone)
6594     {
6595       attr_x = ExitX;
6596       attr_y = ExitY;
6597     }
6598     else
6599     {
6600       int i;
6601
6602       for (i = 0; i < MAX_PLAYERS; i++)
6603       {
6604         struct PlayerInfo *player = &stored_player[i];
6605         int jx = player->jx, jy = player->jy;
6606
6607         if (!player->active)
6608           continue;
6609
6610         if (attr_x == -1 ||
6611             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6612         {
6613           attr_x = jx;
6614           attr_y = jy;
6615         }
6616       }
6617     }
6618
6619     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6620         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6621          game.engine_version < VERSION_IDENT(3,1,0,0)))
6622     {
6623       attr_x = ZX;
6624       attr_y = ZY;
6625     }
6626
6627     if (element == EL_PENGUIN)
6628     {
6629       int i;
6630       static int xy[4][2] =
6631       {
6632         { 0, -1 },
6633         { -1, 0 },
6634         { +1, 0 },
6635         { 0, +1 }
6636       };
6637
6638       for (i = 0; i < NUM_DIRECTIONS; i++)
6639       {
6640         int ex = x + xy[i][0];
6641         int ey = y + xy[i][1];
6642
6643         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6644                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6645                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6646                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6647         {
6648           attr_x = ex;
6649           attr_y = ey;
6650           break;
6651         }
6652       }
6653     }
6654
6655     MovDir[x][y] = MV_NONE;
6656     if (attr_x < x)
6657       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6658     else if (attr_x > x)
6659       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6660     if (attr_y < y)
6661       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6662     else if (attr_y > y)
6663       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6664
6665     if (element == EL_ROBOT)
6666     {
6667       int newx, newy;
6668
6669       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6670         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6671       Moving2Blocked(x, y, &newx, &newy);
6672
6673       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6674         MovDelay[x][y] = 8 + 8 * !RND(3);
6675       else
6676         MovDelay[x][y] = 16;
6677     }
6678     else if (element == EL_PENGUIN)
6679     {
6680       int newx, newy;
6681
6682       MovDelay[x][y] = 1;
6683
6684       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6685       {
6686         boolean first_horiz = RND(2);
6687         int new_move_dir = MovDir[x][y];
6688
6689         MovDir[x][y] =
6690           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6691         Moving2Blocked(x, y, &newx, &newy);
6692
6693         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6694           return;
6695
6696         MovDir[x][y] =
6697           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6698         Moving2Blocked(x, y, &newx, &newy);
6699
6700         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6701           return;
6702
6703         MovDir[x][y] = old_move_dir;
6704         return;
6705       }
6706     }
6707     else if (element == EL_SATELLITE)
6708     {
6709       int newx, newy;
6710
6711       MovDelay[x][y] = 1;
6712
6713       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6714       {
6715         boolean first_horiz = RND(2);
6716         int new_move_dir = MovDir[x][y];
6717
6718         MovDir[x][y] =
6719           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6720         Moving2Blocked(x, y, &newx, &newy);
6721
6722         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6723           return;
6724
6725         MovDir[x][y] =
6726           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6727         Moving2Blocked(x, y, &newx, &newy);
6728
6729         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6730           return;
6731
6732         MovDir[x][y] = old_move_dir;
6733         return;
6734       }
6735     }
6736     else if (element == EL_EMC_ANDROID)
6737     {
6738       static int check_pos[16] =
6739       {
6740         -1,             /*  0 => (invalid)          */
6741         7,              /*  1 => MV_LEFT            */
6742         3,              /*  2 => MV_RIGHT           */
6743         -1,             /*  3 => (invalid)          */
6744         1,              /*  4 =>            MV_UP   */
6745         0,              /*  5 => MV_LEFT  | MV_UP   */
6746         2,              /*  6 => MV_RIGHT | MV_UP   */
6747         -1,             /*  7 => (invalid)          */
6748         5,              /*  8 =>            MV_DOWN */
6749         6,              /*  9 => MV_LEFT  | MV_DOWN */
6750         4,              /* 10 => MV_RIGHT | MV_DOWN */
6751         -1,             /* 11 => (invalid)          */
6752         -1,             /* 12 => (invalid)          */
6753         -1,             /* 13 => (invalid)          */
6754         -1,             /* 14 => (invalid)          */
6755         -1,             /* 15 => (invalid)          */
6756       };
6757       static struct
6758       {
6759         int dx, dy;
6760         int dir;
6761       } check_xy[8] =
6762       {
6763         { -1, -1,       MV_LEFT  | MV_UP   },
6764         {  0, -1,                  MV_UP   },
6765         { +1, -1,       MV_RIGHT | MV_UP   },
6766         { +1,  0,       MV_RIGHT           },
6767         { +1, +1,       MV_RIGHT | MV_DOWN },
6768         {  0, +1,                  MV_DOWN },
6769         { -1, +1,       MV_LEFT  | MV_DOWN },
6770         { -1,  0,       MV_LEFT            },
6771       };
6772       int start_pos, check_order;
6773       boolean can_clone = FALSE;
6774       int i;
6775
6776       /* check if there is any free field around current position */
6777       for (i = 0; i < 8; i++)
6778       {
6779         int newx = x + check_xy[i].dx;
6780         int newy = y + check_xy[i].dy;
6781
6782         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6783         {
6784           can_clone = TRUE;
6785
6786           break;
6787         }
6788       }
6789
6790       if (can_clone)            /* randomly find an element to clone */
6791       {
6792         can_clone = FALSE;
6793
6794         start_pos = check_pos[RND(8)];
6795         check_order = (RND(2) ? -1 : +1);
6796
6797         for (i = 0; i < 8; i++)
6798         {
6799           int pos_raw = start_pos + i * check_order;
6800           int pos = (pos_raw + 8) % 8;
6801           int newx = x + check_xy[pos].dx;
6802           int newy = y + check_xy[pos].dy;
6803
6804           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6805           {
6806             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6807             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6808
6809             Store[x][y] = Feld[newx][newy];
6810
6811             can_clone = TRUE;
6812
6813             break;
6814           }
6815         }
6816       }
6817
6818       if (can_clone)            /* randomly find a direction to move */
6819       {
6820         can_clone = FALSE;
6821
6822         start_pos = check_pos[RND(8)];
6823         check_order = (RND(2) ? -1 : +1);
6824
6825         for (i = 0; i < 8; i++)
6826         {
6827           int pos_raw = start_pos + i * check_order;
6828           int pos = (pos_raw + 8) % 8;
6829           int newx = x + check_xy[pos].dx;
6830           int newy = y + check_xy[pos].dy;
6831           int new_move_dir = check_xy[pos].dir;
6832
6833           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6834           {
6835             MovDir[x][y] = new_move_dir;
6836             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6837
6838             can_clone = TRUE;
6839
6840             break;
6841           }
6842         }
6843       }
6844
6845       if (can_clone)            /* cloning and moving successful */
6846         return;
6847
6848       /* cannot clone -- try to move towards player */
6849
6850       start_pos = check_pos[MovDir[x][y] & 0x0f];
6851       check_order = (RND(2) ? -1 : +1);
6852
6853       for (i = 0; i < 3; i++)
6854       {
6855         /* first check start_pos, then previous/next or (next/previous) pos */
6856         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6857         int pos = (pos_raw + 8) % 8;
6858         int newx = x + check_xy[pos].dx;
6859         int newy = y + check_xy[pos].dy;
6860         int new_move_dir = check_xy[pos].dir;
6861
6862         if (IS_PLAYER(newx, newy))
6863           break;
6864
6865         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6866         {
6867           MovDir[x][y] = new_move_dir;
6868           MovDelay[x][y] = level.android_move_time * 8 + 1;
6869
6870           break;
6871         }
6872       }
6873     }
6874   }
6875   else if (move_pattern == MV_TURNING_LEFT ||
6876            move_pattern == MV_TURNING_RIGHT ||
6877            move_pattern == MV_TURNING_LEFT_RIGHT ||
6878            move_pattern == MV_TURNING_RIGHT_LEFT ||
6879            move_pattern == MV_TURNING_RANDOM ||
6880            move_pattern == MV_ALL_DIRECTIONS)
6881   {
6882     boolean can_turn_left =
6883       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6884     boolean can_turn_right =
6885       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6886
6887     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6888       return;
6889
6890     if (move_pattern == MV_TURNING_LEFT)
6891       MovDir[x][y] = left_dir;
6892     else if (move_pattern == MV_TURNING_RIGHT)
6893       MovDir[x][y] = right_dir;
6894     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6895       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6896     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6897       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6898     else if (move_pattern == MV_TURNING_RANDOM)
6899       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6900                       can_turn_right && !can_turn_left ? right_dir :
6901                       RND(2) ? left_dir : right_dir);
6902     else if (can_turn_left && can_turn_right)
6903       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6904     else if (can_turn_left)
6905       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6906     else if (can_turn_right)
6907       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6908     else
6909       MovDir[x][y] = back_dir;
6910
6911     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6912   }
6913   else if (move_pattern == MV_HORIZONTAL ||
6914            move_pattern == MV_VERTICAL)
6915   {
6916     if (move_pattern & old_move_dir)
6917       MovDir[x][y] = back_dir;
6918     else if (move_pattern == MV_HORIZONTAL)
6919       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6920     else if (move_pattern == MV_VERTICAL)
6921       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6922
6923     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6924   }
6925   else if (move_pattern & MV_ANY_DIRECTION)
6926   {
6927     MovDir[x][y] = move_pattern;
6928     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6929   }
6930   else if (move_pattern & MV_WIND_DIRECTION)
6931   {
6932     MovDir[x][y] = game.wind_direction;
6933     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6934   }
6935   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6936   {
6937     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6938       MovDir[x][y] = left_dir;
6939     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6940       MovDir[x][y] = right_dir;
6941
6942     if (MovDir[x][y] != old_move_dir)
6943       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6944   }
6945   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6946   {
6947     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6948       MovDir[x][y] = right_dir;
6949     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6950       MovDir[x][y] = left_dir;
6951
6952     if (MovDir[x][y] != old_move_dir)
6953       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6954   }
6955   else if (move_pattern == MV_TOWARDS_PLAYER ||
6956            move_pattern == MV_AWAY_FROM_PLAYER)
6957   {
6958     int attr_x = -1, attr_y = -1;
6959     int newx, newy;
6960     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6961
6962     if (AllPlayersGone)
6963     {
6964       attr_x = ExitX;
6965       attr_y = ExitY;
6966     }
6967     else
6968     {
6969       int i;
6970
6971       for (i = 0; i < MAX_PLAYERS; i++)
6972       {
6973         struct PlayerInfo *player = &stored_player[i];
6974         int jx = player->jx, jy = player->jy;
6975
6976         if (!player->active)
6977           continue;
6978
6979         if (attr_x == -1 ||
6980             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6981         {
6982           attr_x = jx;
6983           attr_y = jy;
6984         }
6985       }
6986     }
6987
6988     MovDir[x][y] = MV_NONE;
6989     if (attr_x < x)
6990       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6991     else if (attr_x > x)
6992       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6993     if (attr_y < y)
6994       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6995     else if (attr_y > y)
6996       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6997
6998     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6999
7000     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7001     {
7002       boolean first_horiz = RND(2);
7003       int new_move_dir = MovDir[x][y];
7004
7005       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7006       {
7007         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7008         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7009
7010         return;
7011       }
7012
7013       MovDir[x][y] =
7014         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7015       Moving2Blocked(x, y, &newx, &newy);
7016
7017       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7018         return;
7019
7020       MovDir[x][y] =
7021         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7022       Moving2Blocked(x, y, &newx, &newy);
7023
7024       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7025         return;
7026
7027       MovDir[x][y] = old_move_dir;
7028     }
7029   }
7030   else if (move_pattern == MV_WHEN_PUSHED ||
7031            move_pattern == MV_WHEN_DROPPED)
7032   {
7033     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7034       MovDir[x][y] = MV_NONE;
7035
7036     MovDelay[x][y] = 0;
7037   }
7038   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7039   {
7040     static int test_xy[7][2] =
7041     {
7042       { 0, -1 },
7043       { -1, 0 },
7044       { +1, 0 },
7045       { 0, +1 },
7046       { 0, -1 },
7047       { -1, 0 },
7048       { +1, 0 },
7049     };
7050     static int test_dir[7] =
7051     {
7052       MV_UP,
7053       MV_LEFT,
7054       MV_RIGHT,
7055       MV_DOWN,
7056       MV_UP,
7057       MV_LEFT,
7058       MV_RIGHT,
7059     };
7060     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7061     int move_preference = -1000000;     /* start with very low preference */
7062     int new_move_dir = MV_NONE;
7063     int start_test = RND(4);
7064     int i;
7065
7066     for (i = 0; i < NUM_DIRECTIONS; i++)
7067     {
7068       int move_dir = test_dir[start_test + i];
7069       int move_dir_preference;
7070
7071       xx = x + test_xy[start_test + i][0];
7072       yy = y + test_xy[start_test + i][1];
7073
7074       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7075           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7076       {
7077         new_move_dir = move_dir;
7078
7079         break;
7080       }
7081
7082       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7083         continue;
7084
7085       move_dir_preference = -1 * RunnerVisit[xx][yy];
7086       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7087         move_dir_preference = PlayerVisit[xx][yy];
7088
7089       if (move_dir_preference > move_preference)
7090       {
7091         /* prefer field that has not been visited for the longest time */
7092         move_preference = move_dir_preference;
7093         new_move_dir = move_dir;
7094       }
7095       else if (move_dir_preference == move_preference &&
7096                move_dir == old_move_dir)
7097       {
7098         /* prefer last direction when all directions are preferred equally */
7099         move_preference = move_dir_preference;
7100         new_move_dir = move_dir;
7101       }
7102     }
7103
7104     MovDir[x][y] = new_move_dir;
7105     if (old_move_dir != new_move_dir)
7106       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7107   }
7108 }
7109
7110 static void TurnRound(int x, int y)
7111 {
7112   int direction = MovDir[x][y];
7113
7114   TurnRoundExt(x, y);
7115
7116   GfxDir[x][y] = MovDir[x][y];
7117
7118   if (direction != MovDir[x][y])
7119     GfxFrame[x][y] = 0;
7120
7121   if (MovDelay[x][y])
7122     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7123
7124   ResetGfxFrame(x, y, FALSE);
7125 }
7126
7127 static boolean JustBeingPushed(int x, int y)
7128 {
7129   int i;
7130
7131   for (i = 0; i < MAX_PLAYERS; i++)
7132   {
7133     struct PlayerInfo *player = &stored_player[i];
7134
7135     if (player->active && player->is_pushing && player->MovPos)
7136     {
7137       int next_jx = player->jx + (player->jx - player->last_jx);
7138       int next_jy = player->jy + (player->jy - player->last_jy);
7139
7140       if (x == next_jx && y == next_jy)
7141         return TRUE;
7142     }
7143   }
7144
7145   return FALSE;
7146 }
7147
7148 void StartMoving(int x, int y)
7149 {
7150   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7151   int element = Feld[x][y];
7152
7153   if (Stop[x][y])
7154     return;
7155
7156   if (MovDelay[x][y] == 0)
7157     GfxAction[x][y] = ACTION_DEFAULT;
7158
7159   if (CAN_FALL(element) && y < lev_fieldy - 1)
7160   {
7161     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7162         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7163       if (JustBeingPushed(x, y))
7164         return;
7165
7166     if (element == EL_QUICKSAND_FULL)
7167     {
7168       if (IS_FREE(x, y + 1))
7169       {
7170         InitMovingField(x, y, MV_DOWN);
7171         started_moving = TRUE;
7172
7173         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7174 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7175         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7176           Store[x][y] = EL_ROCK;
7177 #else
7178         Store[x][y] = EL_ROCK;
7179 #endif
7180
7181         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7182       }
7183       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7184       {
7185         if (!MovDelay[x][y])
7186         {
7187           MovDelay[x][y] = TILEY + 1;
7188
7189           ResetGfxAnimation(x, y);
7190           ResetGfxAnimation(x, y + 1);
7191         }
7192
7193         if (MovDelay[x][y])
7194         {
7195           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7196           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7197
7198           MovDelay[x][y]--;
7199           if (MovDelay[x][y])
7200             return;
7201         }
7202
7203         Feld[x][y] = EL_QUICKSAND_EMPTY;
7204         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7205         Store[x][y + 1] = Store[x][y];
7206         Store[x][y] = 0;
7207
7208         PlayLevelSoundAction(x, y, ACTION_FILLING);
7209       }
7210       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7211       {
7212         if (!MovDelay[x][y])
7213         {
7214           MovDelay[x][y] = TILEY + 1;
7215
7216           ResetGfxAnimation(x, y);
7217           ResetGfxAnimation(x, y + 1);
7218         }
7219
7220         if (MovDelay[x][y])
7221         {
7222           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7223           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7224
7225           MovDelay[x][y]--;
7226           if (MovDelay[x][y])
7227             return;
7228         }
7229
7230         Feld[x][y] = EL_QUICKSAND_EMPTY;
7231         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7232         Store[x][y + 1] = Store[x][y];
7233         Store[x][y] = 0;
7234
7235         PlayLevelSoundAction(x, y, ACTION_FILLING);
7236       }
7237     }
7238     else if (element == EL_QUICKSAND_FAST_FULL)
7239     {
7240       if (IS_FREE(x, y + 1))
7241       {
7242         InitMovingField(x, y, MV_DOWN);
7243         started_moving = TRUE;
7244
7245         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7246 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7247         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7248           Store[x][y] = EL_ROCK;
7249 #else
7250         Store[x][y] = EL_ROCK;
7251 #endif
7252
7253         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7254       }
7255       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7256       {
7257         if (!MovDelay[x][y])
7258         {
7259           MovDelay[x][y] = TILEY + 1;
7260
7261           ResetGfxAnimation(x, y);
7262           ResetGfxAnimation(x, y + 1);
7263         }
7264
7265         if (MovDelay[x][y])
7266         {
7267           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7268           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7269
7270           MovDelay[x][y]--;
7271           if (MovDelay[x][y])
7272             return;
7273         }
7274
7275         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7276         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7277         Store[x][y + 1] = Store[x][y];
7278         Store[x][y] = 0;
7279
7280         PlayLevelSoundAction(x, y, ACTION_FILLING);
7281       }
7282       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7283       {
7284         if (!MovDelay[x][y])
7285         {
7286           MovDelay[x][y] = TILEY + 1;
7287
7288           ResetGfxAnimation(x, y);
7289           ResetGfxAnimation(x, y + 1);
7290         }
7291
7292         if (MovDelay[x][y])
7293         {
7294           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7295           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7296
7297           MovDelay[x][y]--;
7298           if (MovDelay[x][y])
7299             return;
7300         }
7301
7302         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7303         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7304         Store[x][y + 1] = Store[x][y];
7305         Store[x][y] = 0;
7306
7307         PlayLevelSoundAction(x, y, ACTION_FILLING);
7308       }
7309     }
7310     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7311              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7312     {
7313       InitMovingField(x, y, MV_DOWN);
7314       started_moving = TRUE;
7315
7316       Feld[x][y] = EL_QUICKSAND_FILLING;
7317       Store[x][y] = element;
7318
7319       PlayLevelSoundAction(x, y, ACTION_FILLING);
7320     }
7321     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7322              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7323     {
7324       InitMovingField(x, y, MV_DOWN);
7325       started_moving = TRUE;
7326
7327       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7328       Store[x][y] = element;
7329
7330       PlayLevelSoundAction(x, y, ACTION_FILLING);
7331     }
7332     else if (element == EL_MAGIC_WALL_FULL)
7333     {
7334       if (IS_FREE(x, y + 1))
7335       {
7336         InitMovingField(x, y, MV_DOWN);
7337         started_moving = TRUE;
7338
7339         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7340         Store[x][y] = EL_CHANGED(Store[x][y]);
7341       }
7342       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7343       {
7344         if (!MovDelay[x][y])
7345           MovDelay[x][y] = TILEY / 4 + 1;
7346
7347         if (MovDelay[x][y])
7348         {
7349           MovDelay[x][y]--;
7350           if (MovDelay[x][y])
7351             return;
7352         }
7353
7354         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7355         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7356         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7357         Store[x][y] = 0;
7358       }
7359     }
7360     else if (element == EL_BD_MAGIC_WALL_FULL)
7361     {
7362       if (IS_FREE(x, y + 1))
7363       {
7364         InitMovingField(x, y, MV_DOWN);
7365         started_moving = TRUE;
7366
7367         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7368         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7369       }
7370       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7371       {
7372         if (!MovDelay[x][y])
7373           MovDelay[x][y] = TILEY / 4 + 1;
7374
7375         if (MovDelay[x][y])
7376         {
7377           MovDelay[x][y]--;
7378           if (MovDelay[x][y])
7379             return;
7380         }
7381
7382         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7383         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7384         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7385         Store[x][y] = 0;
7386       }
7387     }
7388     else if (element == EL_DC_MAGIC_WALL_FULL)
7389     {
7390       if (IS_FREE(x, y + 1))
7391       {
7392         InitMovingField(x, y, MV_DOWN);
7393         started_moving = TRUE;
7394
7395         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7396         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7397       }
7398       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7399       {
7400         if (!MovDelay[x][y])
7401           MovDelay[x][y] = TILEY / 4 + 1;
7402
7403         if (MovDelay[x][y])
7404         {
7405           MovDelay[x][y]--;
7406           if (MovDelay[x][y])
7407             return;
7408         }
7409
7410         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7411         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7412         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7413         Store[x][y] = 0;
7414       }
7415     }
7416     else if ((CAN_PASS_MAGIC_WALL(element) &&
7417               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7418                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7419              (CAN_PASS_DC_MAGIC_WALL(element) &&
7420               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7421
7422     {
7423       InitMovingField(x, y, MV_DOWN);
7424       started_moving = TRUE;
7425
7426       Feld[x][y] =
7427         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7428          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7429          EL_DC_MAGIC_WALL_FILLING);
7430       Store[x][y] = element;
7431     }
7432     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7433     {
7434       SplashAcid(x, y + 1);
7435
7436       InitMovingField(x, y, MV_DOWN);
7437       started_moving = TRUE;
7438
7439       Store[x][y] = EL_ACID;
7440     }
7441     else if (
7442              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7443               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7444              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7445               CAN_FALL(element) && WasJustFalling[x][y] &&
7446               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7447
7448              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7449               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7450               (Feld[x][y + 1] == EL_BLOCKED)))
7451     {
7452       /* this is needed for a special case not covered by calling "Impact()"
7453          from "ContinueMoving()": if an element moves to a tile directly below
7454          another element which was just falling on that tile (which was empty
7455          in the previous frame), the falling element above would just stop
7456          instead of smashing the element below (in previous version, the above
7457          element was just checked for "moving" instead of "falling", resulting
7458          in incorrect smashes caused by horizontal movement of the above
7459          element; also, the case of the player being the element to smash was
7460          simply not covered here... :-/ ) */
7461
7462       CheckCollision[x][y] = 0;
7463       CheckImpact[x][y] = 0;
7464
7465       Impact(x, y);
7466     }
7467     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7468     {
7469       if (MovDir[x][y] == MV_NONE)
7470       {
7471         InitMovingField(x, y, MV_DOWN);
7472         started_moving = TRUE;
7473       }
7474     }
7475     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7476     {
7477       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7478         MovDir[x][y] = MV_DOWN;
7479
7480       InitMovingField(x, y, MV_DOWN);
7481       started_moving = TRUE;
7482     }
7483     else if (element == EL_AMOEBA_DROP)
7484     {
7485       Feld[x][y] = EL_AMOEBA_GROWING;
7486       Store[x][y] = EL_AMOEBA_WET;
7487     }
7488     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7489               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7490              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7491              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7492     {
7493       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7494                                 (IS_FREE(x - 1, y + 1) ||
7495                                  Feld[x - 1][y + 1] == EL_ACID));
7496       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7497                                 (IS_FREE(x + 1, y + 1) ||
7498                                  Feld[x + 1][y + 1] == EL_ACID));
7499       boolean can_fall_any  = (can_fall_left || can_fall_right);
7500       boolean can_fall_both = (can_fall_left && can_fall_right);
7501       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7502
7503       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7504       {
7505         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7506           can_fall_right = FALSE;
7507         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7508           can_fall_left = FALSE;
7509         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7510           can_fall_right = FALSE;
7511         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7512           can_fall_left = FALSE;
7513
7514         can_fall_any  = (can_fall_left || can_fall_right);
7515         can_fall_both = FALSE;
7516       }
7517
7518       if (can_fall_both)
7519       {
7520         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7521           can_fall_right = FALSE;       /* slip down on left side */
7522         else
7523           can_fall_left = !(can_fall_right = RND(2));
7524
7525         can_fall_both = FALSE;
7526       }
7527
7528       if (can_fall_any)
7529       {
7530         /* if not determined otherwise, prefer left side for slipping down */
7531         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7532         started_moving = TRUE;
7533       }
7534     }
7535     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7536     {
7537       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7538       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7539       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7540       int belt_dir = game.belt_dir[belt_nr];
7541
7542       if ((belt_dir == MV_LEFT  && left_is_free) ||
7543           (belt_dir == MV_RIGHT && right_is_free))
7544       {
7545         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7546
7547         InitMovingField(x, y, belt_dir);
7548         started_moving = TRUE;
7549
7550         Pushed[x][y] = TRUE;
7551         Pushed[nextx][y] = TRUE;
7552
7553         GfxAction[x][y] = ACTION_DEFAULT;
7554       }
7555       else
7556       {
7557         MovDir[x][y] = 0;       /* if element was moving, stop it */
7558       }
7559     }
7560   }
7561
7562   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7563   if (CAN_MOVE(element) && !started_moving)
7564   {
7565     int move_pattern = element_info[element].move_pattern;
7566     int newx, newy;
7567
7568     Moving2Blocked(x, y, &newx, &newy);
7569
7570     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7571       return;
7572
7573     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7574         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7575     {
7576       WasJustMoving[x][y] = 0;
7577       CheckCollision[x][y] = 0;
7578
7579       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7580
7581       if (Feld[x][y] != element)        /* element has changed */
7582         return;
7583     }
7584
7585     if (!MovDelay[x][y])        /* start new movement phase */
7586     {
7587       /* all objects that can change their move direction after each step
7588          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7589
7590       if (element != EL_YAMYAM &&
7591           element != EL_DARK_YAMYAM &&
7592           element != EL_PACMAN &&
7593           !(move_pattern & MV_ANY_DIRECTION) &&
7594           move_pattern != MV_TURNING_LEFT &&
7595           move_pattern != MV_TURNING_RIGHT &&
7596           move_pattern != MV_TURNING_LEFT_RIGHT &&
7597           move_pattern != MV_TURNING_RIGHT_LEFT &&
7598           move_pattern != MV_TURNING_RANDOM)
7599       {
7600         TurnRound(x, y);
7601
7602         if (MovDelay[x][y] && (element == EL_BUG ||
7603                                element == EL_SPACESHIP ||
7604                                element == EL_SP_SNIKSNAK ||
7605                                element == EL_SP_ELECTRON ||
7606                                element == EL_MOLE))
7607           TEST_DrawLevelField(x, y);
7608       }
7609     }
7610
7611     if (MovDelay[x][y])         /* wait some time before next movement */
7612     {
7613       MovDelay[x][y]--;
7614
7615       if (element == EL_ROBOT ||
7616           element == EL_YAMYAM ||
7617           element == EL_DARK_YAMYAM)
7618       {
7619         DrawLevelElementAnimationIfNeeded(x, y, element);
7620         PlayLevelSoundAction(x, y, ACTION_WAITING);
7621       }
7622       else if (element == EL_SP_ELECTRON)
7623         DrawLevelElementAnimationIfNeeded(x, y, element);
7624       else if (element == EL_DRAGON)
7625       {
7626         int i;
7627         int dir = MovDir[x][y];
7628         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7629         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7630         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7631                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7632                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7633                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7634         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7635
7636         GfxAction[x][y] = ACTION_ATTACKING;
7637
7638         if (IS_PLAYER(x, y))
7639           DrawPlayerField(x, y);
7640         else
7641           TEST_DrawLevelField(x, y);
7642
7643         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7644
7645         for (i = 1; i <= 3; i++)
7646         {
7647           int xx = x + i * dx;
7648           int yy = y + i * dy;
7649           int sx = SCREENX(xx);
7650           int sy = SCREENY(yy);
7651           int flame_graphic = graphic + (i - 1);
7652
7653           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7654             break;
7655
7656           if (MovDelay[x][y])
7657           {
7658             int flamed = MovingOrBlocked2Element(xx, yy);
7659
7660             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7661               Bang(xx, yy);
7662             else
7663               RemoveMovingField(xx, yy);
7664
7665             ChangeDelay[xx][yy] = 0;
7666
7667             Feld[xx][yy] = EL_FLAMES;
7668
7669             if (IN_SCR_FIELD(sx, sy))
7670             {
7671               TEST_DrawLevelFieldCrumbled(xx, yy);
7672               DrawGraphic(sx, sy, flame_graphic, frame);
7673             }
7674           }
7675           else
7676           {
7677             if (Feld[xx][yy] == EL_FLAMES)
7678               Feld[xx][yy] = EL_EMPTY;
7679             TEST_DrawLevelField(xx, yy);
7680           }
7681         }
7682       }
7683
7684       if (MovDelay[x][y])       /* element still has to wait some time */
7685       {
7686         PlayLevelSoundAction(x, y, ACTION_WAITING);
7687
7688         return;
7689       }
7690     }
7691
7692     /* now make next step */
7693
7694     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7695
7696     if (DONT_COLLIDE_WITH(element) &&
7697         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7698         !PLAYER_ENEMY_PROTECTED(newx, newy))
7699     {
7700       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7701
7702       return;
7703     }
7704
7705     else if (CAN_MOVE_INTO_ACID(element) &&
7706              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7707              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7708              (MovDir[x][y] == MV_DOWN ||
7709               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7710     {
7711       SplashAcid(newx, newy);
7712       Store[x][y] = EL_ACID;
7713     }
7714     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7715     {
7716       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7717           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7718           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7719           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7720       {
7721         RemoveField(x, y);
7722         TEST_DrawLevelField(x, y);
7723
7724         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7725         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7726           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7727
7728         local_player->friends_still_needed--;
7729         if (!local_player->friends_still_needed &&
7730             !local_player->GameOver && AllPlayersGone)
7731           PlayerWins(local_player);
7732
7733         return;
7734       }
7735       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7736       {
7737         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7738           TEST_DrawLevelField(newx, newy);
7739         else
7740           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7741       }
7742       else if (!IS_FREE(newx, newy))
7743       {
7744         GfxAction[x][y] = ACTION_WAITING;
7745
7746         if (IS_PLAYER(x, y))
7747           DrawPlayerField(x, y);
7748         else
7749           TEST_DrawLevelField(x, y);
7750
7751         return;
7752       }
7753     }
7754     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7755     {
7756       if (IS_FOOD_PIG(Feld[newx][newy]))
7757       {
7758         if (IS_MOVING(newx, newy))
7759           RemoveMovingField(newx, newy);
7760         else
7761         {
7762           Feld[newx][newy] = EL_EMPTY;
7763           TEST_DrawLevelField(newx, newy);
7764         }
7765
7766         PlayLevelSound(x, y, SND_PIG_DIGGING);
7767       }
7768       else if (!IS_FREE(newx, newy))
7769       {
7770         if (IS_PLAYER(x, y))
7771           DrawPlayerField(x, y);
7772         else
7773           TEST_DrawLevelField(x, y);
7774
7775         return;
7776       }
7777     }
7778     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7779     {
7780       if (Store[x][y] != EL_EMPTY)
7781       {
7782         boolean can_clone = FALSE;
7783         int xx, yy;
7784
7785         /* check if element to clone is still there */
7786         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7787         {
7788           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7789           {
7790             can_clone = TRUE;
7791
7792             break;
7793           }
7794         }
7795
7796         /* cannot clone or target field not free anymore -- do not clone */
7797         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7798           Store[x][y] = EL_EMPTY;
7799       }
7800
7801       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7802       {
7803         if (IS_MV_DIAGONAL(MovDir[x][y]))
7804         {
7805           int diagonal_move_dir = MovDir[x][y];
7806           int stored = Store[x][y];
7807           int change_delay = 8;
7808           int graphic;
7809
7810           /* android is moving diagonally */
7811
7812           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7813
7814           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7815           GfxElement[x][y] = EL_EMC_ANDROID;
7816           GfxAction[x][y] = ACTION_SHRINKING;
7817           GfxDir[x][y] = diagonal_move_dir;
7818           ChangeDelay[x][y] = change_delay;
7819
7820           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7821                                    GfxDir[x][y]);
7822
7823           DrawLevelGraphicAnimation(x, y, graphic);
7824           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7825
7826           if (Feld[newx][newy] == EL_ACID)
7827           {
7828             SplashAcid(newx, newy);
7829
7830             return;
7831           }
7832
7833           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7834
7835           Store[newx][newy] = EL_EMC_ANDROID;
7836           GfxElement[newx][newy] = EL_EMC_ANDROID;
7837           GfxAction[newx][newy] = ACTION_GROWING;
7838           GfxDir[newx][newy] = diagonal_move_dir;
7839           ChangeDelay[newx][newy] = change_delay;
7840
7841           graphic = el_act_dir2img(GfxElement[newx][newy],
7842                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7843
7844           DrawLevelGraphicAnimation(newx, newy, graphic);
7845           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7846
7847           return;
7848         }
7849         else
7850         {
7851           Feld[newx][newy] = EL_EMPTY;
7852           TEST_DrawLevelField(newx, newy);
7853
7854           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7855         }
7856       }
7857       else if (!IS_FREE(newx, newy))
7858       {
7859         return;
7860       }
7861     }
7862     else if (IS_CUSTOM_ELEMENT(element) &&
7863              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7864     {
7865       if (!DigFieldByCE(newx, newy, element))
7866         return;
7867
7868       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7869       {
7870         RunnerVisit[x][y] = FrameCounter;
7871         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7872       }
7873     }
7874     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7875     {
7876       if (!IS_FREE(newx, newy))
7877       {
7878         if (IS_PLAYER(x, y))
7879           DrawPlayerField(x, y);
7880         else
7881           TEST_DrawLevelField(x, y);
7882
7883         return;
7884       }
7885       else
7886       {
7887         boolean wanna_flame = !RND(10);
7888         int dx = newx - x, dy = newy - y;
7889         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7890         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7891         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7892                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7893         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7894                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7895
7896         if ((wanna_flame ||
7897              IS_CLASSIC_ENEMY(element1) ||
7898              IS_CLASSIC_ENEMY(element2)) &&
7899             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7900             element1 != EL_FLAMES && element2 != EL_FLAMES)
7901         {
7902           ResetGfxAnimation(x, y);
7903           GfxAction[x][y] = ACTION_ATTACKING;
7904
7905           if (IS_PLAYER(x, y))
7906             DrawPlayerField(x, y);
7907           else
7908             TEST_DrawLevelField(x, y);
7909
7910           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7911
7912           MovDelay[x][y] = 50;
7913
7914           Feld[newx][newy] = EL_FLAMES;
7915           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7916             Feld[newx1][newy1] = EL_FLAMES;
7917           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7918             Feld[newx2][newy2] = EL_FLAMES;
7919
7920           return;
7921         }
7922       }
7923     }
7924     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7925              Feld[newx][newy] == EL_DIAMOND)
7926     {
7927       if (IS_MOVING(newx, newy))
7928         RemoveMovingField(newx, newy);
7929       else
7930       {
7931         Feld[newx][newy] = EL_EMPTY;
7932         TEST_DrawLevelField(newx, newy);
7933       }
7934
7935       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7936     }
7937     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7938              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7939     {
7940       if (AmoebaNr[newx][newy])
7941       {
7942         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7943         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7944             Feld[newx][newy] == EL_BD_AMOEBA)
7945           AmoebaCnt[AmoebaNr[newx][newy]]--;
7946       }
7947
7948       if (IS_MOVING(newx, newy))
7949       {
7950         RemoveMovingField(newx, newy);
7951       }
7952       else
7953       {
7954         Feld[newx][newy] = EL_EMPTY;
7955         TEST_DrawLevelField(newx, newy);
7956       }
7957
7958       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7959     }
7960     else if ((element == EL_PACMAN || element == EL_MOLE)
7961              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7962     {
7963       if (AmoebaNr[newx][newy])
7964       {
7965         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7966         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7967             Feld[newx][newy] == EL_BD_AMOEBA)
7968           AmoebaCnt[AmoebaNr[newx][newy]]--;
7969       }
7970
7971       if (element == EL_MOLE)
7972       {
7973         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7974         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7975
7976         ResetGfxAnimation(x, y);
7977         GfxAction[x][y] = ACTION_DIGGING;
7978         TEST_DrawLevelField(x, y);
7979
7980         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7981
7982         return;                         /* wait for shrinking amoeba */
7983       }
7984       else      /* element == EL_PACMAN */
7985       {
7986         Feld[newx][newy] = EL_EMPTY;
7987         TEST_DrawLevelField(newx, newy);
7988         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7989       }
7990     }
7991     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7992              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7993               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7994     {
7995       /* wait for shrinking amoeba to completely disappear */
7996       return;
7997     }
7998     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7999     {
8000       /* object was running against a wall */
8001
8002       TurnRound(x, y);
8003
8004       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8005         DrawLevelElementAnimation(x, y, element);
8006
8007       if (DONT_TOUCH(element))
8008         TestIfBadThingTouchesPlayer(x, y);
8009
8010       return;
8011     }
8012
8013     InitMovingField(x, y, MovDir[x][y]);
8014
8015     PlayLevelSoundAction(x, y, ACTION_MOVING);
8016   }
8017
8018   if (MovDir[x][y])
8019     ContinueMoving(x, y);
8020 }
8021
8022 void ContinueMoving(int x, int y)
8023 {
8024   int element = Feld[x][y];
8025   struct ElementInfo *ei = &element_info[element];
8026   int direction = MovDir[x][y];
8027   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8028   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8029   int newx = x + dx, newy = y + dy;
8030   int stored = Store[x][y];
8031   int stored_new = Store[newx][newy];
8032   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8033   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8034   boolean last_line = (newy == lev_fieldy - 1);
8035
8036   MovPos[x][y] += getElementMoveStepsize(x, y);
8037
8038   if (pushed_by_player) /* special case: moving object pushed by player */
8039     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8040
8041   if (ABS(MovPos[x][y]) < TILEX)
8042   {
8043     TEST_DrawLevelField(x, y);
8044
8045     return;     /* element is still moving */
8046   }
8047
8048   /* element reached destination field */
8049
8050   Feld[x][y] = EL_EMPTY;
8051   Feld[newx][newy] = element;
8052   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8053
8054   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8055   {
8056     element = Feld[newx][newy] = EL_ACID;
8057   }
8058   else if (element == EL_MOLE)
8059   {
8060     Feld[x][y] = EL_SAND;
8061
8062     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8063   }
8064   else if (element == EL_QUICKSAND_FILLING)
8065   {
8066     element = Feld[newx][newy] = get_next_element(element);
8067     Store[newx][newy] = Store[x][y];
8068   }
8069   else if (element == EL_QUICKSAND_EMPTYING)
8070   {
8071     Feld[x][y] = get_next_element(element);
8072     element = Feld[newx][newy] = Store[x][y];
8073   }
8074   else if (element == EL_QUICKSAND_FAST_FILLING)
8075   {
8076     element = Feld[newx][newy] = get_next_element(element);
8077     Store[newx][newy] = Store[x][y];
8078   }
8079   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8080   {
8081     Feld[x][y] = get_next_element(element);
8082     element = Feld[newx][newy] = Store[x][y];
8083   }
8084   else if (element == EL_MAGIC_WALL_FILLING)
8085   {
8086     element = Feld[newx][newy] = get_next_element(element);
8087     if (!game.magic_wall_active)
8088       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8089     Store[newx][newy] = Store[x][y];
8090   }
8091   else if (element == EL_MAGIC_WALL_EMPTYING)
8092   {
8093     Feld[x][y] = get_next_element(element);
8094     if (!game.magic_wall_active)
8095       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8096     element = Feld[newx][newy] = Store[x][y];
8097
8098     InitField(newx, newy, FALSE);
8099   }
8100   else if (element == EL_BD_MAGIC_WALL_FILLING)
8101   {
8102     element = Feld[newx][newy] = get_next_element(element);
8103     if (!game.magic_wall_active)
8104       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8105     Store[newx][newy] = Store[x][y];
8106   }
8107   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8108   {
8109     Feld[x][y] = get_next_element(element);
8110     if (!game.magic_wall_active)
8111       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8112     element = Feld[newx][newy] = Store[x][y];
8113
8114     InitField(newx, newy, FALSE);
8115   }
8116   else if (element == EL_DC_MAGIC_WALL_FILLING)
8117   {
8118     element = Feld[newx][newy] = get_next_element(element);
8119     if (!game.magic_wall_active)
8120       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8121     Store[newx][newy] = Store[x][y];
8122   }
8123   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8124   {
8125     Feld[x][y] = get_next_element(element);
8126     if (!game.magic_wall_active)
8127       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8128     element = Feld[newx][newy] = Store[x][y];
8129
8130     InitField(newx, newy, FALSE);
8131   }
8132   else if (element == EL_AMOEBA_DROPPING)
8133   {
8134     Feld[x][y] = get_next_element(element);
8135     element = Feld[newx][newy] = Store[x][y];
8136   }
8137   else if (element == EL_SOKOBAN_OBJECT)
8138   {
8139     if (Back[x][y])
8140       Feld[x][y] = Back[x][y];
8141
8142     if (Back[newx][newy])
8143       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8144
8145     Back[x][y] = Back[newx][newy] = 0;
8146   }
8147
8148   Store[x][y] = EL_EMPTY;
8149   MovPos[x][y] = 0;
8150   MovDir[x][y] = 0;
8151   MovDelay[x][y] = 0;
8152
8153   MovDelay[newx][newy] = 0;
8154
8155   if (CAN_CHANGE_OR_HAS_ACTION(element))
8156   {
8157     /* copy element change control values to new field */
8158     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8159     ChangePage[newx][newy]  = ChangePage[x][y];
8160     ChangeCount[newx][newy] = ChangeCount[x][y];
8161     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8162   }
8163
8164   CustomValue[newx][newy] = CustomValue[x][y];
8165
8166   ChangeDelay[x][y] = 0;
8167   ChangePage[x][y] = -1;
8168   ChangeCount[x][y] = 0;
8169   ChangeEvent[x][y] = -1;
8170
8171   CustomValue[x][y] = 0;
8172
8173   /* copy animation control values to new field */
8174   GfxFrame[newx][newy]  = GfxFrame[x][y];
8175   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8176   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8177   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8178
8179   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8180
8181   /* some elements can leave other elements behind after moving */
8182   if (ei->move_leave_element != EL_EMPTY &&
8183       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8184       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8185   {
8186     int move_leave_element = ei->move_leave_element;
8187
8188     /* this makes it possible to leave the removed element again */
8189     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8190       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8191
8192     Feld[x][y] = move_leave_element;
8193
8194     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8195       MovDir[x][y] = direction;
8196
8197     InitField(x, y, FALSE);
8198
8199     if (GFX_CRUMBLED(Feld[x][y]))
8200       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8201
8202     if (ELEM_IS_PLAYER(move_leave_element))
8203       RelocatePlayer(x, y, move_leave_element);
8204   }
8205
8206   /* do this after checking for left-behind element */
8207   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8208
8209   if (!CAN_MOVE(element) ||
8210       (CAN_FALL(element) && direction == MV_DOWN &&
8211        (element == EL_SPRING ||
8212         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8213         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8214     GfxDir[x][y] = MovDir[newx][newy] = 0;
8215
8216   TEST_DrawLevelField(x, y);
8217   TEST_DrawLevelField(newx, newy);
8218
8219   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8220
8221   /* prevent pushed element from moving on in pushed direction */
8222   if (pushed_by_player && CAN_MOVE(element) &&
8223       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8224       !(element_info[element].move_pattern & direction))
8225     TurnRound(newx, newy);
8226
8227   /* prevent elements on conveyor belt from moving on in last direction */
8228   if (pushed_by_conveyor && CAN_FALL(element) &&
8229       direction & MV_HORIZONTAL)
8230     MovDir[newx][newy] = 0;
8231
8232   if (!pushed_by_player)
8233   {
8234     int nextx = newx + dx, nexty = newy + dy;
8235     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8236
8237     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8238
8239     if (CAN_FALL(element) && direction == MV_DOWN)
8240       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8241
8242     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8243       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8244
8245     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8246       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8247   }
8248
8249   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8250   {
8251     TestIfBadThingTouchesPlayer(newx, newy);
8252     TestIfBadThingTouchesFriend(newx, newy);
8253
8254     if (!IS_CUSTOM_ELEMENT(element))
8255       TestIfBadThingTouchesOtherBadThing(newx, newy);
8256   }
8257   else if (element == EL_PENGUIN)
8258     TestIfFriendTouchesBadThing(newx, newy);
8259
8260   if (DONT_GET_HIT_BY(element))
8261   {
8262     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8263   }
8264
8265   /* give the player one last chance (one more frame) to move away */
8266   if (CAN_FALL(element) && direction == MV_DOWN &&
8267       (last_line || (!IS_FREE(x, newy + 1) &&
8268                      (!IS_PLAYER(x, newy + 1) ||
8269                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8270     Impact(x, newy);
8271
8272   if (pushed_by_player && !game.use_change_when_pushing_bug)
8273   {
8274     int push_side = MV_DIR_OPPOSITE(direction);
8275     struct PlayerInfo *player = PLAYERINFO(x, y);
8276
8277     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8278                                player->index_bit, push_side);
8279     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8280                                         player->index_bit, push_side);
8281   }
8282
8283   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8284     MovDelay[newx][newy] = 1;
8285
8286   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8287
8288   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8289   TestIfElementHitsCustomElement(newx, newy, direction);
8290   TestIfPlayerTouchesCustomElement(newx, newy);
8291   TestIfElementTouchesCustomElement(newx, newy);
8292
8293   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8294       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8295     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8296                              MV_DIR_OPPOSITE(direction));
8297 }
8298
8299 int AmoebeNachbarNr(int ax, int ay)
8300 {
8301   int i;
8302   int element = Feld[ax][ay];
8303   int group_nr = 0;
8304   static int xy[4][2] =
8305   {
8306     { 0, -1 },
8307     { -1, 0 },
8308     { +1, 0 },
8309     { 0, +1 }
8310   };
8311
8312   for (i = 0; i < NUM_DIRECTIONS; i++)
8313   {
8314     int x = ax + xy[i][0];
8315     int y = ay + xy[i][1];
8316
8317     if (!IN_LEV_FIELD(x, y))
8318       continue;
8319
8320     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8321       group_nr = AmoebaNr[x][y];
8322   }
8323
8324   return group_nr;
8325 }
8326
8327 void AmoebenVereinigen(int ax, int ay)
8328 {
8329   int i, x, y, xx, yy;
8330   int new_group_nr = AmoebaNr[ax][ay];
8331   static int xy[4][2] =
8332   {
8333     { 0, -1 },
8334     { -1, 0 },
8335     { +1, 0 },
8336     { 0, +1 }
8337   };
8338
8339   if (new_group_nr == 0)
8340     return;
8341
8342   for (i = 0; i < NUM_DIRECTIONS; i++)
8343   {
8344     x = ax + xy[i][0];
8345     y = ay + xy[i][1];
8346
8347     if (!IN_LEV_FIELD(x, y))
8348       continue;
8349
8350     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8351          Feld[x][y] == EL_BD_AMOEBA ||
8352          Feld[x][y] == EL_AMOEBA_DEAD) &&
8353         AmoebaNr[x][y] != new_group_nr)
8354     {
8355       int old_group_nr = AmoebaNr[x][y];
8356
8357       if (old_group_nr == 0)
8358         return;
8359
8360       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8361       AmoebaCnt[old_group_nr] = 0;
8362       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8363       AmoebaCnt2[old_group_nr] = 0;
8364
8365       SCAN_PLAYFIELD(xx, yy)
8366       {
8367         if (AmoebaNr[xx][yy] == old_group_nr)
8368           AmoebaNr[xx][yy] = new_group_nr;
8369       }
8370     }
8371   }
8372 }
8373
8374 void AmoebeUmwandeln(int ax, int ay)
8375 {
8376   int i, x, y;
8377
8378   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8379   {
8380     int group_nr = AmoebaNr[ax][ay];
8381
8382 #ifdef DEBUG
8383     if (group_nr == 0)
8384     {
8385       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8386       printf("AmoebeUmwandeln(): This should never happen!\n");
8387       return;
8388     }
8389 #endif
8390
8391     SCAN_PLAYFIELD(x, y)
8392     {
8393       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8394       {
8395         AmoebaNr[x][y] = 0;
8396         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8397       }
8398     }
8399
8400     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8401                             SND_AMOEBA_TURNING_TO_GEM :
8402                             SND_AMOEBA_TURNING_TO_ROCK));
8403     Bang(ax, ay);
8404   }
8405   else
8406   {
8407     static int xy[4][2] =
8408     {
8409       { 0, -1 },
8410       { -1, 0 },
8411       { +1, 0 },
8412       { 0, +1 }
8413     };
8414
8415     for (i = 0; i < NUM_DIRECTIONS; i++)
8416     {
8417       x = ax + xy[i][0];
8418       y = ay + xy[i][1];
8419
8420       if (!IN_LEV_FIELD(x, y))
8421         continue;
8422
8423       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8424       {
8425         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8426                               SND_AMOEBA_TURNING_TO_GEM :
8427                               SND_AMOEBA_TURNING_TO_ROCK));
8428         Bang(x, y);
8429       }
8430     }
8431   }
8432 }
8433
8434 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8435 {
8436   int x, y;
8437   int group_nr = AmoebaNr[ax][ay];
8438   boolean done = FALSE;
8439
8440 #ifdef DEBUG
8441   if (group_nr == 0)
8442   {
8443     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8444     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8445     return;
8446   }
8447 #endif
8448
8449   SCAN_PLAYFIELD(x, y)
8450   {
8451     if (AmoebaNr[x][y] == group_nr &&
8452         (Feld[x][y] == EL_AMOEBA_DEAD ||
8453          Feld[x][y] == EL_BD_AMOEBA ||
8454          Feld[x][y] == EL_AMOEBA_GROWING))
8455     {
8456       AmoebaNr[x][y] = 0;
8457       Feld[x][y] = new_element;
8458       InitField(x, y, FALSE);
8459       TEST_DrawLevelField(x, y);
8460       done = TRUE;
8461     }
8462   }
8463
8464   if (done)
8465     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8466                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8467                             SND_BD_AMOEBA_TURNING_TO_GEM));
8468 }
8469
8470 void AmoebeWaechst(int x, int y)
8471 {
8472   static unsigned int sound_delay = 0;
8473   static unsigned int sound_delay_value = 0;
8474
8475   if (!MovDelay[x][y])          /* start new growing cycle */
8476   {
8477     MovDelay[x][y] = 7;
8478
8479     if (DelayReached(&sound_delay, sound_delay_value))
8480     {
8481       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8482       sound_delay_value = 30;
8483     }
8484   }
8485
8486   if (MovDelay[x][y])           /* wait some time before growing bigger */
8487   {
8488     MovDelay[x][y]--;
8489     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8490     {
8491       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8492                                            6 - MovDelay[x][y]);
8493
8494       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8495     }
8496
8497     if (!MovDelay[x][y])
8498     {
8499       Feld[x][y] = Store[x][y];
8500       Store[x][y] = 0;
8501       TEST_DrawLevelField(x, y);
8502     }
8503   }
8504 }
8505
8506 void AmoebaDisappearing(int x, int y)
8507 {
8508   static unsigned int sound_delay = 0;
8509   static unsigned int sound_delay_value = 0;
8510
8511   if (!MovDelay[x][y])          /* start new shrinking cycle */
8512   {
8513     MovDelay[x][y] = 7;
8514
8515     if (DelayReached(&sound_delay, sound_delay_value))
8516       sound_delay_value = 30;
8517   }
8518
8519   if (MovDelay[x][y])           /* wait some time before shrinking */
8520   {
8521     MovDelay[x][y]--;
8522     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8523     {
8524       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8525                                            6 - MovDelay[x][y]);
8526
8527       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8528     }
8529
8530     if (!MovDelay[x][y])
8531     {
8532       Feld[x][y] = EL_EMPTY;
8533       TEST_DrawLevelField(x, y);
8534
8535       /* don't let mole enter this field in this cycle;
8536          (give priority to objects falling to this field from above) */
8537       Stop[x][y] = TRUE;
8538     }
8539   }
8540 }
8541
8542 void AmoebeAbleger(int ax, int ay)
8543 {
8544   int i;
8545   int element = Feld[ax][ay];
8546   int graphic = el2img(element);
8547   int newax = ax, neway = ay;
8548   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8549   static int xy[4][2] =
8550   {
8551     { 0, -1 },
8552     { -1, 0 },
8553     { +1, 0 },
8554     { 0, +1 }
8555   };
8556
8557   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8558   {
8559     Feld[ax][ay] = EL_AMOEBA_DEAD;
8560     TEST_DrawLevelField(ax, ay);
8561     return;
8562   }
8563
8564   if (IS_ANIMATED(graphic))
8565     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8566
8567   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8568     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8569
8570   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8571   {
8572     MovDelay[ax][ay]--;
8573     if (MovDelay[ax][ay])
8574       return;
8575   }
8576
8577   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8578   {
8579     int start = RND(4);
8580     int x = ax + xy[start][0];
8581     int y = ay + xy[start][1];
8582
8583     if (!IN_LEV_FIELD(x, y))
8584       return;
8585
8586     if (IS_FREE(x, y) ||
8587         CAN_GROW_INTO(Feld[x][y]) ||
8588         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8589         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8590     {
8591       newax = x;
8592       neway = y;
8593     }
8594
8595     if (newax == ax && neway == ay)
8596       return;
8597   }
8598   else                          /* normal or "filled" (BD style) amoeba */
8599   {
8600     int start = RND(4);
8601     boolean waiting_for_player = FALSE;
8602
8603     for (i = 0; i < NUM_DIRECTIONS; i++)
8604     {
8605       int j = (start + i) % 4;
8606       int x = ax + xy[j][0];
8607       int y = ay + xy[j][1];
8608
8609       if (!IN_LEV_FIELD(x, y))
8610         continue;
8611
8612       if (IS_FREE(x, y) ||
8613           CAN_GROW_INTO(Feld[x][y]) ||
8614           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8615           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8616       {
8617         newax = x;
8618         neway = y;
8619         break;
8620       }
8621       else if (IS_PLAYER(x, y))
8622         waiting_for_player = TRUE;
8623     }
8624
8625     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8626     {
8627       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8628       {
8629         Feld[ax][ay] = EL_AMOEBA_DEAD;
8630         TEST_DrawLevelField(ax, ay);
8631         AmoebaCnt[AmoebaNr[ax][ay]]--;
8632
8633         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8634         {
8635           if (element == EL_AMOEBA_FULL)
8636             AmoebeUmwandeln(ax, ay);
8637           else if (element == EL_BD_AMOEBA)
8638             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8639         }
8640       }
8641       return;
8642     }
8643     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8644     {
8645       /* amoeba gets larger by growing in some direction */
8646
8647       int new_group_nr = AmoebaNr[ax][ay];
8648
8649 #ifdef DEBUG
8650   if (new_group_nr == 0)
8651   {
8652     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8653     printf("AmoebeAbleger(): This should never happen!\n");
8654     return;
8655   }
8656 #endif
8657
8658       AmoebaNr[newax][neway] = new_group_nr;
8659       AmoebaCnt[new_group_nr]++;
8660       AmoebaCnt2[new_group_nr]++;
8661
8662       /* if amoeba touches other amoeba(s) after growing, unify them */
8663       AmoebenVereinigen(newax, neway);
8664
8665       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8666       {
8667         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8668         return;
8669       }
8670     }
8671   }
8672
8673   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8674       (neway == lev_fieldy - 1 && newax != ax))
8675   {
8676     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8677     Store[newax][neway] = element;
8678   }
8679   else if (neway == ay || element == EL_EMC_DRIPPER)
8680   {
8681     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8682
8683     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8684   }
8685   else
8686   {
8687     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8688     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8689     Store[ax][ay] = EL_AMOEBA_DROP;
8690     ContinueMoving(ax, ay);
8691     return;
8692   }
8693
8694   TEST_DrawLevelField(newax, neway);
8695 }
8696
8697 void Life(int ax, int ay)
8698 {
8699   int x1, y1, x2, y2;
8700   int life_time = 40;
8701   int element = Feld[ax][ay];
8702   int graphic = el2img(element);
8703   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8704                          level.biomaze);
8705   boolean changed = FALSE;
8706
8707   if (IS_ANIMATED(graphic))
8708     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8709
8710   if (Stop[ax][ay])
8711     return;
8712
8713   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8714     MovDelay[ax][ay] = life_time;
8715
8716   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8717   {
8718     MovDelay[ax][ay]--;
8719     if (MovDelay[ax][ay])
8720       return;
8721   }
8722
8723   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8724   {
8725     int xx = ax+x1, yy = ay+y1;
8726     int nachbarn = 0;
8727
8728     if (!IN_LEV_FIELD(xx, yy))
8729       continue;
8730
8731     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8732     {
8733       int x = xx+x2, y = yy+y2;
8734
8735       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8736         continue;
8737
8738       if (((Feld[x][y] == element ||
8739             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8740            !Stop[x][y]) ||
8741           (IS_FREE(x, y) && Stop[x][y]))
8742         nachbarn++;
8743     }
8744
8745     if (xx == ax && yy == ay)           /* field in the middle */
8746     {
8747       if (nachbarn < life_parameter[0] ||
8748           nachbarn > life_parameter[1])
8749       {
8750         Feld[xx][yy] = EL_EMPTY;
8751         if (!Stop[xx][yy])
8752           TEST_DrawLevelField(xx, yy);
8753         Stop[xx][yy] = TRUE;
8754         changed = TRUE;
8755       }
8756     }
8757     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8758     {                                   /* free border field */
8759       if (nachbarn >= life_parameter[2] &&
8760           nachbarn <= life_parameter[3])
8761       {
8762         Feld[xx][yy] = element;
8763         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8764         if (!Stop[xx][yy])
8765           TEST_DrawLevelField(xx, yy);
8766         Stop[xx][yy] = TRUE;
8767         changed = TRUE;
8768       }
8769     }
8770   }
8771
8772   if (changed)
8773     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8774                    SND_GAME_OF_LIFE_GROWING);
8775 }
8776
8777 static void InitRobotWheel(int x, int y)
8778 {
8779   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8780 }
8781
8782 static void RunRobotWheel(int x, int y)
8783 {
8784   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8785 }
8786
8787 static void StopRobotWheel(int x, int y)
8788 {
8789   if (ZX == x && ZY == y)
8790   {
8791     ZX = ZY = -1;
8792
8793     game.robot_wheel_active = FALSE;
8794   }
8795 }
8796
8797 static void InitTimegateWheel(int x, int y)
8798 {
8799   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8800 }
8801
8802 static void RunTimegateWheel(int x, int y)
8803 {
8804   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8805 }
8806
8807 static void InitMagicBallDelay(int x, int y)
8808 {
8809   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8810 }
8811
8812 static void ActivateMagicBall(int bx, int by)
8813 {
8814   int x, y;
8815
8816   if (level.ball_random)
8817   {
8818     int pos_border = RND(8);    /* select one of the eight border elements */
8819     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8820     int xx = pos_content % 3;
8821     int yy = pos_content / 3;
8822
8823     x = bx - 1 + xx;
8824     y = by - 1 + yy;
8825
8826     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8827       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8828   }
8829   else
8830   {
8831     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8832     {
8833       int xx = x - bx + 1;
8834       int yy = y - by + 1;
8835
8836       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8837         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8838     }
8839   }
8840
8841   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8842 }
8843
8844 void CheckExit(int x, int y)
8845 {
8846   if (local_player->gems_still_needed > 0 ||
8847       local_player->sokobanfields_still_needed > 0 ||
8848       local_player->lights_still_needed > 0)
8849   {
8850     int element = Feld[x][y];
8851     int graphic = el2img(element);
8852
8853     if (IS_ANIMATED(graphic))
8854       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8855
8856     return;
8857   }
8858
8859   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8860     return;
8861
8862   Feld[x][y] = EL_EXIT_OPENING;
8863
8864   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8865 }
8866
8867 void CheckExitEM(int x, int y)
8868 {
8869   if (local_player->gems_still_needed > 0 ||
8870       local_player->sokobanfields_still_needed > 0 ||
8871       local_player->lights_still_needed > 0)
8872   {
8873     int element = Feld[x][y];
8874     int graphic = el2img(element);
8875
8876     if (IS_ANIMATED(graphic))
8877       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8878
8879     return;
8880   }
8881
8882   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8883     return;
8884
8885   Feld[x][y] = EL_EM_EXIT_OPENING;
8886
8887   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8888 }
8889
8890 void CheckExitSteel(int x, int y)
8891 {
8892   if (local_player->gems_still_needed > 0 ||
8893       local_player->sokobanfields_still_needed > 0 ||
8894       local_player->lights_still_needed > 0)
8895   {
8896     int element = Feld[x][y];
8897     int graphic = el2img(element);
8898
8899     if (IS_ANIMATED(graphic))
8900       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8901
8902     return;
8903   }
8904
8905   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8906     return;
8907
8908   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8909
8910   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8911 }
8912
8913 void CheckExitSteelEM(int x, int y)
8914 {
8915   if (local_player->gems_still_needed > 0 ||
8916       local_player->sokobanfields_still_needed > 0 ||
8917       local_player->lights_still_needed > 0)
8918   {
8919     int element = Feld[x][y];
8920     int graphic = el2img(element);
8921
8922     if (IS_ANIMATED(graphic))
8923       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8924
8925     return;
8926   }
8927
8928   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8929     return;
8930
8931   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8932
8933   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8934 }
8935
8936 void CheckExitSP(int x, int y)
8937 {
8938   if (local_player->gems_still_needed > 0)
8939   {
8940     int element = Feld[x][y];
8941     int graphic = el2img(element);
8942
8943     if (IS_ANIMATED(graphic))
8944       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8945
8946     return;
8947   }
8948
8949   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8950     return;
8951
8952   Feld[x][y] = EL_SP_EXIT_OPENING;
8953
8954   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8955 }
8956
8957 static void CloseAllOpenTimegates()
8958 {
8959   int x, y;
8960
8961   SCAN_PLAYFIELD(x, y)
8962   {
8963     int element = Feld[x][y];
8964
8965     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8966     {
8967       Feld[x][y] = EL_TIMEGATE_CLOSING;
8968
8969       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8970     }
8971   }
8972 }
8973
8974 void DrawTwinkleOnField(int x, int y)
8975 {
8976   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8977     return;
8978
8979   if (Feld[x][y] == EL_BD_DIAMOND)
8980     return;
8981
8982   if (MovDelay[x][y] == 0)      /* next animation frame */
8983     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8984
8985   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8986   {
8987     MovDelay[x][y]--;
8988
8989     DrawLevelElementAnimation(x, y, Feld[x][y]);
8990
8991     if (MovDelay[x][y] != 0)
8992     {
8993       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8994                                            10 - MovDelay[x][y]);
8995
8996       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8997     }
8998   }
8999 }
9000
9001 void MauerWaechst(int x, int y)
9002 {
9003   int delay = 6;
9004
9005   if (!MovDelay[x][y])          /* next animation frame */
9006     MovDelay[x][y] = 3 * delay;
9007
9008   if (MovDelay[x][y])           /* wait some time before next frame */
9009   {
9010     MovDelay[x][y]--;
9011
9012     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9013     {
9014       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9015       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9016
9017       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9018     }
9019
9020     if (!MovDelay[x][y])
9021     {
9022       if (MovDir[x][y] == MV_LEFT)
9023       {
9024         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9025           TEST_DrawLevelField(x - 1, y);
9026       }
9027       else if (MovDir[x][y] == MV_RIGHT)
9028       {
9029         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9030           TEST_DrawLevelField(x + 1, y);
9031       }
9032       else if (MovDir[x][y] == MV_UP)
9033       {
9034         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9035           TEST_DrawLevelField(x, y - 1);
9036       }
9037       else
9038       {
9039         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9040           TEST_DrawLevelField(x, y + 1);
9041       }
9042
9043       Feld[x][y] = Store[x][y];
9044       Store[x][y] = 0;
9045       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9046       TEST_DrawLevelField(x, y);
9047     }
9048   }
9049 }
9050
9051 void MauerAbleger(int ax, int ay)
9052 {
9053   int element = Feld[ax][ay];
9054   int graphic = el2img(element);
9055   boolean oben_frei = FALSE, unten_frei = FALSE;
9056   boolean links_frei = FALSE, rechts_frei = FALSE;
9057   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9058   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9059   boolean new_wall = FALSE;
9060
9061   if (IS_ANIMATED(graphic))
9062     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9063
9064   if (!MovDelay[ax][ay])        /* start building new wall */
9065     MovDelay[ax][ay] = 6;
9066
9067   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9068   {
9069     MovDelay[ax][ay]--;
9070     if (MovDelay[ax][ay])
9071       return;
9072   }
9073
9074   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9075     oben_frei = TRUE;
9076   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9077     unten_frei = TRUE;
9078   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9079     links_frei = TRUE;
9080   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9081     rechts_frei = TRUE;
9082
9083   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9084       element == EL_EXPANDABLE_WALL_ANY)
9085   {
9086     if (oben_frei)
9087     {
9088       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9089       Store[ax][ay-1] = element;
9090       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9091       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9092         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9093                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9094       new_wall = TRUE;
9095     }
9096     if (unten_frei)
9097     {
9098       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9099       Store[ax][ay+1] = element;
9100       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9101       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9102         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9103                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9104       new_wall = TRUE;
9105     }
9106   }
9107
9108   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9109       element == EL_EXPANDABLE_WALL_ANY ||
9110       element == EL_EXPANDABLE_WALL ||
9111       element == EL_BD_EXPANDABLE_WALL)
9112   {
9113     if (links_frei)
9114     {
9115       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9116       Store[ax-1][ay] = element;
9117       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9118       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9119         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9120                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9121       new_wall = TRUE;
9122     }
9123
9124     if (rechts_frei)
9125     {
9126       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9127       Store[ax+1][ay] = element;
9128       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9129       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9130         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9131                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9132       new_wall = TRUE;
9133     }
9134   }
9135
9136   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9137     TEST_DrawLevelField(ax, ay);
9138
9139   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9140     oben_massiv = TRUE;
9141   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9142     unten_massiv = TRUE;
9143   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9144     links_massiv = TRUE;
9145   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9146     rechts_massiv = TRUE;
9147
9148   if (((oben_massiv && unten_massiv) ||
9149        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9150        element == EL_EXPANDABLE_WALL) &&
9151       ((links_massiv && rechts_massiv) ||
9152        element == EL_EXPANDABLE_WALL_VERTICAL))
9153     Feld[ax][ay] = EL_WALL;
9154
9155   if (new_wall)
9156     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9157 }
9158
9159 void MauerAblegerStahl(int ax, int ay)
9160 {
9161   int element = Feld[ax][ay];
9162   int graphic = el2img(element);
9163   boolean oben_frei = FALSE, unten_frei = FALSE;
9164   boolean links_frei = FALSE, rechts_frei = FALSE;
9165   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9166   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9167   boolean new_wall = FALSE;
9168
9169   if (IS_ANIMATED(graphic))
9170     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9171
9172   if (!MovDelay[ax][ay])        /* start building new wall */
9173     MovDelay[ax][ay] = 6;
9174
9175   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9176   {
9177     MovDelay[ax][ay]--;
9178     if (MovDelay[ax][ay])
9179       return;
9180   }
9181
9182   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9183     oben_frei = TRUE;
9184   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9185     unten_frei = TRUE;
9186   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9187     links_frei = TRUE;
9188   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9189     rechts_frei = TRUE;
9190
9191   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9192       element == EL_EXPANDABLE_STEELWALL_ANY)
9193   {
9194     if (oben_frei)
9195     {
9196       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9197       Store[ax][ay-1] = element;
9198       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9199       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9200         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9201                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9202       new_wall = TRUE;
9203     }
9204     if (unten_frei)
9205     {
9206       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9207       Store[ax][ay+1] = element;
9208       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9209       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9210         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9211                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9212       new_wall = TRUE;
9213     }
9214   }
9215
9216   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9217       element == EL_EXPANDABLE_STEELWALL_ANY)
9218   {
9219     if (links_frei)
9220     {
9221       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9222       Store[ax-1][ay] = element;
9223       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9224       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9225         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9226                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9227       new_wall = TRUE;
9228     }
9229
9230     if (rechts_frei)
9231     {
9232       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9233       Store[ax+1][ay] = element;
9234       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9235       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9236         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9237                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9238       new_wall = TRUE;
9239     }
9240   }
9241
9242   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9243     oben_massiv = TRUE;
9244   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9245     unten_massiv = TRUE;
9246   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9247     links_massiv = TRUE;
9248   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9249     rechts_massiv = TRUE;
9250
9251   if (((oben_massiv && unten_massiv) ||
9252        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9253       ((links_massiv && rechts_massiv) ||
9254        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9255     Feld[ax][ay] = EL_STEELWALL;
9256
9257   if (new_wall)
9258     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9259 }
9260
9261 void CheckForDragon(int x, int y)
9262 {
9263   int i, j;
9264   boolean dragon_found = FALSE;
9265   static int xy[4][2] =
9266   {
9267     { 0, -1 },
9268     { -1, 0 },
9269     { +1, 0 },
9270     { 0, +1 }
9271   };
9272
9273   for (i = 0; i < NUM_DIRECTIONS; i++)
9274   {
9275     for (j = 0; j < 4; j++)
9276     {
9277       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9278
9279       if (IN_LEV_FIELD(xx, yy) &&
9280           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9281       {
9282         if (Feld[xx][yy] == EL_DRAGON)
9283           dragon_found = TRUE;
9284       }
9285       else
9286         break;
9287     }
9288   }
9289
9290   if (!dragon_found)
9291   {
9292     for (i = 0; i < NUM_DIRECTIONS; i++)
9293     {
9294       for (j = 0; j < 3; j++)
9295       {
9296         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9297   
9298         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9299         {
9300           Feld[xx][yy] = EL_EMPTY;
9301           TEST_DrawLevelField(xx, yy);
9302         }
9303         else
9304           break;
9305       }
9306     }
9307   }
9308 }
9309
9310 static void InitBuggyBase(int x, int y)
9311 {
9312   int element = Feld[x][y];
9313   int activating_delay = FRAMES_PER_SECOND / 4;
9314
9315   ChangeDelay[x][y] =
9316     (element == EL_SP_BUGGY_BASE ?
9317      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9318      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9319      activating_delay :
9320      element == EL_SP_BUGGY_BASE_ACTIVE ?
9321      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9322 }
9323
9324 static void WarnBuggyBase(int x, int y)
9325 {
9326   int i;
9327   static int xy[4][2] =
9328   {
9329     { 0, -1 },
9330     { -1, 0 },
9331     { +1, 0 },
9332     { 0, +1 }
9333   };
9334
9335   for (i = 0; i < NUM_DIRECTIONS; i++)
9336   {
9337     int xx = x + xy[i][0];
9338     int yy = y + xy[i][1];
9339
9340     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9341     {
9342       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9343
9344       break;
9345     }
9346   }
9347 }
9348
9349 static void InitTrap(int x, int y)
9350 {
9351   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9352 }
9353
9354 static void ActivateTrap(int x, int y)
9355 {
9356   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9357 }
9358
9359 static void ChangeActiveTrap(int x, int y)
9360 {
9361   int graphic = IMG_TRAP_ACTIVE;
9362
9363   /* if new animation frame was drawn, correct crumbled sand border */
9364   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9365     TEST_DrawLevelFieldCrumbled(x, y);
9366 }
9367
9368 static int getSpecialActionElement(int element, int number, int base_element)
9369 {
9370   return (element != EL_EMPTY ? element :
9371           number != -1 ? base_element + number - 1 :
9372           EL_EMPTY);
9373 }
9374
9375 static int getModifiedActionNumber(int value_old, int operator, int operand,
9376                                    int value_min, int value_max)
9377 {
9378   int value_new = (operator == CA_MODE_SET      ? operand :
9379                    operator == CA_MODE_ADD      ? value_old + operand :
9380                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9381                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9382                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9383                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9384                    value_old);
9385
9386   return (value_new < value_min ? value_min :
9387           value_new > value_max ? value_max :
9388           value_new);
9389 }
9390
9391 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9392 {
9393   struct ElementInfo *ei = &element_info[element];
9394   struct ElementChangeInfo *change = &ei->change_page[page];
9395   int target_element = change->target_element;
9396   int action_type = change->action_type;
9397   int action_mode = change->action_mode;
9398   int action_arg = change->action_arg;
9399   int action_element = change->action_element;
9400   int i;
9401
9402   if (!change->has_action)
9403     return;
9404
9405   /* ---------- determine action paramater values -------------------------- */
9406
9407   int level_time_value =
9408     (level.time > 0 ? TimeLeft :
9409      TimePlayed);
9410
9411   int action_arg_element_raw =
9412     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9413      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9414      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9415      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9416      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9417      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9418      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9419      EL_EMPTY);
9420   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9421
9422   int action_arg_direction =
9423     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9424      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9425      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9426      change->actual_trigger_side :
9427      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9428      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9429      MV_NONE);
9430
9431   int action_arg_number_min =
9432     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9433      CA_ARG_MIN);
9434
9435   int action_arg_number_max =
9436     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9437      action_type == CA_SET_LEVEL_GEMS ? 999 :
9438      action_type == CA_SET_LEVEL_TIME ? 9999 :
9439      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9440      action_type == CA_SET_CE_VALUE ? 9999 :
9441      action_type == CA_SET_CE_SCORE ? 9999 :
9442      CA_ARG_MAX);
9443
9444   int action_arg_number_reset =
9445     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9446      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9447      action_type == CA_SET_LEVEL_TIME ? level.time :
9448      action_type == CA_SET_LEVEL_SCORE ? 0 :
9449      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9450      action_type == CA_SET_CE_SCORE ? 0 :
9451      0);
9452
9453   int action_arg_number =
9454     (action_arg <= CA_ARG_MAX ? action_arg :
9455      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9456      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9457      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9458      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9459      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9460      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9461      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9462      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9463      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9464      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9465      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9466      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9467      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9468      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9469      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9470      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9471      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9472      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9473      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9474      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9475      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9476      -1);
9477
9478   int action_arg_number_old =
9479     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9480      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9481      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9482      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9483      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9484      0);
9485
9486   int action_arg_number_new =
9487     getModifiedActionNumber(action_arg_number_old,
9488                             action_mode, action_arg_number,
9489                             action_arg_number_min, action_arg_number_max);
9490
9491   int trigger_player_bits =
9492     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9493      change->actual_trigger_player_bits : change->trigger_player);
9494
9495   int action_arg_player_bits =
9496     (action_arg >= CA_ARG_PLAYER_1 &&
9497      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9498      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9499      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9500      PLAYER_BITS_ANY);
9501
9502   /* ---------- execute action  -------------------------------------------- */
9503
9504   switch (action_type)
9505   {
9506     case CA_NO_ACTION:
9507     {
9508       return;
9509     }
9510
9511     /* ---------- level actions  ------------------------------------------- */
9512
9513     case CA_RESTART_LEVEL:
9514     {
9515       game.restart_level = TRUE;
9516
9517       break;
9518     }
9519
9520     case CA_SHOW_ENVELOPE:
9521     {
9522       int element = getSpecialActionElement(action_arg_element,
9523                                             action_arg_number, EL_ENVELOPE_1);
9524
9525       if (IS_ENVELOPE(element))
9526         local_player->show_envelope = element;
9527
9528       break;
9529     }
9530
9531     case CA_SET_LEVEL_TIME:
9532     {
9533       if (level.time > 0)       /* only modify limited time value */
9534       {
9535         TimeLeft = action_arg_number_new;
9536
9537         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9538
9539         DisplayGameControlValues();
9540
9541         if (!TimeLeft && setup.time_limit)
9542           for (i = 0; i < MAX_PLAYERS; i++)
9543             KillPlayer(&stored_player[i]);
9544       }
9545
9546       break;
9547     }
9548
9549     case CA_SET_LEVEL_SCORE:
9550     {
9551       local_player->score = action_arg_number_new;
9552
9553       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9554
9555       DisplayGameControlValues();
9556
9557       break;
9558     }
9559
9560     case CA_SET_LEVEL_GEMS:
9561     {
9562       local_player->gems_still_needed = action_arg_number_new;
9563
9564       game_panel_controls[GAME_PANEL_GEMS].value =
9565         local_player->gems_still_needed;
9566
9567       DisplayGameControlValues();
9568
9569       break;
9570     }
9571
9572     case CA_SET_LEVEL_WIND:
9573     {
9574       game.wind_direction = action_arg_direction;
9575
9576       break;
9577     }
9578
9579     case CA_SET_LEVEL_RANDOM_SEED:
9580     {
9581       /* ensure that setting a new random seed while playing is predictable */
9582       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9583
9584       break;
9585     }
9586
9587     /* ---------- player actions  ------------------------------------------ */
9588
9589     case CA_MOVE_PLAYER:
9590     {
9591       /* automatically move to the next field in specified direction */
9592       for (i = 0; i < MAX_PLAYERS; i++)
9593         if (trigger_player_bits & (1 << i))
9594           stored_player[i].programmed_action = action_arg_direction;
9595
9596       break;
9597     }
9598
9599     case CA_EXIT_PLAYER:
9600     {
9601       for (i = 0; i < MAX_PLAYERS; i++)
9602         if (action_arg_player_bits & (1 << i))
9603           PlayerWins(&stored_player[i]);
9604
9605       break;
9606     }
9607
9608     case CA_KILL_PLAYER:
9609     {
9610       for (i = 0; i < MAX_PLAYERS; i++)
9611         if (action_arg_player_bits & (1 << i))
9612           KillPlayer(&stored_player[i]);
9613
9614       break;
9615     }
9616
9617     case CA_SET_PLAYER_KEYS:
9618     {
9619       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9620       int element = getSpecialActionElement(action_arg_element,
9621                                             action_arg_number, EL_KEY_1);
9622
9623       if (IS_KEY(element))
9624       {
9625         for (i = 0; i < MAX_PLAYERS; i++)
9626         {
9627           if (trigger_player_bits & (1 << i))
9628           {
9629             stored_player[i].key[KEY_NR(element)] = key_state;
9630
9631             DrawGameDoorValues();
9632           }
9633         }
9634       }
9635
9636       break;
9637     }
9638
9639     case CA_SET_PLAYER_SPEED:
9640     {
9641       for (i = 0; i < MAX_PLAYERS; i++)
9642       {
9643         if (trigger_player_bits & (1 << i))
9644         {
9645           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9646
9647           if (action_arg == CA_ARG_SPEED_FASTER &&
9648               stored_player[i].cannot_move)
9649           {
9650             action_arg_number = STEPSIZE_VERY_SLOW;
9651           }
9652           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9653                    action_arg == CA_ARG_SPEED_FASTER)
9654           {
9655             action_arg_number = 2;
9656             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9657                            CA_MODE_MULTIPLY);
9658           }
9659           else if (action_arg == CA_ARG_NUMBER_RESET)
9660           {
9661             action_arg_number = level.initial_player_stepsize[i];
9662           }
9663
9664           move_stepsize =
9665             getModifiedActionNumber(move_stepsize,
9666                                     action_mode,
9667                                     action_arg_number,
9668                                     action_arg_number_min,
9669                                     action_arg_number_max);
9670
9671           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9672         }
9673       }
9674
9675       break;
9676     }
9677
9678     case CA_SET_PLAYER_SHIELD:
9679     {
9680       for (i = 0; i < MAX_PLAYERS; i++)
9681       {
9682         if (trigger_player_bits & (1 << i))
9683         {
9684           if (action_arg == CA_ARG_SHIELD_OFF)
9685           {
9686             stored_player[i].shield_normal_time_left = 0;
9687             stored_player[i].shield_deadly_time_left = 0;
9688           }
9689           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9690           {
9691             stored_player[i].shield_normal_time_left = 999999;
9692           }
9693           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9694           {
9695             stored_player[i].shield_normal_time_left = 999999;
9696             stored_player[i].shield_deadly_time_left = 999999;
9697           }
9698         }
9699       }
9700
9701       break;
9702     }
9703
9704     case CA_SET_PLAYER_GRAVITY:
9705     {
9706       for (i = 0; i < MAX_PLAYERS; i++)
9707       {
9708         if (trigger_player_bits & (1 << i))
9709         {
9710           stored_player[i].gravity =
9711             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9712              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9713              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9714              stored_player[i].gravity);
9715         }
9716       }
9717
9718       break;
9719     }
9720
9721     case CA_SET_PLAYER_ARTWORK:
9722     {
9723       for (i = 0; i < MAX_PLAYERS; i++)
9724       {
9725         if (trigger_player_bits & (1 << i))
9726         {
9727           int artwork_element = action_arg_element;
9728
9729           if (action_arg == CA_ARG_ELEMENT_RESET)
9730             artwork_element =
9731               (level.use_artwork_element[i] ? level.artwork_element[i] :
9732                stored_player[i].element_nr);
9733
9734           if (stored_player[i].artwork_element != artwork_element)
9735             stored_player[i].Frame = 0;
9736
9737           stored_player[i].artwork_element = artwork_element;
9738
9739           SetPlayerWaiting(&stored_player[i], FALSE);
9740
9741           /* set number of special actions for bored and sleeping animation */
9742           stored_player[i].num_special_action_bored =
9743             get_num_special_action(artwork_element,
9744                                    ACTION_BORING_1, ACTION_BORING_LAST);
9745           stored_player[i].num_special_action_sleeping =
9746             get_num_special_action(artwork_element,
9747                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9748         }
9749       }
9750
9751       break;
9752     }
9753
9754     case CA_SET_PLAYER_INVENTORY:
9755     {
9756       for (i = 0; i < MAX_PLAYERS; i++)
9757       {
9758         struct PlayerInfo *player = &stored_player[i];
9759         int j, k;
9760
9761         if (trigger_player_bits & (1 << i))
9762         {
9763           int inventory_element = action_arg_element;
9764
9765           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9766               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9767               action_arg == CA_ARG_ELEMENT_ACTION)
9768           {
9769             int element = inventory_element;
9770             int collect_count = element_info[element].collect_count_initial;
9771
9772             if (!IS_CUSTOM_ELEMENT(element))
9773               collect_count = 1;
9774
9775             if (collect_count == 0)
9776               player->inventory_infinite_element = element;
9777             else
9778               for (k = 0; k < collect_count; k++)
9779                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9780                   player->inventory_element[player->inventory_size++] =
9781                     element;
9782           }
9783           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9784                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9785                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9786           {
9787             if (player->inventory_infinite_element != EL_UNDEFINED &&
9788                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9789                                      action_arg_element_raw))
9790               player->inventory_infinite_element = EL_UNDEFINED;
9791
9792             for (k = 0, j = 0; j < player->inventory_size; j++)
9793             {
9794               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9795                                         action_arg_element_raw))
9796                 player->inventory_element[k++] = player->inventory_element[j];
9797             }
9798
9799             player->inventory_size = k;
9800           }
9801           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9802           {
9803             if (player->inventory_size > 0)
9804             {
9805               for (j = 0; j < player->inventory_size - 1; j++)
9806                 player->inventory_element[j] = player->inventory_element[j + 1];
9807
9808               player->inventory_size--;
9809             }
9810           }
9811           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9812           {
9813             if (player->inventory_size > 0)
9814               player->inventory_size--;
9815           }
9816           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9817           {
9818             player->inventory_infinite_element = EL_UNDEFINED;
9819             player->inventory_size = 0;
9820           }
9821           else if (action_arg == CA_ARG_INVENTORY_RESET)
9822           {
9823             player->inventory_infinite_element = EL_UNDEFINED;
9824             player->inventory_size = 0;
9825
9826             if (level.use_initial_inventory[i])
9827             {
9828               for (j = 0; j < level.initial_inventory_size[i]; j++)
9829               {
9830                 int element = level.initial_inventory_content[i][j];
9831                 int collect_count = element_info[element].collect_count_initial;
9832
9833                 if (!IS_CUSTOM_ELEMENT(element))
9834                   collect_count = 1;
9835
9836                 if (collect_count == 0)
9837                   player->inventory_infinite_element = element;
9838                 else
9839                   for (k = 0; k < collect_count; k++)
9840                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9841                       player->inventory_element[player->inventory_size++] =
9842                         element;
9843               }
9844             }
9845           }
9846         }
9847       }
9848
9849       break;
9850     }
9851
9852     /* ---------- CE actions  ---------------------------------------------- */
9853
9854     case CA_SET_CE_VALUE:
9855     {
9856       int last_ce_value = CustomValue[x][y];
9857
9858       CustomValue[x][y] = action_arg_number_new;
9859
9860       if (CustomValue[x][y] != last_ce_value)
9861       {
9862         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9863         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9864
9865         if (CustomValue[x][y] == 0)
9866         {
9867           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9868           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9869         }
9870       }
9871
9872       break;
9873     }
9874
9875     case CA_SET_CE_SCORE:
9876     {
9877       int last_ce_score = ei->collect_score;
9878
9879       ei->collect_score = action_arg_number_new;
9880
9881       if (ei->collect_score != last_ce_score)
9882       {
9883         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9884         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9885
9886         if (ei->collect_score == 0)
9887         {
9888           int xx, yy;
9889
9890           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9891           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9892
9893           /*
9894             This is a very special case that seems to be a mixture between
9895             CheckElementChange() and CheckTriggeredElementChange(): while
9896             the first one only affects single elements that are triggered
9897             directly, the second one affects multiple elements in the playfield
9898             that are triggered indirectly by another element. This is a third
9899             case: Changing the CE score always affects multiple identical CEs,
9900             so every affected CE must be checked, not only the single CE for
9901             which the CE score was changed in the first place (as every instance
9902             of that CE shares the same CE score, and therefore also can change)!
9903           */
9904           SCAN_PLAYFIELD(xx, yy)
9905           {
9906             if (Feld[xx][yy] == element)
9907               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9908                                  CE_SCORE_GETS_ZERO);
9909           }
9910         }
9911       }
9912
9913       break;
9914     }
9915
9916     case CA_SET_CE_ARTWORK:
9917     {
9918       int artwork_element = action_arg_element;
9919       boolean reset_frame = FALSE;
9920       int xx, yy;
9921
9922       if (action_arg == CA_ARG_ELEMENT_RESET)
9923         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9924                            element);
9925
9926       if (ei->gfx_element != artwork_element)
9927         reset_frame = TRUE;
9928
9929       ei->gfx_element = artwork_element;
9930
9931       SCAN_PLAYFIELD(xx, yy)
9932       {
9933         if (Feld[xx][yy] == element)
9934         {
9935           if (reset_frame)
9936           {
9937             ResetGfxAnimation(xx, yy);
9938             ResetRandomAnimationValue(xx, yy);
9939           }
9940
9941           TEST_DrawLevelField(xx, yy);
9942         }
9943       }
9944
9945       break;
9946     }
9947
9948     /* ---------- engine actions  ------------------------------------------ */
9949
9950     case CA_SET_ENGINE_SCAN_MODE:
9951     {
9952       InitPlayfieldScanMode(action_arg);
9953
9954       break;
9955     }
9956
9957     default:
9958       break;
9959   }
9960 }
9961
9962 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9963 {
9964   int old_element = Feld[x][y];
9965   int new_element = GetElementFromGroupElement(element);
9966   int previous_move_direction = MovDir[x][y];
9967   int last_ce_value = CustomValue[x][y];
9968   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9969   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9970   boolean add_player_onto_element = (new_element_is_player &&
9971                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9972                                      IS_WALKABLE(old_element));
9973
9974   if (!add_player_onto_element)
9975   {
9976     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9977       RemoveMovingField(x, y);
9978     else
9979       RemoveField(x, y);
9980
9981     Feld[x][y] = new_element;
9982
9983     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9984       MovDir[x][y] = previous_move_direction;
9985
9986     if (element_info[new_element].use_last_ce_value)
9987       CustomValue[x][y] = last_ce_value;
9988
9989     InitField_WithBug1(x, y, FALSE);
9990
9991     new_element = Feld[x][y];   /* element may have changed */
9992
9993     ResetGfxAnimation(x, y);
9994     ResetRandomAnimationValue(x, y);
9995
9996     TEST_DrawLevelField(x, y);
9997
9998     if (GFX_CRUMBLED(new_element))
9999       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10000   }
10001
10002   /* check if element under the player changes from accessible to unaccessible
10003      (needed for special case of dropping element which then changes) */
10004   /* (must be checked after creating new element for walkable group elements) */
10005   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10006       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10007   {
10008     Bang(x, y);
10009
10010     return;
10011   }
10012
10013   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10014   if (new_element_is_player)
10015     RelocatePlayer(x, y, new_element);
10016
10017   if (is_change)
10018     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10019
10020   TestIfBadThingTouchesPlayer(x, y);
10021   TestIfPlayerTouchesCustomElement(x, y);
10022   TestIfElementTouchesCustomElement(x, y);
10023 }
10024
10025 static void CreateField(int x, int y, int element)
10026 {
10027   CreateFieldExt(x, y, element, FALSE);
10028 }
10029
10030 static void CreateElementFromChange(int x, int y, int element)
10031 {
10032   element = GET_VALID_RUNTIME_ELEMENT(element);
10033
10034   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10035   {
10036     int old_element = Feld[x][y];
10037
10038     /* prevent changed element from moving in same engine frame
10039        unless both old and new element can either fall or move */
10040     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10041         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10042       Stop[x][y] = TRUE;
10043   }
10044
10045   CreateFieldExt(x, y, element, TRUE);
10046 }
10047
10048 static boolean ChangeElement(int x, int y, int element, int page)
10049 {
10050   struct ElementInfo *ei = &element_info[element];
10051   struct ElementChangeInfo *change = &ei->change_page[page];
10052   int ce_value = CustomValue[x][y];
10053   int ce_score = ei->collect_score;
10054   int target_element;
10055   int old_element = Feld[x][y];
10056
10057   /* always use default change event to prevent running into a loop */
10058   if (ChangeEvent[x][y] == -1)
10059     ChangeEvent[x][y] = CE_DELAY;
10060
10061   if (ChangeEvent[x][y] == CE_DELAY)
10062   {
10063     /* reset actual trigger element, trigger player and action element */
10064     change->actual_trigger_element = EL_EMPTY;
10065     change->actual_trigger_player = EL_EMPTY;
10066     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10067     change->actual_trigger_side = CH_SIDE_NONE;
10068     change->actual_trigger_ce_value = 0;
10069     change->actual_trigger_ce_score = 0;
10070   }
10071
10072   /* do not change elements more than a specified maximum number of changes */
10073   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10074     return FALSE;
10075
10076   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10077
10078   if (change->explode)
10079   {
10080     Bang(x, y);
10081
10082     return TRUE;
10083   }
10084
10085   if (change->use_target_content)
10086   {
10087     boolean complete_replace = TRUE;
10088     boolean can_replace[3][3];
10089     int xx, yy;
10090
10091     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10092     {
10093       boolean is_empty;
10094       boolean is_walkable;
10095       boolean is_diggable;
10096       boolean is_collectible;
10097       boolean is_removable;
10098       boolean is_destructible;
10099       int ex = x + xx - 1;
10100       int ey = y + yy - 1;
10101       int content_element = change->target_content.e[xx][yy];
10102       int e;
10103
10104       can_replace[xx][yy] = TRUE;
10105
10106       if (ex == x && ey == y)   /* do not check changing element itself */
10107         continue;
10108
10109       if (content_element == EL_EMPTY_SPACE)
10110       {
10111         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10112
10113         continue;
10114       }
10115
10116       if (!IN_LEV_FIELD(ex, ey))
10117       {
10118         can_replace[xx][yy] = FALSE;
10119         complete_replace = FALSE;
10120
10121         continue;
10122       }
10123
10124       e = Feld[ex][ey];
10125
10126       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10127         e = MovingOrBlocked2Element(ex, ey);
10128
10129       is_empty = (IS_FREE(ex, ey) ||
10130                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10131
10132       is_walkable     = (is_empty || IS_WALKABLE(e));
10133       is_diggable     = (is_empty || IS_DIGGABLE(e));
10134       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10135       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10136       is_removable    = (is_diggable || is_collectible);
10137
10138       can_replace[xx][yy] =
10139         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10140           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10141           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10142           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10143           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10144           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10145          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10146
10147       if (!can_replace[xx][yy])
10148         complete_replace = FALSE;
10149     }
10150
10151     if (!change->only_if_complete || complete_replace)
10152     {
10153       boolean something_has_changed = FALSE;
10154
10155       if (change->only_if_complete && change->use_random_replace &&
10156           RND(100) < change->random_percentage)
10157         return FALSE;
10158
10159       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10160       {
10161         int ex = x + xx - 1;
10162         int ey = y + yy - 1;
10163         int content_element;
10164
10165         if (can_replace[xx][yy] && (!change->use_random_replace ||
10166                                     RND(100) < change->random_percentage))
10167         {
10168           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10169             RemoveMovingField(ex, ey);
10170
10171           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10172
10173           content_element = change->target_content.e[xx][yy];
10174           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10175                                               ce_value, ce_score);
10176
10177           CreateElementFromChange(ex, ey, target_element);
10178
10179           something_has_changed = TRUE;
10180
10181           /* for symmetry reasons, freeze newly created border elements */
10182           if (ex != x || ey != y)
10183             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10184         }
10185       }
10186
10187       if (something_has_changed)
10188       {
10189         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10190         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10191       }
10192     }
10193   }
10194   else
10195   {
10196     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10197                                         ce_value, ce_score);
10198
10199     if (element == EL_DIAGONAL_GROWING ||
10200         element == EL_DIAGONAL_SHRINKING)
10201     {
10202       target_element = Store[x][y];
10203
10204       Store[x][y] = EL_EMPTY;
10205     }
10206
10207     CreateElementFromChange(x, y, target_element);
10208
10209     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10210     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10211   }
10212
10213   /* this uses direct change before indirect change */
10214   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10215
10216   return TRUE;
10217 }
10218
10219 static void HandleElementChange(int x, int y, int page)
10220 {
10221   int element = MovingOrBlocked2Element(x, y);
10222   struct ElementInfo *ei = &element_info[element];
10223   struct ElementChangeInfo *change = &ei->change_page[page];
10224   boolean handle_action_before_change = FALSE;
10225
10226 #ifdef DEBUG
10227   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10228       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10229   {
10230     printf("\n\n");
10231     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10232            x, y, element, element_info[element].token_name);
10233     printf("HandleElementChange(): This should never happen!\n");
10234     printf("\n\n");
10235   }
10236 #endif
10237
10238   /* this can happen with classic bombs on walkable, changing elements */
10239   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10240   {
10241     return;
10242   }
10243
10244   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10245   {
10246     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10247
10248     if (change->can_change)
10249     {
10250       /* !!! not clear why graphic animation should be reset at all here !!! */
10251       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10252       /* when a custom element is about to change (for example by change delay),
10253          do not reset graphic animation when the custom element is moving */
10254       if (!IS_MOVING(x, y))
10255       {
10256         ResetGfxAnimation(x, y);
10257         ResetRandomAnimationValue(x, y);
10258       }
10259
10260       if (change->pre_change_function)
10261         change->pre_change_function(x, y);
10262     }
10263   }
10264
10265   ChangeDelay[x][y]--;
10266
10267   if (ChangeDelay[x][y] != 0)           /* continue element change */
10268   {
10269     if (change->can_change)
10270     {
10271       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10272
10273       if (IS_ANIMATED(graphic))
10274         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10275
10276       if (change->change_function)
10277         change->change_function(x, y);
10278     }
10279   }
10280   else                                  /* finish element change */
10281   {
10282     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10283     {
10284       page = ChangePage[x][y];
10285       ChangePage[x][y] = -1;
10286
10287       change = &ei->change_page[page];
10288     }
10289
10290     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10291     {
10292       ChangeDelay[x][y] = 1;            /* try change after next move step */
10293       ChangePage[x][y] = page;          /* remember page to use for change */
10294
10295       return;
10296     }
10297
10298     /* special case: set new level random seed before changing element */
10299     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10300       handle_action_before_change = TRUE;
10301
10302     if (change->has_action && handle_action_before_change)
10303       ExecuteCustomElementAction(x, y, element, page);
10304
10305     if (change->can_change)
10306     {
10307       if (ChangeElement(x, y, element, page))
10308       {
10309         if (change->post_change_function)
10310           change->post_change_function(x, y);
10311       }
10312     }
10313
10314     if (change->has_action && !handle_action_before_change)
10315       ExecuteCustomElementAction(x, y, element, page);
10316   }
10317 }
10318
10319 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10320                                               int trigger_element,
10321                                               int trigger_event,
10322                                               int trigger_player,
10323                                               int trigger_side,
10324                                               int trigger_page)
10325 {
10326   boolean change_done_any = FALSE;
10327   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10328   int i;
10329
10330   if (!(trigger_events[trigger_element][trigger_event]))
10331     return FALSE;
10332
10333   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10334
10335   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10336   {
10337     int element = EL_CUSTOM_START + i;
10338     boolean change_done = FALSE;
10339     int p;
10340
10341     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10342         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10343       continue;
10344
10345     for (p = 0; p < element_info[element].num_change_pages; p++)
10346     {
10347       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10348
10349       if (change->can_change_or_has_action &&
10350           change->has_event[trigger_event] &&
10351           change->trigger_side & trigger_side &&
10352           change->trigger_player & trigger_player &&
10353           change->trigger_page & trigger_page_bits &&
10354           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10355       {
10356         change->actual_trigger_element = trigger_element;
10357         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10358         change->actual_trigger_player_bits = trigger_player;
10359         change->actual_trigger_side = trigger_side;
10360         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10361         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10362
10363         if ((change->can_change && !change_done) || change->has_action)
10364         {
10365           int x, y;
10366
10367           SCAN_PLAYFIELD(x, y)
10368           {
10369             if (Feld[x][y] == element)
10370             {
10371               if (change->can_change && !change_done)
10372               {
10373                 /* if element already changed in this frame, not only prevent
10374                    another element change (checked in ChangeElement()), but
10375                    also prevent additional element actions for this element */
10376
10377                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10378                     !level.use_action_after_change_bug)
10379                   continue;
10380
10381                 ChangeDelay[x][y] = 1;
10382                 ChangeEvent[x][y] = trigger_event;
10383
10384                 HandleElementChange(x, y, p);
10385               }
10386               else if (change->has_action)
10387               {
10388                 /* if element already changed in this frame, not only prevent
10389                    another element change (checked in ChangeElement()), but
10390                    also prevent additional element actions for this element */
10391
10392                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10393                     !level.use_action_after_change_bug)
10394                   continue;
10395
10396                 ExecuteCustomElementAction(x, y, element, p);
10397                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10398               }
10399             }
10400           }
10401
10402           if (change->can_change)
10403           {
10404             change_done = TRUE;
10405             change_done_any = TRUE;
10406           }
10407         }
10408       }
10409     }
10410   }
10411
10412   RECURSION_LOOP_DETECTION_END();
10413
10414   return change_done_any;
10415 }
10416
10417 static boolean CheckElementChangeExt(int x, int y,
10418                                      int element,
10419                                      int trigger_element,
10420                                      int trigger_event,
10421                                      int trigger_player,
10422                                      int trigger_side)
10423 {
10424   boolean change_done = FALSE;
10425   int p;
10426
10427   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10428       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10429     return FALSE;
10430
10431   if (Feld[x][y] == EL_BLOCKED)
10432   {
10433     Blocked2Moving(x, y, &x, &y);
10434     element = Feld[x][y];
10435   }
10436
10437   /* check if element has already changed or is about to change after moving */
10438   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10439        Feld[x][y] != element) ||
10440
10441       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10442        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10443         ChangePage[x][y] != -1)))
10444     return FALSE;
10445
10446   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10447
10448   for (p = 0; p < element_info[element].num_change_pages; p++)
10449   {
10450     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10451
10452     /* check trigger element for all events where the element that is checked
10453        for changing interacts with a directly adjacent element -- this is
10454        different to element changes that affect other elements to change on the
10455        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10456     boolean check_trigger_element =
10457       (trigger_event == CE_TOUCHING_X ||
10458        trigger_event == CE_HITTING_X ||
10459        trigger_event == CE_HIT_BY_X ||
10460        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10461
10462     if (change->can_change_or_has_action &&
10463         change->has_event[trigger_event] &&
10464         change->trigger_side & trigger_side &&
10465         change->trigger_player & trigger_player &&
10466         (!check_trigger_element ||
10467          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10468     {
10469       change->actual_trigger_element = trigger_element;
10470       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10471       change->actual_trigger_player_bits = trigger_player;
10472       change->actual_trigger_side = trigger_side;
10473       change->actual_trigger_ce_value = CustomValue[x][y];
10474       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10475
10476       /* special case: trigger element not at (x,y) position for some events */
10477       if (check_trigger_element)
10478       {
10479         static struct
10480         {
10481           int dx, dy;
10482         } move_xy[] =
10483           {
10484             {  0,  0 },
10485             { -1,  0 },
10486             { +1,  0 },
10487             {  0,  0 },
10488             {  0, -1 },
10489             {  0,  0 }, { 0, 0 }, { 0, 0 },
10490             {  0, +1 }
10491           };
10492
10493         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10494         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10495
10496         change->actual_trigger_ce_value = CustomValue[xx][yy];
10497         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10498       }
10499
10500       if (change->can_change && !change_done)
10501       {
10502         ChangeDelay[x][y] = 1;
10503         ChangeEvent[x][y] = trigger_event;
10504
10505         HandleElementChange(x, y, p);
10506
10507         change_done = TRUE;
10508       }
10509       else if (change->has_action)
10510       {
10511         ExecuteCustomElementAction(x, y, element, p);
10512         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10513       }
10514     }
10515   }
10516
10517   RECURSION_LOOP_DETECTION_END();
10518
10519   return change_done;
10520 }
10521
10522 static void PlayPlayerSound(struct PlayerInfo *player)
10523 {
10524   int jx = player->jx, jy = player->jy;
10525   int sound_element = player->artwork_element;
10526   int last_action = player->last_action_waiting;
10527   int action = player->action_waiting;
10528
10529   if (player->is_waiting)
10530   {
10531     if (action != last_action)
10532       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10533     else
10534       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10535   }
10536   else
10537   {
10538     if (action != last_action)
10539       StopSound(element_info[sound_element].sound[last_action]);
10540
10541     if (last_action == ACTION_SLEEPING)
10542       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10543   }
10544 }
10545
10546 static void PlayAllPlayersSound()
10547 {
10548   int i;
10549
10550   for (i = 0; i < MAX_PLAYERS; i++)
10551     if (stored_player[i].active)
10552       PlayPlayerSound(&stored_player[i]);
10553 }
10554
10555 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10556 {
10557   boolean last_waiting = player->is_waiting;
10558   int move_dir = player->MovDir;
10559
10560   player->dir_waiting = move_dir;
10561   player->last_action_waiting = player->action_waiting;
10562
10563   if (is_waiting)
10564   {
10565     if (!last_waiting)          /* not waiting -> waiting */
10566     {
10567       player->is_waiting = TRUE;
10568
10569       player->frame_counter_bored =
10570         FrameCounter +
10571         game.player_boring_delay_fixed +
10572         GetSimpleRandom(game.player_boring_delay_random);
10573       player->frame_counter_sleeping =
10574         FrameCounter +
10575         game.player_sleeping_delay_fixed +
10576         GetSimpleRandom(game.player_sleeping_delay_random);
10577
10578       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10579     }
10580
10581     if (game.player_sleeping_delay_fixed +
10582         game.player_sleeping_delay_random > 0 &&
10583         player->anim_delay_counter == 0 &&
10584         player->post_delay_counter == 0 &&
10585         FrameCounter >= player->frame_counter_sleeping)
10586       player->is_sleeping = TRUE;
10587     else if (game.player_boring_delay_fixed +
10588              game.player_boring_delay_random > 0 &&
10589              FrameCounter >= player->frame_counter_bored)
10590       player->is_bored = TRUE;
10591
10592     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10593                               player->is_bored ? ACTION_BORING :
10594                               ACTION_WAITING);
10595
10596     if (player->is_sleeping && player->use_murphy)
10597     {
10598       /* special case for sleeping Murphy when leaning against non-free tile */
10599
10600       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10601           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10602            !IS_MOVING(player->jx - 1, player->jy)))
10603         move_dir = MV_LEFT;
10604       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10605                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10606                 !IS_MOVING(player->jx + 1, player->jy)))
10607         move_dir = MV_RIGHT;
10608       else
10609         player->is_sleeping = FALSE;
10610
10611       player->dir_waiting = move_dir;
10612     }
10613
10614     if (player->is_sleeping)
10615     {
10616       if (player->num_special_action_sleeping > 0)
10617       {
10618         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10619         {
10620           int last_special_action = player->special_action_sleeping;
10621           int num_special_action = player->num_special_action_sleeping;
10622           int special_action =
10623             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10624              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10625              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10626              last_special_action + 1 : ACTION_SLEEPING);
10627           int special_graphic =
10628             el_act_dir2img(player->artwork_element, special_action, move_dir);
10629
10630           player->anim_delay_counter =
10631             graphic_info[special_graphic].anim_delay_fixed +
10632             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10633           player->post_delay_counter =
10634             graphic_info[special_graphic].post_delay_fixed +
10635             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10636
10637           player->special_action_sleeping = special_action;
10638         }
10639
10640         if (player->anim_delay_counter > 0)
10641         {
10642           player->action_waiting = player->special_action_sleeping;
10643           player->anim_delay_counter--;
10644         }
10645         else if (player->post_delay_counter > 0)
10646         {
10647           player->post_delay_counter--;
10648         }
10649       }
10650     }
10651     else if (player->is_bored)
10652     {
10653       if (player->num_special_action_bored > 0)
10654       {
10655         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10656         {
10657           int special_action =
10658             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10659           int special_graphic =
10660             el_act_dir2img(player->artwork_element, special_action, move_dir);
10661
10662           player->anim_delay_counter =
10663             graphic_info[special_graphic].anim_delay_fixed +
10664             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10665           player->post_delay_counter =
10666             graphic_info[special_graphic].post_delay_fixed +
10667             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10668
10669           player->special_action_bored = special_action;
10670         }
10671
10672         if (player->anim_delay_counter > 0)
10673         {
10674           player->action_waiting = player->special_action_bored;
10675           player->anim_delay_counter--;
10676         }
10677         else if (player->post_delay_counter > 0)
10678         {
10679           player->post_delay_counter--;
10680         }
10681       }
10682     }
10683   }
10684   else if (last_waiting)        /* waiting -> not waiting */
10685   {
10686     player->is_waiting = FALSE;
10687     player->is_bored = FALSE;
10688     player->is_sleeping = FALSE;
10689
10690     player->frame_counter_bored = -1;
10691     player->frame_counter_sleeping = -1;
10692
10693     player->anim_delay_counter = 0;
10694     player->post_delay_counter = 0;
10695
10696     player->dir_waiting = player->MovDir;
10697     player->action_waiting = ACTION_DEFAULT;
10698
10699     player->special_action_bored = ACTION_DEFAULT;
10700     player->special_action_sleeping = ACTION_DEFAULT;
10701   }
10702 }
10703
10704 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10705 {
10706   static boolean player_was_moving = FALSE;
10707   static boolean player_was_snapping = FALSE;
10708   static boolean player_was_dropping = FALSE;
10709
10710   if ((!player->is_moving  && player_was_moving) ||
10711       (player->MovPos == 0 && player_was_moving) ||
10712       (player->is_snapping && !player_was_snapping) ||
10713       (player->is_dropping && !player_was_dropping))
10714   {
10715     if (!SaveEngineSnapshotToList())
10716       return;
10717
10718     player_was_moving = FALSE;
10719     player_was_snapping = TRUE;
10720     player_was_dropping = TRUE;
10721   }
10722   else
10723   {
10724     if (player->is_moving)
10725       player_was_moving = TRUE;
10726
10727     if (!player->is_snapping)
10728       player_was_snapping = FALSE;
10729
10730     if (!player->is_dropping)
10731       player_was_dropping = FALSE;
10732   }
10733 }
10734
10735 static void CheckSingleStepMode(struct PlayerInfo *player)
10736 {
10737   if (tape.single_step && tape.recording && !tape.pausing)
10738   {
10739     /* as it is called "single step mode", just return to pause mode when the
10740        player stopped moving after one tile (or never starts moving at all) */
10741     if (!player->is_moving && !player->is_pushing)
10742     {
10743       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10744       SnapField(player, 0, 0);                  /* stop snapping */
10745     }
10746   }
10747
10748   CheckSaveEngineSnapshot(player);
10749 }
10750
10751 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10752 {
10753   int left      = player_action & JOY_LEFT;
10754   int right     = player_action & JOY_RIGHT;
10755   int up        = player_action & JOY_UP;
10756   int down      = player_action & JOY_DOWN;
10757   int button1   = player_action & JOY_BUTTON_1;
10758   int button2   = player_action & JOY_BUTTON_2;
10759   int dx        = (left ? -1 : right ? 1 : 0);
10760   int dy        = (up   ? -1 : down  ? 1 : 0);
10761
10762   if (!player->active || tape.pausing)
10763     return 0;
10764
10765   if (player_action)
10766   {
10767     if (button1)
10768       SnapField(player, dx, dy);
10769     else
10770     {
10771       if (button2)
10772         DropElement(player);
10773
10774       MovePlayer(player, dx, dy);
10775     }
10776
10777     CheckSingleStepMode(player);
10778
10779     SetPlayerWaiting(player, FALSE);
10780
10781     return player_action;
10782   }
10783   else
10784   {
10785     /* no actions for this player (no input at player's configured device) */
10786
10787     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10788     SnapField(player, 0, 0);
10789     CheckGravityMovementWhenNotMoving(player);
10790
10791     if (player->MovPos == 0)
10792       SetPlayerWaiting(player, TRUE);
10793
10794     if (player->MovPos == 0)    /* needed for tape.playing */
10795       player->is_moving = FALSE;
10796
10797     player->is_dropping = FALSE;
10798     player->is_dropping_pressed = FALSE;
10799     player->drop_pressed_delay = 0;
10800
10801     CheckSingleStepMode(player);
10802
10803     return 0;
10804   }
10805 }
10806
10807 static void CheckLevelTime()
10808 {
10809   int i;
10810
10811   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10812   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10813   {
10814     if (level.native_em_level->lev->home == 0)  /* all players at home */
10815     {
10816       PlayerWins(local_player);
10817
10818       AllPlayersGone = TRUE;
10819
10820       level.native_em_level->lev->home = -1;
10821     }
10822
10823     if (level.native_em_level->ply[0]->alive == 0 &&
10824         level.native_em_level->ply[1]->alive == 0 &&
10825         level.native_em_level->ply[2]->alive == 0 &&
10826         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10827       AllPlayersGone = TRUE;
10828   }
10829   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10830   {
10831     if (game_sp.LevelSolved &&
10832         !game_sp.GameOver)                              /* game won */
10833     {
10834       PlayerWins(local_player);
10835
10836       game_sp.GameOver = TRUE;
10837
10838       AllPlayersGone = TRUE;
10839     }
10840
10841     if (game_sp.GameOver)                               /* game lost */
10842       AllPlayersGone = TRUE;
10843   }
10844
10845   if (TimeFrames >= FRAMES_PER_SECOND)
10846   {
10847     TimeFrames = 0;
10848     TapeTime++;
10849
10850     for (i = 0; i < MAX_PLAYERS; i++)
10851     {
10852       struct PlayerInfo *player = &stored_player[i];
10853
10854       if (SHIELD_ON(player))
10855       {
10856         player->shield_normal_time_left--;
10857
10858         if (player->shield_deadly_time_left > 0)
10859           player->shield_deadly_time_left--;
10860       }
10861     }
10862
10863     if (!local_player->LevelSolved && !level.use_step_counter)
10864     {
10865       TimePlayed++;
10866
10867       if (TimeLeft > 0)
10868       {
10869         TimeLeft--;
10870
10871         if (TimeLeft <= 10 && setup.time_limit)
10872           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10873
10874         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10875            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10876
10877         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10878
10879         if (!TimeLeft && setup.time_limit)
10880         {
10881           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10882             level.native_em_level->lev->killed_out_of_time = TRUE;
10883           else
10884             for (i = 0; i < MAX_PLAYERS; i++)
10885               KillPlayer(&stored_player[i]);
10886         }
10887       }
10888       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10889       {
10890         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10891       }
10892
10893       level.native_em_level->lev->time =
10894         (game.no_time_limit ? TimePlayed : TimeLeft);
10895     }
10896
10897     if (tape.recording || tape.playing)
10898       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10899   }
10900
10901   if (tape.recording || tape.playing)
10902     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10903
10904   UpdateAndDisplayGameControlValues();
10905 }
10906
10907 void AdvanceFrameAndPlayerCounters(int player_nr)
10908 {
10909   int i;
10910
10911   /* advance frame counters (global frame counter and time frame counter) */
10912   FrameCounter++;
10913   TimeFrames++;
10914
10915   /* advance player counters (counters for move delay, move animation etc.) */
10916   for (i = 0; i < MAX_PLAYERS; i++)
10917   {
10918     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10919     int move_delay_value = stored_player[i].move_delay_value;
10920     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10921
10922     if (!advance_player_counters)       /* not all players may be affected */
10923       continue;
10924
10925     if (move_frames == 0)       /* less than one move per game frame */
10926     {
10927       int stepsize = TILEX / move_delay_value;
10928       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10929       int count = (stored_player[i].is_moving ?
10930                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10931
10932       if (count % delay == 0)
10933         move_frames = 1;
10934     }
10935
10936     stored_player[i].Frame += move_frames;
10937
10938     if (stored_player[i].MovPos != 0)
10939       stored_player[i].StepFrame += move_frames;
10940
10941     if (stored_player[i].move_delay > 0)
10942       stored_player[i].move_delay--;
10943
10944     /* due to bugs in previous versions, counter must count up, not down */
10945     if (stored_player[i].push_delay != -1)
10946       stored_player[i].push_delay++;
10947
10948     if (stored_player[i].drop_delay > 0)
10949       stored_player[i].drop_delay--;
10950
10951     if (stored_player[i].is_dropping_pressed)
10952       stored_player[i].drop_pressed_delay++;
10953   }
10954 }
10955
10956 void StartGameActions(boolean init_network_game, boolean record_tape,
10957                       int random_seed)
10958 {
10959   unsigned int new_random_seed = InitRND(random_seed);
10960
10961   if (record_tape)
10962     TapeStartRecording(new_random_seed);
10963
10964 #if defined(NETWORK_AVALIABLE)
10965   if (init_network_game)
10966   {
10967     SendToServer_StartPlaying();
10968
10969     return;
10970   }
10971 #endif
10972
10973   InitGame();
10974 }
10975
10976 void GameActions()
10977 {
10978   static unsigned int game_frame_delay = 0;
10979   unsigned int game_frame_delay_value;
10980   byte *recorded_player_action;
10981   byte summarized_player_action = 0;
10982   byte tape_action[MAX_PLAYERS];
10983   int i;
10984
10985   /* detect endless loops, caused by custom element programming */
10986   if (recursion_loop_detected && recursion_loop_depth == 0)
10987   {
10988     char *message = getStringCat3("Internal Error! Element ",
10989                                   EL_NAME(recursion_loop_element),
10990                                   " caused endless loop! Quit the game?");
10991
10992     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10993           EL_NAME(recursion_loop_element));
10994
10995     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10996
10997     recursion_loop_detected = FALSE;    /* if game should be continued */
10998
10999     free(message);
11000
11001     return;
11002   }
11003
11004   if (game.restart_level)
11005     StartGameActions(options.network, setup.autorecord, level.random_seed);
11006
11007   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11008   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11009   {
11010     if (level.native_em_level->lev->home == 0)  /* all players at home */
11011     {
11012       PlayerWins(local_player);
11013
11014       AllPlayersGone = TRUE;
11015
11016       level.native_em_level->lev->home = -1;
11017     }
11018
11019     if (level.native_em_level->ply[0]->alive == 0 &&
11020         level.native_em_level->ply[1]->alive == 0 &&
11021         level.native_em_level->ply[2]->alive == 0 &&
11022         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11023       AllPlayersGone = TRUE;
11024   }
11025   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11026   {
11027     if (game_sp.LevelSolved &&
11028         !game_sp.GameOver)                              /* game won */
11029     {
11030       PlayerWins(local_player);
11031
11032       game_sp.GameOver = TRUE;
11033
11034       AllPlayersGone = TRUE;
11035     }
11036
11037     if (game_sp.GameOver)                               /* game lost */
11038       AllPlayersGone = TRUE;
11039   }
11040
11041   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11042     GameWon();
11043
11044   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11045     TapeStop();
11046
11047   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11048     return;
11049
11050   game_frame_delay_value =
11051     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11052
11053   if (tape.playing && tape.warp_forward && !tape.pausing)
11054     game_frame_delay_value = 0;
11055
11056 #if 0
11057   /* ---------- main game synchronization point ---------- */
11058
11059   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11060
11061   printf("::: skip == %d\n", skip);
11062
11063 #else
11064   /* ---------- main game synchronization point ---------- */
11065
11066   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11067 #endif
11068
11069   if (network_playing && !network_player_action_received)
11070   {
11071     /* try to get network player actions in time */
11072
11073 #if defined(NETWORK_AVALIABLE)
11074     /* last chance to get network player actions without main loop delay */
11075     HandleNetworking();
11076 #endif
11077
11078     /* game was quit by network peer */
11079     if (game_status != GAME_MODE_PLAYING)
11080       return;
11081
11082     if (!network_player_action_received)
11083       return;           /* failed to get network player actions in time */
11084
11085     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11086   }
11087
11088   if (tape.pausing)
11089     return;
11090
11091   /* at this point we know that we really continue executing the game */
11092
11093   network_player_action_received = FALSE;
11094
11095   /* when playing tape, read previously recorded player input from tape data */
11096   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11097
11098   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11099   if (tape.pausing)
11100     return;
11101
11102   if (tape.set_centered_player)
11103   {
11104     game.centered_player_nr_next = tape.centered_player_nr_next;
11105     game.set_centered_player = TRUE;
11106   }
11107
11108   for (i = 0; i < MAX_PLAYERS; i++)
11109   {
11110     summarized_player_action |= stored_player[i].action;
11111
11112     if (!network_playing && (game.team_mode || tape.playing))
11113       stored_player[i].effective_action = stored_player[i].action;
11114   }
11115
11116 #if defined(NETWORK_AVALIABLE)
11117   if (network_playing)
11118     SendToServer_MovePlayer(summarized_player_action);
11119 #endif
11120
11121   if (!options.network && !game.team_mode)
11122     local_player->effective_action = summarized_player_action;
11123
11124   if (tape.recording &&
11125       setup.team_mode &&
11126       setup.input_on_focus &&
11127       game.centered_player_nr != -1)
11128   {
11129     for (i = 0; i < MAX_PLAYERS; i++)
11130       stored_player[i].effective_action =
11131         (i == game.centered_player_nr ? summarized_player_action : 0);
11132   }
11133
11134   if (recorded_player_action != NULL)
11135     for (i = 0; i < MAX_PLAYERS; i++)
11136       stored_player[i].effective_action = recorded_player_action[i];
11137
11138   for (i = 0; i < MAX_PLAYERS; i++)
11139   {
11140     tape_action[i] = stored_player[i].effective_action;
11141
11142     /* (this may happen in the RND game engine if a player was not present on
11143        the playfield on level start, but appeared later from a custom element */
11144     if (setup.team_mode &&
11145         tape.recording &&
11146         tape_action[i] &&
11147         !tape.player_participates[i])
11148       tape.player_participates[i] = TRUE;
11149   }
11150
11151   /* only record actions from input devices, but not programmed actions */
11152   if (tape.recording)
11153     TapeRecordAction(tape_action);
11154
11155 #if USE_NEW_PLAYER_ASSIGNMENTS
11156   // !!! also map player actions in single player mode !!!
11157   // if (game.team_mode)
11158   {
11159     byte mapped_action[MAX_PLAYERS];
11160
11161 #if DEBUG_PLAYER_ACTIONS
11162     printf(":::");
11163     for (i = 0; i < MAX_PLAYERS; i++)
11164       printf(" %d, ", stored_player[i].effective_action);
11165 #endif
11166
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11169
11170     for (i = 0; i < MAX_PLAYERS; i++)
11171       stored_player[i].effective_action = mapped_action[i];
11172
11173 #if DEBUG_PLAYER_ACTIONS
11174     printf(" =>");
11175     for (i = 0; i < MAX_PLAYERS; i++)
11176       printf(" %d, ", stored_player[i].effective_action);
11177     printf("\n");
11178 #endif
11179   }
11180 #if DEBUG_PLAYER_ACTIONS
11181   else
11182   {
11183     printf(":::");
11184     for (i = 0; i < MAX_PLAYERS; i++)
11185       printf(" %d, ", stored_player[i].effective_action);
11186     printf("\n");
11187   }
11188 #endif
11189 #endif
11190
11191   for (i = 0; i < MAX_PLAYERS; i++)
11192   {
11193     // allow engine snapshot in case of changed movement attempt
11194     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11195         (stored_player[i].effective_action & KEY_MOTION))
11196       game.snapshot.changed_action = TRUE;
11197
11198     // allow engine snapshot in case of snapping/dropping attempt
11199     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11200         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11201       game.snapshot.changed_action = TRUE;
11202
11203     game.snapshot.last_action[i] = stored_player[i].effective_action;
11204   }
11205
11206   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11207   {
11208     GameActions_EM_Main();
11209   }
11210   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11211   {
11212     GameActions_SP_Main();
11213   }
11214   else
11215   {
11216     GameActions_RND_Main();
11217   }
11218
11219   BlitScreenToBitmap(backbuffer);
11220
11221   CheckLevelTime();
11222
11223   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11224
11225   if (options.debug)                    /* calculate frames per second */
11226   {
11227     static unsigned int fps_counter = 0;
11228     static int fps_frames = 0;
11229     unsigned int fps_delay_ms = Counter() - fps_counter;
11230
11231     fps_frames++;
11232
11233     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11234     {
11235       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11236
11237       fps_frames = 0;
11238       fps_counter = Counter();
11239     }
11240
11241     redraw_mask |= REDRAW_FPS;
11242   }
11243 }
11244
11245 void GameActions_EM_Main()
11246 {
11247   byte effective_action[MAX_PLAYERS];
11248   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11249   int i;
11250
11251   for (i = 0; i < MAX_PLAYERS; i++)
11252     effective_action[i] = stored_player[i].effective_action;
11253
11254   GameActions_EM(effective_action, warp_mode);
11255 }
11256
11257 void GameActions_SP_Main()
11258 {
11259   byte effective_action[MAX_PLAYERS];
11260   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11261   int i;
11262
11263   for (i = 0; i < MAX_PLAYERS; i++)
11264     effective_action[i] = stored_player[i].effective_action;
11265
11266   GameActions_SP(effective_action, warp_mode);
11267 }
11268
11269 void GameActions_RND_Main()
11270 {
11271   GameActions_RND();
11272 }
11273
11274 void GameActions_RND()
11275 {
11276   int magic_wall_x = 0, magic_wall_y = 0;
11277   int i, x, y, element, graphic;
11278
11279   InitPlayfieldScanModeVars();
11280
11281   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11282   {
11283     SCAN_PLAYFIELD(x, y)
11284     {
11285       ChangeCount[x][y] = 0;
11286       ChangeEvent[x][y] = -1;
11287     }
11288   }
11289
11290   if (game.set_centered_player)
11291   {
11292     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11293
11294     /* switching to "all players" only possible if all players fit to screen */
11295     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11296     {
11297       game.centered_player_nr_next = game.centered_player_nr;
11298       game.set_centered_player = FALSE;
11299     }
11300
11301     /* do not switch focus to non-existing (or non-active) player */
11302     if (game.centered_player_nr_next >= 0 &&
11303         !stored_player[game.centered_player_nr_next].active)
11304     {
11305       game.centered_player_nr_next = game.centered_player_nr;
11306       game.set_centered_player = FALSE;
11307     }
11308   }
11309
11310   if (game.set_centered_player &&
11311       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11312   {
11313     int sx, sy;
11314
11315     if (game.centered_player_nr_next == -1)
11316     {
11317       setScreenCenteredToAllPlayers(&sx, &sy);
11318     }
11319     else
11320     {
11321       sx = stored_player[game.centered_player_nr_next].jx;
11322       sy = stored_player[game.centered_player_nr_next].jy;
11323     }
11324
11325     game.centered_player_nr = game.centered_player_nr_next;
11326     game.set_centered_player = FALSE;
11327
11328     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11329     DrawGameDoorValues();
11330   }
11331
11332   for (i = 0; i < MAX_PLAYERS; i++)
11333   {
11334     int actual_player_action = stored_player[i].effective_action;
11335
11336 #if 1
11337     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11338        - rnd_equinox_tetrachloride 048
11339        - rnd_equinox_tetrachloride_ii 096
11340        - rnd_emanuel_schmieg 002
11341        - doctor_sloan_ww 001, 020
11342     */
11343     if (stored_player[i].MovPos == 0)
11344       CheckGravityMovement(&stored_player[i]);
11345 #endif
11346
11347     /* overwrite programmed action with tape action */
11348     if (stored_player[i].programmed_action)
11349       actual_player_action = stored_player[i].programmed_action;
11350
11351     PlayerActions(&stored_player[i], actual_player_action);
11352
11353     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11354   }
11355
11356   ScrollScreen(NULL, SCROLL_GO_ON);
11357
11358   /* for backwards compatibility, the following code emulates a fixed bug that
11359      occured when pushing elements (causing elements that just made their last
11360      pushing step to already (if possible) make their first falling step in the
11361      same game frame, which is bad); this code is also needed to use the famous
11362      "spring push bug" which is used in older levels and might be wanted to be
11363      used also in newer levels, but in this case the buggy pushing code is only
11364      affecting the "spring" element and no other elements */
11365
11366   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11367   {
11368     for (i = 0; i < MAX_PLAYERS; i++)
11369     {
11370       struct PlayerInfo *player = &stored_player[i];
11371       int x = player->jx;
11372       int y = player->jy;
11373
11374       if (player->active && player->is_pushing && player->is_moving &&
11375           IS_MOVING(x, y) &&
11376           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11377            Feld[x][y] == EL_SPRING))
11378       {
11379         ContinueMoving(x, y);
11380
11381         /* continue moving after pushing (this is actually a bug) */
11382         if (!IS_MOVING(x, y))
11383           Stop[x][y] = FALSE;
11384       }
11385     }
11386   }
11387
11388   SCAN_PLAYFIELD(x, y)
11389   {
11390     ChangeCount[x][y] = 0;
11391     ChangeEvent[x][y] = -1;
11392
11393     /* this must be handled before main playfield loop */
11394     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11395     {
11396       MovDelay[x][y]--;
11397       if (MovDelay[x][y] <= 0)
11398         RemoveField(x, y);
11399     }
11400
11401     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11402     {
11403       MovDelay[x][y]--;
11404       if (MovDelay[x][y] <= 0)
11405       {
11406         RemoveField(x, y);
11407         TEST_DrawLevelField(x, y);
11408
11409         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11410       }
11411     }
11412
11413 #if DEBUG
11414     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11415     {
11416       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11417       printf("GameActions(): This should never happen!\n");
11418
11419       ChangePage[x][y] = -1;
11420     }
11421 #endif
11422
11423     Stop[x][y] = FALSE;
11424     if (WasJustMoving[x][y] > 0)
11425       WasJustMoving[x][y]--;
11426     if (WasJustFalling[x][y] > 0)
11427       WasJustFalling[x][y]--;
11428     if (CheckCollision[x][y] > 0)
11429       CheckCollision[x][y]--;
11430     if (CheckImpact[x][y] > 0)
11431       CheckImpact[x][y]--;
11432
11433     GfxFrame[x][y]++;
11434
11435     /* reset finished pushing action (not done in ContinueMoving() to allow
11436        continuous pushing animation for elements with zero push delay) */
11437     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11438     {
11439       ResetGfxAnimation(x, y);
11440       TEST_DrawLevelField(x, y);
11441     }
11442
11443 #if DEBUG
11444     if (IS_BLOCKED(x, y))
11445     {
11446       int oldx, oldy;
11447
11448       Blocked2Moving(x, y, &oldx, &oldy);
11449       if (!IS_MOVING(oldx, oldy))
11450       {
11451         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11452         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11453         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11454         printf("GameActions(): This should never happen!\n");
11455       }
11456     }
11457 #endif
11458   }
11459
11460   SCAN_PLAYFIELD(x, y)
11461   {
11462     element = Feld[x][y];
11463     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11464
11465     ResetGfxFrame(x, y, TRUE);
11466
11467     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11468         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11469       ResetRandomAnimationValue(x, y);
11470
11471     SetRandomAnimationValue(x, y);
11472
11473     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11474
11475     if (IS_INACTIVE(element))
11476     {
11477       if (IS_ANIMATED(graphic))
11478         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11479
11480       continue;
11481     }
11482
11483     /* this may take place after moving, so 'element' may have changed */
11484     if (IS_CHANGING(x, y) &&
11485         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11486     {
11487       int page = element_info[element].event_page_nr[CE_DELAY];
11488
11489       HandleElementChange(x, y, page);
11490
11491       element = Feld[x][y];
11492       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11493     }
11494
11495     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11496     {
11497       StartMoving(x, y);
11498
11499       element = Feld[x][y];
11500       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11501
11502       if (IS_ANIMATED(graphic) &&
11503           !IS_MOVING(x, y) &&
11504           !Stop[x][y])
11505         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11506
11507       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11508         TEST_DrawTwinkleOnField(x, y);
11509     }
11510     else if ((element == EL_ACID ||
11511               element == EL_EXIT_OPEN ||
11512               element == EL_EM_EXIT_OPEN ||
11513               element == EL_SP_EXIT_OPEN ||
11514               element == EL_STEEL_EXIT_OPEN ||
11515               element == EL_EM_STEEL_EXIT_OPEN ||
11516               element == EL_SP_TERMINAL ||
11517               element == EL_SP_TERMINAL_ACTIVE ||
11518               element == EL_EXTRA_TIME ||
11519               element == EL_SHIELD_NORMAL ||
11520               element == EL_SHIELD_DEADLY) &&
11521              IS_ANIMATED(graphic))
11522       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11523     else if (IS_MOVING(x, y))
11524       ContinueMoving(x, y);
11525     else if (IS_ACTIVE_BOMB(element))
11526       CheckDynamite(x, y);
11527     else if (element == EL_AMOEBA_GROWING)
11528       AmoebeWaechst(x, y);
11529     else if (element == EL_AMOEBA_SHRINKING)
11530       AmoebaDisappearing(x, y);
11531
11532 #if !USE_NEW_AMOEBA_CODE
11533     else if (IS_AMOEBALIVE(element))
11534       AmoebeAbleger(x, y);
11535 #endif
11536
11537     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11538       Life(x, y);
11539     else if (element == EL_EXIT_CLOSED)
11540       CheckExit(x, y);
11541     else if (element == EL_EM_EXIT_CLOSED)
11542       CheckExitEM(x, y);
11543     else if (element == EL_STEEL_EXIT_CLOSED)
11544       CheckExitSteel(x, y);
11545     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11546       CheckExitSteelEM(x, y);
11547     else if (element == EL_SP_EXIT_CLOSED)
11548       CheckExitSP(x, y);
11549     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11550              element == EL_EXPANDABLE_STEELWALL_GROWING)
11551       MauerWaechst(x, y);
11552     else if (element == EL_EXPANDABLE_WALL ||
11553              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11554              element == EL_EXPANDABLE_WALL_VERTICAL ||
11555              element == EL_EXPANDABLE_WALL_ANY ||
11556              element == EL_BD_EXPANDABLE_WALL)
11557       MauerAbleger(x, y);
11558     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11559              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11560              element == EL_EXPANDABLE_STEELWALL_ANY)
11561       MauerAblegerStahl(x, y);
11562     else if (element == EL_FLAMES)
11563       CheckForDragon(x, y);
11564     else if (element == EL_EXPLOSION)
11565       ; /* drawing of correct explosion animation is handled separately */
11566     else if (element == EL_ELEMENT_SNAPPING ||
11567              element == EL_DIAGONAL_SHRINKING ||
11568              element == EL_DIAGONAL_GROWING)
11569     {
11570       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11571
11572       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11573     }
11574     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11575       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11576
11577     if (IS_BELT_ACTIVE(element))
11578       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11579
11580     if (game.magic_wall_active)
11581     {
11582       int jx = local_player->jx, jy = local_player->jy;
11583
11584       /* play the element sound at the position nearest to the player */
11585       if ((element == EL_MAGIC_WALL_FULL ||
11586            element == EL_MAGIC_WALL_ACTIVE ||
11587            element == EL_MAGIC_WALL_EMPTYING ||
11588            element == EL_BD_MAGIC_WALL_FULL ||
11589            element == EL_BD_MAGIC_WALL_ACTIVE ||
11590            element == EL_BD_MAGIC_WALL_EMPTYING ||
11591            element == EL_DC_MAGIC_WALL_FULL ||
11592            element == EL_DC_MAGIC_WALL_ACTIVE ||
11593            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11594           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11595       {
11596         magic_wall_x = x;
11597         magic_wall_y = y;
11598       }
11599     }
11600   }
11601
11602 #if USE_NEW_AMOEBA_CODE
11603   /* new experimental amoeba growth stuff */
11604   if (!(FrameCounter % 8))
11605   {
11606     static unsigned int random = 1684108901;
11607
11608     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11609     {
11610       x = RND(lev_fieldx);
11611       y = RND(lev_fieldy);
11612       element = Feld[x][y];
11613
11614       if (!IS_PLAYER(x,y) &&
11615           (element == EL_EMPTY ||
11616            CAN_GROW_INTO(element) ||
11617            element == EL_QUICKSAND_EMPTY ||
11618            element == EL_QUICKSAND_FAST_EMPTY ||
11619            element == EL_ACID_SPLASH_LEFT ||
11620            element == EL_ACID_SPLASH_RIGHT))
11621       {
11622         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11623             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11624             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11625             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11626           Feld[x][y] = EL_AMOEBA_DROP;
11627       }
11628
11629       random = random * 129 + 1;
11630     }
11631   }
11632 #endif
11633
11634   game.explosions_delayed = FALSE;
11635
11636   SCAN_PLAYFIELD(x, y)
11637   {
11638     element = Feld[x][y];
11639
11640     if (ExplodeField[x][y])
11641       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11642     else if (element == EL_EXPLOSION)
11643       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11644
11645     ExplodeField[x][y] = EX_TYPE_NONE;
11646   }
11647
11648   game.explosions_delayed = TRUE;
11649
11650   if (game.magic_wall_active)
11651   {
11652     if (!(game.magic_wall_time_left % 4))
11653     {
11654       int element = Feld[magic_wall_x][magic_wall_y];
11655
11656       if (element == EL_BD_MAGIC_WALL_FULL ||
11657           element == EL_BD_MAGIC_WALL_ACTIVE ||
11658           element == EL_BD_MAGIC_WALL_EMPTYING)
11659         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11660       else if (element == EL_DC_MAGIC_WALL_FULL ||
11661                element == EL_DC_MAGIC_WALL_ACTIVE ||
11662                element == EL_DC_MAGIC_WALL_EMPTYING)
11663         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11664       else
11665         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11666     }
11667
11668     if (game.magic_wall_time_left > 0)
11669     {
11670       game.magic_wall_time_left--;
11671
11672       if (!game.magic_wall_time_left)
11673       {
11674         SCAN_PLAYFIELD(x, y)
11675         {
11676           element = Feld[x][y];
11677
11678           if (element == EL_MAGIC_WALL_ACTIVE ||
11679               element == EL_MAGIC_WALL_FULL)
11680           {
11681             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11682             TEST_DrawLevelField(x, y);
11683           }
11684           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11685                    element == EL_BD_MAGIC_WALL_FULL)
11686           {
11687             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11688             TEST_DrawLevelField(x, y);
11689           }
11690           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11691                    element == EL_DC_MAGIC_WALL_FULL)
11692           {
11693             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11694             TEST_DrawLevelField(x, y);
11695           }
11696         }
11697
11698         game.magic_wall_active = FALSE;
11699       }
11700     }
11701   }
11702
11703   if (game.light_time_left > 0)
11704   {
11705     game.light_time_left--;
11706
11707     if (game.light_time_left == 0)
11708       RedrawAllLightSwitchesAndInvisibleElements();
11709   }
11710
11711   if (game.timegate_time_left > 0)
11712   {
11713     game.timegate_time_left--;
11714
11715     if (game.timegate_time_left == 0)
11716       CloseAllOpenTimegates();
11717   }
11718
11719   if (game.lenses_time_left > 0)
11720   {
11721     game.lenses_time_left--;
11722
11723     if (game.lenses_time_left == 0)
11724       RedrawAllInvisibleElementsForLenses();
11725   }
11726
11727   if (game.magnify_time_left > 0)
11728   {
11729     game.magnify_time_left--;
11730
11731     if (game.magnify_time_left == 0)
11732       RedrawAllInvisibleElementsForMagnifier();
11733   }
11734
11735   for (i = 0; i < MAX_PLAYERS; i++)
11736   {
11737     struct PlayerInfo *player = &stored_player[i];
11738
11739     if (SHIELD_ON(player))
11740     {
11741       if (player->shield_deadly_time_left)
11742         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11743       else if (player->shield_normal_time_left)
11744         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11745     }
11746   }
11747
11748 #if USE_DELAYED_GFX_REDRAW
11749   SCAN_PLAYFIELD(x, y)
11750   {
11751     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11752     {
11753       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11754          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11755
11756       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11757         DrawLevelField(x, y);
11758
11759       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11760         DrawLevelFieldCrumbled(x, y);
11761
11762       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11763         DrawLevelFieldCrumbledNeighbours(x, y);
11764
11765       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11766         DrawTwinkleOnField(x, y);
11767     }
11768
11769     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11770   }
11771 #endif
11772
11773   DrawAllPlayers();
11774   PlayAllPlayersSound();
11775
11776   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11777   {
11778     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11779
11780     local_player->show_envelope = 0;
11781   }
11782
11783   /* use random number generator in every frame to make it less predictable */
11784   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11785     RND(1);
11786 }
11787
11788 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11789 {
11790   int min_x = x, min_y = y, max_x = x, max_y = y;
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 || &stored_player[i] == player)
11798       continue;
11799
11800     min_x = MIN(min_x, jx);
11801     min_y = MIN(min_y, jy);
11802     max_x = MAX(max_x, jx);
11803     max_y = MAX(max_y, jy);
11804   }
11805
11806   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11807 }
11808
11809 static boolean AllPlayersInVisibleScreen()
11810 {
11811   int i;
11812
11813   for (i = 0; i < MAX_PLAYERS; i++)
11814   {
11815     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11816
11817     if (!stored_player[i].active)
11818       continue;
11819
11820     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11821       return FALSE;
11822   }
11823
11824   return TRUE;
11825 }
11826
11827 void ScrollLevel(int dx, int dy)
11828 {
11829   int scroll_offset = 2 * TILEX_VAR;
11830   int x, y;
11831
11832   BlitBitmap(drawto_field, drawto_field,
11833              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11834              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11835              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11836              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11837              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11838              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11839
11840   if (dx != 0)
11841   {
11842     x = (dx == 1 ? BX1 : BX2);
11843     for (y = BY1; y <= BY2; y++)
11844       DrawScreenField(x, y);
11845   }
11846
11847   if (dy != 0)
11848   {
11849     y = (dy == 1 ? BY1 : BY2);
11850     for (x = BX1; x <= BX2; x++)
11851       DrawScreenField(x, y);
11852   }
11853
11854   redraw_mask |= REDRAW_FIELD;
11855 }
11856
11857 static boolean canFallDown(struct PlayerInfo *player)
11858 {
11859   int jx = player->jx, jy = player->jy;
11860
11861   return (IN_LEV_FIELD(jx, jy + 1) &&
11862           (IS_FREE(jx, jy + 1) ||
11863            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11864           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11865           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11866 }
11867
11868 static boolean canPassField(int x, int y, int move_dir)
11869 {
11870   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11871   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11872   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11873   int nextx = x + dx;
11874   int nexty = y + dy;
11875   int element = Feld[x][y];
11876
11877   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11878           !CAN_MOVE(element) &&
11879           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11880           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11881           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11882 }
11883
11884 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11885 {
11886   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11887   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11888   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11889   int newx = x + dx;
11890   int newy = y + dy;
11891
11892   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11893           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11894           (IS_DIGGABLE(Feld[newx][newy]) ||
11895            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11896            canPassField(newx, newy, move_dir)));
11897 }
11898
11899 static void CheckGravityMovement(struct PlayerInfo *player)
11900 {
11901   if (player->gravity && !player->programmed_action)
11902   {
11903     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11904     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11905     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11906     int jx = player->jx, jy = player->jy;
11907     boolean player_is_moving_to_valid_field =
11908       (!player_is_snapping &&
11909        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11910         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11911     boolean player_can_fall_down = canFallDown(player);
11912
11913     if (player_can_fall_down &&
11914         !player_is_moving_to_valid_field)
11915       player->programmed_action = MV_DOWN;
11916   }
11917 }
11918
11919 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11920 {
11921   return CheckGravityMovement(player);
11922
11923   if (player->gravity && !player->programmed_action)
11924   {
11925     int jx = player->jx, jy = player->jy;
11926     boolean field_under_player_is_free =
11927       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11928     boolean player_is_standing_on_valid_field =
11929       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11930        (IS_WALKABLE(Feld[jx][jy]) &&
11931         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11932
11933     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11934       player->programmed_action = MV_DOWN;
11935   }
11936 }
11937
11938 /*
11939   MovePlayerOneStep()
11940   -----------------------------------------------------------------------------
11941   dx, dy:               direction (non-diagonal) to try to move the player to
11942   real_dx, real_dy:     direction as read from input device (can be diagonal)
11943 */
11944
11945 boolean MovePlayerOneStep(struct PlayerInfo *player,
11946                           int dx, int dy, int real_dx, int real_dy)
11947 {
11948   int jx = player->jx, jy = player->jy;
11949   int new_jx = jx + dx, new_jy = jy + dy;
11950   int can_move;
11951   boolean player_can_move = !player->cannot_move;
11952
11953   if (!player->active || (!dx && !dy))
11954     return MP_NO_ACTION;
11955
11956   player->MovDir = (dx < 0 ? MV_LEFT :
11957                     dx > 0 ? MV_RIGHT :
11958                     dy < 0 ? MV_UP :
11959                     dy > 0 ? MV_DOWN :  MV_NONE);
11960
11961   if (!IN_LEV_FIELD(new_jx, new_jy))
11962     return MP_NO_ACTION;
11963
11964   if (!player_can_move)
11965   {
11966     if (player->MovPos == 0)
11967     {
11968       player->is_moving = FALSE;
11969       player->is_digging = FALSE;
11970       player->is_collecting = FALSE;
11971       player->is_snapping = FALSE;
11972       player->is_pushing = FALSE;
11973     }
11974   }
11975
11976   if (!options.network && game.centered_player_nr == -1 &&
11977       !AllPlayersInSight(player, new_jx, new_jy))
11978     return MP_NO_ACTION;
11979
11980   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11981   if (can_move != MP_MOVING)
11982     return can_move;
11983
11984   /* check if DigField() has caused relocation of the player */
11985   if (player->jx != jx || player->jy != jy)
11986     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11987
11988   StorePlayer[jx][jy] = 0;
11989   player->last_jx = jx;
11990   player->last_jy = jy;
11991   player->jx = new_jx;
11992   player->jy = new_jy;
11993   StorePlayer[new_jx][new_jy] = player->element_nr;
11994
11995   if (player->move_delay_value_next != -1)
11996   {
11997     player->move_delay_value = player->move_delay_value_next;
11998     player->move_delay_value_next = -1;
11999   }
12000
12001   player->MovPos =
12002     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12003
12004   player->step_counter++;
12005
12006   PlayerVisit[jx][jy] = FrameCounter;
12007
12008   player->is_moving = TRUE;
12009
12010 #if 1
12011   /* should better be called in MovePlayer(), but this breaks some tapes */
12012   ScrollPlayer(player, SCROLL_INIT);
12013 #endif
12014
12015   return MP_MOVING;
12016 }
12017
12018 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12019 {
12020   int jx = player->jx, jy = player->jy;
12021   int old_jx = jx, old_jy = jy;
12022   int moved = MP_NO_ACTION;
12023
12024   if (!player->active)
12025     return FALSE;
12026
12027   if (!dx && !dy)
12028   {
12029     if (player->MovPos == 0)
12030     {
12031       player->is_moving = FALSE;
12032       player->is_digging = FALSE;
12033       player->is_collecting = FALSE;
12034       player->is_snapping = FALSE;
12035       player->is_pushing = FALSE;
12036     }
12037
12038     return FALSE;
12039   }
12040
12041   if (player->move_delay > 0)
12042     return FALSE;
12043
12044   player->move_delay = -1;              /* set to "uninitialized" value */
12045
12046   /* store if player is automatically moved to next field */
12047   player->is_auto_moving = (player->programmed_action != MV_NONE);
12048
12049   /* remove the last programmed player action */
12050   player->programmed_action = 0;
12051
12052   if (player->MovPos)
12053   {
12054     /* should only happen if pre-1.2 tape recordings are played */
12055     /* this is only for backward compatibility */
12056
12057     int original_move_delay_value = player->move_delay_value;
12058
12059 #if DEBUG
12060     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12061            tape.counter);
12062 #endif
12063
12064     /* scroll remaining steps with finest movement resolution */
12065     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12066
12067     while (player->MovPos)
12068     {
12069       ScrollPlayer(player, SCROLL_GO_ON);
12070       ScrollScreen(NULL, SCROLL_GO_ON);
12071
12072       AdvanceFrameAndPlayerCounters(player->index_nr);
12073
12074       DrawAllPlayers();
12075       BackToFront();
12076     }
12077
12078     player->move_delay_value = original_move_delay_value;
12079   }
12080
12081   player->is_active = FALSE;
12082
12083   if (player->last_move_dir & MV_HORIZONTAL)
12084   {
12085     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12086       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12087   }
12088   else
12089   {
12090     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12091       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12092   }
12093
12094   if (!moved && !player->is_active)
12095   {
12096     player->is_moving = FALSE;
12097     player->is_digging = FALSE;
12098     player->is_collecting = FALSE;
12099     player->is_snapping = FALSE;
12100     player->is_pushing = FALSE;
12101   }
12102
12103   jx = player->jx;
12104   jy = player->jy;
12105
12106   if (moved & MP_MOVING && !ScreenMovPos &&
12107       (player->index_nr == game.centered_player_nr ||
12108        game.centered_player_nr == -1))
12109   {
12110     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12111     int offset = game.scroll_delay_value;
12112
12113     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12114     {
12115       /* actual player has left the screen -- scroll in that direction */
12116       if (jx != old_jx)         /* player has moved horizontally */
12117         scroll_x += (jx - old_jx);
12118       else                      /* player has moved vertically */
12119         scroll_y += (jy - old_jy);
12120     }
12121     else
12122     {
12123       if (jx != old_jx)         /* player has moved horizontally */
12124       {
12125         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12126             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12127           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12128
12129         /* don't scroll over playfield boundaries */
12130         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12131           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12132
12133         /* don't scroll more than one field at a time */
12134         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12135
12136         /* don't scroll against the player's moving direction */
12137         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12138             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12139           scroll_x = old_scroll_x;
12140       }
12141       else                      /* player has moved vertically */
12142       {
12143         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12144             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12145           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12146
12147         /* don't scroll over playfield boundaries */
12148         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12149           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12150
12151         /* don't scroll more than one field at a time */
12152         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12153
12154         /* don't scroll against the player's moving direction */
12155         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12156             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12157           scroll_y = old_scroll_y;
12158       }
12159     }
12160
12161     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12162     {
12163       if (!options.network && game.centered_player_nr == -1 &&
12164           !AllPlayersInVisibleScreen())
12165       {
12166         scroll_x = old_scroll_x;
12167         scroll_y = old_scroll_y;
12168       }
12169       else
12170       {
12171         ScrollScreen(player, SCROLL_INIT);
12172         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12173       }
12174     }
12175   }
12176
12177   player->StepFrame = 0;
12178
12179   if (moved & MP_MOVING)
12180   {
12181     if (old_jx != jx && old_jy == jy)
12182       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12183     else if (old_jx == jx && old_jy != jy)
12184       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12185
12186     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12187
12188     player->last_move_dir = player->MovDir;
12189     player->is_moving = TRUE;
12190     player->is_snapping = FALSE;
12191     player->is_switching = FALSE;
12192     player->is_dropping = FALSE;
12193     player->is_dropping_pressed = FALSE;
12194     player->drop_pressed_delay = 0;
12195
12196 #if 0
12197     /* should better be called here than above, but this breaks some tapes */
12198     ScrollPlayer(player, SCROLL_INIT);
12199 #endif
12200   }
12201   else
12202   {
12203     CheckGravityMovementWhenNotMoving(player);
12204
12205     player->is_moving = FALSE;
12206
12207     /* at this point, the player is allowed to move, but cannot move right now
12208        (e.g. because of something blocking the way) -- ensure that the player
12209        is also allowed to move in the next frame (in old versions before 3.1.1,
12210        the player was forced to wait again for eight frames before next try) */
12211
12212     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12213       player->move_delay = 0;   /* allow direct movement in the next frame */
12214   }
12215
12216   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12217     player->move_delay = player->move_delay_value;
12218
12219   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12220   {
12221     TestIfPlayerTouchesBadThing(jx, jy);
12222     TestIfPlayerTouchesCustomElement(jx, jy);
12223   }
12224
12225   if (!player->active)
12226     RemovePlayer(player);
12227
12228   return moved;
12229 }
12230
12231 void ScrollPlayer(struct PlayerInfo *player, int mode)
12232 {
12233   int jx = player->jx, jy = player->jy;
12234   int last_jx = player->last_jx, last_jy = player->last_jy;
12235   int move_stepsize = TILEX / player->move_delay_value;
12236
12237   if (!player->active)
12238     return;
12239
12240   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12241     return;
12242
12243   if (mode == SCROLL_INIT)
12244   {
12245     player->actual_frame_counter = FrameCounter;
12246     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12247
12248     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12249         Feld[last_jx][last_jy] == EL_EMPTY)
12250     {
12251       int last_field_block_delay = 0;   /* start with no blocking at all */
12252       int block_delay_adjustment = player->block_delay_adjustment;
12253
12254       /* if player blocks last field, add delay for exactly one move */
12255       if (player->block_last_field)
12256       {
12257         last_field_block_delay += player->move_delay_value;
12258
12259         /* when blocking enabled, prevent moving up despite gravity */
12260         if (player->gravity && player->MovDir == MV_UP)
12261           block_delay_adjustment = -1;
12262       }
12263
12264       /* add block delay adjustment (also possible when not blocking) */
12265       last_field_block_delay += block_delay_adjustment;
12266
12267       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12268       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12269     }
12270
12271     if (player->MovPos != 0)    /* player has not yet reached destination */
12272       return;
12273   }
12274   else if (!FrameReached(&player->actual_frame_counter, 1))
12275     return;
12276
12277   if (player->MovPos != 0)
12278   {
12279     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12280     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12281
12282     /* before DrawPlayer() to draw correct player graphic for this case */
12283     if (player->MovPos == 0)
12284       CheckGravityMovement(player);
12285   }
12286
12287   if (player->MovPos == 0)      /* player reached destination field */
12288   {
12289     if (player->move_delay_reset_counter > 0)
12290     {
12291       player->move_delay_reset_counter--;
12292
12293       if (player->move_delay_reset_counter == 0)
12294       {
12295         /* continue with normal speed after quickly moving through gate */
12296         HALVE_PLAYER_SPEED(player);
12297
12298         /* be able to make the next move without delay */
12299         player->move_delay = 0;
12300       }
12301     }
12302
12303     player->last_jx = jx;
12304     player->last_jy = jy;
12305
12306     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12307         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12308         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12309         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12310         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12311         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12312         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12313         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12314     {
12315       DrawPlayer(player);       /* needed here only to cleanup last field */
12316       RemovePlayer(player);
12317
12318       if (local_player->friends_still_needed == 0 ||
12319           IS_SP_ELEMENT(Feld[jx][jy]))
12320         PlayerWins(player);
12321     }
12322
12323     /* this breaks one level: "machine", level 000 */
12324     {
12325       int move_direction = player->MovDir;
12326       int enter_side = MV_DIR_OPPOSITE(move_direction);
12327       int leave_side = move_direction;
12328       int old_jx = last_jx;
12329       int old_jy = last_jy;
12330       int old_element = Feld[old_jx][old_jy];
12331       int new_element = Feld[jx][jy];
12332
12333       if (IS_CUSTOM_ELEMENT(old_element))
12334         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12335                                    CE_LEFT_BY_PLAYER,
12336                                    player->index_bit, leave_side);
12337
12338       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12339                                           CE_PLAYER_LEAVES_X,
12340                                           player->index_bit, leave_side);
12341
12342       if (IS_CUSTOM_ELEMENT(new_element))
12343         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12344                                    player->index_bit, enter_side);
12345
12346       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12347                                           CE_PLAYER_ENTERS_X,
12348                                           player->index_bit, enter_side);
12349
12350       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12351                                         CE_MOVE_OF_X, move_direction);
12352     }
12353
12354     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12355     {
12356       TestIfPlayerTouchesBadThing(jx, jy);
12357       TestIfPlayerTouchesCustomElement(jx, jy);
12358
12359       /* needed because pushed element has not yet reached its destination,
12360          so it would trigger a change event at its previous field location */
12361       if (!player->is_pushing)
12362         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12363
12364       if (!player->active)
12365         RemovePlayer(player);
12366     }
12367
12368     if (!local_player->LevelSolved && level.use_step_counter)
12369     {
12370       int i;
12371
12372       TimePlayed++;
12373
12374       if (TimeLeft > 0)
12375       {
12376         TimeLeft--;
12377
12378         if (TimeLeft <= 10 && setup.time_limit)
12379           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12380
12381         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12382
12383         DisplayGameControlValues();
12384
12385         if (!TimeLeft && setup.time_limit)
12386           for (i = 0; i < MAX_PLAYERS; i++)
12387             KillPlayer(&stored_player[i]);
12388       }
12389       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12390       {
12391         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12392
12393         DisplayGameControlValues();
12394       }
12395     }
12396
12397     if (tape.single_step && tape.recording && !tape.pausing &&
12398         !player->programmed_action)
12399       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12400
12401     if (!player->programmed_action)
12402       CheckSaveEngineSnapshot(player);
12403   }
12404 }
12405
12406 void ScrollScreen(struct PlayerInfo *player, int mode)
12407 {
12408   static unsigned int screen_frame_counter = 0;
12409
12410   if (mode == SCROLL_INIT)
12411   {
12412     /* set scrolling step size according to actual player's moving speed */
12413     ScrollStepSize = TILEX / player->move_delay_value;
12414
12415     screen_frame_counter = FrameCounter;
12416     ScreenMovDir = player->MovDir;
12417     ScreenMovPos = player->MovPos;
12418     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12419     return;
12420   }
12421   else if (!FrameReached(&screen_frame_counter, 1))
12422     return;
12423
12424   if (ScreenMovPos)
12425   {
12426     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12427     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12428     redraw_mask |= REDRAW_FIELD;
12429   }
12430   else
12431     ScreenMovDir = MV_NONE;
12432 }
12433
12434 void TestIfPlayerTouchesCustomElement(int x, int y)
12435 {
12436   static int xy[4][2] =
12437   {
12438     { 0, -1 },
12439     { -1, 0 },
12440     { +1, 0 },
12441     { 0, +1 }
12442   };
12443   static int trigger_sides[4][2] =
12444   {
12445     /* center side       border side */
12446     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12447     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12448     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12449     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12450   };
12451   static int touch_dir[4] =
12452   {
12453     MV_LEFT | MV_RIGHT,
12454     MV_UP   | MV_DOWN,
12455     MV_UP   | MV_DOWN,
12456     MV_LEFT | MV_RIGHT
12457   };
12458   int center_element = Feld[x][y];      /* should always be non-moving! */
12459   int i;
12460
12461   for (i = 0; i < NUM_DIRECTIONS; i++)
12462   {
12463     int xx = x + xy[i][0];
12464     int yy = y + xy[i][1];
12465     int center_side = trigger_sides[i][0];
12466     int border_side = trigger_sides[i][1];
12467     int border_element;
12468
12469     if (!IN_LEV_FIELD(xx, yy))
12470       continue;
12471
12472     if (IS_PLAYER(x, y))                /* player found at center element */
12473     {
12474       struct PlayerInfo *player = PLAYERINFO(x, y);
12475
12476       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12477         border_element = Feld[xx][yy];          /* may be moving! */
12478       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12479         border_element = Feld[xx][yy];
12480       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12481         border_element = MovingOrBlocked2Element(xx, yy);
12482       else
12483         continue;               /* center and border element do not touch */
12484
12485       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12486                                  player->index_bit, border_side);
12487       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12488                                           CE_PLAYER_TOUCHES_X,
12489                                           player->index_bit, border_side);
12490
12491       {
12492         /* use player element that is initially defined in the level playfield,
12493            not the player element that corresponds to the runtime player number
12494            (example: a level that contains EL_PLAYER_3 as the only player would
12495            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12496         int player_element = PLAYERINFO(x, y)->initial_element;
12497
12498         CheckElementChangeBySide(xx, yy, border_element, player_element,
12499                                  CE_TOUCHING_X, border_side);
12500       }
12501     }
12502     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12503     {
12504       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12505
12506       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12507       {
12508         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12509           continue;             /* center and border element do not touch */
12510       }
12511
12512       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12513                                  player->index_bit, center_side);
12514       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12515                                           CE_PLAYER_TOUCHES_X,
12516                                           player->index_bit, center_side);
12517
12518       {
12519         /* use player element that is initially defined in the level playfield,
12520            not the player element that corresponds to the runtime player number
12521            (example: a level that contains EL_PLAYER_3 as the only player would
12522            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12523         int player_element = PLAYERINFO(xx, yy)->initial_element;
12524
12525         CheckElementChangeBySide(x, y, center_element, player_element,
12526                                  CE_TOUCHING_X, center_side);
12527       }
12528
12529       break;
12530     }
12531   }
12532 }
12533
12534 void TestIfElementTouchesCustomElement(int x, int y)
12535 {
12536   static int xy[4][2] =
12537   {
12538     { 0, -1 },
12539     { -1, 0 },
12540     { +1, 0 },
12541     { 0, +1 }
12542   };
12543   static int trigger_sides[4][2] =
12544   {
12545     /* center side      border side */
12546     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12547     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12548     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12549     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12550   };
12551   static int touch_dir[4] =
12552   {
12553     MV_LEFT | MV_RIGHT,
12554     MV_UP   | MV_DOWN,
12555     MV_UP   | MV_DOWN,
12556     MV_LEFT | MV_RIGHT
12557   };
12558   boolean change_center_element = FALSE;
12559   int center_element = Feld[x][y];      /* should always be non-moving! */
12560   int border_element_old[NUM_DIRECTIONS];
12561   int i;
12562
12563   for (i = 0; i < NUM_DIRECTIONS; i++)
12564   {
12565     int xx = x + xy[i][0];
12566     int yy = y + xy[i][1];
12567     int border_element;
12568
12569     border_element_old[i] = -1;
12570
12571     if (!IN_LEV_FIELD(xx, yy))
12572       continue;
12573
12574     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12575       border_element = Feld[xx][yy];    /* may be moving! */
12576     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12577       border_element = Feld[xx][yy];
12578     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12579       border_element = MovingOrBlocked2Element(xx, yy);
12580     else
12581       continue;                 /* center and border element do not touch */
12582
12583     border_element_old[i] = border_element;
12584   }
12585
12586   for (i = 0; i < NUM_DIRECTIONS; i++)
12587   {
12588     int xx = x + xy[i][0];
12589     int yy = y + xy[i][1];
12590     int center_side = trigger_sides[i][0];
12591     int border_element = border_element_old[i];
12592
12593     if (border_element == -1)
12594       continue;
12595
12596     /* check for change of border element */
12597     CheckElementChangeBySide(xx, yy, border_element, center_element,
12598                              CE_TOUCHING_X, center_side);
12599
12600     /* (center element cannot be player, so we dont have to check this here) */
12601   }
12602
12603   for (i = 0; i < NUM_DIRECTIONS; i++)
12604   {
12605     int xx = x + xy[i][0];
12606     int yy = y + xy[i][1];
12607     int border_side = trigger_sides[i][1];
12608     int border_element = border_element_old[i];
12609
12610     if (border_element == -1)
12611       continue;
12612
12613     /* check for change of center element (but change it only once) */
12614     if (!change_center_element)
12615       change_center_element =
12616         CheckElementChangeBySide(x, y, center_element, border_element,
12617                                  CE_TOUCHING_X, border_side);
12618
12619     if (IS_PLAYER(xx, yy))
12620     {
12621       /* use player element that is initially defined in the level playfield,
12622          not the player element that corresponds to the runtime player number
12623          (example: a level that contains EL_PLAYER_3 as the only player would
12624          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12625       int player_element = PLAYERINFO(xx, yy)->initial_element;
12626
12627       CheckElementChangeBySide(x, y, center_element, player_element,
12628                                CE_TOUCHING_X, border_side);
12629     }
12630   }
12631 }
12632
12633 void TestIfElementHitsCustomElement(int x, int y, int direction)
12634 {
12635   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12636   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12637   int hitx = x + dx, hity = y + dy;
12638   int hitting_element = Feld[x][y];
12639   int touched_element;
12640
12641   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12642     return;
12643
12644   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12645                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12646
12647   if (IN_LEV_FIELD(hitx, hity))
12648   {
12649     int opposite_direction = MV_DIR_OPPOSITE(direction);
12650     int hitting_side = direction;
12651     int touched_side = opposite_direction;
12652     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12653                           MovDir[hitx][hity] != direction ||
12654                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12655
12656     object_hit = TRUE;
12657
12658     if (object_hit)
12659     {
12660       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12661                                CE_HITTING_X, touched_side);
12662
12663       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12664                                CE_HIT_BY_X, hitting_side);
12665
12666       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12667                                CE_HIT_BY_SOMETHING, opposite_direction);
12668
12669       if (IS_PLAYER(hitx, hity))
12670       {
12671         /* use player element that is initially defined in the level playfield,
12672            not the player element that corresponds to the runtime player number
12673            (example: a level that contains EL_PLAYER_3 as the only player would
12674            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12675         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12676
12677         CheckElementChangeBySide(x, y, hitting_element, player_element,
12678                                  CE_HITTING_X, touched_side);
12679       }
12680     }
12681   }
12682
12683   /* "hitting something" is also true when hitting the playfield border */
12684   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12685                            CE_HITTING_SOMETHING, direction);
12686 }
12687
12688 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12689 {
12690   int i, kill_x = -1, kill_y = -1;
12691
12692   int bad_element = -1;
12693   static int test_xy[4][2] =
12694   {
12695     { 0, -1 },
12696     { -1, 0 },
12697     { +1, 0 },
12698     { 0, +1 }
12699   };
12700   static int test_dir[4] =
12701   {
12702     MV_UP,
12703     MV_LEFT,
12704     MV_RIGHT,
12705     MV_DOWN
12706   };
12707
12708   for (i = 0; i < NUM_DIRECTIONS; i++)
12709   {
12710     int test_x, test_y, test_move_dir, test_element;
12711
12712     test_x = good_x + test_xy[i][0];
12713     test_y = good_y + test_xy[i][1];
12714
12715     if (!IN_LEV_FIELD(test_x, test_y))
12716       continue;
12717
12718     test_move_dir =
12719       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12720
12721     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12722
12723     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12724        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12725     */
12726     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12727         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12728     {
12729       kill_x = test_x;
12730       kill_y = test_y;
12731       bad_element = test_element;
12732
12733       break;
12734     }
12735   }
12736
12737   if (kill_x != -1 || kill_y != -1)
12738   {
12739     if (IS_PLAYER(good_x, good_y))
12740     {
12741       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12742
12743       if (player->shield_deadly_time_left > 0 &&
12744           !IS_INDESTRUCTIBLE(bad_element))
12745         Bang(kill_x, kill_y);
12746       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12747         KillPlayer(player);
12748     }
12749     else
12750       Bang(good_x, good_y);
12751   }
12752 }
12753
12754 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12755 {
12756   int i, kill_x = -1, kill_y = -1;
12757   int bad_element = Feld[bad_x][bad_y];
12758   static int test_xy[4][2] =
12759   {
12760     { 0, -1 },
12761     { -1, 0 },
12762     { +1, 0 },
12763     { 0, +1 }
12764   };
12765   static int touch_dir[4] =
12766   {
12767     MV_LEFT | MV_RIGHT,
12768     MV_UP   | MV_DOWN,
12769     MV_UP   | MV_DOWN,
12770     MV_LEFT | MV_RIGHT
12771   };
12772   static int test_dir[4] =
12773   {
12774     MV_UP,
12775     MV_LEFT,
12776     MV_RIGHT,
12777     MV_DOWN
12778   };
12779
12780   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12781     return;
12782
12783   for (i = 0; i < NUM_DIRECTIONS; i++)
12784   {
12785     int test_x, test_y, test_move_dir, test_element;
12786
12787     test_x = bad_x + test_xy[i][0];
12788     test_y = bad_y + test_xy[i][1];
12789
12790     if (!IN_LEV_FIELD(test_x, test_y))
12791       continue;
12792
12793     test_move_dir =
12794       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12795
12796     test_element = Feld[test_x][test_y];
12797
12798     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12799        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12800     */
12801     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12802         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12803     {
12804       /* good thing is player or penguin that does not move away */
12805       if (IS_PLAYER(test_x, test_y))
12806       {
12807         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12808
12809         if (bad_element == EL_ROBOT && player->is_moving)
12810           continue;     /* robot does not kill player if he is moving */
12811
12812         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12813         {
12814           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12815             continue;           /* center and border element do not touch */
12816         }
12817
12818         kill_x = test_x;
12819         kill_y = test_y;
12820
12821         break;
12822       }
12823       else if (test_element == EL_PENGUIN)
12824       {
12825         kill_x = test_x;
12826         kill_y = test_y;
12827
12828         break;
12829       }
12830     }
12831   }
12832
12833   if (kill_x != -1 || kill_y != -1)
12834   {
12835     if (IS_PLAYER(kill_x, kill_y))
12836     {
12837       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12838
12839       if (player->shield_deadly_time_left > 0 &&
12840           !IS_INDESTRUCTIBLE(bad_element))
12841         Bang(bad_x, bad_y);
12842       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12843         KillPlayer(player);
12844     }
12845     else
12846       Bang(kill_x, kill_y);
12847   }
12848 }
12849
12850 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12851 {
12852   int bad_element = Feld[bad_x][bad_y];
12853   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12854   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12855   int test_x = bad_x + dx, test_y = bad_y + dy;
12856   int test_move_dir, test_element;
12857   int kill_x = -1, kill_y = -1;
12858
12859   if (!IN_LEV_FIELD(test_x, test_y))
12860     return;
12861
12862   test_move_dir =
12863     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12864
12865   test_element = Feld[test_x][test_y];
12866
12867   if (test_move_dir != bad_move_dir)
12868   {
12869     /* good thing can be player or penguin that does not move away */
12870     if (IS_PLAYER(test_x, test_y))
12871     {
12872       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12873
12874       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12875          player as being hit when he is moving towards the bad thing, because
12876          the "get hit by" condition would be lost after the player stops) */
12877       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12878         return;         /* player moves away from bad thing */
12879
12880       kill_x = test_x;
12881       kill_y = test_y;
12882     }
12883     else if (test_element == EL_PENGUIN)
12884     {
12885       kill_x = test_x;
12886       kill_y = test_y;
12887     }
12888   }
12889
12890   if (kill_x != -1 || kill_y != -1)
12891   {
12892     if (IS_PLAYER(kill_x, kill_y))
12893     {
12894       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12895
12896       if (player->shield_deadly_time_left > 0 &&
12897           !IS_INDESTRUCTIBLE(bad_element))
12898         Bang(bad_x, bad_y);
12899       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12900         KillPlayer(player);
12901     }
12902     else
12903       Bang(kill_x, kill_y);
12904   }
12905 }
12906
12907 void TestIfPlayerTouchesBadThing(int x, int y)
12908 {
12909   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12910 }
12911
12912 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12913 {
12914   TestIfGoodThingHitsBadThing(x, y, move_dir);
12915 }
12916
12917 void TestIfBadThingTouchesPlayer(int x, int y)
12918 {
12919   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12920 }
12921
12922 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12923 {
12924   TestIfBadThingHitsGoodThing(x, y, move_dir);
12925 }
12926
12927 void TestIfFriendTouchesBadThing(int x, int y)
12928 {
12929   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12930 }
12931
12932 void TestIfBadThingTouchesFriend(int x, int y)
12933 {
12934   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12935 }
12936
12937 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12938 {
12939   int i, kill_x = bad_x, kill_y = bad_y;
12940   static int xy[4][2] =
12941   {
12942     { 0, -1 },
12943     { -1, 0 },
12944     { +1, 0 },
12945     { 0, +1 }
12946   };
12947
12948   for (i = 0; i < NUM_DIRECTIONS; i++)
12949   {
12950     int x, y, element;
12951
12952     x = bad_x + xy[i][0];
12953     y = bad_y + xy[i][1];
12954     if (!IN_LEV_FIELD(x, y))
12955       continue;
12956
12957     element = Feld[x][y];
12958     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12959         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12960     {
12961       kill_x = x;
12962       kill_y = y;
12963       break;
12964     }
12965   }
12966
12967   if (kill_x != bad_x || kill_y != bad_y)
12968     Bang(bad_x, bad_y);
12969 }
12970
12971 void KillPlayer(struct PlayerInfo *player)
12972 {
12973   int jx = player->jx, jy = player->jy;
12974
12975   if (!player->active)
12976     return;
12977
12978 #if 0
12979   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12980          player->killed, player->active, player->reanimated);
12981 #endif
12982
12983   /* the following code was introduced to prevent an infinite loop when calling
12984      -> Bang()
12985      -> CheckTriggeredElementChangeExt()
12986      -> ExecuteCustomElementAction()
12987      -> KillPlayer()
12988      -> (infinitely repeating the above sequence of function calls)
12989      which occurs when killing the player while having a CE with the setting
12990      "kill player X when explosion of <player X>"; the solution using a new
12991      field "player->killed" was chosen for backwards compatibility, although
12992      clever use of the fields "player->active" etc. would probably also work */
12993 #if 1
12994   if (player->killed)
12995     return;
12996 #endif
12997
12998   player->killed = TRUE;
12999
13000   /* remove accessible field at the player's position */
13001   Feld[jx][jy] = EL_EMPTY;
13002
13003   /* deactivate shield (else Bang()/Explode() would not work right) */
13004   player->shield_normal_time_left = 0;
13005   player->shield_deadly_time_left = 0;
13006
13007 #if 0
13008   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13009          player->killed, player->active, player->reanimated);
13010 #endif
13011
13012   Bang(jx, jy);
13013
13014 #if 0
13015   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13016          player->killed, player->active, player->reanimated);
13017 #endif
13018
13019   if (player->reanimated)       /* killed player may have been reanimated */
13020     player->killed = player->reanimated = FALSE;
13021   else
13022     BuryPlayer(player);
13023 }
13024
13025 static void KillPlayerUnlessEnemyProtected(int x, int y)
13026 {
13027   if (!PLAYER_ENEMY_PROTECTED(x, y))
13028     KillPlayer(PLAYERINFO(x, y));
13029 }
13030
13031 static void KillPlayerUnlessExplosionProtected(int x, int y)
13032 {
13033   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13034     KillPlayer(PLAYERINFO(x, y));
13035 }
13036
13037 void BuryPlayer(struct PlayerInfo *player)
13038 {
13039   int jx = player->jx, jy = player->jy;
13040
13041   if (!player->active)
13042     return;
13043
13044   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13045   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13046
13047   player->GameOver = TRUE;
13048   RemovePlayer(player);
13049 }
13050
13051 void RemovePlayer(struct PlayerInfo *player)
13052 {
13053   int jx = player->jx, jy = player->jy;
13054   int i, found = FALSE;
13055
13056   player->present = FALSE;
13057   player->active = FALSE;
13058
13059   if (!ExplodeField[jx][jy])
13060     StorePlayer[jx][jy] = 0;
13061
13062   if (player->is_moving)
13063     TEST_DrawLevelField(player->last_jx, player->last_jy);
13064
13065   for (i = 0; i < MAX_PLAYERS; i++)
13066     if (stored_player[i].active)
13067       found = TRUE;
13068
13069   if (!found)
13070     AllPlayersGone = TRUE;
13071
13072   ExitX = ZX = jx;
13073   ExitY = ZY = jy;
13074 }
13075
13076 static void setFieldForSnapping(int x, int y, int element, int direction)
13077 {
13078   struct ElementInfo *ei = &element_info[element];
13079   int direction_bit = MV_DIR_TO_BIT(direction);
13080   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13081   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13082                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13083
13084   Feld[x][y] = EL_ELEMENT_SNAPPING;
13085   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13086
13087   ResetGfxAnimation(x, y);
13088
13089   GfxElement[x][y] = element;
13090   GfxAction[x][y] = action;
13091   GfxDir[x][y] = direction;
13092   GfxFrame[x][y] = -1;
13093 }
13094
13095 /*
13096   =============================================================================
13097   checkDiagonalPushing()
13098   -----------------------------------------------------------------------------
13099   check if diagonal input device direction results in pushing of object
13100   (by checking if the alternative direction is walkable, diggable, ...)
13101   =============================================================================
13102 */
13103
13104 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13105                                     int x, int y, int real_dx, int real_dy)
13106 {
13107   int jx, jy, dx, dy, xx, yy;
13108
13109   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13110     return TRUE;
13111
13112   /* diagonal direction: check alternative direction */
13113   jx = player->jx;
13114   jy = player->jy;
13115   dx = x - jx;
13116   dy = y - jy;
13117   xx = jx + (dx == 0 ? real_dx : 0);
13118   yy = jy + (dy == 0 ? real_dy : 0);
13119
13120   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13121 }
13122
13123 /*
13124   =============================================================================
13125   DigField()
13126   -----------------------------------------------------------------------------
13127   x, y:                 field next to player (non-diagonal) to try to dig to
13128   real_dx, real_dy:     direction as read from input device (can be diagonal)
13129   =============================================================================
13130 */
13131
13132 static int DigField(struct PlayerInfo *player,
13133                     int oldx, int oldy, int x, int y,
13134                     int real_dx, int real_dy, int mode)
13135 {
13136   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13137   boolean player_was_pushing = player->is_pushing;
13138   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13139   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13140   int jx = oldx, jy = oldy;
13141   int dx = x - jx, dy = y - jy;
13142   int nextx = x + dx, nexty = y + dy;
13143   int move_direction = (dx == -1 ? MV_LEFT  :
13144                         dx == +1 ? MV_RIGHT :
13145                         dy == -1 ? MV_UP    :
13146                         dy == +1 ? MV_DOWN  : MV_NONE);
13147   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13148   int dig_side = MV_DIR_OPPOSITE(move_direction);
13149   int old_element = Feld[jx][jy];
13150   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13151   int collect_count;
13152
13153   if (is_player)                /* function can also be called by EL_PENGUIN */
13154   {
13155     if (player->MovPos == 0)
13156     {
13157       player->is_digging = FALSE;
13158       player->is_collecting = FALSE;
13159     }
13160
13161     if (player->MovPos == 0)    /* last pushing move finished */
13162       player->is_pushing = FALSE;
13163
13164     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13165     {
13166       player->is_switching = FALSE;
13167       player->push_delay = -1;
13168
13169       return MP_NO_ACTION;
13170     }
13171   }
13172
13173   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13174     old_element = Back[jx][jy];
13175
13176   /* in case of element dropped at player position, check background */
13177   else if (Back[jx][jy] != EL_EMPTY &&
13178            game.engine_version >= VERSION_IDENT(2,2,0,0))
13179     old_element = Back[jx][jy];
13180
13181   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13182     return MP_NO_ACTION;        /* field has no opening in this direction */
13183
13184   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13185     return MP_NO_ACTION;        /* field has no opening in this direction */
13186
13187   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13188   {
13189     SplashAcid(x, y);
13190
13191     Feld[jx][jy] = player->artwork_element;
13192     InitMovingField(jx, jy, MV_DOWN);
13193     Store[jx][jy] = EL_ACID;
13194     ContinueMoving(jx, jy);
13195     BuryPlayer(player);
13196
13197     return MP_DONT_RUN_INTO;
13198   }
13199
13200   if (player_can_move && DONT_RUN_INTO(element))
13201   {
13202     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13203
13204     return MP_DONT_RUN_INTO;
13205   }
13206
13207   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13208     return MP_NO_ACTION;
13209
13210   collect_count = element_info[element].collect_count_initial;
13211
13212   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13213     return MP_NO_ACTION;
13214
13215   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13216     player_can_move = player_can_move_or_snap;
13217
13218   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13219       game.engine_version >= VERSION_IDENT(2,2,0,0))
13220   {
13221     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13222                                player->index_bit, dig_side);
13223     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13224                                         player->index_bit, dig_side);
13225
13226     if (element == EL_DC_LANDMINE)
13227       Bang(x, y);
13228
13229     if (Feld[x][y] != element)          /* field changed by snapping */
13230       return MP_ACTION;
13231
13232     return MP_NO_ACTION;
13233   }
13234
13235   if (player->gravity && is_player && !player->is_auto_moving &&
13236       canFallDown(player) && move_direction != MV_DOWN &&
13237       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13238     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13239
13240   if (player_can_move &&
13241       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13242   {
13243     int sound_element = SND_ELEMENT(element);
13244     int sound_action = ACTION_WALKING;
13245
13246     if (IS_RND_GATE(element))
13247     {
13248       if (!player->key[RND_GATE_NR(element)])
13249         return MP_NO_ACTION;
13250     }
13251     else if (IS_RND_GATE_GRAY(element))
13252     {
13253       if (!player->key[RND_GATE_GRAY_NR(element)])
13254         return MP_NO_ACTION;
13255     }
13256     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13257     {
13258       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13259         return MP_NO_ACTION;
13260     }
13261     else if (element == EL_EXIT_OPEN ||
13262              element == EL_EM_EXIT_OPEN ||
13263              element == EL_EM_EXIT_OPENING ||
13264              element == EL_STEEL_EXIT_OPEN ||
13265              element == EL_EM_STEEL_EXIT_OPEN ||
13266              element == EL_EM_STEEL_EXIT_OPENING ||
13267              element == EL_SP_EXIT_OPEN ||
13268              element == EL_SP_EXIT_OPENING)
13269     {
13270       sound_action = ACTION_PASSING;    /* player is passing exit */
13271     }
13272     else if (element == EL_EMPTY)
13273     {
13274       sound_action = ACTION_MOVING;             /* nothing to walk on */
13275     }
13276
13277     /* play sound from background or player, whatever is available */
13278     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13279       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13280     else
13281       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13282   }
13283   else if (player_can_move &&
13284            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13285   {
13286     if (!ACCESS_FROM(element, opposite_direction))
13287       return MP_NO_ACTION;      /* field not accessible from this direction */
13288
13289     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13290       return MP_NO_ACTION;
13291
13292     if (IS_EM_GATE(element))
13293     {
13294       if (!player->key[EM_GATE_NR(element)])
13295         return MP_NO_ACTION;
13296     }
13297     else if (IS_EM_GATE_GRAY(element))
13298     {
13299       if (!player->key[EM_GATE_GRAY_NR(element)])
13300         return MP_NO_ACTION;
13301     }
13302     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13303     {
13304       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13305         return MP_NO_ACTION;
13306     }
13307     else if (IS_EMC_GATE(element))
13308     {
13309       if (!player->key[EMC_GATE_NR(element)])
13310         return MP_NO_ACTION;
13311     }
13312     else if (IS_EMC_GATE_GRAY(element))
13313     {
13314       if (!player->key[EMC_GATE_GRAY_NR(element)])
13315         return MP_NO_ACTION;
13316     }
13317     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13318     {
13319       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13320         return MP_NO_ACTION;
13321     }
13322     else if (element == EL_DC_GATE_WHITE ||
13323              element == EL_DC_GATE_WHITE_GRAY ||
13324              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13325     {
13326       if (player->num_white_keys == 0)
13327         return MP_NO_ACTION;
13328
13329       player->num_white_keys--;
13330     }
13331     else if (IS_SP_PORT(element))
13332     {
13333       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13334           element == EL_SP_GRAVITY_PORT_RIGHT ||
13335           element == EL_SP_GRAVITY_PORT_UP ||
13336           element == EL_SP_GRAVITY_PORT_DOWN)
13337         player->gravity = !player->gravity;
13338       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13339                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13340                element == EL_SP_GRAVITY_ON_PORT_UP ||
13341                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13342         player->gravity = TRUE;
13343       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13344                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13345                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13346                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13347         player->gravity = FALSE;
13348     }
13349
13350     /* automatically move to the next field with double speed */
13351     player->programmed_action = move_direction;
13352
13353     if (player->move_delay_reset_counter == 0)
13354     {
13355       player->move_delay_reset_counter = 2;     /* two double speed steps */
13356
13357       DOUBLE_PLAYER_SPEED(player);
13358     }
13359
13360     PlayLevelSoundAction(x, y, ACTION_PASSING);
13361   }
13362   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13363   {
13364     RemoveField(x, y);
13365
13366     if (mode != DF_SNAP)
13367     {
13368       GfxElement[x][y] = GFX_ELEMENT(element);
13369       player->is_digging = TRUE;
13370     }
13371
13372     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13373
13374     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13375                                         player->index_bit, dig_side);
13376
13377     if (mode == DF_SNAP)
13378     {
13379       if (level.block_snap_field)
13380         setFieldForSnapping(x, y, element, move_direction);
13381       else
13382         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13383
13384       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13385                                           player->index_bit, dig_side);
13386     }
13387   }
13388   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13389   {
13390     RemoveField(x, y);
13391
13392     if (is_player && mode != DF_SNAP)
13393     {
13394       GfxElement[x][y] = element;
13395       player->is_collecting = TRUE;
13396     }
13397
13398     if (element == EL_SPEED_PILL)
13399     {
13400       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13401     }
13402     else if (element == EL_EXTRA_TIME && level.time > 0)
13403     {
13404       TimeLeft += level.extra_time;
13405
13406       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13407
13408       DisplayGameControlValues();
13409     }
13410     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13411     {
13412       player->shield_normal_time_left += level.shield_normal_time;
13413       if (element == EL_SHIELD_DEADLY)
13414         player->shield_deadly_time_left += level.shield_deadly_time;
13415     }
13416     else if (element == EL_DYNAMITE ||
13417              element == EL_EM_DYNAMITE ||
13418              element == EL_SP_DISK_RED)
13419     {
13420       if (player->inventory_size < MAX_INVENTORY_SIZE)
13421         player->inventory_element[player->inventory_size++] = element;
13422
13423       DrawGameDoorValues();
13424     }
13425     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13426     {
13427       player->dynabomb_count++;
13428       player->dynabombs_left++;
13429     }
13430     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13431     {
13432       player->dynabomb_size++;
13433     }
13434     else if (element == EL_DYNABOMB_INCREASE_POWER)
13435     {
13436       player->dynabomb_xl = TRUE;
13437     }
13438     else if (IS_KEY(element))
13439     {
13440       player->key[KEY_NR(element)] = TRUE;
13441
13442       DrawGameDoorValues();
13443     }
13444     else if (element == EL_DC_KEY_WHITE)
13445     {
13446       player->num_white_keys++;
13447
13448       /* display white keys? */
13449       /* DrawGameDoorValues(); */
13450     }
13451     else if (IS_ENVELOPE(element))
13452     {
13453       player->show_envelope = element;
13454     }
13455     else if (element == EL_EMC_LENSES)
13456     {
13457       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13458
13459       RedrawAllInvisibleElementsForLenses();
13460     }
13461     else if (element == EL_EMC_MAGNIFIER)
13462     {
13463       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13464
13465       RedrawAllInvisibleElementsForMagnifier();
13466     }
13467     else if (IS_DROPPABLE(element) ||
13468              IS_THROWABLE(element))     /* can be collected and dropped */
13469     {
13470       int i;
13471
13472       if (collect_count == 0)
13473         player->inventory_infinite_element = element;
13474       else
13475         for (i = 0; i < collect_count; i++)
13476           if (player->inventory_size < MAX_INVENTORY_SIZE)
13477             player->inventory_element[player->inventory_size++] = element;
13478
13479       DrawGameDoorValues();
13480     }
13481     else if (collect_count > 0)
13482     {
13483       local_player->gems_still_needed -= collect_count;
13484       if (local_player->gems_still_needed < 0)
13485         local_player->gems_still_needed = 0;
13486
13487       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13488
13489       DisplayGameControlValues();
13490     }
13491
13492     RaiseScoreElement(element);
13493     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13494
13495     if (is_player)
13496       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13497                                           player->index_bit, dig_side);
13498
13499     if (mode == DF_SNAP)
13500     {
13501       if (level.block_snap_field)
13502         setFieldForSnapping(x, y, element, move_direction);
13503       else
13504         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13505
13506       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13507                                           player->index_bit, dig_side);
13508     }
13509   }
13510   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13511   {
13512     if (mode == DF_SNAP && element != EL_BD_ROCK)
13513       return MP_NO_ACTION;
13514
13515     if (CAN_FALL(element) && dy)
13516       return MP_NO_ACTION;
13517
13518     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13519         !(element == EL_SPRING && level.use_spring_bug))
13520       return MP_NO_ACTION;
13521
13522     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13523         ((move_direction & MV_VERTICAL &&
13524           ((element_info[element].move_pattern & MV_LEFT &&
13525             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13526            (element_info[element].move_pattern & MV_RIGHT &&
13527             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13528          (move_direction & MV_HORIZONTAL &&
13529           ((element_info[element].move_pattern & MV_UP &&
13530             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13531            (element_info[element].move_pattern & MV_DOWN &&
13532             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13533       return MP_NO_ACTION;
13534
13535     /* do not push elements already moving away faster than player */
13536     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13537         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13538       return MP_NO_ACTION;
13539
13540     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13541     {
13542       if (player->push_delay_value == -1 || !player_was_pushing)
13543         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13544     }
13545     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13546     {
13547       if (player->push_delay_value == -1)
13548         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13549     }
13550     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13551     {
13552       if (!player->is_pushing)
13553         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13554     }
13555
13556     player->is_pushing = TRUE;
13557     player->is_active = TRUE;
13558
13559     if (!(IN_LEV_FIELD(nextx, nexty) &&
13560           (IS_FREE(nextx, nexty) ||
13561            (IS_SB_ELEMENT(element) &&
13562             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13563            (IS_CUSTOM_ELEMENT(element) &&
13564             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13565       return MP_NO_ACTION;
13566
13567     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13568       return MP_NO_ACTION;
13569
13570     if (player->push_delay == -1)       /* new pushing; restart delay */
13571       player->push_delay = 0;
13572
13573     if (player->push_delay < player->push_delay_value &&
13574         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13575         element != EL_SPRING && element != EL_BALLOON)
13576     {
13577       /* make sure that there is no move delay before next try to push */
13578       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13579         player->move_delay = 0;
13580
13581       return MP_NO_ACTION;
13582     }
13583
13584     if (IS_CUSTOM_ELEMENT(element) &&
13585         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13586     {
13587       if (!DigFieldByCE(nextx, nexty, element))
13588         return MP_NO_ACTION;
13589     }
13590
13591     if (IS_SB_ELEMENT(element))
13592     {
13593       if (element == EL_SOKOBAN_FIELD_FULL)
13594       {
13595         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13596         local_player->sokobanfields_still_needed++;
13597       }
13598
13599       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13600       {
13601         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13602         local_player->sokobanfields_still_needed--;
13603       }
13604
13605       Feld[x][y] = EL_SOKOBAN_OBJECT;
13606
13607       if (Back[x][y] == Back[nextx][nexty])
13608         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13609       else if (Back[x][y] != 0)
13610         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13611                                     ACTION_EMPTYING);
13612       else
13613         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13614                                     ACTION_FILLING);
13615
13616       if (local_player->sokobanfields_still_needed == 0 &&
13617           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13618       {
13619         PlayerWins(player);
13620
13621         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13622       }
13623     }
13624     else
13625       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13626
13627     InitMovingField(x, y, move_direction);
13628     GfxAction[x][y] = ACTION_PUSHING;
13629
13630     if (mode == DF_SNAP)
13631       ContinueMoving(x, y);
13632     else
13633       MovPos[x][y] = (dx != 0 ? dx : dy);
13634
13635     Pushed[x][y] = TRUE;
13636     Pushed[nextx][nexty] = TRUE;
13637
13638     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13639       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13640     else
13641       player->push_delay_value = -1;    /* get new value later */
13642
13643     /* check for element change _after_ element has been pushed */
13644     if (game.use_change_when_pushing_bug)
13645     {
13646       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13647                                  player->index_bit, dig_side);
13648       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13649                                           player->index_bit, dig_side);
13650     }
13651   }
13652   else if (IS_SWITCHABLE(element))
13653   {
13654     if (PLAYER_SWITCHING(player, x, y))
13655     {
13656       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13657                                           player->index_bit, dig_side);
13658
13659       return MP_ACTION;
13660     }
13661
13662     player->is_switching = TRUE;
13663     player->switch_x = x;
13664     player->switch_y = y;
13665
13666     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13667
13668     if (element == EL_ROBOT_WHEEL)
13669     {
13670       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13671       ZX = x;
13672       ZY = y;
13673
13674       game.robot_wheel_active = TRUE;
13675
13676       TEST_DrawLevelField(x, y);
13677     }
13678     else if (element == EL_SP_TERMINAL)
13679     {
13680       int xx, yy;
13681
13682       SCAN_PLAYFIELD(xx, yy)
13683       {
13684         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13685           Bang(xx, yy);
13686         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13687           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13688       }
13689     }
13690     else if (IS_BELT_SWITCH(element))
13691     {
13692       ToggleBeltSwitch(x, y);
13693     }
13694     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13695              element == EL_SWITCHGATE_SWITCH_DOWN ||
13696              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13697              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13698     {
13699       ToggleSwitchgateSwitch(x, y);
13700     }
13701     else if (element == EL_LIGHT_SWITCH ||
13702              element == EL_LIGHT_SWITCH_ACTIVE)
13703     {
13704       ToggleLightSwitch(x, y);
13705     }
13706     else if (element == EL_TIMEGATE_SWITCH ||
13707              element == EL_DC_TIMEGATE_SWITCH)
13708     {
13709       ActivateTimegateSwitch(x, y);
13710     }
13711     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13712              element == EL_BALLOON_SWITCH_RIGHT ||
13713              element == EL_BALLOON_SWITCH_UP    ||
13714              element == EL_BALLOON_SWITCH_DOWN  ||
13715              element == EL_BALLOON_SWITCH_NONE  ||
13716              element == EL_BALLOON_SWITCH_ANY)
13717     {
13718       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13719                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13720                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13721                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13722                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13723                              move_direction);
13724     }
13725     else if (element == EL_LAMP)
13726     {
13727       Feld[x][y] = EL_LAMP_ACTIVE;
13728       local_player->lights_still_needed--;
13729
13730       ResetGfxAnimation(x, y);
13731       TEST_DrawLevelField(x, y);
13732     }
13733     else if (element == EL_TIME_ORB_FULL)
13734     {
13735       Feld[x][y] = EL_TIME_ORB_EMPTY;
13736
13737       if (level.time > 0 || level.use_time_orb_bug)
13738       {
13739         TimeLeft += level.time_orb_time;
13740         game.no_time_limit = FALSE;
13741
13742         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13743
13744         DisplayGameControlValues();
13745       }
13746
13747       ResetGfxAnimation(x, y);
13748       TEST_DrawLevelField(x, y);
13749     }
13750     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13751              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13752     {
13753       int xx, yy;
13754
13755       game.ball_state = !game.ball_state;
13756
13757       SCAN_PLAYFIELD(xx, yy)
13758       {
13759         int e = Feld[xx][yy];
13760
13761         if (game.ball_state)
13762         {
13763           if (e == EL_EMC_MAGIC_BALL)
13764             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13765           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13766             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13767         }
13768         else
13769         {
13770           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13771             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13772           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13773             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13774         }
13775       }
13776     }
13777
13778     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13779                                         player->index_bit, dig_side);
13780
13781     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13782                                         player->index_bit, dig_side);
13783
13784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13785                                         player->index_bit, dig_side);
13786
13787     return MP_ACTION;
13788   }
13789   else
13790   {
13791     if (!PLAYER_SWITCHING(player, x, y))
13792     {
13793       player->is_switching = TRUE;
13794       player->switch_x = x;
13795       player->switch_y = y;
13796
13797       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13798                                  player->index_bit, dig_side);
13799       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13800                                           player->index_bit, dig_side);
13801
13802       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13803                                  player->index_bit, dig_side);
13804       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13805                                           player->index_bit, dig_side);
13806     }
13807
13808     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13809                                player->index_bit, dig_side);
13810     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13811                                         player->index_bit, dig_side);
13812
13813     return MP_NO_ACTION;
13814   }
13815
13816   player->push_delay = -1;
13817
13818   if (is_player)                /* function can also be called by EL_PENGUIN */
13819   {
13820     if (Feld[x][y] != element)          /* really digged/collected something */
13821     {
13822       player->is_collecting = !player->is_digging;
13823       player->is_active = TRUE;
13824     }
13825   }
13826
13827   return MP_MOVING;
13828 }
13829
13830 static boolean DigFieldByCE(int x, int y, int digging_element)
13831 {
13832   int element = Feld[x][y];
13833
13834   if (!IS_FREE(x, y))
13835   {
13836     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13837                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13838                   ACTION_BREAKING);
13839
13840     /* no element can dig solid indestructible elements */
13841     if (IS_INDESTRUCTIBLE(element) &&
13842         !IS_DIGGABLE(element) &&
13843         !IS_COLLECTIBLE(element))
13844       return FALSE;
13845
13846     if (AmoebaNr[x][y] &&
13847         (element == EL_AMOEBA_FULL ||
13848          element == EL_BD_AMOEBA ||
13849          element == EL_AMOEBA_GROWING))
13850     {
13851       AmoebaCnt[AmoebaNr[x][y]]--;
13852       AmoebaCnt2[AmoebaNr[x][y]]--;
13853     }
13854
13855     if (IS_MOVING(x, y))
13856       RemoveMovingField(x, y);
13857     else
13858     {
13859       RemoveField(x, y);
13860       TEST_DrawLevelField(x, y);
13861     }
13862
13863     /* if digged element was about to explode, prevent the explosion */
13864     ExplodeField[x][y] = EX_TYPE_NONE;
13865
13866     PlayLevelSoundAction(x, y, action);
13867   }
13868
13869   Store[x][y] = EL_EMPTY;
13870
13871   /* this makes it possible to leave the removed element again */
13872   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13873     Store[x][y] = element;
13874
13875   return TRUE;
13876 }
13877
13878 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13879 {
13880   int jx = player->jx, jy = player->jy;
13881   int x = jx + dx, y = jy + dy;
13882   int snap_direction = (dx == -1 ? MV_LEFT  :
13883                         dx == +1 ? MV_RIGHT :
13884                         dy == -1 ? MV_UP    :
13885                         dy == +1 ? MV_DOWN  : MV_NONE);
13886   boolean can_continue_snapping = (level.continuous_snapping &&
13887                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13888
13889   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13890     return FALSE;
13891
13892   if (!player->active || !IN_LEV_FIELD(x, y))
13893     return FALSE;
13894
13895   if (dx && dy)
13896     return FALSE;
13897
13898   if (!dx && !dy)
13899   {
13900     if (player->MovPos == 0)
13901       player->is_pushing = FALSE;
13902
13903     player->is_snapping = FALSE;
13904
13905     if (player->MovPos == 0)
13906     {
13907       player->is_moving = FALSE;
13908       player->is_digging = FALSE;
13909       player->is_collecting = FALSE;
13910     }
13911
13912     return FALSE;
13913   }
13914
13915   /* prevent snapping with already pressed snap key when not allowed */
13916   if (player->is_snapping && !can_continue_snapping)
13917     return FALSE;
13918
13919   player->MovDir = snap_direction;
13920
13921   if (player->MovPos == 0)
13922   {
13923     player->is_moving = FALSE;
13924     player->is_digging = FALSE;
13925     player->is_collecting = FALSE;
13926   }
13927
13928   player->is_dropping = FALSE;
13929   player->is_dropping_pressed = FALSE;
13930   player->drop_pressed_delay = 0;
13931
13932   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13933     return FALSE;
13934
13935   player->is_snapping = TRUE;
13936   player->is_active = TRUE;
13937
13938   if (player->MovPos == 0)
13939   {
13940     player->is_moving = FALSE;
13941     player->is_digging = FALSE;
13942     player->is_collecting = FALSE;
13943   }
13944
13945   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13946     TEST_DrawLevelField(player->last_jx, player->last_jy);
13947
13948   TEST_DrawLevelField(x, y);
13949
13950   return TRUE;
13951 }
13952
13953 static boolean DropElement(struct PlayerInfo *player)
13954 {
13955   int old_element, new_element;
13956   int dropx = player->jx, dropy = player->jy;
13957   int drop_direction = player->MovDir;
13958   int drop_side = drop_direction;
13959   int drop_element = get_next_dropped_element(player);
13960
13961   player->is_dropping_pressed = TRUE;
13962
13963   /* do not drop an element on top of another element; when holding drop key
13964      pressed without moving, dropped element must move away before the next
13965      element can be dropped (this is especially important if the next element
13966      is dynamite, which can be placed on background for historical reasons) */
13967   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13968     return MP_ACTION;
13969
13970   if (IS_THROWABLE(drop_element))
13971   {
13972     dropx += GET_DX_FROM_DIR(drop_direction);
13973     dropy += GET_DY_FROM_DIR(drop_direction);
13974
13975     if (!IN_LEV_FIELD(dropx, dropy))
13976       return FALSE;
13977   }
13978
13979   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13980   new_element = drop_element;           /* default: no change when dropping */
13981
13982   /* check if player is active, not moving and ready to drop */
13983   if (!player->active || player->MovPos || player->drop_delay > 0)
13984     return FALSE;
13985
13986   /* check if player has anything that can be dropped */
13987   if (new_element == EL_UNDEFINED)
13988     return FALSE;
13989
13990   /* check if drop key was pressed long enough for EM style dynamite */
13991   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13992     return FALSE;
13993
13994   /* check if anything can be dropped at the current position */
13995   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13996     return FALSE;
13997
13998   /* collected custom elements can only be dropped on empty fields */
13999   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14000     return FALSE;
14001
14002   if (old_element != EL_EMPTY)
14003     Back[dropx][dropy] = old_element;   /* store old element on this field */
14004
14005   ResetGfxAnimation(dropx, dropy);
14006   ResetRandomAnimationValue(dropx, dropy);
14007
14008   if (player->inventory_size > 0 ||
14009       player->inventory_infinite_element != EL_UNDEFINED)
14010   {
14011     if (player->inventory_size > 0)
14012     {
14013       player->inventory_size--;
14014
14015       DrawGameDoorValues();
14016
14017       if (new_element == EL_DYNAMITE)
14018         new_element = EL_DYNAMITE_ACTIVE;
14019       else if (new_element == EL_EM_DYNAMITE)
14020         new_element = EL_EM_DYNAMITE_ACTIVE;
14021       else if (new_element == EL_SP_DISK_RED)
14022         new_element = EL_SP_DISK_RED_ACTIVE;
14023     }
14024
14025     Feld[dropx][dropy] = new_element;
14026
14027     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14028       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14029                           el2img(Feld[dropx][dropy]), 0);
14030
14031     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14032
14033     /* needed if previous element just changed to "empty" in the last frame */
14034     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14035
14036     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14037                                player->index_bit, drop_side);
14038     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14039                                         CE_PLAYER_DROPS_X,
14040                                         player->index_bit, drop_side);
14041
14042     TestIfElementTouchesCustomElement(dropx, dropy);
14043   }
14044   else          /* player is dropping a dyna bomb */
14045   {
14046     player->dynabombs_left--;
14047
14048     Feld[dropx][dropy] = new_element;
14049
14050     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14051       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14052                           el2img(Feld[dropx][dropy]), 0);
14053
14054     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14055   }
14056
14057   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14058     InitField_WithBug1(dropx, dropy, FALSE);
14059
14060   new_element = Feld[dropx][dropy];     /* element might have changed */
14061
14062   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14063       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14064   {
14065     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14066       MovDir[dropx][dropy] = drop_direction;
14067
14068     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14069
14070     /* do not cause impact style collision by dropping elements that can fall */
14071     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14072   }
14073
14074   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14075   player->is_dropping = TRUE;
14076
14077   player->drop_pressed_delay = 0;
14078   player->is_dropping_pressed = FALSE;
14079
14080   player->drop_x = dropx;
14081   player->drop_y = dropy;
14082
14083   return TRUE;
14084 }
14085
14086 /* ------------------------------------------------------------------------- */
14087 /* game sound playing functions                                              */
14088 /* ------------------------------------------------------------------------- */
14089
14090 static int *loop_sound_frame = NULL;
14091 static int *loop_sound_volume = NULL;
14092
14093 void InitPlayLevelSound()
14094 {
14095   int num_sounds = getSoundListSize();
14096
14097   checked_free(loop_sound_frame);
14098   checked_free(loop_sound_volume);
14099
14100   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14101   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14102 }
14103
14104 static void PlayLevelSound(int x, int y, int nr)
14105 {
14106   int sx = SCREENX(x), sy = SCREENY(y);
14107   int volume, stereo_position;
14108   int max_distance = 8;
14109   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14110
14111   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14112       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14113     return;
14114
14115   if (!IN_LEV_FIELD(x, y) ||
14116       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14117       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14118     return;
14119
14120   volume = SOUND_MAX_VOLUME;
14121
14122   if (!IN_SCR_FIELD(sx, sy))
14123   {
14124     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14125     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14126
14127     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14128   }
14129
14130   stereo_position = (SOUND_MAX_LEFT +
14131                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14132                      (SCR_FIELDX + 2 * max_distance));
14133
14134   if (IS_LOOP_SOUND(nr))
14135   {
14136     /* This assures that quieter loop sounds do not overwrite louder ones,
14137        while restarting sound volume comparison with each new game frame. */
14138
14139     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14140       return;
14141
14142     loop_sound_volume[nr] = volume;
14143     loop_sound_frame[nr] = FrameCounter;
14144   }
14145
14146   PlaySoundExt(nr, volume, stereo_position, type);
14147 }
14148
14149 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14150 {
14151   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14152                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14153                  y < LEVELY(BY1) ? LEVELY(BY1) :
14154                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14155                  sound_action);
14156 }
14157
14158 static void PlayLevelSoundAction(int x, int y, int action)
14159 {
14160   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14161 }
14162
14163 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14164 {
14165   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14166
14167   if (sound_effect != SND_UNDEFINED)
14168     PlayLevelSound(x, y, sound_effect);
14169 }
14170
14171 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14172                                               int action)
14173 {
14174   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14175
14176   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14177     PlayLevelSound(x, y, sound_effect);
14178 }
14179
14180 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14181 {
14182   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14183
14184   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14185     PlayLevelSound(x, y, sound_effect);
14186 }
14187
14188 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14189 {
14190   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14191
14192   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14193     StopSound(sound_effect);
14194 }
14195
14196 static void PlayLevelMusic()
14197 {
14198   if (levelset.music[level_nr] != MUS_UNDEFINED)
14199     PlayMusic(levelset.music[level_nr]);        /* from config file */
14200   else
14201     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14202 }
14203
14204 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14205 {
14206   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14207   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14208   int x = xx - 1 - offset;
14209   int y = yy - 1 - offset;
14210
14211   switch (sample)
14212   {
14213     case SAMPLE_blank:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14215       break;
14216
14217     case SAMPLE_roll:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14219       break;
14220
14221     case SAMPLE_stone:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14223       break;
14224
14225     case SAMPLE_nut:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14227       break;
14228
14229     case SAMPLE_crack:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14231       break;
14232
14233     case SAMPLE_bug:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14235       break;
14236
14237     case SAMPLE_tank:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14239       break;
14240
14241     case SAMPLE_android_clone:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14243       break;
14244
14245     case SAMPLE_android_move:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14247       break;
14248
14249     case SAMPLE_spring:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14251       break;
14252
14253     case SAMPLE_slurp:
14254       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14255       break;
14256
14257     case SAMPLE_eater:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14259       break;
14260
14261     case SAMPLE_eater_eat:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14263       break;
14264
14265     case SAMPLE_alien:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14267       break;
14268
14269     case SAMPLE_collect:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14271       break;
14272
14273     case SAMPLE_diamond:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14275       break;
14276
14277     case SAMPLE_squash:
14278       /* !!! CHECK THIS !!! */
14279 #if 1
14280       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14281 #else
14282       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14283 #endif
14284       break;
14285
14286     case SAMPLE_wonderfall:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14288       break;
14289
14290     case SAMPLE_drip:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14292       break;
14293
14294     case SAMPLE_push:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14296       break;
14297
14298     case SAMPLE_dirt:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14300       break;
14301
14302     case SAMPLE_acid:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14304       break;
14305
14306     case SAMPLE_ball:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14308       break;
14309
14310     case SAMPLE_grow:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14312       break;
14313
14314     case SAMPLE_wonder:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14316       break;
14317
14318     case SAMPLE_door:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14320       break;
14321
14322     case SAMPLE_exit_open:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14324       break;
14325
14326     case SAMPLE_exit_leave:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14328       break;
14329
14330     case SAMPLE_dynamite:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14332       break;
14333
14334     case SAMPLE_tick:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14336       break;
14337
14338     case SAMPLE_press:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14340       break;
14341
14342     case SAMPLE_wheel:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14344       break;
14345
14346     case SAMPLE_boom:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14348       break;
14349
14350     case SAMPLE_die:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14352       break;
14353
14354     case SAMPLE_time:
14355       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14356       break;
14357
14358     default:
14359       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14360       break;
14361   }
14362 }
14363
14364 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14365 {
14366   int element = map_element_SP_to_RND(element_sp);
14367   int action = map_action_SP_to_RND(action_sp);
14368   int offset = (setup.sp_show_border_elements ? 0 : 1);
14369   int x = xx - offset;
14370   int y = yy - offset;
14371
14372   PlayLevelSoundElementAction(x, y, element, action);
14373 }
14374
14375 void RaiseScore(int value)
14376 {
14377   local_player->score += value;
14378
14379   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14380
14381   DisplayGameControlValues();
14382 }
14383
14384 void RaiseScoreElement(int element)
14385 {
14386   switch (element)
14387   {
14388     case EL_EMERALD:
14389     case EL_BD_DIAMOND:
14390     case EL_EMERALD_YELLOW:
14391     case EL_EMERALD_RED:
14392     case EL_EMERALD_PURPLE:
14393     case EL_SP_INFOTRON:
14394       RaiseScore(level.score[SC_EMERALD]);
14395       break;
14396     case EL_DIAMOND:
14397       RaiseScore(level.score[SC_DIAMOND]);
14398       break;
14399     case EL_CRYSTAL:
14400       RaiseScore(level.score[SC_CRYSTAL]);
14401       break;
14402     case EL_PEARL:
14403       RaiseScore(level.score[SC_PEARL]);
14404       break;
14405     case EL_BUG:
14406     case EL_BD_BUTTERFLY:
14407     case EL_SP_ELECTRON:
14408       RaiseScore(level.score[SC_BUG]);
14409       break;
14410     case EL_SPACESHIP:
14411     case EL_BD_FIREFLY:
14412     case EL_SP_SNIKSNAK:
14413       RaiseScore(level.score[SC_SPACESHIP]);
14414       break;
14415     case EL_YAMYAM:
14416     case EL_DARK_YAMYAM:
14417       RaiseScore(level.score[SC_YAMYAM]);
14418       break;
14419     case EL_ROBOT:
14420       RaiseScore(level.score[SC_ROBOT]);
14421       break;
14422     case EL_PACMAN:
14423       RaiseScore(level.score[SC_PACMAN]);
14424       break;
14425     case EL_NUT:
14426       RaiseScore(level.score[SC_NUT]);
14427       break;
14428     case EL_DYNAMITE:
14429     case EL_EM_DYNAMITE:
14430     case EL_SP_DISK_RED:
14431     case EL_DYNABOMB_INCREASE_NUMBER:
14432     case EL_DYNABOMB_INCREASE_SIZE:
14433     case EL_DYNABOMB_INCREASE_POWER:
14434       RaiseScore(level.score[SC_DYNAMITE]);
14435       break;
14436     case EL_SHIELD_NORMAL:
14437     case EL_SHIELD_DEADLY:
14438       RaiseScore(level.score[SC_SHIELD]);
14439       break;
14440     case EL_EXTRA_TIME:
14441       RaiseScore(level.extra_time_score);
14442       break;
14443     case EL_KEY_1:
14444     case EL_KEY_2:
14445     case EL_KEY_3:
14446     case EL_KEY_4:
14447     case EL_EM_KEY_1:
14448     case EL_EM_KEY_2:
14449     case EL_EM_KEY_3:
14450     case EL_EM_KEY_4:
14451     case EL_EMC_KEY_5:
14452     case EL_EMC_KEY_6:
14453     case EL_EMC_KEY_7:
14454     case EL_EMC_KEY_8:
14455     case EL_DC_KEY_WHITE:
14456       RaiseScore(level.score[SC_KEY]);
14457       break;
14458     default:
14459       RaiseScore(element_info[element].collect_score);
14460       break;
14461   }
14462 }
14463
14464 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14465 {
14466   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14467   {
14468     /* closing door required in case of envelope style request dialogs */
14469     if (!skip_request)
14470       CloseDoor(DOOR_CLOSE_1);
14471
14472 #if defined(NETWORK_AVALIABLE)
14473     if (options.network)
14474       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14475     else
14476 #endif
14477     {
14478       if (quick_quit)
14479       {
14480         FadeSkipNextFadeIn();
14481
14482         game_status = GAME_MODE_MAIN;
14483
14484         DrawMainMenu();
14485       }
14486       else
14487       {
14488         game_status = GAME_MODE_MAIN;
14489
14490         DrawMainMenu();
14491       }
14492     }
14493   }
14494   else          /* continue playing the game */
14495   {
14496     if (tape.playing && tape.deactivate_display)
14497       TapeDeactivateDisplayOff(TRUE);
14498
14499     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14500
14501     if (tape.playing && tape.deactivate_display)
14502       TapeDeactivateDisplayOn();
14503   }
14504 }
14505
14506 void RequestQuitGame(boolean ask_if_really_quit)
14507 {
14508   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14509   boolean skip_request = AllPlayersGone || quick_quit;
14510
14511   RequestQuitGameExt(skip_request, quick_quit,
14512                      "Do you really want to quit the game?");
14513 }
14514
14515
14516 /* ------------------------------------------------------------------------- */
14517 /* random generator functions                                                */
14518 /* ------------------------------------------------------------------------- */
14519
14520 unsigned int InitEngineRandom_RND(int seed)
14521 {
14522   game.num_random_calls = 0;
14523
14524   return InitEngineRandom(seed);
14525 }
14526
14527 unsigned int RND(int max)
14528 {
14529   if (max > 0)
14530   {
14531     game.num_random_calls++;
14532
14533     return GetEngineRandom(max);
14534   }
14535
14536   return 0;
14537 }
14538
14539
14540 /* ------------------------------------------------------------------------- */
14541 /* game engine snapshot handling functions                                   */
14542 /* ------------------------------------------------------------------------- */
14543
14544 struct EngineSnapshotInfo
14545 {
14546   /* runtime values for custom element collect score */
14547   int collect_score[NUM_CUSTOM_ELEMENTS];
14548
14549   /* runtime values for group element choice position */
14550   int choice_pos[NUM_GROUP_ELEMENTS];
14551
14552   /* runtime values for belt position animations */
14553   int belt_graphic[4][NUM_BELT_PARTS];
14554   int belt_anim_mode[4][NUM_BELT_PARTS];
14555 };
14556
14557 static struct EngineSnapshotInfo engine_snapshot_rnd;
14558 static char *snapshot_level_identifier = NULL;
14559 static int snapshot_level_nr = -1;
14560
14561 static void SaveEngineSnapshotValues_RND()
14562 {
14563   static int belt_base_active_element[4] =
14564   {
14565     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14566     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14567     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14568     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14569   };
14570   int i, j;
14571
14572   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14573   {
14574     int element = EL_CUSTOM_START + i;
14575
14576     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14577   }
14578
14579   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14580   {
14581     int element = EL_GROUP_START + i;
14582
14583     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14584   }
14585
14586   for (i = 0; i < 4; i++)
14587   {
14588     for (j = 0; j < NUM_BELT_PARTS; j++)
14589     {
14590       int element = belt_base_active_element[i] + j;
14591       int graphic = el2img(element);
14592       int anim_mode = graphic_info[graphic].anim_mode;
14593
14594       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14595       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14596     }
14597   }
14598 }
14599
14600 static void LoadEngineSnapshotValues_RND()
14601 {
14602   unsigned int num_random_calls = game.num_random_calls;
14603   int i, j;
14604
14605   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14606   {
14607     int element = EL_CUSTOM_START + i;
14608
14609     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14610   }
14611
14612   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14613   {
14614     int element = EL_GROUP_START + i;
14615
14616     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14617   }
14618
14619   for (i = 0; i < 4; i++)
14620   {
14621     for (j = 0; j < NUM_BELT_PARTS; j++)
14622     {
14623       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14624       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14625
14626       graphic_info[graphic].anim_mode = anim_mode;
14627     }
14628   }
14629
14630   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14631   {
14632     InitRND(tape.random_seed);
14633     for (i = 0; i < num_random_calls; i++)
14634       RND(1);
14635   }
14636
14637   if (game.num_random_calls != num_random_calls)
14638   {
14639     Error(ERR_INFO, "number of random calls out of sync");
14640     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14641     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14642     Error(ERR_EXIT, "this should not happen -- please debug");
14643   }
14644 }
14645
14646 void FreeEngineSnapshotSingle()
14647 {
14648   FreeSnapshotSingle();
14649
14650   setString(&snapshot_level_identifier, NULL);
14651   snapshot_level_nr = -1;
14652 }
14653
14654 void FreeEngineSnapshotList()
14655 {
14656   FreeSnapshotList();
14657 }
14658
14659 ListNode *SaveEngineSnapshotBuffers()
14660 {
14661   ListNode *buffers = NULL;
14662
14663   /* copy some special values to a structure better suited for the snapshot */
14664
14665   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14666     SaveEngineSnapshotValues_RND();
14667   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14668     SaveEngineSnapshotValues_EM();
14669   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14670     SaveEngineSnapshotValues_SP(&buffers);
14671
14672   /* save values stored in special snapshot structure */
14673
14674   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14675     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14676   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14677     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14678   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14679     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14680
14681   /* save further RND engine values */
14682
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14686
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14691
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14697
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14701
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14703
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14705
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14708
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14723   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14726   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14727
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14730
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14734
14735   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14737
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14741   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14743
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14745   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14746
14747 #if 0
14748   ListNode *node = engine_snapshot_list_rnd;
14749   int num_bytes = 0;
14750
14751   while (node != NULL)
14752   {
14753     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14754
14755     node = node->next;
14756   }
14757
14758   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14759 #endif
14760
14761   return buffers;
14762 }
14763
14764 void SaveEngineSnapshotSingle()
14765 {
14766   ListNode *buffers = SaveEngineSnapshotBuffers();
14767
14768   /* finally save all snapshot buffers to single snapshot */
14769   SaveSnapshotSingle(buffers);
14770
14771   /* save level identification information */
14772   setString(&snapshot_level_identifier, leveldir_current->identifier);
14773   snapshot_level_nr = level_nr;
14774 }
14775
14776 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14777 {
14778   boolean save_snapshot =
14779     (initial_snapshot ||
14780      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14781      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14782       game.snapshot.changed_action));
14783
14784   game.snapshot.changed_action = FALSE;
14785
14786   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14787       tape.quick_resume ||
14788       !save_snapshot)
14789     return FALSE;
14790
14791   ListNode *buffers = SaveEngineSnapshotBuffers();
14792
14793   /* finally save all snapshot buffers to snapshot list */
14794   SaveSnapshotToList(buffers);
14795
14796   return TRUE;
14797 }
14798
14799 boolean SaveEngineSnapshotToList()
14800 {
14801   return SaveEngineSnapshotToListExt(FALSE);
14802 }
14803
14804 void SaveEngineSnapshotToListInitial()
14805 {
14806   FreeEngineSnapshotList();
14807
14808   SaveEngineSnapshotToListExt(TRUE);
14809 }
14810
14811 void LoadEngineSnapshotValues()
14812 {
14813   /* restore special values from snapshot structure */
14814
14815   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14816     LoadEngineSnapshotValues_RND();
14817   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14818     LoadEngineSnapshotValues_EM();
14819   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14820     LoadEngineSnapshotValues_SP();
14821 }
14822
14823 void LoadEngineSnapshotSingle()
14824 {
14825   LoadSnapshotSingle();
14826
14827   LoadEngineSnapshotValues();
14828 }
14829
14830 void LoadEngineSnapshot_Undo(int steps)
14831 {
14832   LoadSnapshotFromList_Older(steps);
14833
14834   LoadEngineSnapshotValues();
14835 }
14836
14837 void LoadEngineSnapshot_Redo(int steps)
14838 {
14839   LoadSnapshotFromList_Newer(steps);
14840
14841   LoadEngineSnapshotValues();
14842 }
14843
14844 boolean CheckEngineSnapshotSingle()
14845 {
14846   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14847           snapshot_level_nr == level_nr);
14848 }
14849
14850 boolean CheckEngineSnapshotList()
14851 {
14852   return CheckSnapshotList();
14853 }
14854
14855
14856 /* ---------- new game button stuff ---------------------------------------- */
14857
14858 static struct
14859 {
14860   int graphic;
14861   struct XY *pos;
14862   int gadget_id;
14863   char *infotext;
14864 } gamebutton_info[NUM_GAME_BUTTONS] =
14865 {
14866   {
14867     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14868     GAME_CTRL_ID_STOP,                  "stop game"
14869   },
14870   {
14871     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14872     GAME_CTRL_ID_PAUSE,                 "pause game"
14873   },
14874   {
14875     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14876     GAME_CTRL_ID_PLAY,                  "play game"
14877   },
14878   {
14879     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14880     GAME_CTRL_ID_UNDO,                  "undo step"
14881   },
14882   {
14883     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14884     GAME_CTRL_ID_REDO,                  "redo step"
14885   },
14886   {
14887     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14888     GAME_CTRL_ID_SAVE,                  "save game"
14889   },
14890   {
14891     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14892     GAME_CTRL_ID_PAUSE2,                "pause game"
14893   },
14894   {
14895     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14896     GAME_CTRL_ID_LOAD,                  "load game"
14897   },
14898   {
14899     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14900     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14901   },
14902   {
14903     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14904     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14905   },
14906   {
14907     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14908     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14909   }
14910 };
14911
14912 void CreateGameButtons()
14913 {
14914   int i;
14915
14916   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14917   {
14918     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14919     struct XY *pos = gamebutton_info[i].pos;
14920     struct GadgetInfo *gi;
14921     int button_type;
14922     boolean checked;
14923     unsigned int event_mask;
14924     int base_x = (tape.show_game_buttons ? VX : DX);
14925     int base_y = (tape.show_game_buttons ? VY : DY);
14926     int gd_x   = gfx->src_x;
14927     int gd_y   = gfx->src_y;
14928     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14929     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14930     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14931     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14932     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14933     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14934     int id = i;
14935
14936     if (gfx->bitmap == NULL)
14937     {
14938       game_gadget[id] = NULL;
14939
14940       continue;
14941     }
14942
14943     if (id == GAME_CTRL_ID_STOP ||
14944         id == GAME_CTRL_ID_PLAY ||
14945         id == GAME_CTRL_ID_SAVE ||
14946         id == GAME_CTRL_ID_LOAD)
14947     {
14948       button_type = GD_TYPE_NORMAL_BUTTON;
14949       checked = FALSE;
14950       event_mask = GD_EVENT_RELEASED;
14951     }
14952     else if (id == GAME_CTRL_ID_UNDO ||
14953              id == GAME_CTRL_ID_REDO)
14954     {
14955       button_type = GD_TYPE_NORMAL_BUTTON;
14956       checked = FALSE;
14957       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14958     }
14959     else
14960     {
14961       button_type = GD_TYPE_CHECK_BUTTON;
14962       checked =
14963         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14964          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14965          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14966       event_mask = GD_EVENT_PRESSED;
14967     }
14968
14969     gi = CreateGadget(GDI_CUSTOM_ID, id,
14970                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14971                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14972                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14973                       GDI_WIDTH, gfx->width,
14974                       GDI_HEIGHT, gfx->height,
14975                       GDI_TYPE, button_type,
14976                       GDI_STATE, GD_BUTTON_UNPRESSED,
14977                       GDI_CHECKED, checked,
14978                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14979                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14980                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14981                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14982                       GDI_DIRECT_DRAW, FALSE,
14983                       GDI_EVENT_MASK, event_mask,
14984                       GDI_CALLBACK_ACTION, HandleGameButtons,
14985                       GDI_END);
14986
14987     if (gi == NULL)
14988       Error(ERR_EXIT, "cannot create gadget");
14989
14990     game_gadget[id] = gi;
14991   }
14992 }
14993
14994 void FreeGameButtons()
14995 {
14996   int i;
14997
14998   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14999     FreeGadget(game_gadget[i]);
15000 }
15001
15002 static void MapGameButtonsAtSamePosition(int id)
15003 {
15004   int i;
15005
15006   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15007     if (i != id &&
15008         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15009         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15010       MapGadget(game_gadget[i]);
15011 }
15012
15013 static void UnmapGameButtonsAtSamePosition(int id)
15014 {
15015   int i;
15016
15017   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15018     if (i != id &&
15019         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15020         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15021       UnmapGadget(game_gadget[i]);
15022 }
15023
15024 void MapUndoRedoButtons()
15025 {
15026   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15027   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15028
15029   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15030   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15031 }
15032
15033 void UnmapUndoRedoButtons()
15034 {
15035   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15036   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15037
15038   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15039   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15040 }
15041
15042 void MapGameButtons()
15043 {
15044   int i;
15045
15046   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15047     if (i != GAME_CTRL_ID_UNDO &&
15048         i != GAME_CTRL_ID_REDO)
15049       MapGadget(game_gadget[i]);
15050
15051   if (setup.show_snapshot_buttons)
15052   {
15053     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15054     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15055     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15056   }
15057   else
15058   {
15059     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15060     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15061     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15062   }
15063
15064   RedrawGameButtons();
15065 }
15066
15067 void UnmapGameButtons()
15068 {
15069   int i;
15070
15071   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15072     UnmapGadget(game_gadget[i]);
15073 }
15074
15075 void RedrawGameButtons()
15076 {
15077   int i;
15078
15079   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15080     RedrawGadget(game_gadget[i]);
15081
15082   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15083   redraw_mask &= ~REDRAW_ALL;
15084 }
15085
15086 void GameUndoRedoExt()
15087 {
15088   ClearPlayerAction();
15089
15090   tape.pausing = TRUE;
15091
15092   RedrawPlayfield();
15093   UpdateAndDisplayGameControlValues();
15094
15095   DrawCompleteVideoDisplay();
15096   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15097   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15098   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15099                     VIDEO_STATE_1STEP_OFF), 0);
15100
15101   BackToFront();
15102 }
15103
15104 void GameUndo(int steps)
15105 {
15106   if (!CheckEngineSnapshotList())
15107     return;
15108
15109   LoadEngineSnapshot_Undo(steps);
15110
15111   GameUndoRedoExt();
15112 }
15113
15114 void GameRedo(int steps)
15115 {
15116   if (!CheckEngineSnapshotList())
15117     return;
15118
15119   LoadEngineSnapshot_Redo(steps);
15120
15121   GameUndoRedoExt();
15122 }
15123
15124 static void HandleGameButtonsExt(int id, int button)
15125 {
15126   int steps = BUTTON_STEPSIZE(button);
15127   boolean handle_game_buttons =
15128     (game_status == GAME_MODE_PLAYING ||
15129      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15130
15131   if (!handle_game_buttons)
15132     return;
15133
15134   switch (id)
15135   {
15136     case GAME_CTRL_ID_STOP:
15137       if (game_status == GAME_MODE_MAIN)
15138         break;
15139
15140       if (tape.playing)
15141         TapeStop();
15142       else
15143         RequestQuitGame(TRUE);
15144
15145       break;
15146
15147     case GAME_CTRL_ID_PAUSE:
15148     case GAME_CTRL_ID_PAUSE2:
15149       if (options.network && game_status == GAME_MODE_PLAYING)
15150       {
15151 #if defined(NETWORK_AVALIABLE)
15152         if (tape.pausing)
15153           SendToServer_ContinuePlaying();
15154         else
15155           SendToServer_PausePlaying();
15156 #endif
15157       }
15158       else
15159         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15160       break;
15161
15162     case GAME_CTRL_ID_PLAY:
15163       if (game_status == GAME_MODE_MAIN)
15164       {
15165         StartGameActions(options.network, setup.autorecord, level.random_seed);
15166       }
15167       else if (tape.pausing)
15168       {
15169 #if defined(NETWORK_AVALIABLE)
15170         if (options.network)
15171           SendToServer_ContinuePlaying();
15172         else
15173 #endif
15174           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15175       }
15176       break;
15177
15178     case GAME_CTRL_ID_UNDO:
15179       GameUndo(steps);
15180       break;
15181
15182     case GAME_CTRL_ID_REDO:
15183       GameRedo(steps);
15184       break;
15185
15186     case GAME_CTRL_ID_SAVE:
15187       TapeQuickSave();
15188       break;
15189
15190     case GAME_CTRL_ID_LOAD:
15191       TapeQuickLoad();
15192       break;
15193
15194     case SOUND_CTRL_ID_MUSIC:
15195       if (setup.sound_music)
15196       { 
15197         setup.sound_music = FALSE;
15198
15199         FadeMusic();
15200       }
15201       else if (audio.music_available)
15202       { 
15203         setup.sound = setup.sound_music = TRUE;
15204
15205         SetAudioMode(setup.sound);
15206
15207         PlayLevelMusic();
15208       }
15209       break;
15210
15211     case SOUND_CTRL_ID_LOOPS:
15212       if (setup.sound_loops)
15213         setup.sound_loops = FALSE;
15214       else if (audio.loops_available)
15215       {
15216         setup.sound = setup.sound_loops = TRUE;
15217
15218         SetAudioMode(setup.sound);
15219       }
15220       break;
15221
15222     case SOUND_CTRL_ID_SIMPLE:
15223       if (setup.sound_simple)
15224         setup.sound_simple = FALSE;
15225       else if (audio.sound_available)
15226       {
15227         setup.sound = setup.sound_simple = TRUE;
15228
15229         SetAudioMode(setup.sound);
15230       }
15231       break;
15232
15233     default:
15234       break;
15235   }
15236 }
15237
15238 static void HandleGameButtons(struct GadgetInfo *gi)
15239 {
15240   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15241 }
15242
15243 void HandleSoundButtonKeys(Key key)
15244 {
15245
15246   if (key == setup.shortcut.sound_simple)
15247     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15248   else if (key == setup.shortcut.sound_loops)
15249     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15250   else if (key == setup.shortcut.sound_music)
15251     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15252 }