added some (currently deactivated) frame rate debugging code
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086
3087   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3088   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3089   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3090   int initial_move_dir = MV_DOWN;
3091   int i, j, x, y;
3092
3093   game_status = GAME_MODE_PLAYING;
3094
3095   StopAnimation();
3096
3097   if (!game.restart_level)
3098     CloseDoor(DOOR_CLOSE_1);
3099
3100   if (level_editor_test_game)
3101     FadeSkipNextFadeIn();
3102   else
3103     FadeSetEnterScreen();
3104
3105   FadeOut(REDRAW_FIELD);
3106
3107   /* needed if different viewport properties defined for playing */
3108   ChangeViewportPropertiesIfNeeded();
3109
3110   DrawCompleteVideoDisplay();
3111
3112   InitGameEngine();
3113   InitGameControlValues();
3114
3115   /* don't play tapes over network */
3116   network_playing = (options.network && !tape.playing);
3117
3118   for (i = 0; i < MAX_PLAYERS; i++)
3119   {
3120     struct PlayerInfo *player = &stored_player[i];
3121
3122     player->index_nr = i;
3123     player->index_bit = (1 << i);
3124     player->element_nr = EL_PLAYER_1 + i;
3125
3126     player->present = FALSE;
3127     player->active = FALSE;
3128     player->mapped = FALSE;
3129
3130     player->killed = FALSE;
3131     player->reanimated = FALSE;
3132
3133     player->action = 0;
3134     player->effective_action = 0;
3135     player->programmed_action = 0;
3136
3137     player->score = 0;
3138     player->score_final = 0;
3139
3140     player->gems_still_needed = level.gems_needed;
3141     player->sokobanfields_still_needed = 0;
3142     player->lights_still_needed = 0;
3143     player->friends_still_needed = 0;
3144
3145     for (j = 0; j < MAX_NUM_KEYS; j++)
3146       player->key[j] = FALSE;
3147
3148     player->num_white_keys = 0;
3149
3150     player->dynabomb_count = 0;
3151     player->dynabomb_size = 1;
3152     player->dynabombs_left = 0;
3153     player->dynabomb_xl = FALSE;
3154
3155     player->MovDir = initial_move_dir;
3156     player->MovPos = 0;
3157     player->GfxPos = 0;
3158     player->GfxDir = initial_move_dir;
3159     player->GfxAction = ACTION_DEFAULT;
3160     player->Frame = 0;
3161     player->StepFrame = 0;
3162
3163     player->initial_element = player->element_nr;
3164     player->artwork_element =
3165       (level.use_artwork_element[i] ? level.artwork_element[i] :
3166        player->element_nr);
3167     player->use_murphy = FALSE;
3168
3169     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3170     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3171
3172     player->gravity = level.initial_player_gravity[i];
3173
3174     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3175
3176     player->actual_frame_counter = 0;
3177
3178     player->step_counter = 0;
3179
3180     player->last_move_dir = initial_move_dir;
3181
3182     player->is_active = FALSE;
3183
3184     player->is_waiting = FALSE;
3185     player->is_moving = FALSE;
3186     player->is_auto_moving = FALSE;
3187     player->is_digging = FALSE;
3188     player->is_snapping = FALSE;
3189     player->is_collecting = FALSE;
3190     player->is_pushing = FALSE;
3191     player->is_switching = FALSE;
3192     player->is_dropping = FALSE;
3193     player->is_dropping_pressed = FALSE;
3194
3195     player->is_bored = FALSE;
3196     player->is_sleeping = FALSE;
3197
3198     player->frame_counter_bored = -1;
3199     player->frame_counter_sleeping = -1;
3200
3201     player->anim_delay_counter = 0;
3202     player->post_delay_counter = 0;
3203
3204     player->dir_waiting = initial_move_dir;
3205     player->action_waiting = ACTION_DEFAULT;
3206     player->last_action_waiting = ACTION_DEFAULT;
3207     player->special_action_bored = ACTION_DEFAULT;
3208     player->special_action_sleeping = ACTION_DEFAULT;
3209
3210     player->switch_x = -1;
3211     player->switch_y = -1;
3212
3213     player->drop_x = -1;
3214     player->drop_y = -1;
3215
3216     player->show_envelope = 0;
3217
3218     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3219
3220     player->push_delay       = -1;      /* initialized when pushing starts */
3221     player->push_delay_value = game.initial_push_delay_value;
3222
3223     player->drop_delay = 0;
3224     player->drop_pressed_delay = 0;
3225
3226     player->last_jx = -1;
3227     player->last_jy = -1;
3228     player->jx = -1;
3229     player->jy = -1;
3230
3231     player->shield_normal_time_left = 0;
3232     player->shield_deadly_time_left = 0;
3233
3234     player->inventory_infinite_element = EL_UNDEFINED;
3235     player->inventory_size = 0;
3236
3237     if (level.use_initial_inventory[i])
3238     {
3239       for (j = 0; j < level.initial_inventory_size[i]; j++)
3240       {
3241         int element = level.initial_inventory_content[i][j];
3242         int collect_count = element_info[element].collect_count_initial;
3243         int k;
3244
3245         if (!IS_CUSTOM_ELEMENT(element))
3246           collect_count = 1;
3247
3248         if (collect_count == 0)
3249           player->inventory_infinite_element = element;
3250         else
3251           for (k = 0; k < collect_count; k++)
3252             if (player->inventory_size < MAX_INVENTORY_SIZE)
3253               player->inventory_element[player->inventory_size++] = element;
3254       }
3255     }
3256
3257     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3258     SnapField(player, 0, 0);
3259
3260     player->LevelSolved = FALSE;
3261     player->GameOver = FALSE;
3262
3263     player->LevelSolved_GameWon = FALSE;
3264     player->LevelSolved_GameEnd = FALSE;
3265     player->LevelSolved_PanelOff = FALSE;
3266     player->LevelSolved_SaveTape = FALSE;
3267     player->LevelSolved_SaveScore = FALSE;
3268     player->LevelSolved_CountingTime = 0;
3269     player->LevelSolved_CountingScore = 0;
3270
3271     map_player_action[i] = i;
3272   }
3273
3274   network_player_action_received = FALSE;
3275
3276 #if defined(NETWORK_AVALIABLE)
3277   /* initial null action */
3278   if (network_playing)
3279     SendToServer_MovePlayer(MV_NONE);
3280 #endif
3281
3282   ZX = ZY = -1;
3283   ExitX = ExitY = -1;
3284
3285   FrameCounter = 0;
3286   TimeFrames = 0;
3287   TimePlayed = 0;
3288   TimeLeft = level.time;
3289   TapeTime = 0;
3290
3291   ScreenMovDir = MV_NONE;
3292   ScreenMovPos = 0;
3293   ScreenGfxPos = 0;
3294
3295   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3296
3297   AllPlayersGone = FALSE;
3298
3299   game.no_time_limit = (level.time == 0);
3300
3301   game.yamyam_content_nr = 0;
3302   game.robot_wheel_active = FALSE;
3303   game.magic_wall_active = FALSE;
3304   game.magic_wall_time_left = 0;
3305   game.light_time_left = 0;
3306   game.timegate_time_left = 0;
3307   game.switchgate_pos = 0;
3308   game.wind_direction = level.wind_direction_initial;
3309
3310   game.lenses_time_left = 0;
3311   game.magnify_time_left = 0;
3312
3313   game.ball_state = level.ball_state_initial;
3314   game.ball_content_nr = 0;
3315
3316   game.envelope_active = FALSE;
3317
3318   /* set focus to local player for network games, else to all players */
3319   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3320   game.centered_player_nr_next = game.centered_player_nr;
3321   game.set_centered_player = FALSE;
3322
3323   if (network_playing && tape.recording)
3324   {
3325     /* store client dependent player focus when recording network games */
3326     tape.centered_player_nr_next = game.centered_player_nr_next;
3327     tape.set_centered_player = TRUE;
3328   }
3329
3330   for (i = 0; i < NUM_BELTS; i++)
3331   {
3332     game.belt_dir[i] = MV_NONE;
3333     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3334   }
3335
3336   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3337     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3338
3339 #if DEBUG_INIT_PLAYER
3340   if (options.debug)
3341   {
3342     printf("Player status at level initialization:\n");
3343   }
3344 #endif
3345
3346   SCAN_PLAYFIELD(x, y)
3347   {
3348     Feld[x][y] = level.field[x][y];
3349     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3350     ChangeDelay[x][y] = 0;
3351     ChangePage[x][y] = -1;
3352     CustomValue[x][y] = 0;              /* initialized in InitField() */
3353     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3354     AmoebaNr[x][y] = 0;
3355     WasJustMoving[x][y] = 0;
3356     WasJustFalling[x][y] = 0;
3357     CheckCollision[x][y] = 0;
3358     CheckImpact[x][y] = 0;
3359     Stop[x][y] = FALSE;
3360     Pushed[x][y] = FALSE;
3361
3362     ChangeCount[x][y] = 0;
3363     ChangeEvent[x][y] = -1;
3364
3365     ExplodePhase[x][y] = 0;
3366     ExplodeDelay[x][y] = 0;
3367     ExplodeField[x][y] = EX_TYPE_NONE;
3368
3369     RunnerVisit[x][y] = 0;
3370     PlayerVisit[x][y] = 0;
3371
3372     GfxFrame[x][y] = 0;
3373     GfxRandom[x][y] = INIT_GFX_RANDOM();
3374     GfxElement[x][y] = EL_UNDEFINED;
3375     GfxAction[x][y] = ACTION_DEFAULT;
3376     GfxDir[x][y] = MV_NONE;
3377     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3378   }
3379
3380   SCAN_PLAYFIELD(x, y)
3381   {
3382     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3383       emulate_bd = FALSE;
3384     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3385       emulate_sb = FALSE;
3386     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3387       emulate_sp = FALSE;
3388
3389     InitField(x, y, TRUE);
3390
3391     ResetGfxAnimation(x, y);
3392   }
3393
3394   InitBeltMovement();
3395
3396   for (i = 0; i < MAX_PLAYERS; i++)
3397   {
3398     struct PlayerInfo *player = &stored_player[i];
3399
3400     /* set number of special actions for bored and sleeping animation */
3401     player->num_special_action_bored =
3402       get_num_special_action(player->artwork_element,
3403                              ACTION_BORING_1, ACTION_BORING_LAST);
3404     player->num_special_action_sleeping =
3405       get_num_special_action(player->artwork_element,
3406                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3407   }
3408
3409   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3410                     emulate_sb ? EMU_SOKOBAN :
3411                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3412
3413   /* initialize type of slippery elements */
3414   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3415   {
3416     if (!IS_CUSTOM_ELEMENT(i))
3417     {
3418       /* default: elements slip down either to the left or right randomly */
3419       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3420
3421       /* SP style elements prefer to slip down on the left side */
3422       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3423         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3424
3425       /* BD style elements prefer to slip down on the left side */
3426       if (game.emulation == EMU_BOULDERDASH)
3427         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3428     }
3429   }
3430
3431   /* initialize explosion and ignition delay */
3432   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3433   {
3434     if (!IS_CUSTOM_ELEMENT(i))
3435     {
3436       int num_phase = 8;
3437       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3438                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3439                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3440       int last_phase = (num_phase + 1) * delay;
3441       int half_phase = (num_phase / 2) * delay;
3442
3443       element_info[i].explosion_delay = last_phase - 1;
3444       element_info[i].ignition_delay = half_phase;
3445
3446       if (i == EL_BLACK_ORB)
3447         element_info[i].ignition_delay = 1;
3448     }
3449   }
3450
3451   /* correct non-moving belts to start moving left */
3452   for (i = 0; i < NUM_BELTS; i++)
3453     if (game.belt_dir[i] == MV_NONE)
3454       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3455
3456 #if USE_NEW_PLAYER_ASSIGNMENTS
3457   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3458   /* choose default local player */
3459   local_player = &stored_player[0];
3460
3461   for (i = 0; i < MAX_PLAYERS; i++)
3462     stored_player[i].connected = FALSE;
3463
3464   local_player->connected = TRUE;
3465   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3466
3467   if (tape.playing)
3468   {
3469     for (i = 0; i < MAX_PLAYERS; i++)
3470       stored_player[i].connected = tape.player_participates[i];
3471   }
3472   else if (game.team_mode && !options.network)
3473   {
3474     /* try to guess locally connected team mode players (needed for correct
3475        assignment of player figures from level to locally playing players) */
3476
3477     for (i = 0; i < MAX_PLAYERS; i++)
3478       if (setup.input[i].use_joystick ||
3479           setup.input[i].key.left != KSYM_UNDEFINED)
3480         stored_player[i].connected = TRUE;
3481   }
3482
3483 #if DEBUG_INIT_PLAYER
3484   if (options.debug)
3485   {
3486     printf("Player status after level initialization:\n");
3487
3488     for (i = 0; i < MAX_PLAYERS; i++)
3489     {
3490       struct PlayerInfo *player = &stored_player[i];
3491
3492       printf("- player %d: present == %d, connected == %d, active == %d",
3493              i + 1,
3494              player->present,
3495              player->connected,
3496              player->active);
3497
3498       if (local_player == player)
3499         printf(" (local player)");
3500
3501       printf("\n");
3502     }
3503   }
3504 #endif
3505
3506 #if DEBUG_INIT_PLAYER
3507   if (options.debug)
3508     printf("Reassigning players ...\n");
3509 #endif
3510
3511   /* check if any connected player was not found in playfield */
3512   for (i = 0; i < MAX_PLAYERS; i++)
3513   {
3514     struct PlayerInfo *player = &stored_player[i];
3515
3516     if (player->connected && !player->present)
3517     {
3518       struct PlayerInfo *field_player = NULL;
3519
3520 #if DEBUG_INIT_PLAYER
3521       if (options.debug)
3522         printf("- looking for field player for player %d ...\n", i + 1);
3523 #endif
3524
3525       /* assign first free player found that is present in the playfield */
3526
3527       /* first try: look for unmapped playfield player that is not connected */
3528       for (j = 0; j < MAX_PLAYERS; j++)
3529         if (field_player == NULL &&
3530             stored_player[j].present &&
3531             !stored_player[j].mapped &&
3532             !stored_player[j].connected)
3533           field_player = &stored_player[j];
3534
3535       /* second try: look for *any* unmapped playfield player */
3536       for (j = 0; j < MAX_PLAYERS; j++)
3537         if (field_player == NULL &&
3538             stored_player[j].present &&
3539             !stored_player[j].mapped)
3540           field_player = &stored_player[j];
3541
3542       if (field_player != NULL)
3543       {
3544         int jx = field_player->jx, jy = field_player->jy;
3545
3546 #if DEBUG_INIT_PLAYER
3547         if (options.debug)
3548           printf("- found player %d\n", field_player->index_nr + 1);
3549 #endif
3550
3551         player->present = FALSE;
3552         player->active = FALSE;
3553
3554         field_player->present = TRUE;
3555         field_player->active = TRUE;
3556
3557         /*
3558         player->initial_element = field_player->initial_element;
3559         player->artwork_element = field_player->artwork_element;
3560
3561         player->block_last_field       = field_player->block_last_field;
3562         player->block_delay_adjustment = field_player->block_delay_adjustment;
3563         */
3564
3565         StorePlayer[jx][jy] = field_player->element_nr;
3566
3567         field_player->jx = field_player->last_jx = jx;
3568         field_player->jy = field_player->last_jy = jy;
3569
3570         if (local_player == player)
3571           local_player = field_player;
3572
3573         map_player_action[field_player->index_nr] = i;
3574
3575         field_player->mapped = TRUE;
3576
3577 #if DEBUG_INIT_PLAYER
3578         if (options.debug)
3579           printf("- map_player_action[%d] == %d\n",
3580                  field_player->index_nr + 1, i + 1);
3581 #endif
3582       }
3583     }
3584
3585     if (player->connected && player->present)
3586       player->mapped = TRUE;
3587   }
3588
3589 #if DEBUG_INIT_PLAYER
3590   if (options.debug)
3591   {
3592     printf("Player status after player assignment (first stage):\n");
3593
3594     for (i = 0; i < MAX_PLAYERS; i++)
3595     {
3596       struct PlayerInfo *player = &stored_player[i];
3597
3598       printf("- player %d: present == %d, connected == %d, active == %d",
3599              i + 1,
3600              player->present,
3601              player->connected,
3602              player->active);
3603
3604       if (local_player == player)
3605         printf(" (local player)");
3606
3607       printf("\n");
3608     }
3609   }
3610 #endif
3611
3612 #else
3613
3614   /* check if any connected player was not found in playfield */
3615   for (i = 0; i < MAX_PLAYERS; i++)
3616   {
3617     struct PlayerInfo *player = &stored_player[i];
3618
3619     if (player->connected && !player->present)
3620     {
3621       for (j = 0; j < MAX_PLAYERS; j++)
3622       {
3623         struct PlayerInfo *field_player = &stored_player[j];
3624         int jx = field_player->jx, jy = field_player->jy;
3625
3626         /* assign first free player found that is present in the playfield */
3627         if (field_player->present && !field_player->connected)
3628         {
3629           player->present = TRUE;
3630           player->active = TRUE;
3631
3632           field_player->present = FALSE;
3633           field_player->active = FALSE;
3634
3635           player->initial_element = field_player->initial_element;
3636           player->artwork_element = field_player->artwork_element;
3637
3638           player->block_last_field       = field_player->block_last_field;
3639           player->block_delay_adjustment = field_player->block_delay_adjustment;
3640
3641           StorePlayer[jx][jy] = player->element_nr;
3642
3643           player->jx = player->last_jx = jx;
3644           player->jy = player->last_jy = jy;
3645
3646           break;
3647         }
3648       }
3649     }
3650   }
3651 #endif
3652
3653 #if 0
3654   printf("::: local_player->present == %d\n", local_player->present);
3655 #endif
3656
3657   if (tape.playing)
3658   {
3659     /* when playing a tape, eliminate all players who do not participate */
3660
3661 #if USE_NEW_PLAYER_ASSIGNMENTS
3662
3663     if (!game.team_mode)
3664     {
3665       for (i = 0; i < MAX_PLAYERS; i++)
3666       {
3667         if (stored_player[i].active &&
3668             !tape.player_participates[map_player_action[i]])
3669         {
3670           struct PlayerInfo *player = &stored_player[i];
3671           int jx = player->jx, jy = player->jy;
3672
3673 #if DEBUG_INIT_PLAYER
3674           if (options.debug)
3675             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3676 #endif
3677
3678           player->active = FALSE;
3679           StorePlayer[jx][jy] = 0;
3680           Feld[jx][jy] = EL_EMPTY;
3681         }
3682       }
3683     }
3684
3685 #else
3686
3687     for (i = 0; i < MAX_PLAYERS; i++)
3688     {
3689       if (stored_player[i].active &&
3690           !tape.player_participates[i])
3691       {
3692         struct PlayerInfo *player = &stored_player[i];
3693         int jx = player->jx, jy = player->jy;
3694
3695         player->active = FALSE;
3696         StorePlayer[jx][jy] = 0;
3697         Feld[jx][jy] = EL_EMPTY;
3698       }
3699     }
3700 #endif
3701   }
3702   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3703   {
3704     /* when in single player mode, eliminate all but the first active player */
3705
3706     for (i = 0; i < MAX_PLAYERS; i++)
3707     {
3708       if (stored_player[i].active)
3709       {
3710         for (j = i + 1; j < MAX_PLAYERS; j++)
3711         {
3712           if (stored_player[j].active)
3713           {
3714             struct PlayerInfo *player = &stored_player[j];
3715             int jx = player->jx, jy = player->jy;
3716
3717             player->active = FALSE;
3718             player->present = FALSE;
3719
3720             StorePlayer[jx][jy] = 0;
3721             Feld[jx][jy] = EL_EMPTY;
3722           }
3723         }
3724       }
3725     }
3726   }
3727
3728   /* when recording the game, store which players take part in the game */
3729   if (tape.recording)
3730   {
3731 #if USE_NEW_PLAYER_ASSIGNMENTS
3732     for (i = 0; i < MAX_PLAYERS; i++)
3733       if (stored_player[i].connected)
3734         tape.player_participates[i] = TRUE;
3735 #else
3736     for (i = 0; i < MAX_PLAYERS; i++)
3737       if (stored_player[i].active)
3738         tape.player_participates[i] = TRUE;
3739 #endif
3740   }
3741
3742 #if DEBUG_INIT_PLAYER
3743   if (options.debug)
3744   {
3745     printf("Player status after player assignment (final stage):\n");
3746
3747     for (i = 0; i < MAX_PLAYERS; i++)
3748     {
3749       struct PlayerInfo *player = &stored_player[i];
3750
3751       printf("- player %d: present == %d, connected == %d, active == %d",
3752              i + 1,
3753              player->present,
3754              player->connected,
3755              player->active);
3756
3757       if (local_player == player)
3758         printf(" (local player)");
3759
3760       printf("\n");
3761     }
3762   }
3763 #endif
3764
3765   if (BorderElement == EL_EMPTY)
3766   {
3767     SBX_Left = 0;
3768     SBX_Right = lev_fieldx - SCR_FIELDX;
3769     SBY_Upper = 0;
3770     SBY_Lower = lev_fieldy - SCR_FIELDY;
3771   }
3772   else
3773   {
3774     SBX_Left = -1;
3775     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3776     SBY_Upper = -1;
3777     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3778   }
3779
3780   if (full_lev_fieldx <= SCR_FIELDX)
3781     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3782   if (full_lev_fieldy <= SCR_FIELDY)
3783     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3784
3785   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3786     SBX_Left--;
3787   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3788     SBY_Upper--;
3789
3790   /* if local player not found, look for custom element that might create
3791      the player (make some assumptions about the right custom element) */
3792   if (!local_player->present)
3793   {
3794     int start_x = 0, start_y = 0;
3795     int found_rating = 0;
3796     int found_element = EL_UNDEFINED;
3797     int player_nr = local_player->index_nr;
3798
3799     SCAN_PLAYFIELD(x, y)
3800     {
3801       int element = Feld[x][y];
3802       int content;
3803       int xx, yy;
3804       boolean is_player;
3805
3806       if (level.use_start_element[player_nr] &&
3807           level.start_element[player_nr] == element &&
3808           found_rating < 4)
3809       {
3810         start_x = x;
3811         start_y = y;
3812
3813         found_rating = 4;
3814         found_element = element;
3815       }
3816
3817       if (!IS_CUSTOM_ELEMENT(element))
3818         continue;
3819
3820       if (CAN_CHANGE(element))
3821       {
3822         for (i = 0; i < element_info[element].num_change_pages; i++)
3823         {
3824           /* check for player created from custom element as single target */
3825           content = element_info[element].change_page[i].target_element;
3826           is_player = ELEM_IS_PLAYER(content);
3827
3828           if (is_player && (found_rating < 3 ||
3829                             (found_rating == 3 && element < found_element)))
3830           {
3831             start_x = x;
3832             start_y = y;
3833
3834             found_rating = 3;
3835             found_element = element;
3836           }
3837         }
3838       }
3839
3840       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3841       {
3842         /* check for player created from custom element as explosion content */
3843         content = element_info[element].content.e[xx][yy];
3844         is_player = ELEM_IS_PLAYER(content);
3845
3846         if (is_player && (found_rating < 2 ||
3847                           (found_rating == 2 && element < found_element)))
3848         {
3849           start_x = x + xx - 1;
3850           start_y = y + yy - 1;
3851
3852           found_rating = 2;
3853           found_element = element;
3854         }
3855
3856         if (!CAN_CHANGE(element))
3857           continue;
3858
3859         for (i = 0; i < element_info[element].num_change_pages; i++)
3860         {
3861           /* check for player created from custom element as extended target */
3862           content =
3863             element_info[element].change_page[i].target_content.e[xx][yy];
3864
3865           is_player = ELEM_IS_PLAYER(content);
3866
3867           if (is_player && (found_rating < 1 ||
3868                             (found_rating == 1 && element < found_element)))
3869           {
3870             start_x = x + xx - 1;
3871             start_y = y + yy - 1;
3872
3873             found_rating = 1;
3874             found_element = element;
3875           }
3876         }
3877       }
3878     }
3879
3880     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3881                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3882                 start_x - MIDPOSX);
3883
3884     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3885                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3886                 start_y - MIDPOSY);
3887   }
3888   else
3889   {
3890     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3891                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3892                 local_player->jx - MIDPOSX);
3893
3894     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3895                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3896                 local_player->jy - MIDPOSY);
3897   }
3898
3899   /* !!! FIX THIS (START) !!! */
3900   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3901   {
3902     InitGameEngine_EM();
3903   }
3904   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3905   {
3906     InitGameEngine_SP();
3907   }
3908   else
3909   {
3910     DrawLevel(REDRAW_FIELD);
3911     DrawAllPlayers();
3912
3913     /* after drawing the level, correct some elements */
3914     if (game.timegate_time_left == 0)
3915       CloseAllOpenTimegates();
3916   }
3917
3918   /* blit playfield from scroll buffer to normal back buffer for fading in */
3919   BlitScreenToBitmap(backbuffer);
3920   /* !!! FIX THIS (END) !!! */
3921
3922   FadeIn(REDRAW_FIELD);
3923
3924 #if 1
3925   // full screen redraw is required at this point in the following cases:
3926   // - special editor door undrawn when game was started from level editor
3927   // - drawing area (playfield) was changed and has to be removed completely
3928   redraw_mask = REDRAW_ALL;
3929   BackToFront();
3930 #endif
3931
3932   if (!game.restart_level)
3933   {
3934     /* copy default game door content to main double buffer */
3935
3936     /* !!! CHECK AGAIN !!! */
3937     SetPanelBackground();
3938     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3939     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3940   }
3941
3942   SetPanelBackground();
3943   SetDrawBackgroundMask(REDRAW_DOOR_1);
3944
3945   UpdateAndDisplayGameControlValues();
3946
3947   if (!game.restart_level)
3948   {
3949     UnmapGameButtons();
3950     UnmapTapeButtons();
3951     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3952     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3953     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3954     MapGameButtons();
3955     MapTapeButtons();
3956
3957     /* copy actual game door content to door double buffer for OpenDoor() */
3958     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3959
3960     OpenDoor(DOOR_OPEN_ALL);
3961
3962     PlaySound(SND_GAME_STARTING);
3963
3964     if (setup.sound_music)
3965       PlayLevelMusic();
3966
3967     KeyboardAutoRepeatOffUnlessAutoplay();
3968
3969 #if DEBUG_INIT_PLAYER
3970     if (options.debug)
3971     {
3972       printf("Player status (final):\n");
3973
3974       for (i = 0; i < MAX_PLAYERS; i++)
3975       {
3976         struct PlayerInfo *player = &stored_player[i];
3977
3978         printf("- player %d: present == %d, connected == %d, active == %d",
3979                i + 1,
3980                player->present,
3981                player->connected,
3982                player->active);
3983
3984         if (local_player == player)
3985           printf(" (local player)");
3986
3987         printf("\n");
3988       }
3989     }
3990 #endif
3991   }
3992
3993   UnmapAllGadgets();
3994
3995   MapGameButtons();
3996   MapTapeButtons();
3997
3998   if (!game.restart_level && !tape.playing)
3999   {
4000     LevelStats_incPlayed(level_nr);
4001
4002     SaveLevelSetup_SeriesInfo();
4003   }
4004
4005   game.restart_level = FALSE;
4006
4007   SaveEngineSnapshotToListInitial();
4008 }
4009
4010 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4011 {
4012   /* this is used for non-R'n'D game engines to update certain engine values */
4013
4014   /* needed to determine if sounds are played within the visible screen area */
4015   scroll_x = actual_scroll_x;
4016   scroll_y = actual_scroll_y;
4017 }
4018
4019 void InitMovDir(int x, int y)
4020 {
4021   int i, element = Feld[x][y];
4022   static int xy[4][2] =
4023   {
4024     {  0, +1 },
4025     { +1,  0 },
4026     {  0, -1 },
4027     { -1,  0 }
4028   };
4029   static int direction[3][4] =
4030   {
4031     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4032     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4033     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4034   };
4035
4036   switch (element)
4037   {
4038     case EL_BUG_RIGHT:
4039     case EL_BUG_UP:
4040     case EL_BUG_LEFT:
4041     case EL_BUG_DOWN:
4042       Feld[x][y] = EL_BUG;
4043       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4044       break;
4045
4046     case EL_SPACESHIP_RIGHT:
4047     case EL_SPACESHIP_UP:
4048     case EL_SPACESHIP_LEFT:
4049     case EL_SPACESHIP_DOWN:
4050       Feld[x][y] = EL_SPACESHIP;
4051       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4052       break;
4053
4054     case EL_BD_BUTTERFLY_RIGHT:
4055     case EL_BD_BUTTERFLY_UP:
4056     case EL_BD_BUTTERFLY_LEFT:
4057     case EL_BD_BUTTERFLY_DOWN:
4058       Feld[x][y] = EL_BD_BUTTERFLY;
4059       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4060       break;
4061
4062     case EL_BD_FIREFLY_RIGHT:
4063     case EL_BD_FIREFLY_UP:
4064     case EL_BD_FIREFLY_LEFT:
4065     case EL_BD_FIREFLY_DOWN:
4066       Feld[x][y] = EL_BD_FIREFLY;
4067       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4068       break;
4069
4070     case EL_PACMAN_RIGHT:
4071     case EL_PACMAN_UP:
4072     case EL_PACMAN_LEFT:
4073     case EL_PACMAN_DOWN:
4074       Feld[x][y] = EL_PACMAN;
4075       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4076       break;
4077
4078     case EL_YAMYAM_LEFT:
4079     case EL_YAMYAM_RIGHT:
4080     case EL_YAMYAM_UP:
4081     case EL_YAMYAM_DOWN:
4082       Feld[x][y] = EL_YAMYAM;
4083       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4084       break;
4085
4086     case EL_SP_SNIKSNAK:
4087       MovDir[x][y] = MV_UP;
4088       break;
4089
4090     case EL_SP_ELECTRON:
4091       MovDir[x][y] = MV_LEFT;
4092       break;
4093
4094     case EL_MOLE_LEFT:
4095     case EL_MOLE_RIGHT:
4096     case EL_MOLE_UP:
4097     case EL_MOLE_DOWN:
4098       Feld[x][y] = EL_MOLE;
4099       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4100       break;
4101
4102     default:
4103       if (IS_CUSTOM_ELEMENT(element))
4104       {
4105         struct ElementInfo *ei = &element_info[element];
4106         int move_direction_initial = ei->move_direction_initial;
4107         int move_pattern = ei->move_pattern;
4108
4109         if (move_direction_initial == MV_START_PREVIOUS)
4110         {
4111           if (MovDir[x][y] != MV_NONE)
4112             return;
4113
4114           move_direction_initial = MV_START_AUTOMATIC;
4115         }
4116
4117         if (move_direction_initial == MV_START_RANDOM)
4118           MovDir[x][y] = 1 << RND(4);
4119         else if (move_direction_initial & MV_ANY_DIRECTION)
4120           MovDir[x][y] = move_direction_initial;
4121         else if (move_pattern == MV_ALL_DIRECTIONS ||
4122                  move_pattern == MV_TURNING_LEFT ||
4123                  move_pattern == MV_TURNING_RIGHT ||
4124                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4125                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4126                  move_pattern == MV_TURNING_RANDOM)
4127           MovDir[x][y] = 1 << RND(4);
4128         else if (move_pattern == MV_HORIZONTAL)
4129           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4130         else if (move_pattern == MV_VERTICAL)
4131           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4132         else if (move_pattern & MV_ANY_DIRECTION)
4133           MovDir[x][y] = element_info[element].move_pattern;
4134         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4135                  move_pattern == MV_ALONG_RIGHT_SIDE)
4136         {
4137           /* use random direction as default start direction */
4138           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4139             MovDir[x][y] = 1 << RND(4);
4140
4141           for (i = 0; i < NUM_DIRECTIONS; i++)
4142           {
4143             int x1 = x + xy[i][0];
4144             int y1 = y + xy[i][1];
4145
4146             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4147             {
4148               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4149                 MovDir[x][y] = direction[0][i];
4150               else
4151                 MovDir[x][y] = direction[1][i];
4152
4153               break;
4154             }
4155           }
4156         }                
4157       }
4158       else
4159       {
4160         MovDir[x][y] = 1 << RND(4);
4161
4162         if (element != EL_BUG &&
4163             element != EL_SPACESHIP &&
4164             element != EL_BD_BUTTERFLY &&
4165             element != EL_BD_FIREFLY)
4166           break;
4167
4168         for (i = 0; i < NUM_DIRECTIONS; i++)
4169         {
4170           int x1 = x + xy[i][0];
4171           int y1 = y + xy[i][1];
4172
4173           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4174           {
4175             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4176             {
4177               MovDir[x][y] = direction[0][i];
4178               break;
4179             }
4180             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4181                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4182             {
4183               MovDir[x][y] = direction[1][i];
4184               break;
4185             }
4186           }
4187         }
4188       }
4189       break;
4190   }
4191
4192   GfxDir[x][y] = MovDir[x][y];
4193 }
4194
4195 void InitAmoebaNr(int x, int y)
4196 {
4197   int i;
4198   int group_nr = AmoebeNachbarNr(x, y);
4199
4200   if (group_nr == 0)
4201   {
4202     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4203     {
4204       if (AmoebaCnt[i] == 0)
4205       {
4206         group_nr = i;
4207         break;
4208       }
4209     }
4210   }
4211
4212   AmoebaNr[x][y] = group_nr;
4213   AmoebaCnt[group_nr]++;
4214   AmoebaCnt2[group_nr]++;
4215 }
4216
4217 static void PlayerWins(struct PlayerInfo *player)
4218 {
4219   player->LevelSolved = TRUE;
4220   player->GameOver = TRUE;
4221
4222   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4223                          level.native_em_level->lev->score : player->score);
4224
4225   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4226                                       TimeLeft);
4227   player->LevelSolved_CountingScore = player->score_final;
4228 }
4229
4230 void GameWon()
4231 {
4232   static int time, time_final;
4233   static int score, score_final;
4234   static int game_over_delay_1 = 0;
4235   static int game_over_delay_2 = 0;
4236   int game_over_delay_value_1 = 50;
4237   int game_over_delay_value_2 = 50;
4238
4239   if (!local_player->LevelSolved_GameWon)
4240   {
4241     int i;
4242
4243     /* do not start end game actions before the player stops moving (to exit) */
4244     if (local_player->MovPos)
4245       return;
4246
4247     local_player->LevelSolved_GameWon = TRUE;
4248     local_player->LevelSolved_SaveTape = tape.recording;
4249     local_player->LevelSolved_SaveScore = !tape.playing;
4250
4251     if (!tape.playing)
4252     {
4253       LevelStats_incSolved(level_nr);
4254
4255       SaveLevelSetup_SeriesInfo();
4256     }
4257
4258     if (tape.auto_play)         /* tape might already be stopped here */
4259       tape.auto_play_level_solved = TRUE;
4260
4261     TapeStop();
4262
4263     game_over_delay_1 = game_over_delay_value_1;
4264     game_over_delay_2 = game_over_delay_value_2;
4265
4266     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4267     score = score_final = local_player->score_final;
4268
4269     if (TimeLeft > 0)
4270     {
4271       time_final = 0;
4272       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4273     }
4274     else if (game.no_time_limit && TimePlayed < 999)
4275     {
4276       time_final = 999;
4277       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4278     }
4279
4280     local_player->score_final = score_final;
4281
4282     if (level_editor_test_game)
4283     {
4284       time = time_final;
4285       score = score_final;
4286
4287       local_player->LevelSolved_CountingTime = time;
4288       local_player->LevelSolved_CountingScore = score;
4289
4290       game_panel_controls[GAME_PANEL_TIME].value = time;
4291       game_panel_controls[GAME_PANEL_SCORE].value = score;
4292
4293       DisplayGameControlValues();
4294     }
4295
4296     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4297     {
4298       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4299       {
4300         /* close exit door after last player */
4301         if ((AllPlayersGone &&
4302              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4303               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4304               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4305             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4306             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4307         {
4308           int element = Feld[ExitX][ExitY];
4309
4310           Feld[ExitX][ExitY] =
4311             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4312              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4313              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4314              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4315              EL_EM_STEEL_EXIT_CLOSING);
4316
4317           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4318         }
4319
4320         /* player disappears */
4321         DrawLevelField(ExitX, ExitY);
4322       }
4323
4324       for (i = 0; i < MAX_PLAYERS; i++)
4325       {
4326         struct PlayerInfo *player = &stored_player[i];
4327
4328         if (player->present)
4329         {
4330           RemovePlayer(player);
4331
4332           /* player disappears */
4333           DrawLevelField(player->jx, player->jy);
4334         }
4335       }
4336     }
4337
4338     PlaySound(SND_GAME_WINNING);
4339   }
4340
4341   if (game_over_delay_1 > 0)
4342   {
4343     game_over_delay_1--;
4344
4345     return;
4346   }
4347
4348   if (time != time_final)
4349   {
4350     int time_to_go = ABS(time_final - time);
4351     int time_count_dir = (time < time_final ? +1 : -1);
4352     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4353
4354     time  += time_count_steps * time_count_dir;
4355     score += time_count_steps * level.score[SC_TIME_BONUS];
4356
4357     local_player->LevelSolved_CountingTime = time;
4358     local_player->LevelSolved_CountingScore = score;
4359
4360     game_panel_controls[GAME_PANEL_TIME].value = time;
4361     game_panel_controls[GAME_PANEL_SCORE].value = score;
4362
4363     DisplayGameControlValues();
4364
4365     if (time == time_final)
4366       StopSound(SND_GAME_LEVELTIME_BONUS);
4367     else if (setup.sound_loops)
4368       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4369     else
4370       PlaySound(SND_GAME_LEVELTIME_BONUS);
4371
4372     return;
4373   }
4374
4375   local_player->LevelSolved_PanelOff = TRUE;
4376
4377   if (game_over_delay_2 > 0)
4378   {
4379     game_over_delay_2--;
4380
4381     return;
4382   }
4383
4384   GameEnd();
4385 }
4386
4387 void GameEnd()
4388 {
4389   int hi_pos;
4390   boolean raise_level = FALSE;
4391
4392   local_player->LevelSolved_GameEnd = TRUE;
4393
4394   if (!global.use_envelope_request)
4395     CloseDoor(DOOR_CLOSE_1);
4396
4397   if (local_player->LevelSolved_SaveTape)
4398   {
4399     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4400   }
4401
4402   CloseDoor(DOOR_CLOSE_ALL);
4403
4404   if (level_editor_test_game)
4405   {
4406     game_status = GAME_MODE_MAIN;
4407
4408     DrawAndFadeInMainMenu(REDRAW_FIELD);
4409
4410     return;
4411   }
4412
4413   if (!local_player->LevelSolved_SaveScore)
4414   {
4415     FadeOut(REDRAW_FIELD);
4416
4417     game_status = GAME_MODE_MAIN;
4418
4419     DrawAndFadeInMainMenu(REDRAW_FIELD);
4420
4421     return;
4422   }
4423
4424   if (level_nr == leveldir_current->handicap_level)
4425   {
4426     leveldir_current->handicap_level++;
4427
4428     SaveLevelSetup_SeriesInfo();
4429   }
4430
4431   if (level_nr < leveldir_current->last_level)
4432     raise_level = TRUE;                 /* advance to next level */
4433
4434   if ((hi_pos = NewHiScore()) >= 0) 
4435   {
4436     game_status = GAME_MODE_SCORES;
4437
4438     /* needed if different viewport properties defined for scores */
4439     ChangeViewportPropertiesIfNeeded();
4440
4441     DrawHallOfFame(hi_pos);
4442
4443     if (raise_level)
4444     {
4445       level_nr++;
4446       TapeErase();
4447     }
4448   }
4449   else
4450   {
4451     FadeOut(REDRAW_FIELD);
4452
4453     game_status = GAME_MODE_MAIN;
4454
4455     if (raise_level)
4456     {
4457       level_nr++;
4458       TapeErase();
4459     }
4460
4461     DrawAndFadeInMainMenu(REDRAW_FIELD);
4462   }
4463 }
4464
4465 int NewHiScore()
4466 {
4467   int k, l;
4468   int position = -1;
4469
4470   LoadScore(level_nr);
4471
4472   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4473       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4474     return -1;
4475
4476   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4477   {
4478     if (local_player->score_final > highscore[k].Score)
4479     {
4480       /* player has made it to the hall of fame */
4481
4482       if (k < MAX_SCORE_ENTRIES - 1)
4483       {
4484         int m = MAX_SCORE_ENTRIES - 1;
4485
4486 #ifdef ONE_PER_NAME
4487         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4488           if (strEqual(setup.player_name, highscore[l].Name))
4489             m = l;
4490         if (m == k)     /* player's new highscore overwrites his old one */
4491           goto put_into_list;
4492 #endif
4493
4494         for (l = m; l > k; l--)
4495         {
4496           strcpy(highscore[l].Name, highscore[l - 1].Name);
4497           highscore[l].Score = highscore[l - 1].Score;
4498         }
4499       }
4500
4501 #ifdef ONE_PER_NAME
4502       put_into_list:
4503 #endif
4504       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4505       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4506       highscore[k].Score = local_player->score_final; 
4507       position = k;
4508       break;
4509     }
4510
4511 #ifdef ONE_PER_NAME
4512     else if (!strncmp(setup.player_name, highscore[k].Name,
4513                       MAX_PLAYER_NAME_LEN))
4514       break;    /* player already there with a higher score */
4515 #endif
4516
4517   }
4518
4519   if (position >= 0) 
4520     SaveScore(level_nr);
4521
4522   return position;
4523 }
4524
4525 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4526 {
4527   int element = Feld[x][y];
4528   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4529   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4530   int horiz_move = (dx != 0);
4531   int sign = (horiz_move ? dx : dy);
4532   int step = sign * element_info[element].move_stepsize;
4533
4534   /* special values for move stepsize for spring and things on conveyor belt */
4535   if (horiz_move)
4536   {
4537     if (CAN_FALL(element) &&
4538         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4539       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4540     else if (element == EL_SPRING)
4541       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4542   }
4543
4544   return step;
4545 }
4546
4547 inline static int getElementMoveStepsize(int x, int y)
4548 {
4549   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4550 }
4551
4552 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4553 {
4554   if (player->GfxAction != action || player->GfxDir != dir)
4555   {
4556     player->GfxAction = action;
4557     player->GfxDir = dir;
4558     player->Frame = 0;
4559     player->StepFrame = 0;
4560   }
4561 }
4562
4563 static void ResetGfxFrame(int x, int y, boolean redraw)
4564 {
4565   int element = Feld[x][y];
4566   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4567   int last_gfx_frame = GfxFrame[x][y];
4568
4569   if (graphic_info[graphic].anim_global_sync)
4570     GfxFrame[x][y] = FrameCounter;
4571   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4572     GfxFrame[x][y] = CustomValue[x][y];
4573   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4574     GfxFrame[x][y] = element_info[element].collect_score;
4575   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4576     GfxFrame[x][y] = ChangeDelay[x][y];
4577
4578   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4579     DrawLevelGraphicAnimation(x, y, graphic);
4580 }
4581
4582 static void ResetGfxAnimation(int x, int y)
4583 {
4584   GfxAction[x][y] = ACTION_DEFAULT;
4585   GfxDir[x][y] = MovDir[x][y];
4586   GfxFrame[x][y] = 0;
4587
4588   ResetGfxFrame(x, y, FALSE);
4589 }
4590
4591 static void ResetRandomAnimationValue(int x, int y)
4592 {
4593   GfxRandom[x][y] = INIT_GFX_RANDOM();
4594 }
4595
4596 void InitMovingField(int x, int y, int direction)
4597 {
4598   int element = Feld[x][y];
4599   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4600   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4601   int newx = x + dx;
4602   int newy = y + dy;
4603   boolean is_moving_before, is_moving_after;
4604
4605   /* check if element was/is moving or being moved before/after mode change */
4606   is_moving_before = (WasJustMoving[x][y] != 0);
4607   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4608
4609   /* reset animation only for moving elements which change direction of moving
4610      or which just started or stopped moving
4611      (else CEs with property "can move" / "not moving" are reset each frame) */
4612   if (is_moving_before != is_moving_after ||
4613       direction != MovDir[x][y])
4614     ResetGfxAnimation(x, y);
4615
4616   MovDir[x][y] = direction;
4617   GfxDir[x][y] = direction;
4618
4619   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4620                      direction == MV_DOWN && CAN_FALL(element) ?
4621                      ACTION_FALLING : ACTION_MOVING);
4622
4623   /* this is needed for CEs with property "can move" / "not moving" */
4624
4625   if (is_moving_after)
4626   {
4627     if (Feld[newx][newy] == EL_EMPTY)
4628       Feld[newx][newy] = EL_BLOCKED;
4629
4630     MovDir[newx][newy] = MovDir[x][y];
4631
4632     CustomValue[newx][newy] = CustomValue[x][y];
4633
4634     GfxFrame[newx][newy] = GfxFrame[x][y];
4635     GfxRandom[newx][newy] = GfxRandom[x][y];
4636     GfxAction[newx][newy] = GfxAction[x][y];
4637     GfxDir[newx][newy] = GfxDir[x][y];
4638   }
4639 }
4640
4641 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4642 {
4643   int direction = MovDir[x][y];
4644   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4645   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4646
4647   *goes_to_x = newx;
4648   *goes_to_y = newy;
4649 }
4650
4651 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4652 {
4653   int oldx = x, oldy = y;
4654   int direction = MovDir[x][y];
4655
4656   if (direction == MV_LEFT)
4657     oldx++;
4658   else if (direction == MV_RIGHT)
4659     oldx--;
4660   else if (direction == MV_UP)
4661     oldy++;
4662   else if (direction == MV_DOWN)
4663     oldy--;
4664
4665   *comes_from_x = oldx;
4666   *comes_from_y = oldy;
4667 }
4668
4669 int MovingOrBlocked2Element(int x, int y)
4670 {
4671   int element = Feld[x][y];
4672
4673   if (element == EL_BLOCKED)
4674   {
4675     int oldx, oldy;
4676
4677     Blocked2Moving(x, y, &oldx, &oldy);
4678     return Feld[oldx][oldy];
4679   }
4680   else
4681     return element;
4682 }
4683
4684 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4685 {
4686   /* like MovingOrBlocked2Element(), but if element is moving
4687      and (x,y) is the field the moving element is just leaving,
4688      return EL_BLOCKED instead of the element value */
4689   int element = Feld[x][y];
4690
4691   if (IS_MOVING(x, y))
4692   {
4693     if (element == EL_BLOCKED)
4694     {
4695       int oldx, oldy;
4696
4697       Blocked2Moving(x, y, &oldx, &oldy);
4698       return Feld[oldx][oldy];
4699     }
4700     else
4701       return EL_BLOCKED;
4702   }
4703   else
4704     return element;
4705 }
4706
4707 static void RemoveField(int x, int y)
4708 {
4709   Feld[x][y] = EL_EMPTY;
4710
4711   MovPos[x][y] = 0;
4712   MovDir[x][y] = 0;
4713   MovDelay[x][y] = 0;
4714
4715   CustomValue[x][y] = 0;
4716
4717   AmoebaNr[x][y] = 0;
4718   ChangeDelay[x][y] = 0;
4719   ChangePage[x][y] = -1;
4720   Pushed[x][y] = FALSE;
4721
4722   GfxElement[x][y] = EL_UNDEFINED;
4723   GfxAction[x][y] = ACTION_DEFAULT;
4724   GfxDir[x][y] = MV_NONE;
4725 }
4726
4727 void RemoveMovingField(int x, int y)
4728 {
4729   int oldx = x, oldy = y, newx = x, newy = y;
4730   int element = Feld[x][y];
4731   int next_element = EL_UNDEFINED;
4732
4733   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4734     return;
4735
4736   if (IS_MOVING(x, y))
4737   {
4738     Moving2Blocked(x, y, &newx, &newy);
4739
4740     if (Feld[newx][newy] != EL_BLOCKED)
4741     {
4742       /* element is moving, but target field is not free (blocked), but
4743          already occupied by something different (example: acid pool);
4744          in this case, only remove the moving field, but not the target */
4745
4746       RemoveField(oldx, oldy);
4747
4748       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4749
4750       TEST_DrawLevelField(oldx, oldy);
4751
4752       return;
4753     }
4754   }
4755   else if (element == EL_BLOCKED)
4756   {
4757     Blocked2Moving(x, y, &oldx, &oldy);
4758     if (!IS_MOVING(oldx, oldy))
4759       return;
4760   }
4761
4762   if (element == EL_BLOCKED &&
4763       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4764        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4765        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4766        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4767        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4768        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4769     next_element = get_next_element(Feld[oldx][oldy]);
4770
4771   RemoveField(oldx, oldy);
4772   RemoveField(newx, newy);
4773
4774   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4775
4776   if (next_element != EL_UNDEFINED)
4777     Feld[oldx][oldy] = next_element;
4778
4779   TEST_DrawLevelField(oldx, oldy);
4780   TEST_DrawLevelField(newx, newy);
4781 }
4782
4783 void DrawDynamite(int x, int y)
4784 {
4785   int sx = SCREENX(x), sy = SCREENY(y);
4786   int graphic = el2img(Feld[x][y]);
4787   int frame;
4788
4789   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4790     return;
4791
4792   if (IS_WALKABLE_INSIDE(Back[x][y]))
4793     return;
4794
4795   if (Back[x][y])
4796     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4797   else if (Store[x][y])
4798     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4799
4800   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4801
4802   if (Back[x][y] || Store[x][y])
4803     DrawGraphicThruMask(sx, sy, graphic, frame);
4804   else
4805     DrawGraphic(sx, sy, graphic, frame);
4806 }
4807
4808 void CheckDynamite(int x, int y)
4809 {
4810   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4811   {
4812     MovDelay[x][y]--;
4813
4814     if (MovDelay[x][y] != 0)
4815     {
4816       DrawDynamite(x, y);
4817       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4818
4819       return;
4820     }
4821   }
4822
4823   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4824
4825   Bang(x, y);
4826 }
4827
4828 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4829 {
4830   boolean num_checked_players = 0;
4831   int i;
4832
4833   for (i = 0; i < MAX_PLAYERS; i++)
4834   {
4835     if (stored_player[i].active)
4836     {
4837       int sx = stored_player[i].jx;
4838       int sy = stored_player[i].jy;
4839
4840       if (num_checked_players == 0)
4841       {
4842         *sx1 = *sx2 = sx;
4843         *sy1 = *sy2 = sy;
4844       }
4845       else
4846       {
4847         *sx1 = MIN(*sx1, sx);
4848         *sy1 = MIN(*sy1, sy);
4849         *sx2 = MAX(*sx2, sx);
4850         *sy2 = MAX(*sy2, sy);
4851       }
4852
4853       num_checked_players++;
4854     }
4855   }
4856 }
4857
4858 static boolean checkIfAllPlayersFitToScreen_RND()
4859 {
4860   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4861
4862   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4863
4864   return (sx2 - sx1 < SCR_FIELDX &&
4865           sy2 - sy1 < SCR_FIELDY);
4866 }
4867
4868 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4869 {
4870   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4871
4872   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4873
4874   *sx = (sx1 + sx2) / 2;
4875   *sy = (sy1 + sy2) / 2;
4876 }
4877
4878 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4879                         boolean center_screen, boolean quick_relocation)
4880 {
4881   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4882   boolean no_delay = (tape.warp_forward);
4883   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4884   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4885
4886   if (quick_relocation)
4887   {
4888     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4889     {
4890       if (!level.shifted_relocation || center_screen)
4891       {
4892         /* quick relocation (without scrolling), with centering of screen */
4893
4894         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4895                     x > SBX_Right + MIDPOSX ? SBX_Right :
4896                     x - MIDPOSX);
4897
4898         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4899                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4900                     y - MIDPOSY);
4901       }
4902       else
4903       {
4904         /* quick relocation (without scrolling), but do not center screen */
4905
4906         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4907                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4908                                old_x - MIDPOSX);
4909
4910         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4911                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4912                                old_y - MIDPOSY);
4913
4914         int offset_x = x + (scroll_x - center_scroll_x);
4915         int offset_y = y + (scroll_y - center_scroll_y);
4916
4917         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4918                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4919                     offset_x - MIDPOSX);
4920
4921         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4922                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4923                     offset_y - MIDPOSY);
4924       }
4925     }
4926     else
4927     {
4928       if (!level.shifted_relocation || center_screen)
4929       {
4930         /* quick relocation (without scrolling), with centering of screen */
4931
4932         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4933                     x > SBX_Right + MIDPOSX ? SBX_Right :
4934                     x - MIDPOSX);
4935
4936         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4937                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4938                     y - MIDPOSY);
4939       }
4940       else
4941       {
4942         /* quick relocation (without scrolling), but do not center screen */
4943
4944         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4945                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4946                                old_x - MIDPOSX);
4947
4948         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4949                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4950                                old_y - MIDPOSY);
4951
4952         int offset_x = x + (scroll_x - center_scroll_x);
4953         int offset_y = y + (scroll_y - center_scroll_y);
4954
4955         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4956                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4957                     offset_x - MIDPOSX);
4958
4959         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4960                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4961                     offset_y - MIDPOSY);
4962       }
4963     }
4964
4965     RedrawPlayfield(TRUE, 0,0,0,0);
4966   }
4967   else
4968   {
4969     int scroll_xx, scroll_yy;
4970
4971     if (!level.shifted_relocation || center_screen)
4972     {
4973       /* visible relocation (with scrolling), with centering of screen */
4974
4975       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4976                    x > SBX_Right + MIDPOSX ? SBX_Right :
4977                    x - MIDPOSX);
4978
4979       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4980                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4981                    y - MIDPOSY);
4982     }
4983     else
4984     {
4985       /* visible relocation (with scrolling), but do not center screen */
4986
4987       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4988                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4989                              old_x - MIDPOSX);
4990
4991       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4992                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4993                              old_y - MIDPOSY);
4994
4995       int offset_x = x + (scroll_x - center_scroll_x);
4996       int offset_y = y + (scroll_y - center_scroll_y);
4997
4998       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4999                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5000                    offset_x - MIDPOSX);
5001
5002       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5003                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5004                    offset_y - MIDPOSY);
5005     }
5006
5007
5008     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5009
5010     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5011     {
5012       int dx = 0, dy = 0;
5013       int fx = FX, fy = FY;
5014
5015       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5016       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5017
5018       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5019         break;
5020
5021       scroll_x -= dx;
5022       scroll_y -= dy;
5023
5024       fx += dx * TILEX / 2;
5025       fy += dy * TILEY / 2;
5026
5027       ScrollLevel(dx, dy);
5028       DrawAllPlayers();
5029
5030       /* scroll in two steps of half tile size to make things smoother */
5031       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5032       Delay(wait_delay_value);
5033
5034       /* scroll second step to align at full tile size */
5035       BackToFront();
5036       Delay(wait_delay_value);
5037     }
5038
5039     DrawAllPlayers();
5040     BackToFront();
5041     Delay(wait_delay_value);
5042   }
5043 }
5044
5045 void RelocatePlayer(int jx, int jy, int el_player_raw)
5046 {
5047   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5048   int player_nr = GET_PLAYER_NR(el_player);
5049   struct PlayerInfo *player = &stored_player[player_nr];
5050   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5051   boolean no_delay = (tape.warp_forward);
5052   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5053   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5054   int old_jx = player->jx;
5055   int old_jy = player->jy;
5056   int old_element = Feld[old_jx][old_jy];
5057   int element = Feld[jx][jy];
5058   boolean player_relocated = (old_jx != jx || old_jy != jy);
5059
5060   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5061   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5062   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5063   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5064   int leave_side_horiz = move_dir_horiz;
5065   int leave_side_vert  = move_dir_vert;
5066   int enter_side = enter_side_horiz | enter_side_vert;
5067   int leave_side = leave_side_horiz | leave_side_vert;
5068
5069   if (player->GameOver)         /* do not reanimate dead player */
5070     return;
5071
5072   if (!player_relocated)        /* no need to relocate the player */
5073     return;
5074
5075   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5076   {
5077     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5078     DrawLevelField(jx, jy);
5079   }
5080
5081   if (player->present)
5082   {
5083     while (player->MovPos)
5084     {
5085       ScrollPlayer(player, SCROLL_GO_ON);
5086       ScrollScreen(NULL, SCROLL_GO_ON);
5087
5088       AdvanceFrameAndPlayerCounters(player->index_nr);
5089
5090       DrawPlayer(player);
5091
5092       BackToFront();
5093       Delay(wait_delay_value);
5094     }
5095
5096     DrawPlayer(player);         /* needed here only to cleanup last field */
5097     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5098
5099     player->is_moving = FALSE;
5100   }
5101
5102   if (IS_CUSTOM_ELEMENT(old_element))
5103     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5104                                CE_LEFT_BY_PLAYER,
5105                                player->index_bit, leave_side);
5106
5107   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5108                                       CE_PLAYER_LEAVES_X,
5109                                       player->index_bit, leave_side);
5110
5111   Feld[jx][jy] = el_player;
5112   InitPlayerField(jx, jy, el_player, TRUE);
5113
5114   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5115      possible that the relocation target field did not contain a player element,
5116      but a walkable element, to which the new player was relocated -- in this
5117      case, restore that (already initialized!) element on the player field */
5118   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5119   {
5120     Feld[jx][jy] = element;     /* restore previously existing element */
5121   }
5122
5123   /* only visually relocate centered player */
5124   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5125                      FALSE, level.instant_relocation);
5126
5127   TestIfPlayerTouchesBadThing(jx, jy);
5128   TestIfPlayerTouchesCustomElement(jx, jy);
5129
5130   if (IS_CUSTOM_ELEMENT(element))
5131     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5132                                player->index_bit, enter_side);
5133
5134   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5135                                       player->index_bit, enter_side);
5136
5137   if (player->is_switching)
5138   {
5139     /* ensure that relocation while still switching an element does not cause
5140        a new element to be treated as also switched directly after relocation
5141        (this is important for teleporter switches that teleport the player to
5142        a place where another teleporter switch is in the same direction, which
5143        would then incorrectly be treated as immediately switched before the
5144        direction key that caused the switch was released) */
5145
5146     player->switch_x += jx - old_jx;
5147     player->switch_y += jy - old_jy;
5148   }
5149 }
5150
5151 void Explode(int ex, int ey, int phase, int mode)
5152 {
5153   int x, y;
5154   int last_phase;
5155   int border_element;
5156
5157   /* !!! eliminate this variable !!! */
5158   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5159
5160   if (game.explosions_delayed)
5161   {
5162     ExplodeField[ex][ey] = mode;
5163     return;
5164   }
5165
5166   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5167   {
5168     int center_element = Feld[ex][ey];
5169     int artwork_element, explosion_element;     /* set these values later */
5170
5171     /* remove things displayed in background while burning dynamite */
5172     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5173       Back[ex][ey] = 0;
5174
5175     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5176     {
5177       /* put moving element to center field (and let it explode there) */
5178       center_element = MovingOrBlocked2Element(ex, ey);
5179       RemoveMovingField(ex, ey);
5180       Feld[ex][ey] = center_element;
5181     }
5182
5183     /* now "center_element" is finally determined -- set related values now */
5184     artwork_element = center_element;           /* for custom player artwork */
5185     explosion_element = center_element;         /* for custom player artwork */
5186
5187     if (IS_PLAYER(ex, ey))
5188     {
5189       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5190
5191       artwork_element = stored_player[player_nr].artwork_element;
5192
5193       if (level.use_explosion_element[player_nr])
5194       {
5195         explosion_element = level.explosion_element[player_nr];
5196         artwork_element = explosion_element;
5197       }
5198     }
5199
5200     if (mode == EX_TYPE_NORMAL ||
5201         mode == EX_TYPE_CENTER ||
5202         mode == EX_TYPE_CROSS)
5203       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5204
5205     last_phase = element_info[explosion_element].explosion_delay + 1;
5206
5207     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5208     {
5209       int xx = x - ex + 1;
5210       int yy = y - ey + 1;
5211       int element;
5212
5213       if (!IN_LEV_FIELD(x, y) ||
5214           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5215           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5216         continue;
5217
5218       element = Feld[x][y];
5219
5220       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5221       {
5222         element = MovingOrBlocked2Element(x, y);
5223
5224         if (!IS_EXPLOSION_PROOF(element))
5225           RemoveMovingField(x, y);
5226       }
5227
5228       /* indestructible elements can only explode in center (but not flames) */
5229       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5230                                            mode == EX_TYPE_BORDER)) ||
5231           element == EL_FLAMES)
5232         continue;
5233
5234       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5235          behaviour, for example when touching a yamyam that explodes to rocks
5236          with active deadly shield, a rock is created under the player !!! */
5237       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5238 #if 0
5239       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5240           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5241            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5242 #else
5243       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5244 #endif
5245       {
5246         if (IS_ACTIVE_BOMB(element))
5247         {
5248           /* re-activate things under the bomb like gate or penguin */
5249           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5250           Back[x][y] = 0;
5251         }
5252
5253         continue;
5254       }
5255
5256       /* save walkable background elements while explosion on same tile */
5257       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5258           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5259         Back[x][y] = element;
5260
5261       /* ignite explodable elements reached by other explosion */
5262       if (element == EL_EXPLOSION)
5263         element = Store2[x][y];
5264
5265       if (AmoebaNr[x][y] &&
5266           (element == EL_AMOEBA_FULL ||
5267            element == EL_BD_AMOEBA ||
5268            element == EL_AMOEBA_GROWING))
5269       {
5270         AmoebaCnt[AmoebaNr[x][y]]--;
5271         AmoebaCnt2[AmoebaNr[x][y]]--;
5272       }
5273
5274       RemoveField(x, y);
5275
5276       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5277       {
5278         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5279
5280         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5281
5282         if (PLAYERINFO(ex, ey)->use_murphy)
5283           Store[x][y] = EL_EMPTY;
5284       }
5285
5286       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5287          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5288       else if (ELEM_IS_PLAYER(center_element))
5289         Store[x][y] = EL_EMPTY;
5290       else if (center_element == EL_YAMYAM)
5291         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5292       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5293         Store[x][y] = element_info[center_element].content.e[xx][yy];
5294 #if 1
5295       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5296          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5297          otherwise) -- FIX THIS !!! */
5298       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5299         Store[x][y] = element_info[element].content.e[1][1];
5300 #else
5301       else if (!CAN_EXPLODE(element))
5302         Store[x][y] = element_info[element].content.e[1][1];
5303 #endif
5304       else
5305         Store[x][y] = EL_EMPTY;
5306
5307       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5308           center_element == EL_AMOEBA_TO_DIAMOND)
5309         Store2[x][y] = element;
5310
5311       Feld[x][y] = EL_EXPLOSION;
5312       GfxElement[x][y] = artwork_element;
5313
5314       ExplodePhase[x][y] = 1;
5315       ExplodeDelay[x][y] = last_phase;
5316
5317       Stop[x][y] = TRUE;
5318     }
5319
5320     if (center_element == EL_YAMYAM)
5321       game.yamyam_content_nr =
5322         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5323
5324     return;
5325   }
5326
5327   if (Stop[ex][ey])
5328     return;
5329
5330   x = ex;
5331   y = ey;
5332
5333   if (phase == 1)
5334     GfxFrame[x][y] = 0;         /* restart explosion animation */
5335
5336   last_phase = ExplodeDelay[x][y];
5337
5338   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5339
5340   /* this can happen if the player leaves an explosion just in time */
5341   if (GfxElement[x][y] == EL_UNDEFINED)
5342     GfxElement[x][y] = EL_EMPTY;
5343
5344   border_element = Store2[x][y];
5345   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5346     border_element = StorePlayer[x][y];
5347
5348   if (phase == element_info[border_element].ignition_delay ||
5349       phase == last_phase)
5350   {
5351     boolean border_explosion = FALSE;
5352
5353     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5354         !PLAYER_EXPLOSION_PROTECTED(x, y))
5355     {
5356       KillPlayerUnlessExplosionProtected(x, y);
5357       border_explosion = TRUE;
5358     }
5359     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5360     {
5361       Feld[x][y] = Store2[x][y];
5362       Store2[x][y] = 0;
5363       Bang(x, y);
5364       border_explosion = TRUE;
5365     }
5366     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5367     {
5368       AmoebeUmwandeln(x, y);
5369       Store2[x][y] = 0;
5370       border_explosion = TRUE;
5371     }
5372
5373     /* if an element just explodes due to another explosion (chain-reaction),
5374        do not immediately end the new explosion when it was the last frame of
5375        the explosion (as it would be done in the following "if"-statement!) */
5376     if (border_explosion && phase == last_phase)
5377       return;
5378   }
5379
5380   if (phase == last_phase)
5381   {
5382     int element;
5383
5384     element = Feld[x][y] = Store[x][y];
5385     Store[x][y] = Store2[x][y] = 0;
5386     GfxElement[x][y] = EL_UNDEFINED;
5387
5388     /* player can escape from explosions and might therefore be still alive */
5389     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5390         element <= EL_PLAYER_IS_EXPLODING_4)
5391     {
5392       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5393       int explosion_element = EL_PLAYER_1 + player_nr;
5394       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5395       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5396
5397       if (level.use_explosion_element[player_nr])
5398         explosion_element = level.explosion_element[player_nr];
5399
5400       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5401                     element_info[explosion_element].content.e[xx][yy]);
5402     }
5403
5404     /* restore probably existing indestructible background element */
5405     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5406       element = Feld[x][y] = Back[x][y];
5407     Back[x][y] = 0;
5408
5409     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5410     GfxDir[x][y] = MV_NONE;
5411     ChangeDelay[x][y] = 0;
5412     ChangePage[x][y] = -1;
5413
5414     CustomValue[x][y] = 0;
5415
5416     InitField_WithBug2(x, y, FALSE);
5417
5418     TEST_DrawLevelField(x, y);
5419
5420     TestIfElementTouchesCustomElement(x, y);
5421
5422     if (GFX_CRUMBLED(element))
5423       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5424
5425     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5426       StorePlayer[x][y] = 0;
5427
5428     if (ELEM_IS_PLAYER(element))
5429       RelocatePlayer(x, y, element);
5430   }
5431   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5432   {
5433     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5434     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5435
5436     if (phase == delay)
5437       TEST_DrawLevelFieldCrumbled(x, y);
5438
5439     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5440     {
5441       DrawLevelElement(x, y, Back[x][y]);
5442       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5443     }
5444     else if (IS_WALKABLE_UNDER(Back[x][y]))
5445     {
5446       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5447       DrawLevelElementThruMask(x, y, Back[x][y]);
5448     }
5449     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5450       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5451   }
5452 }
5453
5454 void DynaExplode(int ex, int ey)
5455 {
5456   int i, j;
5457   int dynabomb_element = Feld[ex][ey];
5458   int dynabomb_size = 1;
5459   boolean dynabomb_xl = FALSE;
5460   struct PlayerInfo *player;
5461   static int xy[4][2] =
5462   {
5463     { 0, -1 },
5464     { -1, 0 },
5465     { +1, 0 },
5466     { 0, +1 }
5467   };
5468
5469   if (IS_ACTIVE_BOMB(dynabomb_element))
5470   {
5471     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5472     dynabomb_size = player->dynabomb_size;
5473     dynabomb_xl = player->dynabomb_xl;
5474     player->dynabombs_left++;
5475   }
5476
5477   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5478
5479   for (i = 0; i < NUM_DIRECTIONS; i++)
5480   {
5481     for (j = 1; j <= dynabomb_size; j++)
5482     {
5483       int x = ex + j * xy[i][0];
5484       int y = ey + j * xy[i][1];
5485       int element;
5486
5487       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5488         break;
5489
5490       element = Feld[x][y];
5491
5492       /* do not restart explosions of fields with active bombs */
5493       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5494         continue;
5495
5496       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5497
5498       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5499           !IS_DIGGABLE(element) && !dynabomb_xl)
5500         break;
5501     }
5502   }
5503 }
5504
5505 void Bang(int x, int y)
5506 {
5507   int element = MovingOrBlocked2Element(x, y);
5508   int explosion_type = EX_TYPE_NORMAL;
5509
5510   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5511   {
5512     struct PlayerInfo *player = PLAYERINFO(x, y);
5513
5514     element = Feld[x][y] = player->initial_element;
5515
5516     if (level.use_explosion_element[player->index_nr])
5517     {
5518       int explosion_element = level.explosion_element[player->index_nr];
5519
5520       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5521         explosion_type = EX_TYPE_CROSS;
5522       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5523         explosion_type = EX_TYPE_CENTER;
5524     }
5525   }
5526
5527   switch (element)
5528   {
5529     case EL_BUG:
5530     case EL_SPACESHIP:
5531     case EL_BD_BUTTERFLY:
5532     case EL_BD_FIREFLY:
5533     case EL_YAMYAM:
5534     case EL_DARK_YAMYAM:
5535     case EL_ROBOT:
5536     case EL_PACMAN:
5537     case EL_MOLE:
5538       RaiseScoreElement(element);
5539       break;
5540
5541     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5542     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5543     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5544     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5545     case EL_DYNABOMB_INCREASE_NUMBER:
5546     case EL_DYNABOMB_INCREASE_SIZE:
5547     case EL_DYNABOMB_INCREASE_POWER:
5548       explosion_type = EX_TYPE_DYNA;
5549       break;
5550
5551     case EL_DC_LANDMINE:
5552       explosion_type = EX_TYPE_CENTER;
5553       break;
5554
5555     case EL_PENGUIN:
5556     case EL_LAMP:
5557     case EL_LAMP_ACTIVE:
5558     case EL_AMOEBA_TO_DIAMOND:
5559       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5560         explosion_type = EX_TYPE_CENTER;
5561       break;
5562
5563     default:
5564       if (element_info[element].explosion_type == EXPLODES_CROSS)
5565         explosion_type = EX_TYPE_CROSS;
5566       else if (element_info[element].explosion_type == EXPLODES_1X1)
5567         explosion_type = EX_TYPE_CENTER;
5568       break;
5569   }
5570
5571   if (explosion_type == EX_TYPE_DYNA)
5572     DynaExplode(x, y);
5573   else
5574     Explode(x, y, EX_PHASE_START, explosion_type);
5575
5576   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5577 }
5578
5579 void SplashAcid(int x, int y)
5580 {
5581   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5582       (!IN_LEV_FIELD(x - 1, y - 2) ||
5583        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5584     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5585
5586   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5587       (!IN_LEV_FIELD(x + 1, y - 2) ||
5588        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5589     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5590
5591   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5592 }
5593
5594 static void InitBeltMovement()
5595 {
5596   static int belt_base_element[4] =
5597   {
5598     EL_CONVEYOR_BELT_1_LEFT,
5599     EL_CONVEYOR_BELT_2_LEFT,
5600     EL_CONVEYOR_BELT_3_LEFT,
5601     EL_CONVEYOR_BELT_4_LEFT
5602   };
5603   static int belt_base_active_element[4] =
5604   {
5605     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5606     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5607     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5608     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5609   };
5610
5611   int x, y, i, j;
5612
5613   /* set frame order for belt animation graphic according to belt direction */
5614   for (i = 0; i < NUM_BELTS; i++)
5615   {
5616     int belt_nr = i;
5617
5618     for (j = 0; j < NUM_BELT_PARTS; j++)
5619     {
5620       int element = belt_base_active_element[belt_nr] + j;
5621       int graphic_1 = el2img(element);
5622       int graphic_2 = el2panelimg(element);
5623
5624       if (game.belt_dir[i] == MV_LEFT)
5625       {
5626         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5627         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5628       }
5629       else
5630       {
5631         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5632         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5633       }
5634     }
5635   }
5636
5637   SCAN_PLAYFIELD(x, y)
5638   {
5639     int element = Feld[x][y];
5640
5641     for (i = 0; i < NUM_BELTS; i++)
5642     {
5643       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5644       {
5645         int e_belt_nr = getBeltNrFromBeltElement(element);
5646         int belt_nr = i;
5647
5648         if (e_belt_nr == belt_nr)
5649         {
5650           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5651
5652           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5653         }
5654       }
5655     }
5656   }
5657 }
5658
5659 static void ToggleBeltSwitch(int x, int y)
5660 {
5661   static int belt_base_element[4] =
5662   {
5663     EL_CONVEYOR_BELT_1_LEFT,
5664     EL_CONVEYOR_BELT_2_LEFT,
5665     EL_CONVEYOR_BELT_3_LEFT,
5666     EL_CONVEYOR_BELT_4_LEFT
5667   };
5668   static int belt_base_active_element[4] =
5669   {
5670     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5671     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5672     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5673     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5674   };
5675   static int belt_base_switch_element[4] =
5676   {
5677     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5678     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5679     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5680     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5681   };
5682   static int belt_move_dir[4] =
5683   {
5684     MV_LEFT,
5685     MV_NONE,
5686     MV_RIGHT,
5687     MV_NONE,
5688   };
5689
5690   int element = Feld[x][y];
5691   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5692   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5693   int belt_dir = belt_move_dir[belt_dir_nr];
5694   int xx, yy, i;
5695
5696   if (!IS_BELT_SWITCH(element))
5697     return;
5698
5699   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5700   game.belt_dir[belt_nr] = belt_dir;
5701
5702   if (belt_dir_nr == 3)
5703     belt_dir_nr = 1;
5704
5705   /* set frame order for belt animation graphic according to belt direction */
5706   for (i = 0; i < NUM_BELT_PARTS; i++)
5707   {
5708     int element = belt_base_active_element[belt_nr] + i;
5709     int graphic_1 = el2img(element);
5710     int graphic_2 = el2panelimg(element);
5711
5712     if (belt_dir == MV_LEFT)
5713     {
5714       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5715       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5716     }
5717     else
5718     {
5719       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5720       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5721     }
5722   }
5723
5724   SCAN_PLAYFIELD(xx, yy)
5725   {
5726     int element = Feld[xx][yy];
5727
5728     if (IS_BELT_SWITCH(element))
5729     {
5730       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5731
5732       if (e_belt_nr == belt_nr)
5733       {
5734         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5735         TEST_DrawLevelField(xx, yy);
5736       }
5737     }
5738     else if (IS_BELT(element) && belt_dir != MV_NONE)
5739     {
5740       int e_belt_nr = getBeltNrFromBeltElement(element);
5741
5742       if (e_belt_nr == belt_nr)
5743       {
5744         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5745
5746         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5747         TEST_DrawLevelField(xx, yy);
5748       }
5749     }
5750     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5751     {
5752       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5753
5754       if (e_belt_nr == belt_nr)
5755       {
5756         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5757
5758         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5759         TEST_DrawLevelField(xx, yy);
5760       }
5761     }
5762   }
5763 }
5764
5765 static void ToggleSwitchgateSwitch(int x, int y)
5766 {
5767   int xx, yy;
5768
5769   game.switchgate_pos = !game.switchgate_pos;
5770
5771   SCAN_PLAYFIELD(xx, yy)
5772   {
5773     int element = Feld[xx][yy];
5774
5775     if (element == EL_SWITCHGATE_SWITCH_UP)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5778       TEST_DrawLevelField(xx, yy);
5779     }
5780     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5781     {
5782       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5783       TEST_DrawLevelField(xx, yy);
5784     }
5785     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5786     {
5787       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5788       TEST_DrawLevelField(xx, yy);
5789     }
5790     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5791     {
5792       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5793       TEST_DrawLevelField(xx, yy);
5794     }
5795     else if (element == EL_SWITCHGATE_OPEN ||
5796              element == EL_SWITCHGATE_OPENING)
5797     {
5798       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5799
5800       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5801     }
5802     else if (element == EL_SWITCHGATE_CLOSED ||
5803              element == EL_SWITCHGATE_CLOSING)
5804     {
5805       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5806
5807       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5808     }
5809   }
5810 }
5811
5812 static int getInvisibleActiveFromInvisibleElement(int element)
5813 {
5814   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5815           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5816           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5817           element);
5818 }
5819
5820 static int getInvisibleFromInvisibleActiveElement(int element)
5821 {
5822   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5823           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5824           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5825           element);
5826 }
5827
5828 static void RedrawAllLightSwitchesAndInvisibleElements()
5829 {
5830   int x, y;
5831
5832   SCAN_PLAYFIELD(x, y)
5833   {
5834     int element = Feld[x][y];
5835
5836     if (element == EL_LIGHT_SWITCH &&
5837         game.light_time_left > 0)
5838     {
5839       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5840       TEST_DrawLevelField(x, y);
5841     }
5842     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5843              game.light_time_left == 0)
5844     {
5845       Feld[x][y] = EL_LIGHT_SWITCH;
5846       TEST_DrawLevelField(x, y);
5847     }
5848     else if (element == EL_EMC_DRIPPER &&
5849              game.light_time_left > 0)
5850     {
5851       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5852       TEST_DrawLevelField(x, y);
5853     }
5854     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5855              game.light_time_left == 0)
5856     {
5857       Feld[x][y] = EL_EMC_DRIPPER;
5858       TEST_DrawLevelField(x, y);
5859     }
5860     else if (element == EL_INVISIBLE_STEELWALL ||
5861              element == EL_INVISIBLE_WALL ||
5862              element == EL_INVISIBLE_SAND)
5863     {
5864       if (game.light_time_left > 0)
5865         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5866
5867       TEST_DrawLevelField(x, y);
5868
5869       /* uncrumble neighbour fields, if needed */
5870       if (element == EL_INVISIBLE_SAND)
5871         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5872     }
5873     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5874              element == EL_INVISIBLE_WALL_ACTIVE ||
5875              element == EL_INVISIBLE_SAND_ACTIVE)
5876     {
5877       if (game.light_time_left == 0)
5878         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5879
5880       TEST_DrawLevelField(x, y);
5881
5882       /* re-crumble neighbour fields, if needed */
5883       if (element == EL_INVISIBLE_SAND)
5884         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5885     }
5886   }
5887 }
5888
5889 static void RedrawAllInvisibleElementsForLenses()
5890 {
5891   int x, y;
5892
5893   SCAN_PLAYFIELD(x, y)
5894   {
5895     int element = Feld[x][y];
5896
5897     if (element == EL_EMC_DRIPPER &&
5898         game.lenses_time_left > 0)
5899     {
5900       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5901       TEST_DrawLevelField(x, y);
5902     }
5903     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5904              game.lenses_time_left == 0)
5905     {
5906       Feld[x][y] = EL_EMC_DRIPPER;
5907       TEST_DrawLevelField(x, y);
5908     }
5909     else if (element == EL_INVISIBLE_STEELWALL ||
5910              element == EL_INVISIBLE_WALL ||
5911              element == EL_INVISIBLE_SAND)
5912     {
5913       if (game.lenses_time_left > 0)
5914         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5915
5916       TEST_DrawLevelField(x, y);
5917
5918       /* uncrumble neighbour fields, if needed */
5919       if (element == EL_INVISIBLE_SAND)
5920         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5921     }
5922     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5923              element == EL_INVISIBLE_WALL_ACTIVE ||
5924              element == EL_INVISIBLE_SAND_ACTIVE)
5925     {
5926       if (game.lenses_time_left == 0)
5927         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5928
5929       TEST_DrawLevelField(x, y);
5930
5931       /* re-crumble neighbour fields, if needed */
5932       if (element == EL_INVISIBLE_SAND)
5933         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5934     }
5935   }
5936 }
5937
5938 static void RedrawAllInvisibleElementsForMagnifier()
5939 {
5940   int x, y;
5941
5942   SCAN_PLAYFIELD(x, y)
5943   {
5944     int element = Feld[x][y];
5945
5946     if (element == EL_EMC_FAKE_GRASS &&
5947         game.magnify_time_left > 0)
5948     {
5949       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5950       TEST_DrawLevelField(x, y);
5951     }
5952     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5953              game.magnify_time_left == 0)
5954     {
5955       Feld[x][y] = EL_EMC_FAKE_GRASS;
5956       TEST_DrawLevelField(x, y);
5957     }
5958     else if (IS_GATE_GRAY(element) &&
5959              game.magnify_time_left > 0)
5960     {
5961       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5962                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5963                     IS_EM_GATE_GRAY(element) ?
5964                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5965                     IS_EMC_GATE_GRAY(element) ?
5966                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5967                     IS_DC_GATE_GRAY(element) ?
5968                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5969                     element);
5970       TEST_DrawLevelField(x, y);
5971     }
5972     else if (IS_GATE_GRAY_ACTIVE(element) &&
5973              game.magnify_time_left == 0)
5974     {
5975       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5976                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5977                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5978                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5979                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5980                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5981                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5982                     EL_DC_GATE_WHITE_GRAY :
5983                     element);
5984       TEST_DrawLevelField(x, y);
5985     }
5986   }
5987 }
5988
5989 static void ToggleLightSwitch(int x, int y)
5990 {
5991   int element = Feld[x][y];
5992
5993   game.light_time_left =
5994     (element == EL_LIGHT_SWITCH ?
5995      level.time_light * FRAMES_PER_SECOND : 0);
5996
5997   RedrawAllLightSwitchesAndInvisibleElements();
5998 }
5999
6000 static void ActivateTimegateSwitch(int x, int y)
6001 {
6002   int xx, yy;
6003
6004   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6005
6006   SCAN_PLAYFIELD(xx, yy)
6007   {
6008     int element = Feld[xx][yy];
6009
6010     if (element == EL_TIMEGATE_CLOSED ||
6011         element == EL_TIMEGATE_CLOSING)
6012     {
6013       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6014       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6015     }
6016
6017     /*
6018     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6019     {
6020       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6021       TEST_DrawLevelField(xx, yy);
6022     }
6023     */
6024
6025   }
6026
6027   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6028                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6029 }
6030
6031 void Impact(int x, int y)
6032 {
6033   boolean last_line = (y == lev_fieldy - 1);
6034   boolean object_hit = FALSE;
6035   boolean impact = (last_line || object_hit);
6036   int element = Feld[x][y];
6037   int smashed = EL_STEELWALL;
6038
6039   if (!last_line)       /* check if element below was hit */
6040   {
6041     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6042       return;
6043
6044     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6045                                          MovDir[x][y + 1] != MV_DOWN ||
6046                                          MovPos[x][y + 1] <= TILEY / 2));
6047
6048     /* do not smash moving elements that left the smashed field in time */
6049     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6050         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6051       object_hit = FALSE;
6052
6053 #if USE_QUICKSAND_IMPACT_BUGFIX
6054     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6055     {
6056       RemoveMovingField(x, y + 1);
6057       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6058       Feld[x][y + 2] = EL_ROCK;
6059       TEST_DrawLevelField(x, y + 2);
6060
6061       object_hit = TRUE;
6062     }
6063
6064     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6065     {
6066       RemoveMovingField(x, y + 1);
6067       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6068       Feld[x][y + 2] = EL_ROCK;
6069       TEST_DrawLevelField(x, y + 2);
6070
6071       object_hit = TRUE;
6072     }
6073 #endif
6074
6075     if (object_hit)
6076       smashed = MovingOrBlocked2Element(x, y + 1);
6077
6078     impact = (last_line || object_hit);
6079   }
6080
6081   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6082   {
6083     SplashAcid(x, y + 1);
6084     return;
6085   }
6086
6087   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6088   /* only reset graphic animation if graphic really changes after impact */
6089   if (impact &&
6090       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6091   {
6092     ResetGfxAnimation(x, y);
6093     TEST_DrawLevelField(x, y);
6094   }
6095
6096   if (impact && CAN_EXPLODE_IMPACT(element))
6097   {
6098     Bang(x, y);
6099     return;
6100   }
6101   else if (impact && element == EL_PEARL &&
6102            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6103   {
6104     ResetGfxAnimation(x, y);
6105
6106     Feld[x][y] = EL_PEARL_BREAKING;
6107     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6108     return;
6109   }
6110   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6111   {
6112     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6113
6114     return;
6115   }
6116
6117   if (impact && element == EL_AMOEBA_DROP)
6118   {
6119     if (object_hit && IS_PLAYER(x, y + 1))
6120       KillPlayerUnlessEnemyProtected(x, y + 1);
6121     else if (object_hit && smashed == EL_PENGUIN)
6122       Bang(x, y + 1);
6123     else
6124     {
6125       Feld[x][y] = EL_AMOEBA_GROWING;
6126       Store[x][y] = EL_AMOEBA_WET;
6127
6128       ResetRandomAnimationValue(x, y);
6129     }
6130     return;
6131   }
6132
6133   if (object_hit)               /* check which object was hit */
6134   {
6135     if ((CAN_PASS_MAGIC_WALL(element) && 
6136          (smashed == EL_MAGIC_WALL ||
6137           smashed == EL_BD_MAGIC_WALL)) ||
6138         (CAN_PASS_DC_MAGIC_WALL(element) &&
6139          smashed == EL_DC_MAGIC_WALL))
6140     {
6141       int xx, yy;
6142       int activated_magic_wall =
6143         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6144          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6145          EL_DC_MAGIC_WALL_ACTIVE);
6146
6147       /* activate magic wall / mill */
6148       SCAN_PLAYFIELD(xx, yy)
6149       {
6150         if (Feld[xx][yy] == smashed)
6151           Feld[xx][yy] = activated_magic_wall;
6152       }
6153
6154       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6155       game.magic_wall_active = TRUE;
6156
6157       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6158                             SND_MAGIC_WALL_ACTIVATING :
6159                             smashed == EL_BD_MAGIC_WALL ?
6160                             SND_BD_MAGIC_WALL_ACTIVATING :
6161                             SND_DC_MAGIC_WALL_ACTIVATING));
6162     }
6163
6164     if (IS_PLAYER(x, y + 1))
6165     {
6166       if (CAN_SMASH_PLAYER(element))
6167       {
6168         KillPlayerUnlessEnemyProtected(x, y + 1);
6169         return;
6170       }
6171     }
6172     else if (smashed == EL_PENGUIN)
6173     {
6174       if (CAN_SMASH_PLAYER(element))
6175       {
6176         Bang(x, y + 1);
6177         return;
6178       }
6179     }
6180     else if (element == EL_BD_DIAMOND)
6181     {
6182       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6183       {
6184         Bang(x, y + 1);
6185         return;
6186       }
6187     }
6188     else if (((element == EL_SP_INFOTRON ||
6189                element == EL_SP_ZONK) &&
6190               (smashed == EL_SP_SNIKSNAK ||
6191                smashed == EL_SP_ELECTRON ||
6192                smashed == EL_SP_DISK_ORANGE)) ||
6193              (element == EL_SP_INFOTRON &&
6194               smashed == EL_SP_DISK_YELLOW))
6195     {
6196       Bang(x, y + 1);
6197       return;
6198     }
6199     else if (CAN_SMASH_EVERYTHING(element))
6200     {
6201       if (IS_CLASSIC_ENEMY(smashed) ||
6202           CAN_EXPLODE_SMASHED(smashed))
6203       {
6204         Bang(x, y + 1);
6205         return;
6206       }
6207       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6208       {
6209         if (smashed == EL_LAMP ||
6210             smashed == EL_LAMP_ACTIVE)
6211         {
6212           Bang(x, y + 1);
6213           return;
6214         }
6215         else if (smashed == EL_NUT)
6216         {
6217           Feld[x][y + 1] = EL_NUT_BREAKING;
6218           PlayLevelSound(x, y, SND_NUT_BREAKING);
6219           RaiseScoreElement(EL_NUT);
6220           return;
6221         }
6222         else if (smashed == EL_PEARL)
6223         {
6224           ResetGfxAnimation(x, y);
6225
6226           Feld[x][y + 1] = EL_PEARL_BREAKING;
6227           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6228           return;
6229         }
6230         else if (smashed == EL_DIAMOND)
6231         {
6232           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6233           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6234           return;
6235         }
6236         else if (IS_BELT_SWITCH(smashed))
6237         {
6238           ToggleBeltSwitch(x, y + 1);
6239         }
6240         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6241                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6242                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6243                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6244         {
6245           ToggleSwitchgateSwitch(x, y + 1);
6246         }
6247         else if (smashed == EL_LIGHT_SWITCH ||
6248                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6249         {
6250           ToggleLightSwitch(x, y + 1);
6251         }
6252         else
6253         {
6254           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6255
6256           CheckElementChangeBySide(x, y + 1, smashed, element,
6257                                    CE_SWITCHED, CH_SIDE_TOP);
6258           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6259                                             CH_SIDE_TOP);
6260         }
6261       }
6262       else
6263       {
6264         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6265       }
6266     }
6267   }
6268
6269   /* play sound of magic wall / mill */
6270   if (!last_line &&
6271       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6272        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6273        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6274   {
6275     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6276       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6277     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6278       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6279     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6280       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6281
6282     return;
6283   }
6284
6285   /* play sound of object that hits the ground */
6286   if (last_line || object_hit)
6287     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6288 }
6289
6290 inline static void TurnRoundExt(int x, int y)
6291 {
6292   static struct
6293   {
6294     int dx, dy;
6295   } move_xy[] =
6296   {
6297     {  0,  0 },
6298     { -1,  0 },
6299     { +1,  0 },
6300     {  0,  0 },
6301     {  0, -1 },
6302     {  0,  0 }, { 0, 0 }, { 0, 0 },
6303     {  0, +1 }
6304   };
6305   static struct
6306   {
6307     int left, right, back;
6308   } turn[] =
6309   {
6310     { 0,        0,              0        },
6311     { MV_DOWN,  MV_UP,          MV_RIGHT },
6312     { MV_UP,    MV_DOWN,        MV_LEFT  },
6313     { 0,        0,              0        },
6314     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6315     { 0,        0,              0        },
6316     { 0,        0,              0        },
6317     { 0,        0,              0        },
6318     { MV_RIGHT, MV_LEFT,        MV_UP    }
6319   };
6320
6321   int element = Feld[x][y];
6322   int move_pattern = element_info[element].move_pattern;
6323
6324   int old_move_dir = MovDir[x][y];
6325   int left_dir  = turn[old_move_dir].left;
6326   int right_dir = turn[old_move_dir].right;
6327   int back_dir  = turn[old_move_dir].back;
6328
6329   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6330   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6331   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6332   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6333
6334   int left_x  = x + left_dx,  left_y  = y + left_dy;
6335   int right_x = x + right_dx, right_y = y + right_dy;
6336   int move_x  = x + move_dx,  move_y  = y + move_dy;
6337
6338   int xx, yy;
6339
6340   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6341   {
6342     TestIfBadThingTouchesOtherBadThing(x, y);
6343
6344     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6345       MovDir[x][y] = right_dir;
6346     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6347       MovDir[x][y] = left_dir;
6348
6349     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6350       MovDelay[x][y] = 9;
6351     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6352       MovDelay[x][y] = 1;
6353   }
6354   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6355   {
6356     TestIfBadThingTouchesOtherBadThing(x, y);
6357
6358     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6359       MovDir[x][y] = left_dir;
6360     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6361       MovDir[x][y] = right_dir;
6362
6363     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6364       MovDelay[x][y] = 9;
6365     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6366       MovDelay[x][y] = 1;
6367   }
6368   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6369   {
6370     TestIfBadThingTouchesOtherBadThing(x, y);
6371
6372     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6373       MovDir[x][y] = left_dir;
6374     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6375       MovDir[x][y] = right_dir;
6376
6377     if (MovDir[x][y] != old_move_dir)
6378       MovDelay[x][y] = 9;
6379   }
6380   else if (element == EL_YAMYAM)
6381   {
6382     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6383     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6384
6385     if (can_turn_left && can_turn_right)
6386       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6387     else if (can_turn_left)
6388       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6389     else if (can_turn_right)
6390       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6391     else
6392       MovDir[x][y] = back_dir;
6393
6394     MovDelay[x][y] = 16 + 16 * RND(3);
6395   }
6396   else if (element == EL_DARK_YAMYAM)
6397   {
6398     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6399                                                          left_x, left_y);
6400     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6401                                                          right_x, right_y);
6402
6403     if (can_turn_left && can_turn_right)
6404       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6405     else if (can_turn_left)
6406       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6407     else if (can_turn_right)
6408       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6409     else
6410       MovDir[x][y] = back_dir;
6411
6412     MovDelay[x][y] = 16 + 16 * RND(3);
6413   }
6414   else if (element == EL_PACMAN)
6415   {
6416     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6417     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6418
6419     if (can_turn_left && can_turn_right)
6420       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6421     else if (can_turn_left)
6422       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6423     else if (can_turn_right)
6424       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6425     else
6426       MovDir[x][y] = back_dir;
6427
6428     MovDelay[x][y] = 6 + RND(40);
6429   }
6430   else if (element == EL_PIG)
6431   {
6432     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6433     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6434     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6435     boolean should_turn_left, should_turn_right, should_move_on;
6436     int rnd_value = 24;
6437     int rnd = RND(rnd_value);
6438
6439     should_turn_left = (can_turn_left &&
6440                         (!can_move_on ||
6441                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6442                                                    y + back_dy + left_dy)));
6443     should_turn_right = (can_turn_right &&
6444                          (!can_move_on ||
6445                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6446                                                     y + back_dy + right_dy)));
6447     should_move_on = (can_move_on &&
6448                       (!can_turn_left ||
6449                        !can_turn_right ||
6450                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6451                                                  y + move_dy + left_dy) ||
6452                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6453                                                  y + move_dy + right_dy)));
6454
6455     if (should_turn_left || should_turn_right || should_move_on)
6456     {
6457       if (should_turn_left && should_turn_right && should_move_on)
6458         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6459                         rnd < 2 * rnd_value / 3 ? right_dir :
6460                         old_move_dir);
6461       else if (should_turn_left && should_turn_right)
6462         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6463       else if (should_turn_left && should_move_on)
6464         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6465       else if (should_turn_right && should_move_on)
6466         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6467       else if (should_turn_left)
6468         MovDir[x][y] = left_dir;
6469       else if (should_turn_right)
6470         MovDir[x][y] = right_dir;
6471       else if (should_move_on)
6472         MovDir[x][y] = old_move_dir;
6473     }
6474     else if (can_move_on && rnd > rnd_value / 8)
6475       MovDir[x][y] = old_move_dir;
6476     else if (can_turn_left && can_turn_right)
6477       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6478     else if (can_turn_left && rnd > rnd_value / 8)
6479       MovDir[x][y] = left_dir;
6480     else if (can_turn_right && rnd > rnd_value/8)
6481       MovDir[x][y] = right_dir;
6482     else
6483       MovDir[x][y] = back_dir;
6484
6485     xx = x + move_xy[MovDir[x][y]].dx;
6486     yy = y + move_xy[MovDir[x][y]].dy;
6487
6488     if (!IN_LEV_FIELD(xx, yy) ||
6489         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6490       MovDir[x][y] = old_move_dir;
6491
6492     MovDelay[x][y] = 0;
6493   }
6494   else if (element == EL_DRAGON)
6495   {
6496     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6497     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6498     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6499     int rnd_value = 24;
6500     int rnd = RND(rnd_value);
6501
6502     if (can_move_on && rnd > rnd_value / 8)
6503       MovDir[x][y] = old_move_dir;
6504     else if (can_turn_left && can_turn_right)
6505       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6506     else if (can_turn_left && rnd > rnd_value / 8)
6507       MovDir[x][y] = left_dir;
6508     else if (can_turn_right && rnd > rnd_value / 8)
6509       MovDir[x][y] = right_dir;
6510     else
6511       MovDir[x][y] = back_dir;
6512
6513     xx = x + move_xy[MovDir[x][y]].dx;
6514     yy = y + move_xy[MovDir[x][y]].dy;
6515
6516     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6517       MovDir[x][y] = old_move_dir;
6518
6519     MovDelay[x][y] = 0;
6520   }
6521   else if (element == EL_MOLE)
6522   {
6523     boolean can_move_on =
6524       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6525                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6526                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6527     if (!can_move_on)
6528     {
6529       boolean can_turn_left =
6530         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6531                               IS_AMOEBOID(Feld[left_x][left_y])));
6532
6533       boolean can_turn_right =
6534         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6535                               IS_AMOEBOID(Feld[right_x][right_y])));
6536
6537       if (can_turn_left && can_turn_right)
6538         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6539       else if (can_turn_left)
6540         MovDir[x][y] = left_dir;
6541       else
6542         MovDir[x][y] = right_dir;
6543     }
6544
6545     if (MovDir[x][y] != old_move_dir)
6546       MovDelay[x][y] = 9;
6547   }
6548   else if (element == EL_BALLOON)
6549   {
6550     MovDir[x][y] = game.wind_direction;
6551     MovDelay[x][y] = 0;
6552   }
6553   else if (element == EL_SPRING)
6554   {
6555     if (MovDir[x][y] & MV_HORIZONTAL)
6556     {
6557       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6558           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6559       {
6560         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6561         ResetGfxAnimation(move_x, move_y);
6562         TEST_DrawLevelField(move_x, move_y);
6563
6564         MovDir[x][y] = back_dir;
6565       }
6566       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6567                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6568         MovDir[x][y] = MV_NONE;
6569     }
6570
6571     MovDelay[x][y] = 0;
6572   }
6573   else if (element == EL_ROBOT ||
6574            element == EL_SATELLITE ||
6575            element == EL_PENGUIN ||
6576            element == EL_EMC_ANDROID)
6577   {
6578     int attr_x = -1, attr_y = -1;
6579
6580     if (AllPlayersGone)
6581     {
6582       attr_x = ExitX;
6583       attr_y = ExitY;
6584     }
6585     else
6586     {
6587       int i;
6588
6589       for (i = 0; i < MAX_PLAYERS; i++)
6590       {
6591         struct PlayerInfo *player = &stored_player[i];
6592         int jx = player->jx, jy = player->jy;
6593
6594         if (!player->active)
6595           continue;
6596
6597         if (attr_x == -1 ||
6598             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6599         {
6600           attr_x = jx;
6601           attr_y = jy;
6602         }
6603       }
6604     }
6605
6606     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6607         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6608          game.engine_version < VERSION_IDENT(3,1,0,0)))
6609     {
6610       attr_x = ZX;
6611       attr_y = ZY;
6612     }
6613
6614     if (element == EL_PENGUIN)
6615     {
6616       int i;
6617       static int xy[4][2] =
6618       {
6619         { 0, -1 },
6620         { -1, 0 },
6621         { +1, 0 },
6622         { 0, +1 }
6623       };
6624
6625       for (i = 0; i < NUM_DIRECTIONS; i++)
6626       {
6627         int ex = x + xy[i][0];
6628         int ey = y + xy[i][1];
6629
6630         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6631                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6632                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6633                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6634         {
6635           attr_x = ex;
6636           attr_y = ey;
6637           break;
6638         }
6639       }
6640     }
6641
6642     MovDir[x][y] = MV_NONE;
6643     if (attr_x < x)
6644       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6645     else if (attr_x > x)
6646       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6647     if (attr_y < y)
6648       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6649     else if (attr_y > y)
6650       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6651
6652     if (element == EL_ROBOT)
6653     {
6654       int newx, newy;
6655
6656       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6657         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6658       Moving2Blocked(x, y, &newx, &newy);
6659
6660       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6661         MovDelay[x][y] = 8 + 8 * !RND(3);
6662       else
6663         MovDelay[x][y] = 16;
6664     }
6665     else if (element == EL_PENGUIN)
6666     {
6667       int newx, newy;
6668
6669       MovDelay[x][y] = 1;
6670
6671       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6672       {
6673         boolean first_horiz = RND(2);
6674         int new_move_dir = MovDir[x][y];
6675
6676         MovDir[x][y] =
6677           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6678         Moving2Blocked(x, y, &newx, &newy);
6679
6680         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6681           return;
6682
6683         MovDir[x][y] =
6684           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6685         Moving2Blocked(x, y, &newx, &newy);
6686
6687         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6688           return;
6689
6690         MovDir[x][y] = old_move_dir;
6691         return;
6692       }
6693     }
6694     else if (element == EL_SATELLITE)
6695     {
6696       int newx, newy;
6697
6698       MovDelay[x][y] = 1;
6699
6700       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6701       {
6702         boolean first_horiz = RND(2);
6703         int new_move_dir = MovDir[x][y];
6704
6705         MovDir[x][y] =
6706           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6707         Moving2Blocked(x, y, &newx, &newy);
6708
6709         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6710           return;
6711
6712         MovDir[x][y] =
6713           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6714         Moving2Blocked(x, y, &newx, &newy);
6715
6716         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6717           return;
6718
6719         MovDir[x][y] = old_move_dir;
6720         return;
6721       }
6722     }
6723     else if (element == EL_EMC_ANDROID)
6724     {
6725       static int check_pos[16] =
6726       {
6727         -1,             /*  0 => (invalid)          */
6728         7,              /*  1 => MV_LEFT            */
6729         3,              /*  2 => MV_RIGHT           */
6730         -1,             /*  3 => (invalid)          */
6731         1,              /*  4 =>            MV_UP   */
6732         0,              /*  5 => MV_LEFT  | MV_UP   */
6733         2,              /*  6 => MV_RIGHT | MV_UP   */
6734         -1,             /*  7 => (invalid)          */
6735         5,              /*  8 =>            MV_DOWN */
6736         6,              /*  9 => MV_LEFT  | MV_DOWN */
6737         4,              /* 10 => MV_RIGHT | MV_DOWN */
6738         -1,             /* 11 => (invalid)          */
6739         -1,             /* 12 => (invalid)          */
6740         -1,             /* 13 => (invalid)          */
6741         -1,             /* 14 => (invalid)          */
6742         -1,             /* 15 => (invalid)          */
6743       };
6744       static struct
6745       {
6746         int dx, dy;
6747         int dir;
6748       } check_xy[8] =
6749       {
6750         { -1, -1,       MV_LEFT  | MV_UP   },
6751         {  0, -1,                  MV_UP   },
6752         { +1, -1,       MV_RIGHT | MV_UP   },
6753         { +1,  0,       MV_RIGHT           },
6754         { +1, +1,       MV_RIGHT | MV_DOWN },
6755         {  0, +1,                  MV_DOWN },
6756         { -1, +1,       MV_LEFT  | MV_DOWN },
6757         { -1,  0,       MV_LEFT            },
6758       };
6759       int start_pos, check_order;
6760       boolean can_clone = FALSE;
6761       int i;
6762
6763       /* check if there is any free field around current position */
6764       for (i = 0; i < 8; i++)
6765       {
6766         int newx = x + check_xy[i].dx;
6767         int newy = y + check_xy[i].dy;
6768
6769         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6770         {
6771           can_clone = TRUE;
6772
6773           break;
6774         }
6775       }
6776
6777       if (can_clone)            /* randomly find an element to clone */
6778       {
6779         can_clone = FALSE;
6780
6781         start_pos = check_pos[RND(8)];
6782         check_order = (RND(2) ? -1 : +1);
6783
6784         for (i = 0; i < 8; i++)
6785         {
6786           int pos_raw = start_pos + i * check_order;
6787           int pos = (pos_raw + 8) % 8;
6788           int newx = x + check_xy[pos].dx;
6789           int newy = y + check_xy[pos].dy;
6790
6791           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6792           {
6793             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6794             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6795
6796             Store[x][y] = Feld[newx][newy];
6797
6798             can_clone = TRUE;
6799
6800             break;
6801           }
6802         }
6803       }
6804
6805       if (can_clone)            /* randomly find a direction to move */
6806       {
6807         can_clone = FALSE;
6808
6809         start_pos = check_pos[RND(8)];
6810         check_order = (RND(2) ? -1 : +1);
6811
6812         for (i = 0; i < 8; i++)
6813         {
6814           int pos_raw = start_pos + i * check_order;
6815           int pos = (pos_raw + 8) % 8;
6816           int newx = x + check_xy[pos].dx;
6817           int newy = y + check_xy[pos].dy;
6818           int new_move_dir = check_xy[pos].dir;
6819
6820           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6821           {
6822             MovDir[x][y] = new_move_dir;
6823             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6824
6825             can_clone = TRUE;
6826
6827             break;
6828           }
6829         }
6830       }
6831
6832       if (can_clone)            /* cloning and moving successful */
6833         return;
6834
6835       /* cannot clone -- try to move towards player */
6836
6837       start_pos = check_pos[MovDir[x][y] & 0x0f];
6838       check_order = (RND(2) ? -1 : +1);
6839
6840       for (i = 0; i < 3; i++)
6841       {
6842         /* first check start_pos, then previous/next or (next/previous) pos */
6843         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6844         int pos = (pos_raw + 8) % 8;
6845         int newx = x + check_xy[pos].dx;
6846         int newy = y + check_xy[pos].dy;
6847         int new_move_dir = check_xy[pos].dir;
6848
6849         if (IS_PLAYER(newx, newy))
6850           break;
6851
6852         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6853         {
6854           MovDir[x][y] = new_move_dir;
6855           MovDelay[x][y] = level.android_move_time * 8 + 1;
6856
6857           break;
6858         }
6859       }
6860     }
6861   }
6862   else if (move_pattern == MV_TURNING_LEFT ||
6863            move_pattern == MV_TURNING_RIGHT ||
6864            move_pattern == MV_TURNING_LEFT_RIGHT ||
6865            move_pattern == MV_TURNING_RIGHT_LEFT ||
6866            move_pattern == MV_TURNING_RANDOM ||
6867            move_pattern == MV_ALL_DIRECTIONS)
6868   {
6869     boolean can_turn_left =
6870       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6871     boolean can_turn_right =
6872       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6873
6874     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6875       return;
6876
6877     if (move_pattern == MV_TURNING_LEFT)
6878       MovDir[x][y] = left_dir;
6879     else if (move_pattern == MV_TURNING_RIGHT)
6880       MovDir[x][y] = right_dir;
6881     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6882       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6883     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6884       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6885     else if (move_pattern == MV_TURNING_RANDOM)
6886       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6887                       can_turn_right && !can_turn_left ? right_dir :
6888                       RND(2) ? left_dir : right_dir);
6889     else if (can_turn_left && can_turn_right)
6890       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6891     else if (can_turn_left)
6892       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6893     else if (can_turn_right)
6894       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6895     else
6896       MovDir[x][y] = back_dir;
6897
6898     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6899   }
6900   else if (move_pattern == MV_HORIZONTAL ||
6901            move_pattern == MV_VERTICAL)
6902   {
6903     if (move_pattern & old_move_dir)
6904       MovDir[x][y] = back_dir;
6905     else if (move_pattern == MV_HORIZONTAL)
6906       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6907     else if (move_pattern == MV_VERTICAL)
6908       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6909
6910     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6911   }
6912   else if (move_pattern & MV_ANY_DIRECTION)
6913   {
6914     MovDir[x][y] = move_pattern;
6915     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6916   }
6917   else if (move_pattern & MV_WIND_DIRECTION)
6918   {
6919     MovDir[x][y] = game.wind_direction;
6920     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6921   }
6922   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6923   {
6924     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6925       MovDir[x][y] = left_dir;
6926     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6927       MovDir[x][y] = right_dir;
6928
6929     if (MovDir[x][y] != old_move_dir)
6930       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6931   }
6932   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6933   {
6934     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6935       MovDir[x][y] = right_dir;
6936     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6937       MovDir[x][y] = left_dir;
6938
6939     if (MovDir[x][y] != old_move_dir)
6940       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6941   }
6942   else if (move_pattern == MV_TOWARDS_PLAYER ||
6943            move_pattern == MV_AWAY_FROM_PLAYER)
6944   {
6945     int attr_x = -1, attr_y = -1;
6946     int newx, newy;
6947     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6948
6949     if (AllPlayersGone)
6950     {
6951       attr_x = ExitX;
6952       attr_y = ExitY;
6953     }
6954     else
6955     {
6956       int i;
6957
6958       for (i = 0; i < MAX_PLAYERS; i++)
6959       {
6960         struct PlayerInfo *player = &stored_player[i];
6961         int jx = player->jx, jy = player->jy;
6962
6963         if (!player->active)
6964           continue;
6965
6966         if (attr_x == -1 ||
6967             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6968         {
6969           attr_x = jx;
6970           attr_y = jy;
6971         }
6972       }
6973     }
6974
6975     MovDir[x][y] = MV_NONE;
6976     if (attr_x < x)
6977       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6978     else if (attr_x > x)
6979       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6980     if (attr_y < y)
6981       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6982     else if (attr_y > y)
6983       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6984
6985     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6986
6987     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6988     {
6989       boolean first_horiz = RND(2);
6990       int new_move_dir = MovDir[x][y];
6991
6992       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6993       {
6994         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6995         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6996
6997         return;
6998       }
6999
7000       MovDir[x][y] =
7001         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7002       Moving2Blocked(x, y, &newx, &newy);
7003
7004       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7005         return;
7006
7007       MovDir[x][y] =
7008         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7009       Moving2Blocked(x, y, &newx, &newy);
7010
7011       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7012         return;
7013
7014       MovDir[x][y] = old_move_dir;
7015     }
7016   }
7017   else if (move_pattern == MV_WHEN_PUSHED ||
7018            move_pattern == MV_WHEN_DROPPED)
7019   {
7020     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7021       MovDir[x][y] = MV_NONE;
7022
7023     MovDelay[x][y] = 0;
7024   }
7025   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7026   {
7027     static int test_xy[7][2] =
7028     {
7029       { 0, -1 },
7030       { -1, 0 },
7031       { +1, 0 },
7032       { 0, +1 },
7033       { 0, -1 },
7034       { -1, 0 },
7035       { +1, 0 },
7036     };
7037     static int test_dir[7] =
7038     {
7039       MV_UP,
7040       MV_LEFT,
7041       MV_RIGHT,
7042       MV_DOWN,
7043       MV_UP,
7044       MV_LEFT,
7045       MV_RIGHT,
7046     };
7047     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7048     int move_preference = -1000000;     /* start with very low preference */
7049     int new_move_dir = MV_NONE;
7050     int start_test = RND(4);
7051     int i;
7052
7053     for (i = 0; i < NUM_DIRECTIONS; i++)
7054     {
7055       int move_dir = test_dir[start_test + i];
7056       int move_dir_preference;
7057
7058       xx = x + test_xy[start_test + i][0];
7059       yy = y + test_xy[start_test + i][1];
7060
7061       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7062           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7063       {
7064         new_move_dir = move_dir;
7065
7066         break;
7067       }
7068
7069       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7070         continue;
7071
7072       move_dir_preference = -1 * RunnerVisit[xx][yy];
7073       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7074         move_dir_preference = PlayerVisit[xx][yy];
7075
7076       if (move_dir_preference > move_preference)
7077       {
7078         /* prefer field that has not been visited for the longest time */
7079         move_preference = move_dir_preference;
7080         new_move_dir = move_dir;
7081       }
7082       else if (move_dir_preference == move_preference &&
7083                move_dir == old_move_dir)
7084       {
7085         /* prefer last direction when all directions are preferred equally */
7086         move_preference = move_dir_preference;
7087         new_move_dir = move_dir;
7088       }
7089     }
7090
7091     MovDir[x][y] = new_move_dir;
7092     if (old_move_dir != new_move_dir)
7093       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7094   }
7095 }
7096
7097 static void TurnRound(int x, int y)
7098 {
7099   int direction = MovDir[x][y];
7100
7101   TurnRoundExt(x, y);
7102
7103   GfxDir[x][y] = MovDir[x][y];
7104
7105   if (direction != MovDir[x][y])
7106     GfxFrame[x][y] = 0;
7107
7108   if (MovDelay[x][y])
7109     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7110
7111   ResetGfxFrame(x, y, FALSE);
7112 }
7113
7114 static boolean JustBeingPushed(int x, int y)
7115 {
7116   int i;
7117
7118   for (i = 0; i < MAX_PLAYERS; i++)
7119   {
7120     struct PlayerInfo *player = &stored_player[i];
7121
7122     if (player->active && player->is_pushing && player->MovPos)
7123     {
7124       int next_jx = player->jx + (player->jx - player->last_jx);
7125       int next_jy = player->jy + (player->jy - player->last_jy);
7126
7127       if (x == next_jx && y == next_jy)
7128         return TRUE;
7129     }
7130   }
7131
7132   return FALSE;
7133 }
7134
7135 void StartMoving(int x, int y)
7136 {
7137   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7138   int element = Feld[x][y];
7139
7140   if (Stop[x][y])
7141     return;
7142
7143   if (MovDelay[x][y] == 0)
7144     GfxAction[x][y] = ACTION_DEFAULT;
7145
7146   if (CAN_FALL(element) && y < lev_fieldy - 1)
7147   {
7148     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7149         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7150       if (JustBeingPushed(x, y))
7151         return;
7152
7153     if (element == EL_QUICKSAND_FULL)
7154     {
7155       if (IS_FREE(x, y + 1))
7156       {
7157         InitMovingField(x, y, MV_DOWN);
7158         started_moving = TRUE;
7159
7160         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7161 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7162         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7163           Store[x][y] = EL_ROCK;
7164 #else
7165         Store[x][y] = EL_ROCK;
7166 #endif
7167
7168         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7169       }
7170       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7171       {
7172         if (!MovDelay[x][y])
7173         {
7174           MovDelay[x][y] = TILEY + 1;
7175
7176           ResetGfxAnimation(x, y);
7177           ResetGfxAnimation(x, y + 1);
7178         }
7179
7180         if (MovDelay[x][y])
7181         {
7182           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7183           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7184
7185           MovDelay[x][y]--;
7186           if (MovDelay[x][y])
7187             return;
7188         }
7189
7190         Feld[x][y] = EL_QUICKSAND_EMPTY;
7191         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7192         Store[x][y + 1] = Store[x][y];
7193         Store[x][y] = 0;
7194
7195         PlayLevelSoundAction(x, y, ACTION_FILLING);
7196       }
7197       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7198       {
7199         if (!MovDelay[x][y])
7200         {
7201           MovDelay[x][y] = TILEY + 1;
7202
7203           ResetGfxAnimation(x, y);
7204           ResetGfxAnimation(x, y + 1);
7205         }
7206
7207         if (MovDelay[x][y])
7208         {
7209           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7210           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7211
7212           MovDelay[x][y]--;
7213           if (MovDelay[x][y])
7214             return;
7215         }
7216
7217         Feld[x][y] = EL_QUICKSAND_EMPTY;
7218         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7219         Store[x][y + 1] = Store[x][y];
7220         Store[x][y] = 0;
7221
7222         PlayLevelSoundAction(x, y, ACTION_FILLING);
7223       }
7224     }
7225     else if (element == EL_QUICKSAND_FAST_FULL)
7226     {
7227       if (IS_FREE(x, y + 1))
7228       {
7229         InitMovingField(x, y, MV_DOWN);
7230         started_moving = TRUE;
7231
7232         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7233 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7234         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7235           Store[x][y] = EL_ROCK;
7236 #else
7237         Store[x][y] = EL_ROCK;
7238 #endif
7239
7240         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7241       }
7242       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7243       {
7244         if (!MovDelay[x][y])
7245         {
7246           MovDelay[x][y] = TILEY + 1;
7247
7248           ResetGfxAnimation(x, y);
7249           ResetGfxAnimation(x, y + 1);
7250         }
7251
7252         if (MovDelay[x][y])
7253         {
7254           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7255           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7256
7257           MovDelay[x][y]--;
7258           if (MovDelay[x][y])
7259             return;
7260         }
7261
7262         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7263         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7264         Store[x][y + 1] = Store[x][y];
7265         Store[x][y] = 0;
7266
7267         PlayLevelSoundAction(x, y, ACTION_FILLING);
7268       }
7269       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7270       {
7271         if (!MovDelay[x][y])
7272         {
7273           MovDelay[x][y] = TILEY + 1;
7274
7275           ResetGfxAnimation(x, y);
7276           ResetGfxAnimation(x, y + 1);
7277         }
7278
7279         if (MovDelay[x][y])
7280         {
7281           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7282           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7283
7284           MovDelay[x][y]--;
7285           if (MovDelay[x][y])
7286             return;
7287         }
7288
7289         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7290         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7291         Store[x][y + 1] = Store[x][y];
7292         Store[x][y] = 0;
7293
7294         PlayLevelSoundAction(x, y, ACTION_FILLING);
7295       }
7296     }
7297     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7298              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7299     {
7300       InitMovingField(x, y, MV_DOWN);
7301       started_moving = TRUE;
7302
7303       Feld[x][y] = EL_QUICKSAND_FILLING;
7304       Store[x][y] = element;
7305
7306       PlayLevelSoundAction(x, y, ACTION_FILLING);
7307     }
7308     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7309              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7310     {
7311       InitMovingField(x, y, MV_DOWN);
7312       started_moving = TRUE;
7313
7314       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7315       Store[x][y] = element;
7316
7317       PlayLevelSoundAction(x, y, ACTION_FILLING);
7318     }
7319     else if (element == EL_MAGIC_WALL_FULL)
7320     {
7321       if (IS_FREE(x, y + 1))
7322       {
7323         InitMovingField(x, y, MV_DOWN);
7324         started_moving = TRUE;
7325
7326         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7327         Store[x][y] = EL_CHANGED(Store[x][y]);
7328       }
7329       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7330       {
7331         if (!MovDelay[x][y])
7332           MovDelay[x][y] = TILEY / 4 + 1;
7333
7334         if (MovDelay[x][y])
7335         {
7336           MovDelay[x][y]--;
7337           if (MovDelay[x][y])
7338             return;
7339         }
7340
7341         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7342         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7343         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7344         Store[x][y] = 0;
7345       }
7346     }
7347     else if (element == EL_BD_MAGIC_WALL_FULL)
7348     {
7349       if (IS_FREE(x, y + 1))
7350       {
7351         InitMovingField(x, y, MV_DOWN);
7352         started_moving = TRUE;
7353
7354         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7355         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7356       }
7357       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7358       {
7359         if (!MovDelay[x][y])
7360           MovDelay[x][y] = TILEY / 4 + 1;
7361
7362         if (MovDelay[x][y])
7363         {
7364           MovDelay[x][y]--;
7365           if (MovDelay[x][y])
7366             return;
7367         }
7368
7369         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7370         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7371         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7372         Store[x][y] = 0;
7373       }
7374     }
7375     else if (element == EL_DC_MAGIC_WALL_FULL)
7376     {
7377       if (IS_FREE(x, y + 1))
7378       {
7379         InitMovingField(x, y, MV_DOWN);
7380         started_moving = TRUE;
7381
7382         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7383         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7384       }
7385       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7386       {
7387         if (!MovDelay[x][y])
7388           MovDelay[x][y] = TILEY / 4 + 1;
7389
7390         if (MovDelay[x][y])
7391         {
7392           MovDelay[x][y]--;
7393           if (MovDelay[x][y])
7394             return;
7395         }
7396
7397         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7398         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7399         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7400         Store[x][y] = 0;
7401       }
7402     }
7403     else if ((CAN_PASS_MAGIC_WALL(element) &&
7404               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7405                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7406              (CAN_PASS_DC_MAGIC_WALL(element) &&
7407               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7408
7409     {
7410       InitMovingField(x, y, MV_DOWN);
7411       started_moving = TRUE;
7412
7413       Feld[x][y] =
7414         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7415          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7416          EL_DC_MAGIC_WALL_FILLING);
7417       Store[x][y] = element;
7418     }
7419     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7420     {
7421       SplashAcid(x, y + 1);
7422
7423       InitMovingField(x, y, MV_DOWN);
7424       started_moving = TRUE;
7425
7426       Store[x][y] = EL_ACID;
7427     }
7428     else if (
7429              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7430               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7431              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7432               CAN_FALL(element) && WasJustFalling[x][y] &&
7433               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7434
7435              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7436               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7437               (Feld[x][y + 1] == EL_BLOCKED)))
7438     {
7439       /* this is needed for a special case not covered by calling "Impact()"
7440          from "ContinueMoving()": if an element moves to a tile directly below
7441          another element which was just falling on that tile (which was empty
7442          in the previous frame), the falling element above would just stop
7443          instead of smashing the element below (in previous version, the above
7444          element was just checked for "moving" instead of "falling", resulting
7445          in incorrect smashes caused by horizontal movement of the above
7446          element; also, the case of the player being the element to smash was
7447          simply not covered here... :-/ ) */
7448
7449       CheckCollision[x][y] = 0;
7450       CheckImpact[x][y] = 0;
7451
7452       Impact(x, y);
7453     }
7454     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7455     {
7456       if (MovDir[x][y] == MV_NONE)
7457       {
7458         InitMovingField(x, y, MV_DOWN);
7459         started_moving = TRUE;
7460       }
7461     }
7462     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7463     {
7464       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7465         MovDir[x][y] = MV_DOWN;
7466
7467       InitMovingField(x, y, MV_DOWN);
7468       started_moving = TRUE;
7469     }
7470     else if (element == EL_AMOEBA_DROP)
7471     {
7472       Feld[x][y] = EL_AMOEBA_GROWING;
7473       Store[x][y] = EL_AMOEBA_WET;
7474     }
7475     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7476               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7477              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7478              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7479     {
7480       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7481                                 (IS_FREE(x - 1, y + 1) ||
7482                                  Feld[x - 1][y + 1] == EL_ACID));
7483       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7484                                 (IS_FREE(x + 1, y + 1) ||
7485                                  Feld[x + 1][y + 1] == EL_ACID));
7486       boolean can_fall_any  = (can_fall_left || can_fall_right);
7487       boolean can_fall_both = (can_fall_left && can_fall_right);
7488       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7489
7490       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7491       {
7492         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7493           can_fall_right = FALSE;
7494         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7495           can_fall_left = FALSE;
7496         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7497           can_fall_right = FALSE;
7498         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7499           can_fall_left = FALSE;
7500
7501         can_fall_any  = (can_fall_left || can_fall_right);
7502         can_fall_both = FALSE;
7503       }
7504
7505       if (can_fall_both)
7506       {
7507         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7508           can_fall_right = FALSE;       /* slip down on left side */
7509         else
7510           can_fall_left = !(can_fall_right = RND(2));
7511
7512         can_fall_both = FALSE;
7513       }
7514
7515       if (can_fall_any)
7516       {
7517         /* if not determined otherwise, prefer left side for slipping down */
7518         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7519         started_moving = TRUE;
7520       }
7521     }
7522     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7523     {
7524       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7525       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7526       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7527       int belt_dir = game.belt_dir[belt_nr];
7528
7529       if ((belt_dir == MV_LEFT  && left_is_free) ||
7530           (belt_dir == MV_RIGHT && right_is_free))
7531       {
7532         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7533
7534         InitMovingField(x, y, belt_dir);
7535         started_moving = TRUE;
7536
7537         Pushed[x][y] = TRUE;
7538         Pushed[nextx][y] = TRUE;
7539
7540         GfxAction[x][y] = ACTION_DEFAULT;
7541       }
7542       else
7543       {
7544         MovDir[x][y] = 0;       /* if element was moving, stop it */
7545       }
7546     }
7547   }
7548
7549   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7550   if (CAN_MOVE(element) && !started_moving)
7551   {
7552     int move_pattern = element_info[element].move_pattern;
7553     int newx, newy;
7554
7555     Moving2Blocked(x, y, &newx, &newy);
7556
7557     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7558       return;
7559
7560     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7561         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7562     {
7563       WasJustMoving[x][y] = 0;
7564       CheckCollision[x][y] = 0;
7565
7566       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7567
7568       if (Feld[x][y] != element)        /* element has changed */
7569         return;
7570     }
7571
7572     if (!MovDelay[x][y])        /* start new movement phase */
7573     {
7574       /* all objects that can change their move direction after each step
7575          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7576
7577       if (element != EL_YAMYAM &&
7578           element != EL_DARK_YAMYAM &&
7579           element != EL_PACMAN &&
7580           !(move_pattern & MV_ANY_DIRECTION) &&
7581           move_pattern != MV_TURNING_LEFT &&
7582           move_pattern != MV_TURNING_RIGHT &&
7583           move_pattern != MV_TURNING_LEFT_RIGHT &&
7584           move_pattern != MV_TURNING_RIGHT_LEFT &&
7585           move_pattern != MV_TURNING_RANDOM)
7586       {
7587         TurnRound(x, y);
7588
7589         if (MovDelay[x][y] && (element == EL_BUG ||
7590                                element == EL_SPACESHIP ||
7591                                element == EL_SP_SNIKSNAK ||
7592                                element == EL_SP_ELECTRON ||
7593                                element == EL_MOLE))
7594           TEST_DrawLevelField(x, y);
7595       }
7596     }
7597
7598     if (MovDelay[x][y])         /* wait some time before next movement */
7599     {
7600       MovDelay[x][y]--;
7601
7602       if (element == EL_ROBOT ||
7603           element == EL_YAMYAM ||
7604           element == EL_DARK_YAMYAM)
7605       {
7606         DrawLevelElementAnimationIfNeeded(x, y, element);
7607         PlayLevelSoundAction(x, y, ACTION_WAITING);
7608       }
7609       else if (element == EL_SP_ELECTRON)
7610         DrawLevelElementAnimationIfNeeded(x, y, element);
7611       else if (element == EL_DRAGON)
7612       {
7613         int i;
7614         int dir = MovDir[x][y];
7615         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7616         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7617         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7618                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7619                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7620                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7621         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7622
7623         GfxAction[x][y] = ACTION_ATTACKING;
7624
7625         if (IS_PLAYER(x, y))
7626           DrawPlayerField(x, y);
7627         else
7628           TEST_DrawLevelField(x, y);
7629
7630         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7631
7632         for (i = 1; i <= 3; i++)
7633         {
7634           int xx = x + i * dx;
7635           int yy = y + i * dy;
7636           int sx = SCREENX(xx);
7637           int sy = SCREENY(yy);
7638           int flame_graphic = graphic + (i - 1);
7639
7640           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7641             break;
7642
7643           if (MovDelay[x][y])
7644           {
7645             int flamed = MovingOrBlocked2Element(xx, yy);
7646
7647             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7648               Bang(xx, yy);
7649             else
7650               RemoveMovingField(xx, yy);
7651
7652             ChangeDelay[xx][yy] = 0;
7653
7654             Feld[xx][yy] = EL_FLAMES;
7655
7656             if (IN_SCR_FIELD(sx, sy))
7657             {
7658               TEST_DrawLevelFieldCrumbled(xx, yy);
7659               DrawGraphic(sx, sy, flame_graphic, frame);
7660             }
7661           }
7662           else
7663           {
7664             if (Feld[xx][yy] == EL_FLAMES)
7665               Feld[xx][yy] = EL_EMPTY;
7666             TEST_DrawLevelField(xx, yy);
7667           }
7668         }
7669       }
7670
7671       if (MovDelay[x][y])       /* element still has to wait some time */
7672       {
7673         PlayLevelSoundAction(x, y, ACTION_WAITING);
7674
7675         return;
7676       }
7677     }
7678
7679     /* now make next step */
7680
7681     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7682
7683     if (DONT_COLLIDE_WITH(element) &&
7684         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7685         !PLAYER_ENEMY_PROTECTED(newx, newy))
7686     {
7687       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7688
7689       return;
7690     }
7691
7692     else if (CAN_MOVE_INTO_ACID(element) &&
7693              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7694              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7695              (MovDir[x][y] == MV_DOWN ||
7696               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7697     {
7698       SplashAcid(newx, newy);
7699       Store[x][y] = EL_ACID;
7700     }
7701     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7702     {
7703       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7704           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7705           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7706           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7707       {
7708         RemoveField(x, y);
7709         TEST_DrawLevelField(x, y);
7710
7711         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7712         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7713           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7714
7715         local_player->friends_still_needed--;
7716         if (!local_player->friends_still_needed &&
7717             !local_player->GameOver && AllPlayersGone)
7718           PlayerWins(local_player);
7719
7720         return;
7721       }
7722       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7723       {
7724         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7725           TEST_DrawLevelField(newx, newy);
7726         else
7727           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7728       }
7729       else if (!IS_FREE(newx, newy))
7730       {
7731         GfxAction[x][y] = ACTION_WAITING;
7732
7733         if (IS_PLAYER(x, y))
7734           DrawPlayerField(x, y);
7735         else
7736           TEST_DrawLevelField(x, y);
7737
7738         return;
7739       }
7740     }
7741     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7742     {
7743       if (IS_FOOD_PIG(Feld[newx][newy]))
7744       {
7745         if (IS_MOVING(newx, newy))
7746           RemoveMovingField(newx, newy);
7747         else
7748         {
7749           Feld[newx][newy] = EL_EMPTY;
7750           TEST_DrawLevelField(newx, newy);
7751         }
7752
7753         PlayLevelSound(x, y, SND_PIG_DIGGING);
7754       }
7755       else if (!IS_FREE(newx, newy))
7756       {
7757         if (IS_PLAYER(x, y))
7758           DrawPlayerField(x, y);
7759         else
7760           TEST_DrawLevelField(x, y);
7761
7762         return;
7763       }
7764     }
7765     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7766     {
7767       if (Store[x][y] != EL_EMPTY)
7768       {
7769         boolean can_clone = FALSE;
7770         int xx, yy;
7771
7772         /* check if element to clone is still there */
7773         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7774         {
7775           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7776           {
7777             can_clone = TRUE;
7778
7779             break;
7780           }
7781         }
7782
7783         /* cannot clone or target field not free anymore -- do not clone */
7784         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7785           Store[x][y] = EL_EMPTY;
7786       }
7787
7788       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7789       {
7790         if (IS_MV_DIAGONAL(MovDir[x][y]))
7791         {
7792           int diagonal_move_dir = MovDir[x][y];
7793           int stored = Store[x][y];
7794           int change_delay = 8;
7795           int graphic;
7796
7797           /* android is moving diagonally */
7798
7799           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7800
7801           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7802           GfxElement[x][y] = EL_EMC_ANDROID;
7803           GfxAction[x][y] = ACTION_SHRINKING;
7804           GfxDir[x][y] = diagonal_move_dir;
7805           ChangeDelay[x][y] = change_delay;
7806
7807           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7808                                    GfxDir[x][y]);
7809
7810           DrawLevelGraphicAnimation(x, y, graphic);
7811           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7812
7813           if (Feld[newx][newy] == EL_ACID)
7814           {
7815             SplashAcid(newx, newy);
7816
7817             return;
7818           }
7819
7820           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7821
7822           Store[newx][newy] = EL_EMC_ANDROID;
7823           GfxElement[newx][newy] = EL_EMC_ANDROID;
7824           GfxAction[newx][newy] = ACTION_GROWING;
7825           GfxDir[newx][newy] = diagonal_move_dir;
7826           ChangeDelay[newx][newy] = change_delay;
7827
7828           graphic = el_act_dir2img(GfxElement[newx][newy],
7829                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7830
7831           DrawLevelGraphicAnimation(newx, newy, graphic);
7832           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7833
7834           return;
7835         }
7836         else
7837         {
7838           Feld[newx][newy] = EL_EMPTY;
7839           TEST_DrawLevelField(newx, newy);
7840
7841           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7842         }
7843       }
7844       else if (!IS_FREE(newx, newy))
7845       {
7846         return;
7847       }
7848     }
7849     else if (IS_CUSTOM_ELEMENT(element) &&
7850              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7851     {
7852       if (!DigFieldByCE(newx, newy, element))
7853         return;
7854
7855       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7856       {
7857         RunnerVisit[x][y] = FrameCounter;
7858         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7859       }
7860     }
7861     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7862     {
7863       if (!IS_FREE(newx, newy))
7864       {
7865         if (IS_PLAYER(x, y))
7866           DrawPlayerField(x, y);
7867         else
7868           TEST_DrawLevelField(x, y);
7869
7870         return;
7871       }
7872       else
7873       {
7874         boolean wanna_flame = !RND(10);
7875         int dx = newx - x, dy = newy - y;
7876         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7877         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7878         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7879                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7880         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7881                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7882
7883         if ((wanna_flame ||
7884              IS_CLASSIC_ENEMY(element1) ||
7885              IS_CLASSIC_ENEMY(element2)) &&
7886             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7887             element1 != EL_FLAMES && element2 != EL_FLAMES)
7888         {
7889           ResetGfxAnimation(x, y);
7890           GfxAction[x][y] = ACTION_ATTACKING;
7891
7892           if (IS_PLAYER(x, y))
7893             DrawPlayerField(x, y);
7894           else
7895             TEST_DrawLevelField(x, y);
7896
7897           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7898
7899           MovDelay[x][y] = 50;
7900
7901           Feld[newx][newy] = EL_FLAMES;
7902           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7903             Feld[newx1][newy1] = EL_FLAMES;
7904           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7905             Feld[newx2][newy2] = EL_FLAMES;
7906
7907           return;
7908         }
7909       }
7910     }
7911     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7912              Feld[newx][newy] == EL_DIAMOND)
7913     {
7914       if (IS_MOVING(newx, newy))
7915         RemoveMovingField(newx, newy);
7916       else
7917       {
7918         Feld[newx][newy] = EL_EMPTY;
7919         TEST_DrawLevelField(newx, newy);
7920       }
7921
7922       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7923     }
7924     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7925              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7926     {
7927       if (AmoebaNr[newx][newy])
7928       {
7929         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7930         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7931             Feld[newx][newy] == EL_BD_AMOEBA)
7932           AmoebaCnt[AmoebaNr[newx][newy]]--;
7933       }
7934
7935       if (IS_MOVING(newx, newy))
7936       {
7937         RemoveMovingField(newx, newy);
7938       }
7939       else
7940       {
7941         Feld[newx][newy] = EL_EMPTY;
7942         TEST_DrawLevelField(newx, newy);
7943       }
7944
7945       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7946     }
7947     else if ((element == EL_PACMAN || element == EL_MOLE)
7948              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7949     {
7950       if (AmoebaNr[newx][newy])
7951       {
7952         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7953         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7954             Feld[newx][newy] == EL_BD_AMOEBA)
7955           AmoebaCnt[AmoebaNr[newx][newy]]--;
7956       }
7957
7958       if (element == EL_MOLE)
7959       {
7960         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7961         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7962
7963         ResetGfxAnimation(x, y);
7964         GfxAction[x][y] = ACTION_DIGGING;
7965         TEST_DrawLevelField(x, y);
7966
7967         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7968
7969         return;                         /* wait for shrinking amoeba */
7970       }
7971       else      /* element == EL_PACMAN */
7972       {
7973         Feld[newx][newy] = EL_EMPTY;
7974         TEST_DrawLevelField(newx, newy);
7975         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7976       }
7977     }
7978     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7979              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7980               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7981     {
7982       /* wait for shrinking amoeba to completely disappear */
7983       return;
7984     }
7985     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7986     {
7987       /* object was running against a wall */
7988
7989       TurnRound(x, y);
7990
7991       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7992         DrawLevelElementAnimation(x, y, element);
7993
7994       if (DONT_TOUCH(element))
7995         TestIfBadThingTouchesPlayer(x, y);
7996
7997       return;
7998     }
7999
8000     InitMovingField(x, y, MovDir[x][y]);
8001
8002     PlayLevelSoundAction(x, y, ACTION_MOVING);
8003   }
8004
8005   if (MovDir[x][y])
8006     ContinueMoving(x, y);
8007 }
8008
8009 void ContinueMoving(int x, int y)
8010 {
8011   int element = Feld[x][y];
8012   struct ElementInfo *ei = &element_info[element];
8013   int direction = MovDir[x][y];
8014   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8015   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8016   int newx = x + dx, newy = y + dy;
8017   int stored = Store[x][y];
8018   int stored_new = Store[newx][newy];
8019   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8020   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8021   boolean last_line = (newy == lev_fieldy - 1);
8022
8023   MovPos[x][y] += getElementMoveStepsize(x, y);
8024
8025   if (pushed_by_player) /* special case: moving object pushed by player */
8026     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8027
8028   if (ABS(MovPos[x][y]) < TILEX)
8029   {
8030     TEST_DrawLevelField(x, y);
8031
8032     return;     /* element is still moving */
8033   }
8034
8035   /* element reached destination field */
8036
8037   Feld[x][y] = EL_EMPTY;
8038   Feld[newx][newy] = element;
8039   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8040
8041   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8042   {
8043     element = Feld[newx][newy] = EL_ACID;
8044   }
8045   else if (element == EL_MOLE)
8046   {
8047     Feld[x][y] = EL_SAND;
8048
8049     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8050   }
8051   else if (element == EL_QUICKSAND_FILLING)
8052   {
8053     element = Feld[newx][newy] = get_next_element(element);
8054     Store[newx][newy] = Store[x][y];
8055   }
8056   else if (element == EL_QUICKSAND_EMPTYING)
8057   {
8058     Feld[x][y] = get_next_element(element);
8059     element = Feld[newx][newy] = Store[x][y];
8060   }
8061   else if (element == EL_QUICKSAND_FAST_FILLING)
8062   {
8063     element = Feld[newx][newy] = get_next_element(element);
8064     Store[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8067   {
8068     Feld[x][y] = get_next_element(element);
8069     element = Feld[newx][newy] = Store[x][y];
8070   }
8071   else if (element == EL_MAGIC_WALL_FILLING)
8072   {
8073     element = Feld[newx][newy] = get_next_element(element);
8074     if (!game.magic_wall_active)
8075       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8076     Store[newx][newy] = Store[x][y];
8077   }
8078   else if (element == EL_MAGIC_WALL_EMPTYING)
8079   {
8080     Feld[x][y] = get_next_element(element);
8081     if (!game.magic_wall_active)
8082       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8083     element = Feld[newx][newy] = Store[x][y];
8084
8085     InitField(newx, newy, FALSE);
8086   }
8087   else if (element == EL_BD_MAGIC_WALL_FILLING)
8088   {
8089     element = Feld[newx][newy] = get_next_element(element);
8090     if (!game.magic_wall_active)
8091       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8092     Store[newx][newy] = Store[x][y];
8093   }
8094   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8095   {
8096     Feld[x][y] = get_next_element(element);
8097     if (!game.magic_wall_active)
8098       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8099     element = Feld[newx][newy] = Store[x][y];
8100
8101     InitField(newx, newy, FALSE);
8102   }
8103   else if (element == EL_DC_MAGIC_WALL_FILLING)
8104   {
8105     element = Feld[newx][newy] = get_next_element(element);
8106     if (!game.magic_wall_active)
8107       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8108     Store[newx][newy] = Store[x][y];
8109   }
8110   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8111   {
8112     Feld[x][y] = get_next_element(element);
8113     if (!game.magic_wall_active)
8114       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8115     element = Feld[newx][newy] = Store[x][y];
8116
8117     InitField(newx, newy, FALSE);
8118   }
8119   else if (element == EL_AMOEBA_DROPPING)
8120   {
8121     Feld[x][y] = get_next_element(element);
8122     element = Feld[newx][newy] = Store[x][y];
8123   }
8124   else if (element == EL_SOKOBAN_OBJECT)
8125   {
8126     if (Back[x][y])
8127       Feld[x][y] = Back[x][y];
8128
8129     if (Back[newx][newy])
8130       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8131
8132     Back[x][y] = Back[newx][newy] = 0;
8133   }
8134
8135   Store[x][y] = EL_EMPTY;
8136   MovPos[x][y] = 0;
8137   MovDir[x][y] = 0;
8138   MovDelay[x][y] = 0;
8139
8140   MovDelay[newx][newy] = 0;
8141
8142   if (CAN_CHANGE_OR_HAS_ACTION(element))
8143   {
8144     /* copy element change control values to new field */
8145     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8146     ChangePage[newx][newy]  = ChangePage[x][y];
8147     ChangeCount[newx][newy] = ChangeCount[x][y];
8148     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8149   }
8150
8151   CustomValue[newx][newy] = CustomValue[x][y];
8152
8153   ChangeDelay[x][y] = 0;
8154   ChangePage[x][y] = -1;
8155   ChangeCount[x][y] = 0;
8156   ChangeEvent[x][y] = -1;
8157
8158   CustomValue[x][y] = 0;
8159
8160   /* copy animation control values to new field */
8161   GfxFrame[newx][newy]  = GfxFrame[x][y];
8162   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8163   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8164   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8165
8166   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8167
8168   /* some elements can leave other elements behind after moving */
8169   if (ei->move_leave_element != EL_EMPTY &&
8170       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8171       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8172   {
8173     int move_leave_element = ei->move_leave_element;
8174
8175     /* this makes it possible to leave the removed element again */
8176     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8177       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8178
8179     Feld[x][y] = move_leave_element;
8180
8181     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8182       MovDir[x][y] = direction;
8183
8184     InitField(x, y, FALSE);
8185
8186     if (GFX_CRUMBLED(Feld[x][y]))
8187       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8188
8189     if (ELEM_IS_PLAYER(move_leave_element))
8190       RelocatePlayer(x, y, move_leave_element);
8191   }
8192
8193   /* do this after checking for left-behind element */
8194   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8195
8196   if (!CAN_MOVE(element) ||
8197       (CAN_FALL(element) && direction == MV_DOWN &&
8198        (element == EL_SPRING ||
8199         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8200         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8201     GfxDir[x][y] = MovDir[newx][newy] = 0;
8202
8203   TEST_DrawLevelField(x, y);
8204   TEST_DrawLevelField(newx, newy);
8205
8206   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8207
8208   /* prevent pushed element from moving on in pushed direction */
8209   if (pushed_by_player && CAN_MOVE(element) &&
8210       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8211       !(element_info[element].move_pattern & direction))
8212     TurnRound(newx, newy);
8213
8214   /* prevent elements on conveyor belt from moving on in last direction */
8215   if (pushed_by_conveyor && CAN_FALL(element) &&
8216       direction & MV_HORIZONTAL)
8217     MovDir[newx][newy] = 0;
8218
8219   if (!pushed_by_player)
8220   {
8221     int nextx = newx + dx, nexty = newy + dy;
8222     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8223
8224     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8225
8226     if (CAN_FALL(element) && direction == MV_DOWN)
8227       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8228
8229     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8230       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8231
8232     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8233       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8234   }
8235
8236   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8237   {
8238     TestIfBadThingTouchesPlayer(newx, newy);
8239     TestIfBadThingTouchesFriend(newx, newy);
8240
8241     if (!IS_CUSTOM_ELEMENT(element))
8242       TestIfBadThingTouchesOtherBadThing(newx, newy);
8243   }
8244   else if (element == EL_PENGUIN)
8245     TestIfFriendTouchesBadThing(newx, newy);
8246
8247   if (DONT_GET_HIT_BY(element))
8248   {
8249     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8250   }
8251
8252   /* give the player one last chance (one more frame) to move away */
8253   if (CAN_FALL(element) && direction == MV_DOWN &&
8254       (last_line || (!IS_FREE(x, newy + 1) &&
8255                      (!IS_PLAYER(x, newy + 1) ||
8256                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8257     Impact(x, newy);
8258
8259   if (pushed_by_player && !game.use_change_when_pushing_bug)
8260   {
8261     int push_side = MV_DIR_OPPOSITE(direction);
8262     struct PlayerInfo *player = PLAYERINFO(x, y);
8263
8264     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8265                                player->index_bit, push_side);
8266     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8267                                         player->index_bit, push_side);
8268   }
8269
8270   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8271     MovDelay[newx][newy] = 1;
8272
8273   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8274
8275   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8276   TestIfElementHitsCustomElement(newx, newy, direction);
8277   TestIfPlayerTouchesCustomElement(newx, newy);
8278   TestIfElementTouchesCustomElement(newx, newy);
8279
8280   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8281       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8282     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8283                              MV_DIR_OPPOSITE(direction));
8284 }
8285
8286 int AmoebeNachbarNr(int ax, int ay)
8287 {
8288   int i;
8289   int element = Feld[ax][ay];
8290   int group_nr = 0;
8291   static int xy[4][2] =
8292   {
8293     { 0, -1 },
8294     { -1, 0 },
8295     { +1, 0 },
8296     { 0, +1 }
8297   };
8298
8299   for (i = 0; i < NUM_DIRECTIONS; i++)
8300   {
8301     int x = ax + xy[i][0];
8302     int y = ay + xy[i][1];
8303
8304     if (!IN_LEV_FIELD(x, y))
8305       continue;
8306
8307     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8308       group_nr = AmoebaNr[x][y];
8309   }
8310
8311   return group_nr;
8312 }
8313
8314 void AmoebenVereinigen(int ax, int ay)
8315 {
8316   int i, x, y, xx, yy;
8317   int new_group_nr = AmoebaNr[ax][ay];
8318   static int xy[4][2] =
8319   {
8320     { 0, -1 },
8321     { -1, 0 },
8322     { +1, 0 },
8323     { 0, +1 }
8324   };
8325
8326   if (new_group_nr == 0)
8327     return;
8328
8329   for (i = 0; i < NUM_DIRECTIONS; i++)
8330   {
8331     x = ax + xy[i][0];
8332     y = ay + xy[i][1];
8333
8334     if (!IN_LEV_FIELD(x, y))
8335       continue;
8336
8337     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8338          Feld[x][y] == EL_BD_AMOEBA ||
8339          Feld[x][y] == EL_AMOEBA_DEAD) &&
8340         AmoebaNr[x][y] != new_group_nr)
8341     {
8342       int old_group_nr = AmoebaNr[x][y];
8343
8344       if (old_group_nr == 0)
8345         return;
8346
8347       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8348       AmoebaCnt[old_group_nr] = 0;
8349       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8350       AmoebaCnt2[old_group_nr] = 0;
8351
8352       SCAN_PLAYFIELD(xx, yy)
8353       {
8354         if (AmoebaNr[xx][yy] == old_group_nr)
8355           AmoebaNr[xx][yy] = new_group_nr;
8356       }
8357     }
8358   }
8359 }
8360
8361 void AmoebeUmwandeln(int ax, int ay)
8362 {
8363   int i, x, y;
8364
8365   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8366   {
8367     int group_nr = AmoebaNr[ax][ay];
8368
8369 #ifdef DEBUG
8370     if (group_nr == 0)
8371     {
8372       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8373       printf("AmoebeUmwandeln(): This should never happen!\n");
8374       return;
8375     }
8376 #endif
8377
8378     SCAN_PLAYFIELD(x, y)
8379     {
8380       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8381       {
8382         AmoebaNr[x][y] = 0;
8383         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8384       }
8385     }
8386
8387     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8388                             SND_AMOEBA_TURNING_TO_GEM :
8389                             SND_AMOEBA_TURNING_TO_ROCK));
8390     Bang(ax, ay);
8391   }
8392   else
8393   {
8394     static int xy[4][2] =
8395     {
8396       { 0, -1 },
8397       { -1, 0 },
8398       { +1, 0 },
8399       { 0, +1 }
8400     };
8401
8402     for (i = 0; i < NUM_DIRECTIONS; i++)
8403     {
8404       x = ax + xy[i][0];
8405       y = ay + xy[i][1];
8406
8407       if (!IN_LEV_FIELD(x, y))
8408         continue;
8409
8410       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8411       {
8412         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8413                               SND_AMOEBA_TURNING_TO_GEM :
8414                               SND_AMOEBA_TURNING_TO_ROCK));
8415         Bang(x, y);
8416       }
8417     }
8418   }
8419 }
8420
8421 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8422 {
8423   int x, y;
8424   int group_nr = AmoebaNr[ax][ay];
8425   boolean done = FALSE;
8426
8427 #ifdef DEBUG
8428   if (group_nr == 0)
8429   {
8430     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8431     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8432     return;
8433   }
8434 #endif
8435
8436   SCAN_PLAYFIELD(x, y)
8437   {
8438     if (AmoebaNr[x][y] == group_nr &&
8439         (Feld[x][y] == EL_AMOEBA_DEAD ||
8440          Feld[x][y] == EL_BD_AMOEBA ||
8441          Feld[x][y] == EL_AMOEBA_GROWING))
8442     {
8443       AmoebaNr[x][y] = 0;
8444       Feld[x][y] = new_element;
8445       InitField(x, y, FALSE);
8446       TEST_DrawLevelField(x, y);
8447       done = TRUE;
8448     }
8449   }
8450
8451   if (done)
8452     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8453                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8454                             SND_BD_AMOEBA_TURNING_TO_GEM));
8455 }
8456
8457 void AmoebeWaechst(int x, int y)
8458 {
8459   static unsigned int sound_delay = 0;
8460   static unsigned int sound_delay_value = 0;
8461
8462   if (!MovDelay[x][y])          /* start new growing cycle */
8463   {
8464     MovDelay[x][y] = 7;
8465
8466     if (DelayReached(&sound_delay, sound_delay_value))
8467     {
8468       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8469       sound_delay_value = 30;
8470     }
8471   }
8472
8473   if (MovDelay[x][y])           /* wait some time before growing bigger */
8474   {
8475     MovDelay[x][y]--;
8476     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8477     {
8478       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8479                                            6 - MovDelay[x][y]);
8480
8481       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8482     }
8483
8484     if (!MovDelay[x][y])
8485     {
8486       Feld[x][y] = Store[x][y];
8487       Store[x][y] = 0;
8488       TEST_DrawLevelField(x, y);
8489     }
8490   }
8491 }
8492
8493 void AmoebaDisappearing(int x, int y)
8494 {
8495   static unsigned int sound_delay = 0;
8496   static unsigned int sound_delay_value = 0;
8497
8498   if (!MovDelay[x][y])          /* start new shrinking cycle */
8499   {
8500     MovDelay[x][y] = 7;
8501
8502     if (DelayReached(&sound_delay, sound_delay_value))
8503       sound_delay_value = 30;
8504   }
8505
8506   if (MovDelay[x][y])           /* wait some time before shrinking */
8507   {
8508     MovDelay[x][y]--;
8509     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8510     {
8511       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8512                                            6 - MovDelay[x][y]);
8513
8514       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8515     }
8516
8517     if (!MovDelay[x][y])
8518     {
8519       Feld[x][y] = EL_EMPTY;
8520       TEST_DrawLevelField(x, y);
8521
8522       /* don't let mole enter this field in this cycle;
8523          (give priority to objects falling to this field from above) */
8524       Stop[x][y] = TRUE;
8525     }
8526   }
8527 }
8528
8529 void AmoebeAbleger(int ax, int ay)
8530 {
8531   int i;
8532   int element = Feld[ax][ay];
8533   int graphic = el2img(element);
8534   int newax = ax, neway = ay;
8535   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8536   static int xy[4][2] =
8537   {
8538     { 0, -1 },
8539     { -1, 0 },
8540     { +1, 0 },
8541     { 0, +1 }
8542   };
8543
8544   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8545   {
8546     Feld[ax][ay] = EL_AMOEBA_DEAD;
8547     TEST_DrawLevelField(ax, ay);
8548     return;
8549   }
8550
8551   if (IS_ANIMATED(graphic))
8552     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8553
8554   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8555     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8556
8557   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8558   {
8559     MovDelay[ax][ay]--;
8560     if (MovDelay[ax][ay])
8561       return;
8562   }
8563
8564   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8565   {
8566     int start = RND(4);
8567     int x = ax + xy[start][0];
8568     int y = ay + xy[start][1];
8569
8570     if (!IN_LEV_FIELD(x, y))
8571       return;
8572
8573     if (IS_FREE(x, y) ||
8574         CAN_GROW_INTO(Feld[x][y]) ||
8575         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8576         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8577     {
8578       newax = x;
8579       neway = y;
8580     }
8581
8582     if (newax == ax && neway == ay)
8583       return;
8584   }
8585   else                          /* normal or "filled" (BD style) amoeba */
8586   {
8587     int start = RND(4);
8588     boolean waiting_for_player = FALSE;
8589
8590     for (i = 0; i < NUM_DIRECTIONS; i++)
8591     {
8592       int j = (start + i) % 4;
8593       int x = ax + xy[j][0];
8594       int y = ay + xy[j][1];
8595
8596       if (!IN_LEV_FIELD(x, y))
8597         continue;
8598
8599       if (IS_FREE(x, y) ||
8600           CAN_GROW_INTO(Feld[x][y]) ||
8601           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8602           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8603       {
8604         newax = x;
8605         neway = y;
8606         break;
8607       }
8608       else if (IS_PLAYER(x, y))
8609         waiting_for_player = TRUE;
8610     }
8611
8612     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8613     {
8614       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8615       {
8616         Feld[ax][ay] = EL_AMOEBA_DEAD;
8617         TEST_DrawLevelField(ax, ay);
8618         AmoebaCnt[AmoebaNr[ax][ay]]--;
8619
8620         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8621         {
8622           if (element == EL_AMOEBA_FULL)
8623             AmoebeUmwandeln(ax, ay);
8624           else if (element == EL_BD_AMOEBA)
8625             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8626         }
8627       }
8628       return;
8629     }
8630     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8631     {
8632       /* amoeba gets larger by growing in some direction */
8633
8634       int new_group_nr = AmoebaNr[ax][ay];
8635
8636 #ifdef DEBUG
8637   if (new_group_nr == 0)
8638   {
8639     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8640     printf("AmoebeAbleger(): This should never happen!\n");
8641     return;
8642   }
8643 #endif
8644
8645       AmoebaNr[newax][neway] = new_group_nr;
8646       AmoebaCnt[new_group_nr]++;
8647       AmoebaCnt2[new_group_nr]++;
8648
8649       /* if amoeba touches other amoeba(s) after growing, unify them */
8650       AmoebenVereinigen(newax, neway);
8651
8652       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8653       {
8654         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8655         return;
8656       }
8657     }
8658   }
8659
8660   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8661       (neway == lev_fieldy - 1 && newax != ax))
8662   {
8663     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8664     Store[newax][neway] = element;
8665   }
8666   else if (neway == ay || element == EL_EMC_DRIPPER)
8667   {
8668     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8669
8670     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8671   }
8672   else
8673   {
8674     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8675     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8676     Store[ax][ay] = EL_AMOEBA_DROP;
8677     ContinueMoving(ax, ay);
8678     return;
8679   }
8680
8681   TEST_DrawLevelField(newax, neway);
8682 }
8683
8684 void Life(int ax, int ay)
8685 {
8686   int x1, y1, x2, y2;
8687   int life_time = 40;
8688   int element = Feld[ax][ay];
8689   int graphic = el2img(element);
8690   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8691                          level.biomaze);
8692   boolean changed = FALSE;
8693
8694   if (IS_ANIMATED(graphic))
8695     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8696
8697   if (Stop[ax][ay])
8698     return;
8699
8700   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8701     MovDelay[ax][ay] = life_time;
8702
8703   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8704   {
8705     MovDelay[ax][ay]--;
8706     if (MovDelay[ax][ay])
8707       return;
8708   }
8709
8710   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8711   {
8712     int xx = ax+x1, yy = ay+y1;
8713     int nachbarn = 0;
8714
8715     if (!IN_LEV_FIELD(xx, yy))
8716       continue;
8717
8718     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8719     {
8720       int x = xx+x2, y = yy+y2;
8721
8722       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8723         continue;
8724
8725       if (((Feld[x][y] == element ||
8726             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8727            !Stop[x][y]) ||
8728           (IS_FREE(x, y) && Stop[x][y]))
8729         nachbarn++;
8730     }
8731
8732     if (xx == ax && yy == ay)           /* field in the middle */
8733     {
8734       if (nachbarn < life_parameter[0] ||
8735           nachbarn > life_parameter[1])
8736       {
8737         Feld[xx][yy] = EL_EMPTY;
8738         if (!Stop[xx][yy])
8739           TEST_DrawLevelField(xx, yy);
8740         Stop[xx][yy] = TRUE;
8741         changed = TRUE;
8742       }
8743     }
8744     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8745     {                                   /* free border field */
8746       if (nachbarn >= life_parameter[2] &&
8747           nachbarn <= life_parameter[3])
8748       {
8749         Feld[xx][yy] = element;
8750         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8751         if (!Stop[xx][yy])
8752           TEST_DrawLevelField(xx, yy);
8753         Stop[xx][yy] = TRUE;
8754         changed = TRUE;
8755       }
8756     }
8757   }
8758
8759   if (changed)
8760     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8761                    SND_GAME_OF_LIFE_GROWING);
8762 }
8763
8764 static void InitRobotWheel(int x, int y)
8765 {
8766   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8767 }
8768
8769 static void RunRobotWheel(int x, int y)
8770 {
8771   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8772 }
8773
8774 static void StopRobotWheel(int x, int y)
8775 {
8776   if (ZX == x && ZY == y)
8777   {
8778     ZX = ZY = -1;
8779
8780     game.robot_wheel_active = FALSE;
8781   }
8782 }
8783
8784 static void InitTimegateWheel(int x, int y)
8785 {
8786   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8787 }
8788
8789 static void RunTimegateWheel(int x, int y)
8790 {
8791   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8792 }
8793
8794 static void InitMagicBallDelay(int x, int y)
8795 {
8796   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8797 }
8798
8799 static void ActivateMagicBall(int bx, int by)
8800 {
8801   int x, y;
8802
8803   if (level.ball_random)
8804   {
8805     int pos_border = RND(8);    /* select one of the eight border elements */
8806     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8807     int xx = pos_content % 3;
8808     int yy = pos_content / 3;
8809
8810     x = bx - 1 + xx;
8811     y = by - 1 + yy;
8812
8813     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8814       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8815   }
8816   else
8817   {
8818     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8819     {
8820       int xx = x - bx + 1;
8821       int yy = y - by + 1;
8822
8823       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8824         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8825     }
8826   }
8827
8828   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8829 }
8830
8831 void CheckExit(int x, int y)
8832 {
8833   if (local_player->gems_still_needed > 0 ||
8834       local_player->sokobanfields_still_needed > 0 ||
8835       local_player->lights_still_needed > 0)
8836   {
8837     int element = Feld[x][y];
8838     int graphic = el2img(element);
8839
8840     if (IS_ANIMATED(graphic))
8841       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8842
8843     return;
8844   }
8845
8846   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8847     return;
8848
8849   Feld[x][y] = EL_EXIT_OPENING;
8850
8851   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8852 }
8853
8854 void CheckExitEM(int x, int y)
8855 {
8856   if (local_player->gems_still_needed > 0 ||
8857       local_player->sokobanfields_still_needed > 0 ||
8858       local_player->lights_still_needed > 0)
8859   {
8860     int element = Feld[x][y];
8861     int graphic = el2img(element);
8862
8863     if (IS_ANIMATED(graphic))
8864       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8865
8866     return;
8867   }
8868
8869   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8870     return;
8871
8872   Feld[x][y] = EL_EM_EXIT_OPENING;
8873
8874   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8875 }
8876
8877 void CheckExitSteel(int x, int y)
8878 {
8879   if (local_player->gems_still_needed > 0 ||
8880       local_player->sokobanfields_still_needed > 0 ||
8881       local_player->lights_still_needed > 0)
8882   {
8883     int element = Feld[x][y];
8884     int graphic = el2img(element);
8885
8886     if (IS_ANIMATED(graphic))
8887       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8888
8889     return;
8890   }
8891
8892   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8893     return;
8894
8895   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8896
8897   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8898 }
8899
8900 void CheckExitSteelEM(int x, int y)
8901 {
8902   if (local_player->gems_still_needed > 0 ||
8903       local_player->sokobanfields_still_needed > 0 ||
8904       local_player->lights_still_needed > 0)
8905   {
8906     int element = Feld[x][y];
8907     int graphic = el2img(element);
8908
8909     if (IS_ANIMATED(graphic))
8910       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8911
8912     return;
8913   }
8914
8915   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8916     return;
8917
8918   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8919
8920   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8921 }
8922
8923 void CheckExitSP(int x, int y)
8924 {
8925   if (local_player->gems_still_needed > 0)
8926   {
8927     int element = Feld[x][y];
8928     int graphic = el2img(element);
8929
8930     if (IS_ANIMATED(graphic))
8931       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8932
8933     return;
8934   }
8935
8936   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8937     return;
8938
8939   Feld[x][y] = EL_SP_EXIT_OPENING;
8940
8941   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8942 }
8943
8944 static void CloseAllOpenTimegates()
8945 {
8946   int x, y;
8947
8948   SCAN_PLAYFIELD(x, y)
8949   {
8950     int element = Feld[x][y];
8951
8952     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8953     {
8954       Feld[x][y] = EL_TIMEGATE_CLOSING;
8955
8956       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8957     }
8958   }
8959 }
8960
8961 void DrawTwinkleOnField(int x, int y)
8962 {
8963   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8964     return;
8965
8966   if (Feld[x][y] == EL_BD_DIAMOND)
8967     return;
8968
8969   if (MovDelay[x][y] == 0)      /* next animation frame */
8970     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8971
8972   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8973   {
8974     MovDelay[x][y]--;
8975
8976     DrawLevelElementAnimation(x, y, Feld[x][y]);
8977
8978     if (MovDelay[x][y] != 0)
8979     {
8980       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8981                                            10 - MovDelay[x][y]);
8982
8983       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8984     }
8985   }
8986 }
8987
8988 void MauerWaechst(int x, int y)
8989 {
8990   int delay = 6;
8991
8992   if (!MovDelay[x][y])          /* next animation frame */
8993     MovDelay[x][y] = 3 * delay;
8994
8995   if (MovDelay[x][y])           /* wait some time before next frame */
8996   {
8997     MovDelay[x][y]--;
8998
8999     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9000     {
9001       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9002       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9003
9004       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9005     }
9006
9007     if (!MovDelay[x][y])
9008     {
9009       if (MovDir[x][y] == MV_LEFT)
9010       {
9011         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9012           TEST_DrawLevelField(x - 1, y);
9013       }
9014       else if (MovDir[x][y] == MV_RIGHT)
9015       {
9016         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9017           TEST_DrawLevelField(x + 1, y);
9018       }
9019       else if (MovDir[x][y] == MV_UP)
9020       {
9021         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9022           TEST_DrawLevelField(x, y - 1);
9023       }
9024       else
9025       {
9026         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9027           TEST_DrawLevelField(x, y + 1);
9028       }
9029
9030       Feld[x][y] = Store[x][y];
9031       Store[x][y] = 0;
9032       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9033       TEST_DrawLevelField(x, y);
9034     }
9035   }
9036 }
9037
9038 void MauerAbleger(int ax, int ay)
9039 {
9040   int element = Feld[ax][ay];
9041   int graphic = el2img(element);
9042   boolean oben_frei = FALSE, unten_frei = FALSE;
9043   boolean links_frei = FALSE, rechts_frei = FALSE;
9044   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9045   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9046   boolean new_wall = FALSE;
9047
9048   if (IS_ANIMATED(graphic))
9049     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9050
9051   if (!MovDelay[ax][ay])        /* start building new wall */
9052     MovDelay[ax][ay] = 6;
9053
9054   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9055   {
9056     MovDelay[ax][ay]--;
9057     if (MovDelay[ax][ay])
9058       return;
9059   }
9060
9061   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9062     oben_frei = TRUE;
9063   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9064     unten_frei = TRUE;
9065   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9066     links_frei = TRUE;
9067   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9068     rechts_frei = TRUE;
9069
9070   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9071       element == EL_EXPANDABLE_WALL_ANY)
9072   {
9073     if (oben_frei)
9074     {
9075       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9076       Store[ax][ay-1] = element;
9077       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9078       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9079         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9080                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9081       new_wall = TRUE;
9082     }
9083     if (unten_frei)
9084     {
9085       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9086       Store[ax][ay+1] = element;
9087       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9088       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9089         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9090                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9091       new_wall = TRUE;
9092     }
9093   }
9094
9095   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9096       element == EL_EXPANDABLE_WALL_ANY ||
9097       element == EL_EXPANDABLE_WALL ||
9098       element == EL_BD_EXPANDABLE_WALL)
9099   {
9100     if (links_frei)
9101     {
9102       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9103       Store[ax-1][ay] = element;
9104       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9105       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9106         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9107                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9108       new_wall = TRUE;
9109     }
9110
9111     if (rechts_frei)
9112     {
9113       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9114       Store[ax+1][ay] = element;
9115       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9116       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9117         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9118                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9119       new_wall = TRUE;
9120     }
9121   }
9122
9123   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9124     TEST_DrawLevelField(ax, ay);
9125
9126   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9127     oben_massiv = TRUE;
9128   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9129     unten_massiv = TRUE;
9130   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9131     links_massiv = TRUE;
9132   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9133     rechts_massiv = TRUE;
9134
9135   if (((oben_massiv && unten_massiv) ||
9136        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9137        element == EL_EXPANDABLE_WALL) &&
9138       ((links_massiv && rechts_massiv) ||
9139        element == EL_EXPANDABLE_WALL_VERTICAL))
9140     Feld[ax][ay] = EL_WALL;
9141
9142   if (new_wall)
9143     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9144 }
9145
9146 void MauerAblegerStahl(int ax, int ay)
9147 {
9148   int element = Feld[ax][ay];
9149   int graphic = el2img(element);
9150   boolean oben_frei = FALSE, unten_frei = FALSE;
9151   boolean links_frei = FALSE, rechts_frei = FALSE;
9152   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9153   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9154   boolean new_wall = FALSE;
9155
9156   if (IS_ANIMATED(graphic))
9157     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9158
9159   if (!MovDelay[ax][ay])        /* start building new wall */
9160     MovDelay[ax][ay] = 6;
9161
9162   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9163   {
9164     MovDelay[ax][ay]--;
9165     if (MovDelay[ax][ay])
9166       return;
9167   }
9168
9169   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9170     oben_frei = TRUE;
9171   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9172     unten_frei = TRUE;
9173   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9174     links_frei = TRUE;
9175   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9176     rechts_frei = TRUE;
9177
9178   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9179       element == EL_EXPANDABLE_STEELWALL_ANY)
9180   {
9181     if (oben_frei)
9182     {
9183       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9184       Store[ax][ay-1] = element;
9185       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9186       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9187         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9188                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9189       new_wall = TRUE;
9190     }
9191     if (unten_frei)
9192     {
9193       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9194       Store[ax][ay+1] = element;
9195       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9196       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9197         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9198                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9199       new_wall = TRUE;
9200     }
9201   }
9202
9203   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9204       element == EL_EXPANDABLE_STEELWALL_ANY)
9205   {
9206     if (links_frei)
9207     {
9208       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9209       Store[ax-1][ay] = element;
9210       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9211       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9212         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9213                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9214       new_wall = TRUE;
9215     }
9216
9217     if (rechts_frei)
9218     {
9219       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9220       Store[ax+1][ay] = element;
9221       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9222       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9223         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9224                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9225       new_wall = TRUE;
9226     }
9227   }
9228
9229   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9230     oben_massiv = TRUE;
9231   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9232     unten_massiv = TRUE;
9233   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9234     links_massiv = TRUE;
9235   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9236     rechts_massiv = TRUE;
9237
9238   if (((oben_massiv && unten_massiv) ||
9239        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9240       ((links_massiv && rechts_massiv) ||
9241        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9242     Feld[ax][ay] = EL_STEELWALL;
9243
9244   if (new_wall)
9245     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9246 }
9247
9248 void CheckForDragon(int x, int y)
9249 {
9250   int i, j;
9251   boolean dragon_found = FALSE;
9252   static int xy[4][2] =
9253   {
9254     { 0, -1 },
9255     { -1, 0 },
9256     { +1, 0 },
9257     { 0, +1 }
9258   };
9259
9260   for (i = 0; i < NUM_DIRECTIONS; i++)
9261   {
9262     for (j = 0; j < 4; j++)
9263     {
9264       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9265
9266       if (IN_LEV_FIELD(xx, yy) &&
9267           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9268       {
9269         if (Feld[xx][yy] == EL_DRAGON)
9270           dragon_found = TRUE;
9271       }
9272       else
9273         break;
9274     }
9275   }
9276
9277   if (!dragon_found)
9278   {
9279     for (i = 0; i < NUM_DIRECTIONS; i++)
9280     {
9281       for (j = 0; j < 3; j++)
9282       {
9283         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9284   
9285         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9286         {
9287           Feld[xx][yy] = EL_EMPTY;
9288           TEST_DrawLevelField(xx, yy);
9289         }
9290         else
9291           break;
9292       }
9293     }
9294   }
9295 }
9296
9297 static void InitBuggyBase(int x, int y)
9298 {
9299   int element = Feld[x][y];
9300   int activating_delay = FRAMES_PER_SECOND / 4;
9301
9302   ChangeDelay[x][y] =
9303     (element == EL_SP_BUGGY_BASE ?
9304      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9305      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9306      activating_delay :
9307      element == EL_SP_BUGGY_BASE_ACTIVE ?
9308      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9309 }
9310
9311 static void WarnBuggyBase(int x, int y)
9312 {
9313   int i;
9314   static int xy[4][2] =
9315   {
9316     { 0, -1 },
9317     { -1, 0 },
9318     { +1, 0 },
9319     { 0, +1 }
9320   };
9321
9322   for (i = 0; i < NUM_DIRECTIONS; i++)
9323   {
9324     int xx = x + xy[i][0];
9325     int yy = y + xy[i][1];
9326
9327     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9328     {
9329       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9330
9331       break;
9332     }
9333   }
9334 }
9335
9336 static void InitTrap(int x, int y)
9337 {
9338   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9339 }
9340
9341 static void ActivateTrap(int x, int y)
9342 {
9343   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9344 }
9345
9346 static void ChangeActiveTrap(int x, int y)
9347 {
9348   int graphic = IMG_TRAP_ACTIVE;
9349
9350   /* if new animation frame was drawn, correct crumbled sand border */
9351   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9352     TEST_DrawLevelFieldCrumbled(x, y);
9353 }
9354
9355 static int getSpecialActionElement(int element, int number, int base_element)
9356 {
9357   return (element != EL_EMPTY ? element :
9358           number != -1 ? base_element + number - 1 :
9359           EL_EMPTY);
9360 }
9361
9362 static int getModifiedActionNumber(int value_old, int operator, int operand,
9363                                    int value_min, int value_max)
9364 {
9365   int value_new = (operator == CA_MODE_SET      ? operand :
9366                    operator == CA_MODE_ADD      ? value_old + operand :
9367                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9368                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9369                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9370                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9371                    value_old);
9372
9373   return (value_new < value_min ? value_min :
9374           value_new > value_max ? value_max :
9375           value_new);
9376 }
9377
9378 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9379 {
9380   struct ElementInfo *ei = &element_info[element];
9381   struct ElementChangeInfo *change = &ei->change_page[page];
9382   int target_element = change->target_element;
9383   int action_type = change->action_type;
9384   int action_mode = change->action_mode;
9385   int action_arg = change->action_arg;
9386   int action_element = change->action_element;
9387   int i;
9388
9389   if (!change->has_action)
9390     return;
9391
9392   /* ---------- determine action paramater values -------------------------- */
9393
9394   int level_time_value =
9395     (level.time > 0 ? TimeLeft :
9396      TimePlayed);
9397
9398   int action_arg_element_raw =
9399     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9400      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9401      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9402      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9403      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9404      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9405      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9406      EL_EMPTY);
9407   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9408
9409   int action_arg_direction =
9410     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9411      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9412      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9413      change->actual_trigger_side :
9414      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9415      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9416      MV_NONE);
9417
9418   int action_arg_number_min =
9419     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9420      CA_ARG_MIN);
9421
9422   int action_arg_number_max =
9423     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9424      action_type == CA_SET_LEVEL_GEMS ? 999 :
9425      action_type == CA_SET_LEVEL_TIME ? 9999 :
9426      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9427      action_type == CA_SET_CE_VALUE ? 9999 :
9428      action_type == CA_SET_CE_SCORE ? 9999 :
9429      CA_ARG_MAX);
9430
9431   int action_arg_number_reset =
9432     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9433      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9434      action_type == CA_SET_LEVEL_TIME ? level.time :
9435      action_type == CA_SET_LEVEL_SCORE ? 0 :
9436      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9437      action_type == CA_SET_CE_SCORE ? 0 :
9438      0);
9439
9440   int action_arg_number =
9441     (action_arg <= CA_ARG_MAX ? action_arg :
9442      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9443      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9444      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9445      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9446      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9447      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9448      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9449      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9450      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9451      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9452      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9453      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9454      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9455      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9456      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9457      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9458      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9459      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9460      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9461      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9462      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9463      -1);
9464
9465   int action_arg_number_old =
9466     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9467      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9468      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9469      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9470      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9471      0);
9472
9473   int action_arg_number_new =
9474     getModifiedActionNumber(action_arg_number_old,
9475                             action_mode, action_arg_number,
9476                             action_arg_number_min, action_arg_number_max);
9477
9478   int trigger_player_bits =
9479     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9480      change->actual_trigger_player_bits : change->trigger_player);
9481
9482   int action_arg_player_bits =
9483     (action_arg >= CA_ARG_PLAYER_1 &&
9484      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9485      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9486      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9487      PLAYER_BITS_ANY);
9488
9489   /* ---------- execute action  -------------------------------------------- */
9490
9491   switch (action_type)
9492   {
9493     case CA_NO_ACTION:
9494     {
9495       return;
9496     }
9497
9498     /* ---------- level actions  ------------------------------------------- */
9499
9500     case CA_RESTART_LEVEL:
9501     {
9502       game.restart_level = TRUE;
9503
9504       break;
9505     }
9506
9507     case CA_SHOW_ENVELOPE:
9508     {
9509       int element = getSpecialActionElement(action_arg_element,
9510                                             action_arg_number, EL_ENVELOPE_1);
9511
9512       if (IS_ENVELOPE(element))
9513         local_player->show_envelope = element;
9514
9515       break;
9516     }
9517
9518     case CA_SET_LEVEL_TIME:
9519     {
9520       if (level.time > 0)       /* only modify limited time value */
9521       {
9522         TimeLeft = action_arg_number_new;
9523
9524         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9525
9526         DisplayGameControlValues();
9527
9528         if (!TimeLeft && setup.time_limit)
9529           for (i = 0; i < MAX_PLAYERS; i++)
9530             KillPlayer(&stored_player[i]);
9531       }
9532
9533       break;
9534     }
9535
9536     case CA_SET_LEVEL_SCORE:
9537     {
9538       local_player->score = action_arg_number_new;
9539
9540       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9541
9542       DisplayGameControlValues();
9543
9544       break;
9545     }
9546
9547     case CA_SET_LEVEL_GEMS:
9548     {
9549       local_player->gems_still_needed = action_arg_number_new;
9550
9551       game_panel_controls[GAME_PANEL_GEMS].value =
9552         local_player->gems_still_needed;
9553
9554       DisplayGameControlValues();
9555
9556       break;
9557     }
9558
9559     case CA_SET_LEVEL_WIND:
9560     {
9561       game.wind_direction = action_arg_direction;
9562
9563       break;
9564     }
9565
9566     case CA_SET_LEVEL_RANDOM_SEED:
9567     {
9568       /* ensure that setting a new random seed while playing is predictable */
9569       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9570
9571       break;
9572     }
9573
9574     /* ---------- player actions  ------------------------------------------ */
9575
9576     case CA_MOVE_PLAYER:
9577     {
9578       /* automatically move to the next field in specified direction */
9579       for (i = 0; i < MAX_PLAYERS; i++)
9580         if (trigger_player_bits & (1 << i))
9581           stored_player[i].programmed_action = action_arg_direction;
9582
9583       break;
9584     }
9585
9586     case CA_EXIT_PLAYER:
9587     {
9588       for (i = 0; i < MAX_PLAYERS; i++)
9589         if (action_arg_player_bits & (1 << i))
9590           PlayerWins(&stored_player[i]);
9591
9592       break;
9593     }
9594
9595     case CA_KILL_PLAYER:
9596     {
9597       for (i = 0; i < MAX_PLAYERS; i++)
9598         if (action_arg_player_bits & (1 << i))
9599           KillPlayer(&stored_player[i]);
9600
9601       break;
9602     }
9603
9604     case CA_SET_PLAYER_KEYS:
9605     {
9606       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9607       int element = getSpecialActionElement(action_arg_element,
9608                                             action_arg_number, EL_KEY_1);
9609
9610       if (IS_KEY(element))
9611       {
9612         for (i = 0; i < MAX_PLAYERS; i++)
9613         {
9614           if (trigger_player_bits & (1 << i))
9615           {
9616             stored_player[i].key[KEY_NR(element)] = key_state;
9617
9618             DrawGameDoorValues();
9619           }
9620         }
9621       }
9622
9623       break;
9624     }
9625
9626     case CA_SET_PLAYER_SPEED:
9627     {
9628       for (i = 0; i < MAX_PLAYERS; i++)
9629       {
9630         if (trigger_player_bits & (1 << i))
9631         {
9632           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9633
9634           if (action_arg == CA_ARG_SPEED_FASTER &&
9635               stored_player[i].cannot_move)
9636           {
9637             action_arg_number = STEPSIZE_VERY_SLOW;
9638           }
9639           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9640                    action_arg == CA_ARG_SPEED_FASTER)
9641           {
9642             action_arg_number = 2;
9643             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9644                            CA_MODE_MULTIPLY);
9645           }
9646           else if (action_arg == CA_ARG_NUMBER_RESET)
9647           {
9648             action_arg_number = level.initial_player_stepsize[i];
9649           }
9650
9651           move_stepsize =
9652             getModifiedActionNumber(move_stepsize,
9653                                     action_mode,
9654                                     action_arg_number,
9655                                     action_arg_number_min,
9656                                     action_arg_number_max);
9657
9658           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9659         }
9660       }
9661
9662       break;
9663     }
9664
9665     case CA_SET_PLAYER_SHIELD:
9666     {
9667       for (i = 0; i < MAX_PLAYERS; i++)
9668       {
9669         if (trigger_player_bits & (1 << i))
9670         {
9671           if (action_arg == CA_ARG_SHIELD_OFF)
9672           {
9673             stored_player[i].shield_normal_time_left = 0;
9674             stored_player[i].shield_deadly_time_left = 0;
9675           }
9676           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9677           {
9678             stored_player[i].shield_normal_time_left = 999999;
9679           }
9680           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9681           {
9682             stored_player[i].shield_normal_time_left = 999999;
9683             stored_player[i].shield_deadly_time_left = 999999;
9684           }
9685         }
9686       }
9687
9688       break;
9689     }
9690
9691     case CA_SET_PLAYER_GRAVITY:
9692     {
9693       for (i = 0; i < MAX_PLAYERS; i++)
9694       {
9695         if (trigger_player_bits & (1 << i))
9696         {
9697           stored_player[i].gravity =
9698             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9699              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9700              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9701              stored_player[i].gravity);
9702         }
9703       }
9704
9705       break;
9706     }
9707
9708     case CA_SET_PLAYER_ARTWORK:
9709     {
9710       for (i = 0; i < MAX_PLAYERS; i++)
9711       {
9712         if (trigger_player_bits & (1 << i))
9713         {
9714           int artwork_element = action_arg_element;
9715
9716           if (action_arg == CA_ARG_ELEMENT_RESET)
9717             artwork_element =
9718               (level.use_artwork_element[i] ? level.artwork_element[i] :
9719                stored_player[i].element_nr);
9720
9721           if (stored_player[i].artwork_element != artwork_element)
9722             stored_player[i].Frame = 0;
9723
9724           stored_player[i].artwork_element = artwork_element;
9725
9726           SetPlayerWaiting(&stored_player[i], FALSE);
9727
9728           /* set number of special actions for bored and sleeping animation */
9729           stored_player[i].num_special_action_bored =
9730             get_num_special_action(artwork_element,
9731                                    ACTION_BORING_1, ACTION_BORING_LAST);
9732           stored_player[i].num_special_action_sleeping =
9733             get_num_special_action(artwork_element,
9734                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9735         }
9736       }
9737
9738       break;
9739     }
9740
9741     case CA_SET_PLAYER_INVENTORY:
9742     {
9743       for (i = 0; i < MAX_PLAYERS; i++)
9744       {
9745         struct PlayerInfo *player = &stored_player[i];
9746         int j, k;
9747
9748         if (trigger_player_bits & (1 << i))
9749         {
9750           int inventory_element = action_arg_element;
9751
9752           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9753               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9754               action_arg == CA_ARG_ELEMENT_ACTION)
9755           {
9756             int element = inventory_element;
9757             int collect_count = element_info[element].collect_count_initial;
9758
9759             if (!IS_CUSTOM_ELEMENT(element))
9760               collect_count = 1;
9761
9762             if (collect_count == 0)
9763               player->inventory_infinite_element = element;
9764             else
9765               for (k = 0; k < collect_count; k++)
9766                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9767                   player->inventory_element[player->inventory_size++] =
9768                     element;
9769           }
9770           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9771                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9772                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9773           {
9774             if (player->inventory_infinite_element != EL_UNDEFINED &&
9775                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9776                                      action_arg_element_raw))
9777               player->inventory_infinite_element = EL_UNDEFINED;
9778
9779             for (k = 0, j = 0; j < player->inventory_size; j++)
9780             {
9781               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9782                                         action_arg_element_raw))
9783                 player->inventory_element[k++] = player->inventory_element[j];
9784             }
9785
9786             player->inventory_size = k;
9787           }
9788           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9789           {
9790             if (player->inventory_size > 0)
9791             {
9792               for (j = 0; j < player->inventory_size - 1; j++)
9793                 player->inventory_element[j] = player->inventory_element[j + 1];
9794
9795               player->inventory_size--;
9796             }
9797           }
9798           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9799           {
9800             if (player->inventory_size > 0)
9801               player->inventory_size--;
9802           }
9803           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9804           {
9805             player->inventory_infinite_element = EL_UNDEFINED;
9806             player->inventory_size = 0;
9807           }
9808           else if (action_arg == CA_ARG_INVENTORY_RESET)
9809           {
9810             player->inventory_infinite_element = EL_UNDEFINED;
9811             player->inventory_size = 0;
9812
9813             if (level.use_initial_inventory[i])
9814             {
9815               for (j = 0; j < level.initial_inventory_size[i]; j++)
9816               {
9817                 int element = level.initial_inventory_content[i][j];
9818                 int collect_count = element_info[element].collect_count_initial;
9819
9820                 if (!IS_CUSTOM_ELEMENT(element))
9821                   collect_count = 1;
9822
9823                 if (collect_count == 0)
9824                   player->inventory_infinite_element = element;
9825                 else
9826                   for (k = 0; k < collect_count; k++)
9827                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9828                       player->inventory_element[player->inventory_size++] =
9829                         element;
9830               }
9831             }
9832           }
9833         }
9834       }
9835
9836       break;
9837     }
9838
9839     /* ---------- CE actions  ---------------------------------------------- */
9840
9841     case CA_SET_CE_VALUE:
9842     {
9843       int last_ce_value = CustomValue[x][y];
9844
9845       CustomValue[x][y] = action_arg_number_new;
9846
9847       if (CustomValue[x][y] != last_ce_value)
9848       {
9849         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9850         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9851
9852         if (CustomValue[x][y] == 0)
9853         {
9854           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9855           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9856         }
9857       }
9858
9859       break;
9860     }
9861
9862     case CA_SET_CE_SCORE:
9863     {
9864       int last_ce_score = ei->collect_score;
9865
9866       ei->collect_score = action_arg_number_new;
9867
9868       if (ei->collect_score != last_ce_score)
9869       {
9870         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9871         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9872
9873         if (ei->collect_score == 0)
9874         {
9875           int xx, yy;
9876
9877           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9878           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9879
9880           /*
9881             This is a very special case that seems to be a mixture between
9882             CheckElementChange() and CheckTriggeredElementChange(): while
9883             the first one only affects single elements that are triggered
9884             directly, the second one affects multiple elements in the playfield
9885             that are triggered indirectly by another element. This is a third
9886             case: Changing the CE score always affects multiple identical CEs,
9887             so every affected CE must be checked, not only the single CE for
9888             which the CE score was changed in the first place (as every instance
9889             of that CE shares the same CE score, and therefore also can change)!
9890           */
9891           SCAN_PLAYFIELD(xx, yy)
9892           {
9893             if (Feld[xx][yy] == element)
9894               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9895                                  CE_SCORE_GETS_ZERO);
9896           }
9897         }
9898       }
9899
9900       break;
9901     }
9902
9903     case CA_SET_CE_ARTWORK:
9904     {
9905       int artwork_element = action_arg_element;
9906       boolean reset_frame = FALSE;
9907       int xx, yy;
9908
9909       if (action_arg == CA_ARG_ELEMENT_RESET)
9910         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9911                            element);
9912
9913       if (ei->gfx_element != artwork_element)
9914         reset_frame = TRUE;
9915
9916       ei->gfx_element = artwork_element;
9917
9918       SCAN_PLAYFIELD(xx, yy)
9919       {
9920         if (Feld[xx][yy] == element)
9921         {
9922           if (reset_frame)
9923           {
9924             ResetGfxAnimation(xx, yy);
9925             ResetRandomAnimationValue(xx, yy);
9926           }
9927
9928           TEST_DrawLevelField(xx, yy);
9929         }
9930       }
9931
9932       break;
9933     }
9934
9935     /* ---------- engine actions  ------------------------------------------ */
9936
9937     case CA_SET_ENGINE_SCAN_MODE:
9938     {
9939       InitPlayfieldScanMode(action_arg);
9940
9941       break;
9942     }
9943
9944     default:
9945       break;
9946   }
9947 }
9948
9949 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9950 {
9951   int old_element = Feld[x][y];
9952   int new_element = GetElementFromGroupElement(element);
9953   int previous_move_direction = MovDir[x][y];
9954   int last_ce_value = CustomValue[x][y];
9955   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9956   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9957   boolean add_player_onto_element = (new_element_is_player &&
9958                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9959                                      IS_WALKABLE(old_element));
9960
9961   if (!add_player_onto_element)
9962   {
9963     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9964       RemoveMovingField(x, y);
9965     else
9966       RemoveField(x, y);
9967
9968     Feld[x][y] = new_element;
9969
9970     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9971       MovDir[x][y] = previous_move_direction;
9972
9973     if (element_info[new_element].use_last_ce_value)
9974       CustomValue[x][y] = last_ce_value;
9975
9976     InitField_WithBug1(x, y, FALSE);
9977
9978     new_element = Feld[x][y];   /* element may have changed */
9979
9980     ResetGfxAnimation(x, y);
9981     ResetRandomAnimationValue(x, y);
9982
9983     TEST_DrawLevelField(x, y);
9984
9985     if (GFX_CRUMBLED(new_element))
9986       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9987   }
9988
9989   /* check if element under the player changes from accessible to unaccessible
9990      (needed for special case of dropping element which then changes) */
9991   /* (must be checked after creating new element for walkable group elements) */
9992   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9993       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9994   {
9995     Bang(x, y);
9996
9997     return;
9998   }
9999
10000   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10001   if (new_element_is_player)
10002     RelocatePlayer(x, y, new_element);
10003
10004   if (is_change)
10005     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10006
10007   TestIfBadThingTouchesPlayer(x, y);
10008   TestIfPlayerTouchesCustomElement(x, y);
10009   TestIfElementTouchesCustomElement(x, y);
10010 }
10011
10012 static void CreateField(int x, int y, int element)
10013 {
10014   CreateFieldExt(x, y, element, FALSE);
10015 }
10016
10017 static void CreateElementFromChange(int x, int y, int element)
10018 {
10019   element = GET_VALID_RUNTIME_ELEMENT(element);
10020
10021   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10022   {
10023     int old_element = Feld[x][y];
10024
10025     /* prevent changed element from moving in same engine frame
10026        unless both old and new element can either fall or move */
10027     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10028         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10029       Stop[x][y] = TRUE;
10030   }
10031
10032   CreateFieldExt(x, y, element, TRUE);
10033 }
10034
10035 static boolean ChangeElement(int x, int y, int element, int page)
10036 {
10037   struct ElementInfo *ei = &element_info[element];
10038   struct ElementChangeInfo *change = &ei->change_page[page];
10039   int ce_value = CustomValue[x][y];
10040   int ce_score = ei->collect_score;
10041   int target_element;
10042   int old_element = Feld[x][y];
10043
10044   /* always use default change event to prevent running into a loop */
10045   if (ChangeEvent[x][y] == -1)
10046     ChangeEvent[x][y] = CE_DELAY;
10047
10048   if (ChangeEvent[x][y] == CE_DELAY)
10049   {
10050     /* reset actual trigger element, trigger player and action element */
10051     change->actual_trigger_element = EL_EMPTY;
10052     change->actual_trigger_player = EL_EMPTY;
10053     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10054     change->actual_trigger_side = CH_SIDE_NONE;
10055     change->actual_trigger_ce_value = 0;
10056     change->actual_trigger_ce_score = 0;
10057   }
10058
10059   /* do not change elements more than a specified maximum number of changes */
10060   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10061     return FALSE;
10062
10063   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10064
10065   if (change->explode)
10066   {
10067     Bang(x, y);
10068
10069     return TRUE;
10070   }
10071
10072   if (change->use_target_content)
10073   {
10074     boolean complete_replace = TRUE;
10075     boolean can_replace[3][3];
10076     int xx, yy;
10077
10078     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10079     {
10080       boolean is_empty;
10081       boolean is_walkable;
10082       boolean is_diggable;
10083       boolean is_collectible;
10084       boolean is_removable;
10085       boolean is_destructible;
10086       int ex = x + xx - 1;
10087       int ey = y + yy - 1;
10088       int content_element = change->target_content.e[xx][yy];
10089       int e;
10090
10091       can_replace[xx][yy] = TRUE;
10092
10093       if (ex == x && ey == y)   /* do not check changing element itself */
10094         continue;
10095
10096       if (content_element == EL_EMPTY_SPACE)
10097       {
10098         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10099
10100         continue;
10101       }
10102
10103       if (!IN_LEV_FIELD(ex, ey))
10104       {
10105         can_replace[xx][yy] = FALSE;
10106         complete_replace = FALSE;
10107
10108         continue;
10109       }
10110
10111       e = Feld[ex][ey];
10112
10113       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10114         e = MovingOrBlocked2Element(ex, ey);
10115
10116       is_empty = (IS_FREE(ex, ey) ||
10117                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10118
10119       is_walkable     = (is_empty || IS_WALKABLE(e));
10120       is_diggable     = (is_empty || IS_DIGGABLE(e));
10121       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10122       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10123       is_removable    = (is_diggable || is_collectible);
10124
10125       can_replace[xx][yy] =
10126         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10127           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10128           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10129           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10130           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10131           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10132          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10133
10134       if (!can_replace[xx][yy])
10135         complete_replace = FALSE;
10136     }
10137
10138     if (!change->only_if_complete || complete_replace)
10139     {
10140       boolean something_has_changed = FALSE;
10141
10142       if (change->only_if_complete && change->use_random_replace &&
10143           RND(100) < change->random_percentage)
10144         return FALSE;
10145
10146       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10147       {
10148         int ex = x + xx - 1;
10149         int ey = y + yy - 1;
10150         int content_element;
10151
10152         if (can_replace[xx][yy] && (!change->use_random_replace ||
10153                                     RND(100) < change->random_percentage))
10154         {
10155           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10156             RemoveMovingField(ex, ey);
10157
10158           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10159
10160           content_element = change->target_content.e[xx][yy];
10161           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10162                                               ce_value, ce_score);
10163
10164           CreateElementFromChange(ex, ey, target_element);
10165
10166           something_has_changed = TRUE;
10167
10168           /* for symmetry reasons, freeze newly created border elements */
10169           if (ex != x || ey != y)
10170             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10171         }
10172       }
10173
10174       if (something_has_changed)
10175       {
10176         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10177         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10178       }
10179     }
10180   }
10181   else
10182   {
10183     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10184                                         ce_value, ce_score);
10185
10186     if (element == EL_DIAGONAL_GROWING ||
10187         element == EL_DIAGONAL_SHRINKING)
10188     {
10189       target_element = Store[x][y];
10190
10191       Store[x][y] = EL_EMPTY;
10192     }
10193
10194     CreateElementFromChange(x, y, target_element);
10195
10196     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10197     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10198   }
10199
10200   /* this uses direct change before indirect change */
10201   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10202
10203   return TRUE;
10204 }
10205
10206 static void HandleElementChange(int x, int y, int page)
10207 {
10208   int element = MovingOrBlocked2Element(x, y);
10209   struct ElementInfo *ei = &element_info[element];
10210   struct ElementChangeInfo *change = &ei->change_page[page];
10211   boolean handle_action_before_change = FALSE;
10212
10213 #ifdef DEBUG
10214   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10215       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10216   {
10217     printf("\n\n");
10218     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10219            x, y, element, element_info[element].token_name);
10220     printf("HandleElementChange(): This should never happen!\n");
10221     printf("\n\n");
10222   }
10223 #endif
10224
10225   /* this can happen with classic bombs on walkable, changing elements */
10226   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10227   {
10228     return;
10229   }
10230
10231   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10232   {
10233     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10234
10235     if (change->can_change)
10236     {
10237       /* !!! not clear why graphic animation should be reset at all here !!! */
10238       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10239       /* when a custom element is about to change (for example by change delay),
10240          do not reset graphic animation when the custom element is moving */
10241       if (!IS_MOVING(x, y))
10242       {
10243         ResetGfxAnimation(x, y);
10244         ResetRandomAnimationValue(x, y);
10245       }
10246
10247       if (change->pre_change_function)
10248         change->pre_change_function(x, y);
10249     }
10250   }
10251
10252   ChangeDelay[x][y]--;
10253
10254   if (ChangeDelay[x][y] != 0)           /* continue element change */
10255   {
10256     if (change->can_change)
10257     {
10258       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10259
10260       if (IS_ANIMATED(graphic))
10261         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10262
10263       if (change->change_function)
10264         change->change_function(x, y);
10265     }
10266   }
10267   else                                  /* finish element change */
10268   {
10269     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10270     {
10271       page = ChangePage[x][y];
10272       ChangePage[x][y] = -1;
10273
10274       change = &ei->change_page[page];
10275     }
10276
10277     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10278     {
10279       ChangeDelay[x][y] = 1;            /* try change after next move step */
10280       ChangePage[x][y] = page;          /* remember page to use for change */
10281
10282       return;
10283     }
10284
10285     /* special case: set new level random seed before changing element */
10286     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10287       handle_action_before_change = TRUE;
10288
10289     if (change->has_action && handle_action_before_change)
10290       ExecuteCustomElementAction(x, y, element, page);
10291
10292     if (change->can_change)
10293     {
10294       if (ChangeElement(x, y, element, page))
10295       {
10296         if (change->post_change_function)
10297           change->post_change_function(x, y);
10298       }
10299     }
10300
10301     if (change->has_action && !handle_action_before_change)
10302       ExecuteCustomElementAction(x, y, element, page);
10303   }
10304 }
10305
10306 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10307                                               int trigger_element,
10308                                               int trigger_event,
10309                                               int trigger_player,
10310                                               int trigger_side,
10311                                               int trigger_page)
10312 {
10313   boolean change_done_any = FALSE;
10314   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10315   int i;
10316
10317   if (!(trigger_events[trigger_element][trigger_event]))
10318     return FALSE;
10319
10320   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10321
10322   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10323   {
10324     int element = EL_CUSTOM_START + i;
10325     boolean change_done = FALSE;
10326     int p;
10327
10328     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10329         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10330       continue;
10331
10332     for (p = 0; p < element_info[element].num_change_pages; p++)
10333     {
10334       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10335
10336       if (change->can_change_or_has_action &&
10337           change->has_event[trigger_event] &&
10338           change->trigger_side & trigger_side &&
10339           change->trigger_player & trigger_player &&
10340           change->trigger_page & trigger_page_bits &&
10341           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10342       {
10343         change->actual_trigger_element = trigger_element;
10344         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10345         change->actual_trigger_player_bits = trigger_player;
10346         change->actual_trigger_side = trigger_side;
10347         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10348         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10349
10350         if ((change->can_change && !change_done) || change->has_action)
10351         {
10352           int x, y;
10353
10354           SCAN_PLAYFIELD(x, y)
10355           {
10356             if (Feld[x][y] == element)
10357             {
10358               if (change->can_change && !change_done)
10359               {
10360                 /* if element already changed in this frame, not only prevent
10361                    another element change (checked in ChangeElement()), but
10362                    also prevent additional element actions for this element */
10363
10364                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10365                     !level.use_action_after_change_bug)
10366                   continue;
10367
10368                 ChangeDelay[x][y] = 1;
10369                 ChangeEvent[x][y] = trigger_event;
10370
10371                 HandleElementChange(x, y, p);
10372               }
10373               else if (change->has_action)
10374               {
10375                 /* if element already changed in this frame, not only prevent
10376                    another element change (checked in ChangeElement()), but
10377                    also prevent additional element actions for this element */
10378
10379                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10380                     !level.use_action_after_change_bug)
10381                   continue;
10382
10383                 ExecuteCustomElementAction(x, y, element, p);
10384                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10385               }
10386             }
10387           }
10388
10389           if (change->can_change)
10390           {
10391             change_done = TRUE;
10392             change_done_any = TRUE;
10393           }
10394         }
10395       }
10396     }
10397   }
10398
10399   RECURSION_LOOP_DETECTION_END();
10400
10401   return change_done_any;
10402 }
10403
10404 static boolean CheckElementChangeExt(int x, int y,
10405                                      int element,
10406                                      int trigger_element,
10407                                      int trigger_event,
10408                                      int trigger_player,
10409                                      int trigger_side)
10410 {
10411   boolean change_done = FALSE;
10412   int p;
10413
10414   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10415       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10416     return FALSE;
10417
10418   if (Feld[x][y] == EL_BLOCKED)
10419   {
10420     Blocked2Moving(x, y, &x, &y);
10421     element = Feld[x][y];
10422   }
10423
10424   /* check if element has already changed or is about to change after moving */
10425   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10426        Feld[x][y] != element) ||
10427
10428       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10429        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10430         ChangePage[x][y] != -1)))
10431     return FALSE;
10432
10433   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10434
10435   for (p = 0; p < element_info[element].num_change_pages; p++)
10436   {
10437     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10438
10439     /* check trigger element for all events where the element that is checked
10440        for changing interacts with a directly adjacent element -- this is
10441        different to element changes that affect other elements to change on the
10442        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10443     boolean check_trigger_element =
10444       (trigger_event == CE_TOUCHING_X ||
10445        trigger_event == CE_HITTING_X ||
10446        trigger_event == CE_HIT_BY_X ||
10447        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10448
10449     if (change->can_change_or_has_action &&
10450         change->has_event[trigger_event] &&
10451         change->trigger_side & trigger_side &&
10452         change->trigger_player & trigger_player &&
10453         (!check_trigger_element ||
10454          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10455     {
10456       change->actual_trigger_element = trigger_element;
10457       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10458       change->actual_trigger_player_bits = trigger_player;
10459       change->actual_trigger_side = trigger_side;
10460       change->actual_trigger_ce_value = CustomValue[x][y];
10461       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10462
10463       /* special case: trigger element not at (x,y) position for some events */
10464       if (check_trigger_element)
10465       {
10466         static struct
10467         {
10468           int dx, dy;
10469         } move_xy[] =
10470           {
10471             {  0,  0 },
10472             { -1,  0 },
10473             { +1,  0 },
10474             {  0,  0 },
10475             {  0, -1 },
10476             {  0,  0 }, { 0, 0 }, { 0, 0 },
10477             {  0, +1 }
10478           };
10479
10480         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10481         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10482
10483         change->actual_trigger_ce_value = CustomValue[xx][yy];
10484         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10485       }
10486
10487       if (change->can_change && !change_done)
10488       {
10489         ChangeDelay[x][y] = 1;
10490         ChangeEvent[x][y] = trigger_event;
10491
10492         HandleElementChange(x, y, p);
10493
10494         change_done = TRUE;
10495       }
10496       else if (change->has_action)
10497       {
10498         ExecuteCustomElementAction(x, y, element, p);
10499         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10500       }
10501     }
10502   }
10503
10504   RECURSION_LOOP_DETECTION_END();
10505
10506   return change_done;
10507 }
10508
10509 static void PlayPlayerSound(struct PlayerInfo *player)
10510 {
10511   int jx = player->jx, jy = player->jy;
10512   int sound_element = player->artwork_element;
10513   int last_action = player->last_action_waiting;
10514   int action = player->action_waiting;
10515
10516   if (player->is_waiting)
10517   {
10518     if (action != last_action)
10519       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10520     else
10521       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10522   }
10523   else
10524   {
10525     if (action != last_action)
10526       StopSound(element_info[sound_element].sound[last_action]);
10527
10528     if (last_action == ACTION_SLEEPING)
10529       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10530   }
10531 }
10532
10533 static void PlayAllPlayersSound()
10534 {
10535   int i;
10536
10537   for (i = 0; i < MAX_PLAYERS; i++)
10538     if (stored_player[i].active)
10539       PlayPlayerSound(&stored_player[i]);
10540 }
10541
10542 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10543 {
10544   boolean last_waiting = player->is_waiting;
10545   int move_dir = player->MovDir;
10546
10547   player->dir_waiting = move_dir;
10548   player->last_action_waiting = player->action_waiting;
10549
10550   if (is_waiting)
10551   {
10552     if (!last_waiting)          /* not waiting -> waiting */
10553     {
10554       player->is_waiting = TRUE;
10555
10556       player->frame_counter_bored =
10557         FrameCounter +
10558         game.player_boring_delay_fixed +
10559         GetSimpleRandom(game.player_boring_delay_random);
10560       player->frame_counter_sleeping =
10561         FrameCounter +
10562         game.player_sleeping_delay_fixed +
10563         GetSimpleRandom(game.player_sleeping_delay_random);
10564
10565       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10566     }
10567
10568     if (game.player_sleeping_delay_fixed +
10569         game.player_sleeping_delay_random > 0 &&
10570         player->anim_delay_counter == 0 &&
10571         player->post_delay_counter == 0 &&
10572         FrameCounter >= player->frame_counter_sleeping)
10573       player->is_sleeping = TRUE;
10574     else if (game.player_boring_delay_fixed +
10575              game.player_boring_delay_random > 0 &&
10576              FrameCounter >= player->frame_counter_bored)
10577       player->is_bored = TRUE;
10578
10579     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10580                               player->is_bored ? ACTION_BORING :
10581                               ACTION_WAITING);
10582
10583     if (player->is_sleeping && player->use_murphy)
10584     {
10585       /* special case for sleeping Murphy when leaning against non-free tile */
10586
10587       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10588           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10589            !IS_MOVING(player->jx - 1, player->jy)))
10590         move_dir = MV_LEFT;
10591       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10592                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10593                 !IS_MOVING(player->jx + 1, player->jy)))
10594         move_dir = MV_RIGHT;
10595       else
10596         player->is_sleeping = FALSE;
10597
10598       player->dir_waiting = move_dir;
10599     }
10600
10601     if (player->is_sleeping)
10602     {
10603       if (player->num_special_action_sleeping > 0)
10604       {
10605         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10606         {
10607           int last_special_action = player->special_action_sleeping;
10608           int num_special_action = player->num_special_action_sleeping;
10609           int special_action =
10610             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10611              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10612              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10613              last_special_action + 1 : ACTION_SLEEPING);
10614           int special_graphic =
10615             el_act_dir2img(player->artwork_element, special_action, move_dir);
10616
10617           player->anim_delay_counter =
10618             graphic_info[special_graphic].anim_delay_fixed +
10619             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10620           player->post_delay_counter =
10621             graphic_info[special_graphic].post_delay_fixed +
10622             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10623
10624           player->special_action_sleeping = special_action;
10625         }
10626
10627         if (player->anim_delay_counter > 0)
10628         {
10629           player->action_waiting = player->special_action_sleeping;
10630           player->anim_delay_counter--;
10631         }
10632         else if (player->post_delay_counter > 0)
10633         {
10634           player->post_delay_counter--;
10635         }
10636       }
10637     }
10638     else if (player->is_bored)
10639     {
10640       if (player->num_special_action_bored > 0)
10641       {
10642         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10643         {
10644           int special_action =
10645             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10646           int special_graphic =
10647             el_act_dir2img(player->artwork_element, special_action, move_dir);
10648
10649           player->anim_delay_counter =
10650             graphic_info[special_graphic].anim_delay_fixed +
10651             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10652           player->post_delay_counter =
10653             graphic_info[special_graphic].post_delay_fixed +
10654             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10655
10656           player->special_action_bored = special_action;
10657         }
10658
10659         if (player->anim_delay_counter > 0)
10660         {
10661           player->action_waiting = player->special_action_bored;
10662           player->anim_delay_counter--;
10663         }
10664         else if (player->post_delay_counter > 0)
10665         {
10666           player->post_delay_counter--;
10667         }
10668       }
10669     }
10670   }
10671   else if (last_waiting)        /* waiting -> not waiting */
10672   {
10673     player->is_waiting = FALSE;
10674     player->is_bored = FALSE;
10675     player->is_sleeping = FALSE;
10676
10677     player->frame_counter_bored = -1;
10678     player->frame_counter_sleeping = -1;
10679
10680     player->anim_delay_counter = 0;
10681     player->post_delay_counter = 0;
10682
10683     player->dir_waiting = player->MovDir;
10684     player->action_waiting = ACTION_DEFAULT;
10685
10686     player->special_action_bored = ACTION_DEFAULT;
10687     player->special_action_sleeping = ACTION_DEFAULT;
10688   }
10689 }
10690
10691 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10692 {
10693   static boolean player_was_moving = FALSE;
10694   static boolean player_was_snapping = FALSE;
10695   static boolean player_was_dropping = FALSE;
10696
10697   if ((!player->is_moving  && player_was_moving) ||
10698       (player->MovPos == 0 && player_was_moving) ||
10699       (player->is_snapping && !player_was_snapping) ||
10700       (player->is_dropping && !player_was_dropping))
10701   {
10702     if (!SaveEngineSnapshotToList())
10703       return;
10704
10705     player_was_moving = FALSE;
10706     player_was_snapping = TRUE;
10707     player_was_dropping = TRUE;
10708   }
10709   else
10710   {
10711     if (player->is_moving)
10712       player_was_moving = TRUE;
10713
10714     if (!player->is_snapping)
10715       player_was_snapping = FALSE;
10716
10717     if (!player->is_dropping)
10718       player_was_dropping = FALSE;
10719   }
10720 }
10721
10722 static void CheckSingleStepMode(struct PlayerInfo *player)
10723 {
10724   if (tape.single_step && tape.recording && !tape.pausing)
10725   {
10726     /* as it is called "single step mode", just return to pause mode when the
10727        player stopped moving after one tile (or never starts moving at all) */
10728     if (!player->is_moving && !player->is_pushing)
10729     {
10730       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10731       SnapField(player, 0, 0);                  /* stop snapping */
10732     }
10733   }
10734
10735   CheckSaveEngineSnapshot(player);
10736 }
10737
10738 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10739 {
10740   int left      = player_action & JOY_LEFT;
10741   int right     = player_action & JOY_RIGHT;
10742   int up        = player_action & JOY_UP;
10743   int down      = player_action & JOY_DOWN;
10744   int button1   = player_action & JOY_BUTTON_1;
10745   int button2   = player_action & JOY_BUTTON_2;
10746   int dx        = (left ? -1 : right ? 1 : 0);
10747   int dy        = (up   ? -1 : down  ? 1 : 0);
10748
10749   if (!player->active || tape.pausing)
10750     return 0;
10751
10752   if (player_action)
10753   {
10754     if (button1)
10755       SnapField(player, dx, dy);
10756     else
10757     {
10758       if (button2)
10759         DropElement(player);
10760
10761       MovePlayer(player, dx, dy);
10762     }
10763
10764     CheckSingleStepMode(player);
10765
10766     SetPlayerWaiting(player, FALSE);
10767
10768     return player_action;
10769   }
10770   else
10771   {
10772     /* no actions for this player (no input at player's configured device) */
10773
10774     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10775     SnapField(player, 0, 0);
10776     CheckGravityMovementWhenNotMoving(player);
10777
10778     if (player->MovPos == 0)
10779       SetPlayerWaiting(player, TRUE);
10780
10781     if (player->MovPos == 0)    /* needed for tape.playing */
10782       player->is_moving = FALSE;
10783
10784     player->is_dropping = FALSE;
10785     player->is_dropping_pressed = FALSE;
10786     player->drop_pressed_delay = 0;
10787
10788     CheckSingleStepMode(player);
10789
10790     return 0;
10791   }
10792 }
10793
10794 static void CheckLevelTime()
10795 {
10796   int i;
10797
10798   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10799   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10800   {
10801     if (level.native_em_level->lev->home == 0)  /* all players at home */
10802     {
10803       PlayerWins(local_player);
10804
10805       AllPlayersGone = TRUE;
10806
10807       level.native_em_level->lev->home = -1;
10808     }
10809
10810     if (level.native_em_level->ply[0]->alive == 0 &&
10811         level.native_em_level->ply[1]->alive == 0 &&
10812         level.native_em_level->ply[2]->alive == 0 &&
10813         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10814       AllPlayersGone = TRUE;
10815   }
10816   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10817   {
10818     if (game_sp.LevelSolved &&
10819         !game_sp.GameOver)                              /* game won */
10820     {
10821       PlayerWins(local_player);
10822
10823       game_sp.GameOver = TRUE;
10824
10825       AllPlayersGone = TRUE;
10826     }
10827
10828     if (game_sp.GameOver)                               /* game lost */
10829       AllPlayersGone = TRUE;
10830   }
10831
10832   if (TimeFrames >= FRAMES_PER_SECOND)
10833   {
10834     TimeFrames = 0;
10835     TapeTime++;
10836
10837     for (i = 0; i < MAX_PLAYERS; i++)
10838     {
10839       struct PlayerInfo *player = &stored_player[i];
10840
10841       if (SHIELD_ON(player))
10842       {
10843         player->shield_normal_time_left--;
10844
10845         if (player->shield_deadly_time_left > 0)
10846           player->shield_deadly_time_left--;
10847       }
10848     }
10849
10850     if (!local_player->LevelSolved && !level.use_step_counter)
10851     {
10852       TimePlayed++;
10853
10854       if (TimeLeft > 0)
10855       {
10856         TimeLeft--;
10857
10858         if (TimeLeft <= 10 && setup.time_limit)
10859           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10860
10861         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10862            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10863
10864         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10865
10866         if (!TimeLeft && setup.time_limit)
10867         {
10868           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10869             level.native_em_level->lev->killed_out_of_time = TRUE;
10870           else
10871             for (i = 0; i < MAX_PLAYERS; i++)
10872               KillPlayer(&stored_player[i]);
10873         }
10874       }
10875       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10876       {
10877         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10878       }
10879
10880       level.native_em_level->lev->time =
10881         (game.no_time_limit ? TimePlayed : TimeLeft);
10882     }
10883
10884     if (tape.recording || tape.playing)
10885       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10886   }
10887
10888   if (tape.recording || tape.playing)
10889     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10890
10891   UpdateAndDisplayGameControlValues();
10892 }
10893
10894 void AdvanceFrameAndPlayerCounters(int player_nr)
10895 {
10896   int i;
10897
10898   /* advance frame counters (global frame counter and time frame counter) */
10899   FrameCounter++;
10900   TimeFrames++;
10901
10902   /* advance player counters (counters for move delay, move animation etc.) */
10903   for (i = 0; i < MAX_PLAYERS; i++)
10904   {
10905     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10906     int move_delay_value = stored_player[i].move_delay_value;
10907     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10908
10909     if (!advance_player_counters)       /* not all players may be affected */
10910       continue;
10911
10912     if (move_frames == 0)       /* less than one move per game frame */
10913     {
10914       int stepsize = TILEX / move_delay_value;
10915       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10916       int count = (stored_player[i].is_moving ?
10917                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10918
10919       if (count % delay == 0)
10920         move_frames = 1;
10921     }
10922
10923     stored_player[i].Frame += move_frames;
10924
10925     if (stored_player[i].MovPos != 0)
10926       stored_player[i].StepFrame += move_frames;
10927
10928     if (stored_player[i].move_delay > 0)
10929       stored_player[i].move_delay--;
10930
10931     /* due to bugs in previous versions, counter must count up, not down */
10932     if (stored_player[i].push_delay != -1)
10933       stored_player[i].push_delay++;
10934
10935     if (stored_player[i].drop_delay > 0)
10936       stored_player[i].drop_delay--;
10937
10938     if (stored_player[i].is_dropping_pressed)
10939       stored_player[i].drop_pressed_delay++;
10940   }
10941 }
10942
10943 void StartGameActions(boolean init_network_game, boolean record_tape,
10944                       int random_seed)
10945 {
10946   unsigned int new_random_seed = InitRND(random_seed);
10947
10948   if (record_tape)
10949     TapeStartRecording(new_random_seed);
10950
10951 #if defined(NETWORK_AVALIABLE)
10952   if (init_network_game)
10953   {
10954     SendToServer_StartPlaying();
10955
10956     return;
10957   }
10958 #endif
10959
10960   InitGame();
10961 }
10962
10963 void GameActions()
10964 {
10965   static unsigned int game_frame_delay = 0;
10966   unsigned int game_frame_delay_value;
10967   byte *recorded_player_action;
10968   byte summarized_player_action = 0;
10969   byte tape_action[MAX_PLAYERS];
10970   int i;
10971
10972   for (i = 0; i < MAX_PLAYERS; i++)
10973   {
10974     struct PlayerInfo *player = &stored_player[i];
10975
10976     // allow engine snapshot if movement attempt was stopped
10977     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10978         (player->action & KEY_MOTION) == 0)
10979       game.snapshot.changed_action = TRUE;
10980
10981     // allow engine snapshot in case of snapping/dropping attempt
10982     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10983         (player->action & KEY_BUTTON) != 0)
10984       game.snapshot.changed_action = TRUE;
10985
10986     game.snapshot.last_action[i] = player->action;
10987   }
10988
10989   /* detect endless loops, caused by custom element programming */
10990   if (recursion_loop_detected && recursion_loop_depth == 0)
10991   {
10992     char *message = getStringCat3("Internal Error! Element ",
10993                                   EL_NAME(recursion_loop_element),
10994                                   " caused endless loop! Quit the game?");
10995
10996     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10997           EL_NAME(recursion_loop_element));
10998
10999     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11000
11001     recursion_loop_detected = FALSE;    /* if game should be continued */
11002
11003     free(message);
11004
11005     return;
11006   }
11007
11008   if (game.restart_level)
11009     StartGameActions(options.network, setup.autorecord, level.random_seed);
11010
11011   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11012   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11013   {
11014     if (level.native_em_level->lev->home == 0)  /* all players at home */
11015     {
11016       PlayerWins(local_player);
11017
11018       AllPlayersGone = TRUE;
11019
11020       level.native_em_level->lev->home = -1;
11021     }
11022
11023     if (level.native_em_level->ply[0]->alive == 0 &&
11024         level.native_em_level->ply[1]->alive == 0 &&
11025         level.native_em_level->ply[2]->alive == 0 &&
11026         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11027       AllPlayersGone = TRUE;
11028   }
11029   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11030   {
11031     if (game_sp.LevelSolved &&
11032         !game_sp.GameOver)                              /* game won */
11033     {
11034       PlayerWins(local_player);
11035
11036       game_sp.GameOver = TRUE;
11037
11038       AllPlayersGone = TRUE;
11039     }
11040
11041     if (game_sp.GameOver)                               /* game lost */
11042       AllPlayersGone = TRUE;
11043   }
11044
11045   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11046     GameWon();
11047
11048   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11049     TapeStop();
11050
11051   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11052     return;
11053
11054   game_frame_delay_value =
11055     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11056
11057   if (tape.playing && tape.warp_forward && !tape.pausing)
11058     game_frame_delay_value = 0;
11059
11060 #if 0
11061   /* ---------- main game synchronization point ---------- */
11062
11063   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11064
11065   printf("::: skip == %d\n", skip);
11066
11067 #else
11068   /* ---------- main game synchronization point ---------- */
11069
11070   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11071 #endif
11072
11073   if (network_playing && !network_player_action_received)
11074   {
11075     /* try to get network player actions in time */
11076
11077 #if defined(NETWORK_AVALIABLE)
11078     /* last chance to get network player actions without main loop delay */
11079     HandleNetworking();
11080 #endif
11081
11082     /* game was quit by network peer */
11083     if (game_status != GAME_MODE_PLAYING)
11084       return;
11085
11086     if (!network_player_action_received)
11087       return;           /* failed to get network player actions in time */
11088
11089     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11090   }
11091
11092   if (tape.pausing)
11093     return;
11094
11095   /* at this point we know that we really continue executing the game */
11096
11097   network_player_action_received = FALSE;
11098
11099   /* when playing tape, read previously recorded player input from tape data */
11100   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11101
11102   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11103   if (tape.pausing)
11104     return;
11105
11106   if (tape.set_centered_player)
11107   {
11108     game.centered_player_nr_next = tape.centered_player_nr_next;
11109     game.set_centered_player = TRUE;
11110   }
11111
11112   for (i = 0; i < MAX_PLAYERS; i++)
11113   {
11114     summarized_player_action |= stored_player[i].action;
11115
11116     if (!network_playing && (game.team_mode || tape.playing))
11117       stored_player[i].effective_action = stored_player[i].action;
11118   }
11119
11120 #if defined(NETWORK_AVALIABLE)
11121   if (network_playing)
11122     SendToServer_MovePlayer(summarized_player_action);
11123 #endif
11124
11125   if (!options.network && !game.team_mode)
11126     local_player->effective_action = summarized_player_action;
11127
11128   if (tape.recording &&
11129       setup.team_mode &&
11130       setup.input_on_focus &&
11131       game.centered_player_nr != -1)
11132   {
11133     for (i = 0; i < MAX_PLAYERS; i++)
11134       stored_player[i].effective_action =
11135         (i == game.centered_player_nr ? summarized_player_action : 0);
11136   }
11137
11138   if (recorded_player_action != NULL)
11139     for (i = 0; i < MAX_PLAYERS; i++)
11140       stored_player[i].effective_action = recorded_player_action[i];
11141
11142   for (i = 0; i < MAX_PLAYERS; i++)
11143   {
11144     tape_action[i] = stored_player[i].effective_action;
11145
11146     /* (this may happen in the RND game engine if a player was not present on
11147        the playfield on level start, but appeared later from a custom element */
11148     if (setup.team_mode &&
11149         tape.recording &&
11150         tape_action[i] &&
11151         !tape.player_participates[i])
11152       tape.player_participates[i] = TRUE;
11153   }
11154
11155   /* only record actions from input devices, but not programmed actions */
11156   if (tape.recording)
11157     TapeRecordAction(tape_action);
11158
11159 #if USE_NEW_PLAYER_ASSIGNMENTS
11160   // !!! also map player actions in single player mode !!!
11161   // if (game.team_mode)
11162   {
11163     byte mapped_action[MAX_PLAYERS];
11164
11165 #if DEBUG_PLAYER_ACTIONS
11166     printf(":::");
11167     for (i = 0; i < MAX_PLAYERS; i++)
11168       printf(" %d, ", stored_player[i].effective_action);
11169 #endif
11170
11171     for (i = 0; i < MAX_PLAYERS; i++)
11172       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11173
11174     for (i = 0; i < MAX_PLAYERS; i++)
11175       stored_player[i].effective_action = mapped_action[i];
11176
11177 #if DEBUG_PLAYER_ACTIONS
11178     printf(" =>");
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180       printf(" %d, ", stored_player[i].effective_action);
11181     printf("\n");
11182 #endif
11183   }
11184 #if DEBUG_PLAYER_ACTIONS
11185   else
11186   {
11187     printf(":::");
11188     for (i = 0; i < MAX_PLAYERS; i++)
11189       printf(" %d, ", stored_player[i].effective_action);
11190     printf("\n");
11191   }
11192 #endif
11193 #endif
11194
11195   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11196   {
11197     GameActions_EM_Main();
11198   }
11199   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11200   {
11201     GameActions_SP_Main();
11202   }
11203   else
11204   {
11205     GameActions_RND_Main();
11206   }
11207
11208   BlitScreenToBitmap(backbuffer);
11209
11210   CheckLevelTime();
11211
11212   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11213
11214   if (options.debug)                    /* calculate frames per second */
11215   {
11216     static unsigned int fps_counter = 0;
11217     static int fps_frames = 0;
11218     unsigned int fps_delay_ms = Counter() - fps_counter;
11219
11220     fps_frames++;
11221
11222     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11223     {
11224       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11225
11226       fps_frames = 0;
11227       fps_counter = Counter();
11228     }
11229
11230     redraw_mask |= REDRAW_FPS;
11231   }
11232 }
11233
11234 void GameActions_EM_Main()
11235 {
11236   byte effective_action[MAX_PLAYERS];
11237   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11238   int i;
11239
11240   for (i = 0; i < MAX_PLAYERS; i++)
11241     effective_action[i] = stored_player[i].effective_action;
11242
11243   GameActions_EM(effective_action, warp_mode);
11244 }
11245
11246 void GameActions_SP_Main()
11247 {
11248   byte effective_action[MAX_PLAYERS];
11249   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11250   int i;
11251
11252   for (i = 0; i < MAX_PLAYERS; i++)
11253     effective_action[i] = stored_player[i].effective_action;
11254
11255   GameActions_SP(effective_action, warp_mode);
11256 }
11257
11258 void GameActions_RND_Main()
11259 {
11260   GameActions_RND();
11261 }
11262
11263 void GameActions_RND()
11264 {
11265   int magic_wall_x = 0, magic_wall_y = 0;
11266   int i, x, y, element, graphic;
11267
11268   InitPlayfieldScanModeVars();
11269
11270   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11271   {
11272     SCAN_PLAYFIELD(x, y)
11273     {
11274       ChangeCount[x][y] = 0;
11275       ChangeEvent[x][y] = -1;
11276     }
11277   }
11278
11279   if (game.set_centered_player)
11280   {
11281     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11282
11283     /* switching to "all players" only possible if all players fit to screen */
11284     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11285     {
11286       game.centered_player_nr_next = game.centered_player_nr;
11287       game.set_centered_player = FALSE;
11288     }
11289
11290     /* do not switch focus to non-existing (or non-active) player */
11291     if (game.centered_player_nr_next >= 0 &&
11292         !stored_player[game.centered_player_nr_next].active)
11293     {
11294       game.centered_player_nr_next = game.centered_player_nr;
11295       game.set_centered_player = FALSE;
11296     }
11297   }
11298
11299   if (game.set_centered_player &&
11300       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11301   {
11302     int sx, sy;
11303
11304     if (game.centered_player_nr_next == -1)
11305     {
11306       setScreenCenteredToAllPlayers(&sx, &sy);
11307     }
11308     else
11309     {
11310       sx = stored_player[game.centered_player_nr_next].jx;
11311       sy = stored_player[game.centered_player_nr_next].jy;
11312     }
11313
11314     game.centered_player_nr = game.centered_player_nr_next;
11315     game.set_centered_player = FALSE;
11316
11317     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11318     DrawGameDoorValues();
11319   }
11320
11321   for (i = 0; i < MAX_PLAYERS; i++)
11322   {
11323     int actual_player_action = stored_player[i].effective_action;
11324
11325 #if 1
11326     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11327        - rnd_equinox_tetrachloride 048
11328        - rnd_equinox_tetrachloride_ii 096
11329        - rnd_emanuel_schmieg 002
11330        - doctor_sloan_ww 001, 020
11331     */
11332     if (stored_player[i].MovPos == 0)
11333       CheckGravityMovement(&stored_player[i]);
11334 #endif
11335
11336     /* overwrite programmed action with tape action */
11337     if (stored_player[i].programmed_action)
11338       actual_player_action = stored_player[i].programmed_action;
11339
11340     PlayerActions(&stored_player[i], actual_player_action);
11341
11342     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11343   }
11344
11345   ScrollScreen(NULL, SCROLL_GO_ON);
11346
11347   /* for backwards compatibility, the following code emulates a fixed bug that
11348      occured when pushing elements (causing elements that just made their last
11349      pushing step to already (if possible) make their first falling step in the
11350      same game frame, which is bad); this code is also needed to use the famous
11351      "spring push bug" which is used in older levels and might be wanted to be
11352      used also in newer levels, but in this case the buggy pushing code is only
11353      affecting the "spring" element and no other elements */
11354
11355   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11356   {
11357     for (i = 0; i < MAX_PLAYERS; i++)
11358     {
11359       struct PlayerInfo *player = &stored_player[i];
11360       int x = player->jx;
11361       int y = player->jy;
11362
11363       if (player->active && player->is_pushing && player->is_moving &&
11364           IS_MOVING(x, y) &&
11365           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11366            Feld[x][y] == EL_SPRING))
11367       {
11368         ContinueMoving(x, y);
11369
11370         /* continue moving after pushing (this is actually a bug) */
11371         if (!IS_MOVING(x, y))
11372           Stop[x][y] = FALSE;
11373       }
11374     }
11375   }
11376
11377   SCAN_PLAYFIELD(x, y)
11378   {
11379     ChangeCount[x][y] = 0;
11380     ChangeEvent[x][y] = -1;
11381
11382     /* this must be handled before main playfield loop */
11383     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11384     {
11385       MovDelay[x][y]--;
11386       if (MovDelay[x][y] <= 0)
11387         RemoveField(x, y);
11388     }
11389
11390     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11391     {
11392       MovDelay[x][y]--;
11393       if (MovDelay[x][y] <= 0)
11394       {
11395         RemoveField(x, y);
11396         TEST_DrawLevelField(x, y);
11397
11398         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11399       }
11400     }
11401
11402 #if DEBUG
11403     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11404     {
11405       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11406       printf("GameActions(): This should never happen!\n");
11407
11408       ChangePage[x][y] = -1;
11409     }
11410 #endif
11411
11412     Stop[x][y] = FALSE;
11413     if (WasJustMoving[x][y] > 0)
11414       WasJustMoving[x][y]--;
11415     if (WasJustFalling[x][y] > 0)
11416       WasJustFalling[x][y]--;
11417     if (CheckCollision[x][y] > 0)
11418       CheckCollision[x][y]--;
11419     if (CheckImpact[x][y] > 0)
11420       CheckImpact[x][y]--;
11421
11422     GfxFrame[x][y]++;
11423
11424     /* reset finished pushing action (not done in ContinueMoving() to allow
11425        continuous pushing animation for elements with zero push delay) */
11426     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11427     {
11428       ResetGfxAnimation(x, y);
11429       TEST_DrawLevelField(x, y);
11430     }
11431
11432 #if DEBUG
11433     if (IS_BLOCKED(x, y))
11434     {
11435       int oldx, oldy;
11436
11437       Blocked2Moving(x, y, &oldx, &oldy);
11438       if (!IS_MOVING(oldx, oldy))
11439       {
11440         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11441         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11442         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11443         printf("GameActions(): This should never happen!\n");
11444       }
11445     }
11446 #endif
11447   }
11448
11449   SCAN_PLAYFIELD(x, y)
11450   {
11451     element = Feld[x][y];
11452     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11453
11454     ResetGfxFrame(x, y, TRUE);
11455
11456     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11457         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11458       ResetRandomAnimationValue(x, y);
11459
11460     SetRandomAnimationValue(x, y);
11461
11462     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11463
11464     if (IS_INACTIVE(element))
11465     {
11466       if (IS_ANIMATED(graphic))
11467         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11468
11469       continue;
11470     }
11471
11472     /* this may take place after moving, so 'element' may have changed */
11473     if (IS_CHANGING(x, y) &&
11474         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11475     {
11476       int page = element_info[element].event_page_nr[CE_DELAY];
11477
11478       HandleElementChange(x, y, page);
11479
11480       element = Feld[x][y];
11481       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11482     }
11483
11484     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11485     {
11486       StartMoving(x, y);
11487
11488       element = Feld[x][y];
11489       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11490
11491       if (IS_ANIMATED(graphic) &&
11492           !IS_MOVING(x, y) &&
11493           !Stop[x][y])
11494         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11495
11496       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11497         TEST_DrawTwinkleOnField(x, y);
11498     }
11499     else if ((element == EL_ACID ||
11500               element == EL_EXIT_OPEN ||
11501               element == EL_EM_EXIT_OPEN ||
11502               element == EL_SP_EXIT_OPEN ||
11503               element == EL_STEEL_EXIT_OPEN ||
11504               element == EL_EM_STEEL_EXIT_OPEN ||
11505               element == EL_SP_TERMINAL ||
11506               element == EL_SP_TERMINAL_ACTIVE ||
11507               element == EL_EXTRA_TIME ||
11508               element == EL_SHIELD_NORMAL ||
11509               element == EL_SHIELD_DEADLY) &&
11510              IS_ANIMATED(graphic))
11511       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11512     else if (IS_MOVING(x, y))
11513       ContinueMoving(x, y);
11514     else if (IS_ACTIVE_BOMB(element))
11515       CheckDynamite(x, y);
11516     else if (element == EL_AMOEBA_GROWING)
11517       AmoebeWaechst(x, y);
11518     else if (element == EL_AMOEBA_SHRINKING)
11519       AmoebaDisappearing(x, y);
11520
11521 #if !USE_NEW_AMOEBA_CODE
11522     else if (IS_AMOEBALIVE(element))
11523       AmoebeAbleger(x, y);
11524 #endif
11525
11526     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11527       Life(x, y);
11528     else if (element == EL_EXIT_CLOSED)
11529       CheckExit(x, y);
11530     else if (element == EL_EM_EXIT_CLOSED)
11531       CheckExitEM(x, y);
11532     else if (element == EL_STEEL_EXIT_CLOSED)
11533       CheckExitSteel(x, y);
11534     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11535       CheckExitSteelEM(x, y);
11536     else if (element == EL_SP_EXIT_CLOSED)
11537       CheckExitSP(x, y);
11538     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11539              element == EL_EXPANDABLE_STEELWALL_GROWING)
11540       MauerWaechst(x, y);
11541     else if (element == EL_EXPANDABLE_WALL ||
11542              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11543              element == EL_EXPANDABLE_WALL_VERTICAL ||
11544              element == EL_EXPANDABLE_WALL_ANY ||
11545              element == EL_BD_EXPANDABLE_WALL)
11546       MauerAbleger(x, y);
11547     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11548              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11549              element == EL_EXPANDABLE_STEELWALL_ANY)
11550       MauerAblegerStahl(x, y);
11551     else if (element == EL_FLAMES)
11552       CheckForDragon(x, y);
11553     else if (element == EL_EXPLOSION)
11554       ; /* drawing of correct explosion animation is handled separately */
11555     else if (element == EL_ELEMENT_SNAPPING ||
11556              element == EL_DIAGONAL_SHRINKING ||
11557              element == EL_DIAGONAL_GROWING)
11558     {
11559       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11560
11561       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11562     }
11563     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11564       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11565
11566     if (IS_BELT_ACTIVE(element))
11567       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11568
11569     if (game.magic_wall_active)
11570     {
11571       int jx = local_player->jx, jy = local_player->jy;
11572
11573       /* play the element sound at the position nearest to the player */
11574       if ((element == EL_MAGIC_WALL_FULL ||
11575            element == EL_MAGIC_WALL_ACTIVE ||
11576            element == EL_MAGIC_WALL_EMPTYING ||
11577            element == EL_BD_MAGIC_WALL_FULL ||
11578            element == EL_BD_MAGIC_WALL_ACTIVE ||
11579            element == EL_BD_MAGIC_WALL_EMPTYING ||
11580            element == EL_DC_MAGIC_WALL_FULL ||
11581            element == EL_DC_MAGIC_WALL_ACTIVE ||
11582            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11583           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11584       {
11585         magic_wall_x = x;
11586         magic_wall_y = y;
11587       }
11588     }
11589   }
11590
11591 #if USE_NEW_AMOEBA_CODE
11592   /* new experimental amoeba growth stuff */
11593   if (!(FrameCounter % 8))
11594   {
11595     static unsigned int random = 1684108901;
11596
11597     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11598     {
11599       x = RND(lev_fieldx);
11600       y = RND(lev_fieldy);
11601       element = Feld[x][y];
11602
11603       if (!IS_PLAYER(x,y) &&
11604           (element == EL_EMPTY ||
11605            CAN_GROW_INTO(element) ||
11606            element == EL_QUICKSAND_EMPTY ||
11607            element == EL_QUICKSAND_FAST_EMPTY ||
11608            element == EL_ACID_SPLASH_LEFT ||
11609            element == EL_ACID_SPLASH_RIGHT))
11610       {
11611         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11612             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11613             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11614             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11615           Feld[x][y] = EL_AMOEBA_DROP;
11616       }
11617
11618       random = random * 129 + 1;
11619     }
11620   }
11621 #endif
11622
11623   game.explosions_delayed = FALSE;
11624
11625   SCAN_PLAYFIELD(x, y)
11626   {
11627     element = Feld[x][y];
11628
11629     if (ExplodeField[x][y])
11630       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11631     else if (element == EL_EXPLOSION)
11632       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11633
11634     ExplodeField[x][y] = EX_TYPE_NONE;
11635   }
11636
11637   game.explosions_delayed = TRUE;
11638
11639   if (game.magic_wall_active)
11640   {
11641     if (!(game.magic_wall_time_left % 4))
11642     {
11643       int element = Feld[magic_wall_x][magic_wall_y];
11644
11645       if (element == EL_BD_MAGIC_WALL_FULL ||
11646           element == EL_BD_MAGIC_WALL_ACTIVE ||
11647           element == EL_BD_MAGIC_WALL_EMPTYING)
11648         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11649       else if (element == EL_DC_MAGIC_WALL_FULL ||
11650                element == EL_DC_MAGIC_WALL_ACTIVE ||
11651                element == EL_DC_MAGIC_WALL_EMPTYING)
11652         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11653       else
11654         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11655     }
11656
11657     if (game.magic_wall_time_left > 0)
11658     {
11659       game.magic_wall_time_left--;
11660
11661       if (!game.magic_wall_time_left)
11662       {
11663         SCAN_PLAYFIELD(x, y)
11664         {
11665           element = Feld[x][y];
11666
11667           if (element == EL_MAGIC_WALL_ACTIVE ||
11668               element == EL_MAGIC_WALL_FULL)
11669           {
11670             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11671             TEST_DrawLevelField(x, y);
11672           }
11673           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11674                    element == EL_BD_MAGIC_WALL_FULL)
11675           {
11676             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11677             TEST_DrawLevelField(x, y);
11678           }
11679           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11680                    element == EL_DC_MAGIC_WALL_FULL)
11681           {
11682             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11683             TEST_DrawLevelField(x, y);
11684           }
11685         }
11686
11687         game.magic_wall_active = FALSE;
11688       }
11689     }
11690   }
11691
11692   if (game.light_time_left > 0)
11693   {
11694     game.light_time_left--;
11695
11696     if (game.light_time_left == 0)
11697       RedrawAllLightSwitchesAndInvisibleElements();
11698   }
11699
11700   if (game.timegate_time_left > 0)
11701   {
11702     game.timegate_time_left--;
11703
11704     if (game.timegate_time_left == 0)
11705       CloseAllOpenTimegates();
11706   }
11707
11708   if (game.lenses_time_left > 0)
11709   {
11710     game.lenses_time_left--;
11711
11712     if (game.lenses_time_left == 0)
11713       RedrawAllInvisibleElementsForLenses();
11714   }
11715
11716   if (game.magnify_time_left > 0)
11717   {
11718     game.magnify_time_left--;
11719
11720     if (game.magnify_time_left == 0)
11721       RedrawAllInvisibleElementsForMagnifier();
11722   }
11723
11724   for (i = 0; i < MAX_PLAYERS; i++)
11725   {
11726     struct PlayerInfo *player = &stored_player[i];
11727
11728     if (SHIELD_ON(player))
11729     {
11730       if (player->shield_deadly_time_left)
11731         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11732       else if (player->shield_normal_time_left)
11733         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11734     }
11735   }
11736
11737 #if USE_DELAYED_GFX_REDRAW
11738   SCAN_PLAYFIELD(x, y)
11739   {
11740     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11741     {
11742       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11743          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11744
11745       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11746         DrawLevelField(x, y);
11747
11748       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11749         DrawLevelFieldCrumbled(x, y);
11750
11751       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11752         DrawLevelFieldCrumbledNeighbours(x, y);
11753
11754       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11755         DrawTwinkleOnField(x, y);
11756     }
11757
11758     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11759   }
11760 #endif
11761
11762   DrawAllPlayers();
11763   PlayAllPlayersSound();
11764
11765   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11766   {
11767     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11768
11769     local_player->show_envelope = 0;
11770   }
11771
11772   /* use random number generator in every frame to make it less predictable */
11773   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11774     RND(1);
11775 }
11776
11777 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11778 {
11779   int min_x = x, min_y = y, max_x = x, max_y = y;
11780   int i;
11781
11782   for (i = 0; i < MAX_PLAYERS; i++)
11783   {
11784     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11785
11786     if (!stored_player[i].active || &stored_player[i] == player)
11787       continue;
11788
11789     min_x = MIN(min_x, jx);
11790     min_y = MIN(min_y, jy);
11791     max_x = MAX(max_x, jx);
11792     max_y = MAX(max_y, jy);
11793   }
11794
11795   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11796 }
11797
11798 static boolean AllPlayersInVisibleScreen()
11799 {
11800   int i;
11801
11802   for (i = 0; i < MAX_PLAYERS; i++)
11803   {
11804     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11805
11806     if (!stored_player[i].active)
11807       continue;
11808
11809     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11810       return FALSE;
11811   }
11812
11813   return TRUE;
11814 }
11815
11816 void ScrollLevel(int dx, int dy)
11817 {
11818   int scroll_offset = 2 * TILEX_VAR;
11819   int x, y;
11820
11821   BlitBitmap(drawto_field, drawto_field,
11822              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11823              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11824              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11825              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11826              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11827              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11828
11829   if (dx != 0)
11830   {
11831     x = (dx == 1 ? BX1 : BX2);
11832     for (y = BY1; y <= BY2; y++)
11833       DrawScreenField(x, y);
11834   }
11835
11836   if (dy != 0)
11837   {
11838     y = (dy == 1 ? BY1 : BY2);
11839     for (x = BX1; x <= BX2; x++)
11840       DrawScreenField(x, y);
11841   }
11842
11843   redraw_mask |= REDRAW_FIELD;
11844 }
11845
11846 static boolean canFallDown(struct PlayerInfo *player)
11847 {
11848   int jx = player->jx, jy = player->jy;
11849
11850   return (IN_LEV_FIELD(jx, jy + 1) &&
11851           (IS_FREE(jx, jy + 1) ||
11852            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11853           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11854           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11855 }
11856
11857 static boolean canPassField(int x, int y, int move_dir)
11858 {
11859   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11860   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11861   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11862   int nextx = x + dx;
11863   int nexty = y + dy;
11864   int element = Feld[x][y];
11865
11866   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11867           !CAN_MOVE(element) &&
11868           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11869           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11870           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11871 }
11872
11873 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11874 {
11875   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11876   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11877   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11878   int newx = x + dx;
11879   int newy = y + dy;
11880
11881   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11882           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11883           (IS_DIGGABLE(Feld[newx][newy]) ||
11884            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11885            canPassField(newx, newy, move_dir)));
11886 }
11887
11888 static void CheckGravityMovement(struct PlayerInfo *player)
11889 {
11890   if (player->gravity && !player->programmed_action)
11891   {
11892     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11893     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11894     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11895     int jx = player->jx, jy = player->jy;
11896     boolean player_is_moving_to_valid_field =
11897       (!player_is_snapping &&
11898        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11899         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11900     boolean player_can_fall_down = canFallDown(player);
11901
11902     if (player_can_fall_down &&
11903         !player_is_moving_to_valid_field)
11904       player->programmed_action = MV_DOWN;
11905   }
11906 }
11907
11908 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11909 {
11910   return CheckGravityMovement(player);
11911
11912   if (player->gravity && !player->programmed_action)
11913   {
11914     int jx = player->jx, jy = player->jy;
11915     boolean field_under_player_is_free =
11916       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11917     boolean player_is_standing_on_valid_field =
11918       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11919        (IS_WALKABLE(Feld[jx][jy]) &&
11920         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11921
11922     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11923       player->programmed_action = MV_DOWN;
11924   }
11925 }
11926
11927 /*
11928   MovePlayerOneStep()
11929   -----------------------------------------------------------------------------
11930   dx, dy:               direction (non-diagonal) to try to move the player to
11931   real_dx, real_dy:     direction as read from input device (can be diagonal)
11932 */
11933
11934 boolean MovePlayerOneStep(struct PlayerInfo *player,
11935                           int dx, int dy, int real_dx, int real_dy)
11936 {
11937   int jx = player->jx, jy = player->jy;
11938   int new_jx = jx + dx, new_jy = jy + dy;
11939   int can_move;
11940   boolean player_can_move = !player->cannot_move;
11941
11942   if (!player->active || (!dx && !dy))
11943     return MP_NO_ACTION;
11944
11945   player->MovDir = (dx < 0 ? MV_LEFT :
11946                     dx > 0 ? MV_RIGHT :
11947                     dy < 0 ? MV_UP :
11948                     dy > 0 ? MV_DOWN :  MV_NONE);
11949
11950   if (!IN_LEV_FIELD(new_jx, new_jy))
11951     return MP_NO_ACTION;
11952
11953   if (!player_can_move)
11954   {
11955     if (player->MovPos == 0)
11956     {
11957       player->is_moving = FALSE;
11958       player->is_digging = FALSE;
11959       player->is_collecting = FALSE;
11960       player->is_snapping = FALSE;
11961       player->is_pushing = FALSE;
11962     }
11963   }
11964
11965   if (!options.network && game.centered_player_nr == -1 &&
11966       !AllPlayersInSight(player, new_jx, new_jy))
11967     return MP_NO_ACTION;
11968
11969   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11970   if (can_move != MP_MOVING)
11971     return can_move;
11972
11973   /* check if DigField() has caused relocation of the player */
11974   if (player->jx != jx || player->jy != jy)
11975     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11976
11977   StorePlayer[jx][jy] = 0;
11978   player->last_jx = jx;
11979   player->last_jy = jy;
11980   player->jx = new_jx;
11981   player->jy = new_jy;
11982   StorePlayer[new_jx][new_jy] = player->element_nr;
11983
11984   if (player->move_delay_value_next != -1)
11985   {
11986     player->move_delay_value = player->move_delay_value_next;
11987     player->move_delay_value_next = -1;
11988   }
11989
11990   player->MovPos =
11991     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11992
11993   player->step_counter++;
11994
11995   PlayerVisit[jx][jy] = FrameCounter;
11996
11997   player->is_moving = TRUE;
11998
11999 #if 1
12000   /* should better be called in MovePlayer(), but this breaks some tapes */
12001   ScrollPlayer(player, SCROLL_INIT);
12002 #endif
12003
12004   return MP_MOVING;
12005 }
12006
12007 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12008 {
12009   int jx = player->jx, jy = player->jy;
12010   int old_jx = jx, old_jy = jy;
12011   int moved = MP_NO_ACTION;
12012
12013   if (!player->active)
12014     return FALSE;
12015
12016   if (!dx && !dy)
12017   {
12018     if (player->MovPos == 0)
12019     {
12020       player->is_moving = FALSE;
12021       player->is_digging = FALSE;
12022       player->is_collecting = FALSE;
12023       player->is_snapping = FALSE;
12024       player->is_pushing = FALSE;
12025     }
12026
12027     return FALSE;
12028   }
12029
12030   if (player->move_delay > 0)
12031     return FALSE;
12032
12033   player->move_delay = -1;              /* set to "uninitialized" value */
12034
12035   /* store if player is automatically moved to next field */
12036   player->is_auto_moving = (player->programmed_action != MV_NONE);
12037
12038   /* remove the last programmed player action */
12039   player->programmed_action = 0;
12040
12041   if (player->MovPos)
12042   {
12043     /* should only happen if pre-1.2 tape recordings are played */
12044     /* this is only for backward compatibility */
12045
12046     int original_move_delay_value = player->move_delay_value;
12047
12048 #if DEBUG
12049     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12050            tape.counter);
12051 #endif
12052
12053     /* scroll remaining steps with finest movement resolution */
12054     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12055
12056     while (player->MovPos)
12057     {
12058       ScrollPlayer(player, SCROLL_GO_ON);
12059       ScrollScreen(NULL, SCROLL_GO_ON);
12060
12061       AdvanceFrameAndPlayerCounters(player->index_nr);
12062
12063       DrawAllPlayers();
12064       BackToFront();
12065     }
12066
12067     player->move_delay_value = original_move_delay_value;
12068   }
12069
12070   player->is_active = FALSE;
12071
12072   if (player->last_move_dir & MV_HORIZONTAL)
12073   {
12074     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12075       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12076   }
12077   else
12078   {
12079     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12080       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12081   }
12082
12083   if (!moved && !player->is_active)
12084   {
12085     player->is_moving = FALSE;
12086     player->is_digging = FALSE;
12087     player->is_collecting = FALSE;
12088     player->is_snapping = FALSE;
12089     player->is_pushing = FALSE;
12090   }
12091
12092   jx = player->jx;
12093   jy = player->jy;
12094
12095   if (moved & MP_MOVING && !ScreenMovPos &&
12096       (player->index_nr == game.centered_player_nr ||
12097        game.centered_player_nr == -1))
12098   {
12099     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12100     int offset = game.scroll_delay_value;
12101
12102     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12103     {
12104       /* actual player has left the screen -- scroll in that direction */
12105       if (jx != old_jx)         /* player has moved horizontally */
12106         scroll_x += (jx - old_jx);
12107       else                      /* player has moved vertically */
12108         scroll_y += (jy - old_jy);
12109     }
12110     else
12111     {
12112       if (jx != old_jx)         /* player has moved horizontally */
12113       {
12114         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12115             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12116           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12117
12118         /* don't scroll over playfield boundaries */
12119         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12120           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12121
12122         /* don't scroll more than one field at a time */
12123         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12124
12125         /* don't scroll against the player's moving direction */
12126         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12127             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12128           scroll_x = old_scroll_x;
12129       }
12130       else                      /* player has moved vertically */
12131       {
12132         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12133             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12134           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12135
12136         /* don't scroll over playfield boundaries */
12137         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12138           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12139
12140         /* don't scroll more than one field at a time */
12141         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12142
12143         /* don't scroll against the player's moving direction */
12144         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12145             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12146           scroll_y = old_scroll_y;
12147       }
12148     }
12149
12150     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12151     {
12152       if (!options.network && game.centered_player_nr == -1 &&
12153           !AllPlayersInVisibleScreen())
12154       {
12155         scroll_x = old_scroll_x;
12156         scroll_y = old_scroll_y;
12157       }
12158       else
12159       {
12160         ScrollScreen(player, SCROLL_INIT);
12161         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12162       }
12163     }
12164   }
12165
12166   player->StepFrame = 0;
12167
12168   if (moved & MP_MOVING)
12169   {
12170     if (old_jx != jx && old_jy == jy)
12171       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12172     else if (old_jx == jx && old_jy != jy)
12173       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12174
12175     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12176
12177     player->last_move_dir = player->MovDir;
12178     player->is_moving = TRUE;
12179     player->is_snapping = FALSE;
12180     player->is_switching = FALSE;
12181     player->is_dropping = FALSE;
12182     player->is_dropping_pressed = FALSE;
12183     player->drop_pressed_delay = 0;
12184
12185 #if 0
12186     /* should better be called here than above, but this breaks some tapes */
12187     ScrollPlayer(player, SCROLL_INIT);
12188 #endif
12189   }
12190   else
12191   {
12192     CheckGravityMovementWhenNotMoving(player);
12193
12194     player->is_moving = FALSE;
12195
12196     /* at this point, the player is allowed to move, but cannot move right now
12197        (e.g. because of something blocking the way) -- ensure that the player
12198        is also allowed to move in the next frame (in old versions before 3.1.1,
12199        the player was forced to wait again for eight frames before next try) */
12200
12201     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12202       player->move_delay = 0;   /* allow direct movement in the next frame */
12203   }
12204
12205   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12206     player->move_delay = player->move_delay_value;
12207
12208   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12209   {
12210     TestIfPlayerTouchesBadThing(jx, jy);
12211     TestIfPlayerTouchesCustomElement(jx, jy);
12212   }
12213
12214   if (!player->active)
12215     RemovePlayer(player);
12216
12217   return moved;
12218 }
12219
12220 void ScrollPlayer(struct PlayerInfo *player, int mode)
12221 {
12222   int jx = player->jx, jy = player->jy;
12223   int last_jx = player->last_jx, last_jy = player->last_jy;
12224   int move_stepsize = TILEX / player->move_delay_value;
12225
12226   if (!player->active)
12227     return;
12228
12229   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12230     return;
12231
12232   if (mode == SCROLL_INIT)
12233   {
12234     player->actual_frame_counter = FrameCounter;
12235     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12236
12237     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12238         Feld[last_jx][last_jy] == EL_EMPTY)
12239     {
12240       int last_field_block_delay = 0;   /* start with no blocking at all */
12241       int block_delay_adjustment = player->block_delay_adjustment;
12242
12243       /* if player blocks last field, add delay for exactly one move */
12244       if (player->block_last_field)
12245       {
12246         last_field_block_delay += player->move_delay_value;
12247
12248         /* when blocking enabled, prevent moving up despite gravity */
12249         if (player->gravity && player->MovDir == MV_UP)
12250           block_delay_adjustment = -1;
12251       }
12252
12253       /* add block delay adjustment (also possible when not blocking) */
12254       last_field_block_delay += block_delay_adjustment;
12255
12256       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12257       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12258     }
12259
12260     if (player->MovPos != 0)    /* player has not yet reached destination */
12261       return;
12262   }
12263   else if (!FrameReached(&player->actual_frame_counter, 1))
12264     return;
12265
12266   if (player->MovPos != 0)
12267   {
12268     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12269     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12270
12271     /* before DrawPlayer() to draw correct player graphic for this case */
12272     if (player->MovPos == 0)
12273       CheckGravityMovement(player);
12274   }
12275
12276   if (player->MovPos == 0)      /* player reached destination field */
12277   {
12278     if (player->move_delay_reset_counter > 0)
12279     {
12280       player->move_delay_reset_counter--;
12281
12282       if (player->move_delay_reset_counter == 0)
12283       {
12284         /* continue with normal speed after quickly moving through gate */
12285         HALVE_PLAYER_SPEED(player);
12286
12287         /* be able to make the next move without delay */
12288         player->move_delay = 0;
12289       }
12290     }
12291
12292     player->last_jx = jx;
12293     player->last_jy = jy;
12294
12295     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12296         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12297         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12298         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12299         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12300         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12301         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12302         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12303     {
12304       DrawPlayer(player);       /* needed here only to cleanup last field */
12305       RemovePlayer(player);
12306
12307       if (local_player->friends_still_needed == 0 ||
12308           IS_SP_ELEMENT(Feld[jx][jy]))
12309         PlayerWins(player);
12310     }
12311
12312     /* this breaks one level: "machine", level 000 */
12313     {
12314       int move_direction = player->MovDir;
12315       int enter_side = MV_DIR_OPPOSITE(move_direction);
12316       int leave_side = move_direction;
12317       int old_jx = last_jx;
12318       int old_jy = last_jy;
12319       int old_element = Feld[old_jx][old_jy];
12320       int new_element = Feld[jx][jy];
12321
12322       if (IS_CUSTOM_ELEMENT(old_element))
12323         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12324                                    CE_LEFT_BY_PLAYER,
12325                                    player->index_bit, leave_side);
12326
12327       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12328                                           CE_PLAYER_LEAVES_X,
12329                                           player->index_bit, leave_side);
12330
12331       if (IS_CUSTOM_ELEMENT(new_element))
12332         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12333                                    player->index_bit, enter_side);
12334
12335       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12336                                           CE_PLAYER_ENTERS_X,
12337                                           player->index_bit, enter_side);
12338
12339       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12340                                         CE_MOVE_OF_X, move_direction);
12341     }
12342
12343     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12344     {
12345       TestIfPlayerTouchesBadThing(jx, jy);
12346       TestIfPlayerTouchesCustomElement(jx, jy);
12347
12348       /* needed because pushed element has not yet reached its destination,
12349          so it would trigger a change event at its previous field location */
12350       if (!player->is_pushing)
12351         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12352
12353       if (!player->active)
12354         RemovePlayer(player);
12355     }
12356
12357     if (!local_player->LevelSolved && level.use_step_counter)
12358     {
12359       int i;
12360
12361       TimePlayed++;
12362
12363       if (TimeLeft > 0)
12364       {
12365         TimeLeft--;
12366
12367         if (TimeLeft <= 10 && setup.time_limit)
12368           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12369
12370         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12371
12372         DisplayGameControlValues();
12373
12374         if (!TimeLeft && setup.time_limit)
12375           for (i = 0; i < MAX_PLAYERS; i++)
12376             KillPlayer(&stored_player[i]);
12377       }
12378       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12379       {
12380         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12381
12382         DisplayGameControlValues();
12383       }
12384     }
12385
12386     if (tape.single_step && tape.recording && !tape.pausing &&
12387         !player->programmed_action)
12388       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12389
12390     if (!player->programmed_action)
12391       CheckSaveEngineSnapshot(player);
12392   }
12393 }
12394
12395 void ScrollScreen(struct PlayerInfo *player, int mode)
12396 {
12397   static unsigned int screen_frame_counter = 0;
12398
12399   if (mode == SCROLL_INIT)
12400   {
12401     /* set scrolling step size according to actual player's moving speed */
12402     ScrollStepSize = TILEX / player->move_delay_value;
12403
12404     screen_frame_counter = FrameCounter;
12405     ScreenMovDir = player->MovDir;
12406     ScreenMovPos = player->MovPos;
12407     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12408     return;
12409   }
12410   else if (!FrameReached(&screen_frame_counter, 1))
12411     return;
12412
12413   if (ScreenMovPos)
12414   {
12415     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12416     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12417     redraw_mask |= REDRAW_FIELD;
12418   }
12419   else
12420     ScreenMovDir = MV_NONE;
12421 }
12422
12423 void TestIfPlayerTouchesCustomElement(int x, int y)
12424 {
12425   static int xy[4][2] =
12426   {
12427     { 0, -1 },
12428     { -1, 0 },
12429     { +1, 0 },
12430     { 0, +1 }
12431   };
12432   static int trigger_sides[4][2] =
12433   {
12434     /* center side       border side */
12435     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12436     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12437     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12438     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12439   };
12440   static int touch_dir[4] =
12441   {
12442     MV_LEFT | MV_RIGHT,
12443     MV_UP   | MV_DOWN,
12444     MV_UP   | MV_DOWN,
12445     MV_LEFT | MV_RIGHT
12446   };
12447   int center_element = Feld[x][y];      /* should always be non-moving! */
12448   int i;
12449
12450   for (i = 0; i < NUM_DIRECTIONS; i++)
12451   {
12452     int xx = x + xy[i][0];
12453     int yy = y + xy[i][1];
12454     int center_side = trigger_sides[i][0];
12455     int border_side = trigger_sides[i][1];
12456     int border_element;
12457
12458     if (!IN_LEV_FIELD(xx, yy))
12459       continue;
12460
12461     if (IS_PLAYER(x, y))                /* player found at center element */
12462     {
12463       struct PlayerInfo *player = PLAYERINFO(x, y);
12464
12465       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12466         border_element = Feld[xx][yy];          /* may be moving! */
12467       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12468         border_element = Feld[xx][yy];
12469       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12470         border_element = MovingOrBlocked2Element(xx, yy);
12471       else
12472         continue;               /* center and border element do not touch */
12473
12474       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12475                                  player->index_bit, border_side);
12476       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12477                                           CE_PLAYER_TOUCHES_X,
12478                                           player->index_bit, border_side);
12479
12480       {
12481         /* use player element that is initially defined in the level playfield,
12482            not the player element that corresponds to the runtime player number
12483            (example: a level that contains EL_PLAYER_3 as the only player would
12484            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12485         int player_element = PLAYERINFO(x, y)->initial_element;
12486
12487         CheckElementChangeBySide(xx, yy, border_element, player_element,
12488                                  CE_TOUCHING_X, border_side);
12489       }
12490     }
12491     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12492     {
12493       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12494
12495       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12496       {
12497         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12498           continue;             /* center and border element do not touch */
12499       }
12500
12501       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12502                                  player->index_bit, center_side);
12503       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12504                                           CE_PLAYER_TOUCHES_X,
12505                                           player->index_bit, center_side);
12506
12507       {
12508         /* use player element that is initially defined in the level playfield,
12509            not the player element that corresponds to the runtime player number
12510            (example: a level that contains EL_PLAYER_3 as the only player would
12511            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12512         int player_element = PLAYERINFO(xx, yy)->initial_element;
12513
12514         CheckElementChangeBySide(x, y, center_element, player_element,
12515                                  CE_TOUCHING_X, center_side);
12516       }
12517
12518       break;
12519     }
12520   }
12521 }
12522
12523 void TestIfElementTouchesCustomElement(int x, int y)
12524 {
12525   static int xy[4][2] =
12526   {
12527     { 0, -1 },
12528     { -1, 0 },
12529     { +1, 0 },
12530     { 0, +1 }
12531   };
12532   static int trigger_sides[4][2] =
12533   {
12534     /* center side      border side */
12535     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12536     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12537     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12538     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12539   };
12540   static int touch_dir[4] =
12541   {
12542     MV_LEFT | MV_RIGHT,
12543     MV_UP   | MV_DOWN,
12544     MV_UP   | MV_DOWN,
12545     MV_LEFT | MV_RIGHT
12546   };
12547   boolean change_center_element = FALSE;
12548   int center_element = Feld[x][y];      /* should always be non-moving! */
12549   int border_element_old[NUM_DIRECTIONS];
12550   int i;
12551
12552   for (i = 0; i < NUM_DIRECTIONS; i++)
12553   {
12554     int xx = x + xy[i][0];
12555     int yy = y + xy[i][1];
12556     int border_element;
12557
12558     border_element_old[i] = -1;
12559
12560     if (!IN_LEV_FIELD(xx, yy))
12561       continue;
12562
12563     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12564       border_element = Feld[xx][yy];    /* may be moving! */
12565     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12566       border_element = Feld[xx][yy];
12567     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12568       border_element = MovingOrBlocked2Element(xx, yy);
12569     else
12570       continue;                 /* center and border element do not touch */
12571
12572     border_element_old[i] = border_element;
12573   }
12574
12575   for (i = 0; i < NUM_DIRECTIONS; i++)
12576   {
12577     int xx = x + xy[i][0];
12578     int yy = y + xy[i][1];
12579     int center_side = trigger_sides[i][0];
12580     int border_element = border_element_old[i];
12581
12582     if (border_element == -1)
12583       continue;
12584
12585     /* check for change of border element */
12586     CheckElementChangeBySide(xx, yy, border_element, center_element,
12587                              CE_TOUCHING_X, center_side);
12588
12589     /* (center element cannot be player, so we dont have to check this here) */
12590   }
12591
12592   for (i = 0; i < NUM_DIRECTIONS; i++)
12593   {
12594     int xx = x + xy[i][0];
12595     int yy = y + xy[i][1];
12596     int border_side = trigger_sides[i][1];
12597     int border_element = border_element_old[i];
12598
12599     if (border_element == -1)
12600       continue;
12601
12602     /* check for change of center element (but change it only once) */
12603     if (!change_center_element)
12604       change_center_element =
12605         CheckElementChangeBySide(x, y, center_element, border_element,
12606                                  CE_TOUCHING_X, border_side);
12607
12608     if (IS_PLAYER(xx, yy))
12609     {
12610       /* use player element that is initially defined in the level playfield,
12611          not the player element that corresponds to the runtime player number
12612          (example: a level that contains EL_PLAYER_3 as the only player would
12613          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12614       int player_element = PLAYERINFO(xx, yy)->initial_element;
12615
12616       CheckElementChangeBySide(x, y, center_element, player_element,
12617                                CE_TOUCHING_X, border_side);
12618     }
12619   }
12620 }
12621
12622 void TestIfElementHitsCustomElement(int x, int y, int direction)
12623 {
12624   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12625   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12626   int hitx = x + dx, hity = y + dy;
12627   int hitting_element = Feld[x][y];
12628   int touched_element;
12629
12630   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12631     return;
12632
12633   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12634                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12635
12636   if (IN_LEV_FIELD(hitx, hity))
12637   {
12638     int opposite_direction = MV_DIR_OPPOSITE(direction);
12639     int hitting_side = direction;
12640     int touched_side = opposite_direction;
12641     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12642                           MovDir[hitx][hity] != direction ||
12643                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12644
12645     object_hit = TRUE;
12646
12647     if (object_hit)
12648     {
12649       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12650                                CE_HITTING_X, touched_side);
12651
12652       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12653                                CE_HIT_BY_X, hitting_side);
12654
12655       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12656                                CE_HIT_BY_SOMETHING, opposite_direction);
12657
12658       if (IS_PLAYER(hitx, hity))
12659       {
12660         /* use player element that is initially defined in the level playfield,
12661            not the player element that corresponds to the runtime player number
12662            (example: a level that contains EL_PLAYER_3 as the only player would
12663            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12664         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12665
12666         CheckElementChangeBySide(x, y, hitting_element, player_element,
12667                                  CE_HITTING_X, touched_side);
12668       }
12669     }
12670   }
12671
12672   /* "hitting something" is also true when hitting the playfield border */
12673   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12674                            CE_HITTING_SOMETHING, direction);
12675 }
12676
12677 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12678 {
12679   int i, kill_x = -1, kill_y = -1;
12680
12681   int bad_element = -1;
12682   static int test_xy[4][2] =
12683   {
12684     { 0, -1 },
12685     { -1, 0 },
12686     { +1, 0 },
12687     { 0, +1 }
12688   };
12689   static int test_dir[4] =
12690   {
12691     MV_UP,
12692     MV_LEFT,
12693     MV_RIGHT,
12694     MV_DOWN
12695   };
12696
12697   for (i = 0; i < NUM_DIRECTIONS; i++)
12698   {
12699     int test_x, test_y, test_move_dir, test_element;
12700
12701     test_x = good_x + test_xy[i][0];
12702     test_y = good_y + test_xy[i][1];
12703
12704     if (!IN_LEV_FIELD(test_x, test_y))
12705       continue;
12706
12707     test_move_dir =
12708       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12709
12710     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12711
12712     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12713        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12714     */
12715     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12716         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12717     {
12718       kill_x = test_x;
12719       kill_y = test_y;
12720       bad_element = test_element;
12721
12722       break;
12723     }
12724   }
12725
12726   if (kill_x != -1 || kill_y != -1)
12727   {
12728     if (IS_PLAYER(good_x, good_y))
12729     {
12730       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12731
12732       if (player->shield_deadly_time_left > 0 &&
12733           !IS_INDESTRUCTIBLE(bad_element))
12734         Bang(kill_x, kill_y);
12735       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12736         KillPlayer(player);
12737     }
12738     else
12739       Bang(good_x, good_y);
12740   }
12741 }
12742
12743 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12744 {
12745   int i, kill_x = -1, kill_y = -1;
12746   int bad_element = Feld[bad_x][bad_y];
12747   static int test_xy[4][2] =
12748   {
12749     { 0, -1 },
12750     { -1, 0 },
12751     { +1, 0 },
12752     { 0, +1 }
12753   };
12754   static int touch_dir[4] =
12755   {
12756     MV_LEFT | MV_RIGHT,
12757     MV_UP   | MV_DOWN,
12758     MV_UP   | MV_DOWN,
12759     MV_LEFT | MV_RIGHT
12760   };
12761   static int test_dir[4] =
12762   {
12763     MV_UP,
12764     MV_LEFT,
12765     MV_RIGHT,
12766     MV_DOWN
12767   };
12768
12769   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12770     return;
12771
12772   for (i = 0; i < NUM_DIRECTIONS; i++)
12773   {
12774     int test_x, test_y, test_move_dir, test_element;
12775
12776     test_x = bad_x + test_xy[i][0];
12777     test_y = bad_y + test_xy[i][1];
12778
12779     if (!IN_LEV_FIELD(test_x, test_y))
12780       continue;
12781
12782     test_move_dir =
12783       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12784
12785     test_element = Feld[test_x][test_y];
12786
12787     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12788        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12789     */
12790     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12791         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12792     {
12793       /* good thing is player or penguin that does not move away */
12794       if (IS_PLAYER(test_x, test_y))
12795       {
12796         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12797
12798         if (bad_element == EL_ROBOT && player->is_moving)
12799           continue;     /* robot does not kill player if he is moving */
12800
12801         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12802         {
12803           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12804             continue;           /* center and border element do not touch */
12805         }
12806
12807         kill_x = test_x;
12808         kill_y = test_y;
12809
12810         break;
12811       }
12812       else if (test_element == EL_PENGUIN)
12813       {
12814         kill_x = test_x;
12815         kill_y = test_y;
12816
12817         break;
12818       }
12819     }
12820   }
12821
12822   if (kill_x != -1 || kill_y != -1)
12823   {
12824     if (IS_PLAYER(kill_x, kill_y))
12825     {
12826       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12827
12828       if (player->shield_deadly_time_left > 0 &&
12829           !IS_INDESTRUCTIBLE(bad_element))
12830         Bang(bad_x, bad_y);
12831       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12832         KillPlayer(player);
12833     }
12834     else
12835       Bang(kill_x, kill_y);
12836   }
12837 }
12838
12839 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12840 {
12841   int bad_element = Feld[bad_x][bad_y];
12842   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12843   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12844   int test_x = bad_x + dx, test_y = bad_y + dy;
12845   int test_move_dir, test_element;
12846   int kill_x = -1, kill_y = -1;
12847
12848   if (!IN_LEV_FIELD(test_x, test_y))
12849     return;
12850
12851   test_move_dir =
12852     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12853
12854   test_element = Feld[test_x][test_y];
12855
12856   if (test_move_dir != bad_move_dir)
12857   {
12858     /* good thing can be player or penguin that does not move away */
12859     if (IS_PLAYER(test_x, test_y))
12860     {
12861       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12862
12863       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12864          player as being hit when he is moving towards the bad thing, because
12865          the "get hit by" condition would be lost after the player stops) */
12866       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12867         return;         /* player moves away from bad thing */
12868
12869       kill_x = test_x;
12870       kill_y = test_y;
12871     }
12872     else if (test_element == EL_PENGUIN)
12873     {
12874       kill_x = test_x;
12875       kill_y = test_y;
12876     }
12877   }
12878
12879   if (kill_x != -1 || kill_y != -1)
12880   {
12881     if (IS_PLAYER(kill_x, kill_y))
12882     {
12883       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12884
12885       if (player->shield_deadly_time_left > 0 &&
12886           !IS_INDESTRUCTIBLE(bad_element))
12887         Bang(bad_x, bad_y);
12888       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12889         KillPlayer(player);
12890     }
12891     else
12892       Bang(kill_x, kill_y);
12893   }
12894 }
12895
12896 void TestIfPlayerTouchesBadThing(int x, int y)
12897 {
12898   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12899 }
12900
12901 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12902 {
12903   TestIfGoodThingHitsBadThing(x, y, move_dir);
12904 }
12905
12906 void TestIfBadThingTouchesPlayer(int x, int y)
12907 {
12908   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12909 }
12910
12911 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12912 {
12913   TestIfBadThingHitsGoodThing(x, y, move_dir);
12914 }
12915
12916 void TestIfFriendTouchesBadThing(int x, int y)
12917 {
12918   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12919 }
12920
12921 void TestIfBadThingTouchesFriend(int x, int y)
12922 {
12923   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12924 }
12925
12926 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12927 {
12928   int i, kill_x = bad_x, kill_y = bad_y;
12929   static int xy[4][2] =
12930   {
12931     { 0, -1 },
12932     { -1, 0 },
12933     { +1, 0 },
12934     { 0, +1 }
12935   };
12936
12937   for (i = 0; i < NUM_DIRECTIONS; i++)
12938   {
12939     int x, y, element;
12940
12941     x = bad_x + xy[i][0];
12942     y = bad_y + xy[i][1];
12943     if (!IN_LEV_FIELD(x, y))
12944       continue;
12945
12946     element = Feld[x][y];
12947     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12948         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12949     {
12950       kill_x = x;
12951       kill_y = y;
12952       break;
12953     }
12954   }
12955
12956   if (kill_x != bad_x || kill_y != bad_y)
12957     Bang(bad_x, bad_y);
12958 }
12959
12960 void KillPlayer(struct PlayerInfo *player)
12961 {
12962   int jx = player->jx, jy = player->jy;
12963
12964   if (!player->active)
12965     return;
12966
12967 #if 0
12968   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12969          player->killed, player->active, player->reanimated);
12970 #endif
12971
12972   /* the following code was introduced to prevent an infinite loop when calling
12973      -> Bang()
12974      -> CheckTriggeredElementChangeExt()
12975      -> ExecuteCustomElementAction()
12976      -> KillPlayer()
12977      -> (infinitely repeating the above sequence of function calls)
12978      which occurs when killing the player while having a CE with the setting
12979      "kill player X when explosion of <player X>"; the solution using a new
12980      field "player->killed" was chosen for backwards compatibility, although
12981      clever use of the fields "player->active" etc. would probably also work */
12982 #if 1
12983   if (player->killed)
12984     return;
12985 #endif
12986
12987   player->killed = TRUE;
12988
12989   /* remove accessible field at the player's position */
12990   Feld[jx][jy] = EL_EMPTY;
12991
12992   /* deactivate shield (else Bang()/Explode() would not work right) */
12993   player->shield_normal_time_left = 0;
12994   player->shield_deadly_time_left = 0;
12995
12996 #if 0
12997   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12998          player->killed, player->active, player->reanimated);
12999 #endif
13000
13001   Bang(jx, jy);
13002
13003 #if 0
13004   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13005          player->killed, player->active, player->reanimated);
13006 #endif
13007
13008   if (player->reanimated)       /* killed player may have been reanimated */
13009     player->killed = player->reanimated = FALSE;
13010   else
13011     BuryPlayer(player);
13012 }
13013
13014 static void KillPlayerUnlessEnemyProtected(int x, int y)
13015 {
13016   if (!PLAYER_ENEMY_PROTECTED(x, y))
13017     KillPlayer(PLAYERINFO(x, y));
13018 }
13019
13020 static void KillPlayerUnlessExplosionProtected(int x, int y)
13021 {
13022   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13023     KillPlayer(PLAYERINFO(x, y));
13024 }
13025
13026 void BuryPlayer(struct PlayerInfo *player)
13027 {
13028   int jx = player->jx, jy = player->jy;
13029
13030   if (!player->active)
13031     return;
13032
13033   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13034   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13035
13036   player->GameOver = TRUE;
13037   RemovePlayer(player);
13038 }
13039
13040 void RemovePlayer(struct PlayerInfo *player)
13041 {
13042   int jx = player->jx, jy = player->jy;
13043   int i, found = FALSE;
13044
13045   player->present = FALSE;
13046   player->active = FALSE;
13047
13048   if (!ExplodeField[jx][jy])
13049     StorePlayer[jx][jy] = 0;
13050
13051   if (player->is_moving)
13052     TEST_DrawLevelField(player->last_jx, player->last_jy);
13053
13054   for (i = 0; i < MAX_PLAYERS; i++)
13055     if (stored_player[i].active)
13056       found = TRUE;
13057
13058   if (!found)
13059     AllPlayersGone = TRUE;
13060
13061   ExitX = ZX = jx;
13062   ExitY = ZY = jy;
13063 }
13064
13065 static void setFieldForSnapping(int x, int y, int element, int direction)
13066 {
13067   struct ElementInfo *ei = &element_info[element];
13068   int direction_bit = MV_DIR_TO_BIT(direction);
13069   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13070   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13071                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13072
13073   Feld[x][y] = EL_ELEMENT_SNAPPING;
13074   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13075
13076   ResetGfxAnimation(x, y);
13077
13078   GfxElement[x][y] = element;
13079   GfxAction[x][y] = action;
13080   GfxDir[x][y] = direction;
13081   GfxFrame[x][y] = -1;
13082 }
13083
13084 /*
13085   =============================================================================
13086   checkDiagonalPushing()
13087   -----------------------------------------------------------------------------
13088   check if diagonal input device direction results in pushing of object
13089   (by checking if the alternative direction is walkable, diggable, ...)
13090   =============================================================================
13091 */
13092
13093 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13094                                     int x, int y, int real_dx, int real_dy)
13095 {
13096   int jx, jy, dx, dy, xx, yy;
13097
13098   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13099     return TRUE;
13100
13101   /* diagonal direction: check alternative direction */
13102   jx = player->jx;
13103   jy = player->jy;
13104   dx = x - jx;
13105   dy = y - jy;
13106   xx = jx + (dx == 0 ? real_dx : 0);
13107   yy = jy + (dy == 0 ? real_dy : 0);
13108
13109   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13110 }
13111
13112 /*
13113   =============================================================================
13114   DigField()
13115   -----------------------------------------------------------------------------
13116   x, y:                 field next to player (non-diagonal) to try to dig to
13117   real_dx, real_dy:     direction as read from input device (can be diagonal)
13118   =============================================================================
13119 */
13120
13121 static int DigField(struct PlayerInfo *player,
13122                     int oldx, int oldy, int x, int y,
13123                     int real_dx, int real_dy, int mode)
13124 {
13125   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13126   boolean player_was_pushing = player->is_pushing;
13127   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13128   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13129   int jx = oldx, jy = oldy;
13130   int dx = x - jx, dy = y - jy;
13131   int nextx = x + dx, nexty = y + dy;
13132   int move_direction = (dx == -1 ? MV_LEFT  :
13133                         dx == +1 ? MV_RIGHT :
13134                         dy == -1 ? MV_UP    :
13135                         dy == +1 ? MV_DOWN  : MV_NONE);
13136   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13137   int dig_side = MV_DIR_OPPOSITE(move_direction);
13138   int old_element = Feld[jx][jy];
13139   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13140   int collect_count;
13141
13142   if (is_player)                /* function can also be called by EL_PENGUIN */
13143   {
13144     if (player->MovPos == 0)
13145     {
13146       player->is_digging = FALSE;
13147       player->is_collecting = FALSE;
13148     }
13149
13150     if (player->MovPos == 0)    /* last pushing move finished */
13151       player->is_pushing = FALSE;
13152
13153     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13154     {
13155       player->is_switching = FALSE;
13156       player->push_delay = -1;
13157
13158       return MP_NO_ACTION;
13159     }
13160   }
13161
13162   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13163     old_element = Back[jx][jy];
13164
13165   /* in case of element dropped at player position, check background */
13166   else if (Back[jx][jy] != EL_EMPTY &&
13167            game.engine_version >= VERSION_IDENT(2,2,0,0))
13168     old_element = Back[jx][jy];
13169
13170   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13171     return MP_NO_ACTION;        /* field has no opening in this direction */
13172
13173   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13174     return MP_NO_ACTION;        /* field has no opening in this direction */
13175
13176   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13177   {
13178     SplashAcid(x, y);
13179
13180     Feld[jx][jy] = player->artwork_element;
13181     InitMovingField(jx, jy, MV_DOWN);
13182     Store[jx][jy] = EL_ACID;
13183     ContinueMoving(jx, jy);
13184     BuryPlayer(player);
13185
13186     return MP_DONT_RUN_INTO;
13187   }
13188
13189   if (player_can_move && DONT_RUN_INTO(element))
13190   {
13191     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13192
13193     return MP_DONT_RUN_INTO;
13194   }
13195
13196   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13197     return MP_NO_ACTION;
13198
13199   collect_count = element_info[element].collect_count_initial;
13200
13201   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13202     return MP_NO_ACTION;
13203
13204   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13205     player_can_move = player_can_move_or_snap;
13206
13207   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13208       game.engine_version >= VERSION_IDENT(2,2,0,0))
13209   {
13210     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13211                                player->index_bit, dig_side);
13212     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13213                                         player->index_bit, dig_side);
13214
13215     if (element == EL_DC_LANDMINE)
13216       Bang(x, y);
13217
13218     if (Feld[x][y] != element)          /* field changed by snapping */
13219       return MP_ACTION;
13220
13221     return MP_NO_ACTION;
13222   }
13223
13224   if (player->gravity && is_player && !player->is_auto_moving &&
13225       canFallDown(player) && move_direction != MV_DOWN &&
13226       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13227     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13228
13229   if (player_can_move &&
13230       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13231   {
13232     int sound_element = SND_ELEMENT(element);
13233     int sound_action = ACTION_WALKING;
13234
13235     if (IS_RND_GATE(element))
13236     {
13237       if (!player->key[RND_GATE_NR(element)])
13238         return MP_NO_ACTION;
13239     }
13240     else if (IS_RND_GATE_GRAY(element))
13241     {
13242       if (!player->key[RND_GATE_GRAY_NR(element)])
13243         return MP_NO_ACTION;
13244     }
13245     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13246     {
13247       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13248         return MP_NO_ACTION;
13249     }
13250     else if (element == EL_EXIT_OPEN ||
13251              element == EL_EM_EXIT_OPEN ||
13252              element == EL_EM_EXIT_OPENING ||
13253              element == EL_STEEL_EXIT_OPEN ||
13254              element == EL_EM_STEEL_EXIT_OPEN ||
13255              element == EL_EM_STEEL_EXIT_OPENING ||
13256              element == EL_SP_EXIT_OPEN ||
13257              element == EL_SP_EXIT_OPENING)
13258     {
13259       sound_action = ACTION_PASSING;    /* player is passing exit */
13260     }
13261     else if (element == EL_EMPTY)
13262     {
13263       sound_action = ACTION_MOVING;             /* nothing to walk on */
13264     }
13265
13266     /* play sound from background or player, whatever is available */
13267     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13268       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13269     else
13270       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13271   }
13272   else if (player_can_move &&
13273            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13274   {
13275     if (!ACCESS_FROM(element, opposite_direction))
13276       return MP_NO_ACTION;      /* field not accessible from this direction */
13277
13278     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13279       return MP_NO_ACTION;
13280
13281     if (IS_EM_GATE(element))
13282     {
13283       if (!player->key[EM_GATE_NR(element)])
13284         return MP_NO_ACTION;
13285     }
13286     else if (IS_EM_GATE_GRAY(element))
13287     {
13288       if (!player->key[EM_GATE_GRAY_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13292     {
13293       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (IS_EMC_GATE(element))
13297     {
13298       if (!player->key[EMC_GATE_NR(element)])
13299         return MP_NO_ACTION;
13300     }
13301     else if (IS_EMC_GATE_GRAY(element))
13302     {
13303       if (!player->key[EMC_GATE_GRAY_NR(element)])
13304         return MP_NO_ACTION;
13305     }
13306     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13307     {
13308       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13309         return MP_NO_ACTION;
13310     }
13311     else if (element == EL_DC_GATE_WHITE ||
13312              element == EL_DC_GATE_WHITE_GRAY ||
13313              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13314     {
13315       if (player->num_white_keys == 0)
13316         return MP_NO_ACTION;
13317
13318       player->num_white_keys--;
13319     }
13320     else if (IS_SP_PORT(element))
13321     {
13322       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13323           element == EL_SP_GRAVITY_PORT_RIGHT ||
13324           element == EL_SP_GRAVITY_PORT_UP ||
13325           element == EL_SP_GRAVITY_PORT_DOWN)
13326         player->gravity = !player->gravity;
13327       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13328                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13329                element == EL_SP_GRAVITY_ON_PORT_UP ||
13330                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13331         player->gravity = TRUE;
13332       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13333                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13334                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13335                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13336         player->gravity = FALSE;
13337     }
13338
13339     /* automatically move to the next field with double speed */
13340     player->programmed_action = move_direction;
13341
13342     if (player->move_delay_reset_counter == 0)
13343     {
13344       player->move_delay_reset_counter = 2;     /* two double speed steps */
13345
13346       DOUBLE_PLAYER_SPEED(player);
13347     }
13348
13349     PlayLevelSoundAction(x, y, ACTION_PASSING);
13350   }
13351   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13352   {
13353     RemoveField(x, y);
13354
13355     if (mode != DF_SNAP)
13356     {
13357       GfxElement[x][y] = GFX_ELEMENT(element);
13358       player->is_digging = TRUE;
13359     }
13360
13361     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13362
13363     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13364                                         player->index_bit, dig_side);
13365
13366     if (mode == DF_SNAP)
13367     {
13368       if (level.block_snap_field)
13369         setFieldForSnapping(x, y, element, move_direction);
13370       else
13371         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13372
13373       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13374                                           player->index_bit, dig_side);
13375     }
13376   }
13377   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13378   {
13379     RemoveField(x, y);
13380
13381     if (is_player && mode != DF_SNAP)
13382     {
13383       GfxElement[x][y] = element;
13384       player->is_collecting = TRUE;
13385     }
13386
13387     if (element == EL_SPEED_PILL)
13388     {
13389       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13390     }
13391     else if (element == EL_EXTRA_TIME && level.time > 0)
13392     {
13393       TimeLeft += level.extra_time;
13394
13395       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13396
13397       DisplayGameControlValues();
13398     }
13399     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13400     {
13401       player->shield_normal_time_left += level.shield_normal_time;
13402       if (element == EL_SHIELD_DEADLY)
13403         player->shield_deadly_time_left += level.shield_deadly_time;
13404     }
13405     else if (element == EL_DYNAMITE ||
13406              element == EL_EM_DYNAMITE ||
13407              element == EL_SP_DISK_RED)
13408     {
13409       if (player->inventory_size < MAX_INVENTORY_SIZE)
13410         player->inventory_element[player->inventory_size++] = element;
13411
13412       DrawGameDoorValues();
13413     }
13414     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13415     {
13416       player->dynabomb_count++;
13417       player->dynabombs_left++;
13418     }
13419     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13420     {
13421       player->dynabomb_size++;
13422     }
13423     else if (element == EL_DYNABOMB_INCREASE_POWER)
13424     {
13425       player->dynabomb_xl = TRUE;
13426     }
13427     else if (IS_KEY(element))
13428     {
13429       player->key[KEY_NR(element)] = TRUE;
13430
13431       DrawGameDoorValues();
13432     }
13433     else if (element == EL_DC_KEY_WHITE)
13434     {
13435       player->num_white_keys++;
13436
13437       /* display white keys? */
13438       /* DrawGameDoorValues(); */
13439     }
13440     else if (IS_ENVELOPE(element))
13441     {
13442       player->show_envelope = element;
13443     }
13444     else if (element == EL_EMC_LENSES)
13445     {
13446       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13447
13448       RedrawAllInvisibleElementsForLenses();
13449     }
13450     else if (element == EL_EMC_MAGNIFIER)
13451     {
13452       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13453
13454       RedrawAllInvisibleElementsForMagnifier();
13455     }
13456     else if (IS_DROPPABLE(element) ||
13457              IS_THROWABLE(element))     /* can be collected and dropped */
13458     {
13459       int i;
13460
13461       if (collect_count == 0)
13462         player->inventory_infinite_element = element;
13463       else
13464         for (i = 0; i < collect_count; i++)
13465           if (player->inventory_size < MAX_INVENTORY_SIZE)
13466             player->inventory_element[player->inventory_size++] = element;
13467
13468       DrawGameDoorValues();
13469     }
13470     else if (collect_count > 0)
13471     {
13472       local_player->gems_still_needed -= collect_count;
13473       if (local_player->gems_still_needed < 0)
13474         local_player->gems_still_needed = 0;
13475
13476       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13477
13478       DisplayGameControlValues();
13479     }
13480
13481     RaiseScoreElement(element);
13482     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13483
13484     if (is_player)
13485       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13486                                           player->index_bit, dig_side);
13487
13488     if (mode == DF_SNAP)
13489     {
13490       if (level.block_snap_field)
13491         setFieldForSnapping(x, y, element, move_direction);
13492       else
13493         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13494
13495       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13496                                           player->index_bit, dig_side);
13497     }
13498   }
13499   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13500   {
13501     if (mode == DF_SNAP && element != EL_BD_ROCK)
13502       return MP_NO_ACTION;
13503
13504     if (CAN_FALL(element) && dy)
13505       return MP_NO_ACTION;
13506
13507     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13508         !(element == EL_SPRING && level.use_spring_bug))
13509       return MP_NO_ACTION;
13510
13511     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13512         ((move_direction & MV_VERTICAL &&
13513           ((element_info[element].move_pattern & MV_LEFT &&
13514             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13515            (element_info[element].move_pattern & MV_RIGHT &&
13516             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13517          (move_direction & MV_HORIZONTAL &&
13518           ((element_info[element].move_pattern & MV_UP &&
13519             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13520            (element_info[element].move_pattern & MV_DOWN &&
13521             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13522       return MP_NO_ACTION;
13523
13524     /* do not push elements already moving away faster than player */
13525     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13526         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13527       return MP_NO_ACTION;
13528
13529     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13530     {
13531       if (player->push_delay_value == -1 || !player_was_pushing)
13532         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13533     }
13534     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13535     {
13536       if (player->push_delay_value == -1)
13537         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13538     }
13539     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13540     {
13541       if (!player->is_pushing)
13542         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13543     }
13544
13545     player->is_pushing = TRUE;
13546     player->is_active = TRUE;
13547
13548     if (!(IN_LEV_FIELD(nextx, nexty) &&
13549           (IS_FREE(nextx, nexty) ||
13550            (IS_SB_ELEMENT(element) &&
13551             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13552            (IS_CUSTOM_ELEMENT(element) &&
13553             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13554       return MP_NO_ACTION;
13555
13556     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13557       return MP_NO_ACTION;
13558
13559     if (player->push_delay == -1)       /* new pushing; restart delay */
13560       player->push_delay = 0;
13561
13562     if (player->push_delay < player->push_delay_value &&
13563         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13564         element != EL_SPRING && element != EL_BALLOON)
13565     {
13566       /* make sure that there is no move delay before next try to push */
13567       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13568         player->move_delay = 0;
13569
13570       return MP_NO_ACTION;
13571     }
13572
13573     if (IS_CUSTOM_ELEMENT(element) &&
13574         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13575     {
13576       if (!DigFieldByCE(nextx, nexty, element))
13577         return MP_NO_ACTION;
13578     }
13579
13580     if (IS_SB_ELEMENT(element))
13581     {
13582       if (element == EL_SOKOBAN_FIELD_FULL)
13583       {
13584         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13585         local_player->sokobanfields_still_needed++;
13586       }
13587
13588       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13589       {
13590         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13591         local_player->sokobanfields_still_needed--;
13592       }
13593
13594       Feld[x][y] = EL_SOKOBAN_OBJECT;
13595
13596       if (Back[x][y] == Back[nextx][nexty])
13597         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13598       else if (Back[x][y] != 0)
13599         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13600                                     ACTION_EMPTYING);
13601       else
13602         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13603                                     ACTION_FILLING);
13604
13605       if (local_player->sokobanfields_still_needed == 0 &&
13606           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13607       {
13608         PlayerWins(player);
13609
13610         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13611       }
13612     }
13613     else
13614       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13615
13616     InitMovingField(x, y, move_direction);
13617     GfxAction[x][y] = ACTION_PUSHING;
13618
13619     if (mode == DF_SNAP)
13620       ContinueMoving(x, y);
13621     else
13622       MovPos[x][y] = (dx != 0 ? dx : dy);
13623
13624     Pushed[x][y] = TRUE;
13625     Pushed[nextx][nexty] = TRUE;
13626
13627     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13628       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13629     else
13630       player->push_delay_value = -1;    /* get new value later */
13631
13632     /* check for element change _after_ element has been pushed */
13633     if (game.use_change_when_pushing_bug)
13634     {
13635       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13636                                  player->index_bit, dig_side);
13637       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13638                                           player->index_bit, dig_side);
13639     }
13640   }
13641   else if (IS_SWITCHABLE(element))
13642   {
13643     if (PLAYER_SWITCHING(player, x, y))
13644     {
13645       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13646                                           player->index_bit, dig_side);
13647
13648       return MP_ACTION;
13649     }
13650
13651     player->is_switching = TRUE;
13652     player->switch_x = x;
13653     player->switch_y = y;
13654
13655     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13656
13657     if (element == EL_ROBOT_WHEEL)
13658     {
13659       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13660       ZX = x;
13661       ZY = y;
13662
13663       game.robot_wheel_active = TRUE;
13664
13665       TEST_DrawLevelField(x, y);
13666     }
13667     else if (element == EL_SP_TERMINAL)
13668     {
13669       int xx, yy;
13670
13671       SCAN_PLAYFIELD(xx, yy)
13672       {
13673         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13674           Bang(xx, yy);
13675         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13676           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13677       }
13678     }
13679     else if (IS_BELT_SWITCH(element))
13680     {
13681       ToggleBeltSwitch(x, y);
13682     }
13683     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13684              element == EL_SWITCHGATE_SWITCH_DOWN ||
13685              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13686              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13687     {
13688       ToggleSwitchgateSwitch(x, y);
13689     }
13690     else if (element == EL_LIGHT_SWITCH ||
13691              element == EL_LIGHT_SWITCH_ACTIVE)
13692     {
13693       ToggleLightSwitch(x, y);
13694     }
13695     else if (element == EL_TIMEGATE_SWITCH ||
13696              element == EL_DC_TIMEGATE_SWITCH)
13697     {
13698       ActivateTimegateSwitch(x, y);
13699     }
13700     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13701              element == EL_BALLOON_SWITCH_RIGHT ||
13702              element == EL_BALLOON_SWITCH_UP    ||
13703              element == EL_BALLOON_SWITCH_DOWN  ||
13704              element == EL_BALLOON_SWITCH_NONE  ||
13705              element == EL_BALLOON_SWITCH_ANY)
13706     {
13707       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13708                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13709                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13710                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13711                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13712                              move_direction);
13713     }
13714     else if (element == EL_LAMP)
13715     {
13716       Feld[x][y] = EL_LAMP_ACTIVE;
13717       local_player->lights_still_needed--;
13718
13719       ResetGfxAnimation(x, y);
13720       TEST_DrawLevelField(x, y);
13721     }
13722     else if (element == EL_TIME_ORB_FULL)
13723     {
13724       Feld[x][y] = EL_TIME_ORB_EMPTY;
13725
13726       if (level.time > 0 || level.use_time_orb_bug)
13727       {
13728         TimeLeft += level.time_orb_time;
13729         game.no_time_limit = FALSE;
13730
13731         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13732
13733         DisplayGameControlValues();
13734       }
13735
13736       ResetGfxAnimation(x, y);
13737       TEST_DrawLevelField(x, y);
13738     }
13739     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13740              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13741     {
13742       int xx, yy;
13743
13744       game.ball_state = !game.ball_state;
13745
13746       SCAN_PLAYFIELD(xx, yy)
13747       {
13748         int e = Feld[xx][yy];
13749
13750         if (game.ball_state)
13751         {
13752           if (e == EL_EMC_MAGIC_BALL)
13753             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13754           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13755             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13756         }
13757         else
13758         {
13759           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13760             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13761           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13762             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13763         }
13764       }
13765     }
13766
13767     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13768                                         player->index_bit, dig_side);
13769
13770     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13771                                         player->index_bit, dig_side);
13772
13773     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13774                                         player->index_bit, dig_side);
13775
13776     return MP_ACTION;
13777   }
13778   else
13779   {
13780     if (!PLAYER_SWITCHING(player, x, y))
13781     {
13782       player->is_switching = TRUE;
13783       player->switch_x = x;
13784       player->switch_y = y;
13785
13786       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13787                                  player->index_bit, dig_side);
13788       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13789                                           player->index_bit, dig_side);
13790
13791       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13792                                  player->index_bit, dig_side);
13793       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13794                                           player->index_bit, dig_side);
13795     }
13796
13797     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13798                                player->index_bit, dig_side);
13799     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13800                                         player->index_bit, dig_side);
13801
13802     return MP_NO_ACTION;
13803   }
13804
13805   player->push_delay = -1;
13806
13807   if (is_player)                /* function can also be called by EL_PENGUIN */
13808   {
13809     if (Feld[x][y] != element)          /* really digged/collected something */
13810     {
13811       player->is_collecting = !player->is_digging;
13812       player->is_active = TRUE;
13813     }
13814   }
13815
13816   return MP_MOVING;
13817 }
13818
13819 static boolean DigFieldByCE(int x, int y, int digging_element)
13820 {
13821   int element = Feld[x][y];
13822
13823   if (!IS_FREE(x, y))
13824   {
13825     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13826                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13827                   ACTION_BREAKING);
13828
13829     /* no element can dig solid indestructible elements */
13830     if (IS_INDESTRUCTIBLE(element) &&
13831         !IS_DIGGABLE(element) &&
13832         !IS_COLLECTIBLE(element))
13833       return FALSE;
13834
13835     if (AmoebaNr[x][y] &&
13836         (element == EL_AMOEBA_FULL ||
13837          element == EL_BD_AMOEBA ||
13838          element == EL_AMOEBA_GROWING))
13839     {
13840       AmoebaCnt[AmoebaNr[x][y]]--;
13841       AmoebaCnt2[AmoebaNr[x][y]]--;
13842     }
13843
13844     if (IS_MOVING(x, y))
13845       RemoveMovingField(x, y);
13846     else
13847     {
13848       RemoveField(x, y);
13849       TEST_DrawLevelField(x, y);
13850     }
13851
13852     /* if digged element was about to explode, prevent the explosion */
13853     ExplodeField[x][y] = EX_TYPE_NONE;
13854
13855     PlayLevelSoundAction(x, y, action);
13856   }
13857
13858   Store[x][y] = EL_EMPTY;
13859
13860   /* this makes it possible to leave the removed element again */
13861   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13862     Store[x][y] = element;
13863
13864   return TRUE;
13865 }
13866
13867 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13868 {
13869   int jx = player->jx, jy = player->jy;
13870   int x = jx + dx, y = jy + dy;
13871   int snap_direction = (dx == -1 ? MV_LEFT  :
13872                         dx == +1 ? MV_RIGHT :
13873                         dy == -1 ? MV_UP    :
13874                         dy == +1 ? MV_DOWN  : MV_NONE);
13875   boolean can_continue_snapping = (level.continuous_snapping &&
13876                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13877
13878   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13879     return FALSE;
13880
13881   if (!player->active || !IN_LEV_FIELD(x, y))
13882     return FALSE;
13883
13884   if (dx && dy)
13885     return FALSE;
13886
13887   if (!dx && !dy)
13888   {
13889     if (player->MovPos == 0)
13890       player->is_pushing = FALSE;
13891
13892     player->is_snapping = FALSE;
13893
13894     if (player->MovPos == 0)
13895     {
13896       player->is_moving = FALSE;
13897       player->is_digging = FALSE;
13898       player->is_collecting = FALSE;
13899     }
13900
13901     return FALSE;
13902   }
13903
13904   /* prevent snapping with already pressed snap key when not allowed */
13905   if (player->is_snapping && !can_continue_snapping)
13906     return FALSE;
13907
13908   player->MovDir = snap_direction;
13909
13910   if (player->MovPos == 0)
13911   {
13912     player->is_moving = FALSE;
13913     player->is_digging = FALSE;
13914     player->is_collecting = FALSE;
13915   }
13916
13917   player->is_dropping = FALSE;
13918   player->is_dropping_pressed = FALSE;
13919   player->drop_pressed_delay = 0;
13920
13921   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13922     return FALSE;
13923
13924   player->is_snapping = TRUE;
13925   player->is_active = TRUE;
13926
13927   if (player->MovPos == 0)
13928   {
13929     player->is_moving = FALSE;
13930     player->is_digging = FALSE;
13931     player->is_collecting = FALSE;
13932   }
13933
13934   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13935     TEST_DrawLevelField(player->last_jx, player->last_jy);
13936
13937   TEST_DrawLevelField(x, y);
13938
13939   return TRUE;
13940 }
13941
13942 static boolean DropElement(struct PlayerInfo *player)
13943 {
13944   int old_element, new_element;
13945   int dropx = player->jx, dropy = player->jy;
13946   int drop_direction = player->MovDir;
13947   int drop_side = drop_direction;
13948   int drop_element = get_next_dropped_element(player);
13949
13950   player->is_dropping_pressed = TRUE;
13951
13952   /* do not drop an element on top of another element; when holding drop key
13953      pressed without moving, dropped element must move away before the next
13954      element can be dropped (this is especially important if the next element
13955      is dynamite, which can be placed on background for historical reasons) */
13956   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13957     return MP_ACTION;
13958
13959   if (IS_THROWABLE(drop_element))
13960   {
13961     dropx += GET_DX_FROM_DIR(drop_direction);
13962     dropy += GET_DY_FROM_DIR(drop_direction);
13963
13964     if (!IN_LEV_FIELD(dropx, dropy))
13965       return FALSE;
13966   }
13967
13968   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13969   new_element = drop_element;           /* default: no change when dropping */
13970
13971   /* check if player is active, not moving and ready to drop */
13972   if (!player->active || player->MovPos || player->drop_delay > 0)
13973     return FALSE;
13974
13975   /* check if player has anything that can be dropped */
13976   if (new_element == EL_UNDEFINED)
13977     return FALSE;
13978
13979   /* check if drop key was pressed long enough for EM style dynamite */
13980   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13981     return FALSE;
13982
13983   /* check if anything can be dropped at the current position */
13984   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13985     return FALSE;
13986
13987   /* collected custom elements can only be dropped on empty fields */
13988   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13989     return FALSE;
13990
13991   if (old_element != EL_EMPTY)
13992     Back[dropx][dropy] = old_element;   /* store old element on this field */
13993
13994   ResetGfxAnimation(dropx, dropy);
13995   ResetRandomAnimationValue(dropx, dropy);
13996
13997   if (player->inventory_size > 0 ||
13998       player->inventory_infinite_element != EL_UNDEFINED)
13999   {
14000     if (player->inventory_size > 0)
14001     {
14002       player->inventory_size--;
14003
14004       DrawGameDoorValues();
14005
14006       if (new_element == EL_DYNAMITE)
14007         new_element = EL_DYNAMITE_ACTIVE;
14008       else if (new_element == EL_EM_DYNAMITE)
14009         new_element = EL_EM_DYNAMITE_ACTIVE;
14010       else if (new_element == EL_SP_DISK_RED)
14011         new_element = EL_SP_DISK_RED_ACTIVE;
14012     }
14013
14014     Feld[dropx][dropy] = new_element;
14015
14016     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14017       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14018                           el2img(Feld[dropx][dropy]), 0);
14019
14020     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14021
14022     /* needed if previous element just changed to "empty" in the last frame */
14023     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14024
14025     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14026                                player->index_bit, drop_side);
14027     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14028                                         CE_PLAYER_DROPS_X,
14029                                         player->index_bit, drop_side);
14030
14031     TestIfElementTouchesCustomElement(dropx, dropy);
14032   }
14033   else          /* player is dropping a dyna bomb */
14034   {
14035     player->dynabombs_left--;
14036
14037     Feld[dropx][dropy] = new_element;
14038
14039     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14040       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14041                           el2img(Feld[dropx][dropy]), 0);
14042
14043     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14044   }
14045
14046   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14047     InitField_WithBug1(dropx, dropy, FALSE);
14048
14049   new_element = Feld[dropx][dropy];     /* element might have changed */
14050
14051   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14052       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14053   {
14054     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14055       MovDir[dropx][dropy] = drop_direction;
14056
14057     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14058
14059     /* do not cause impact style collision by dropping elements that can fall */
14060     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14061   }
14062
14063   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14064   player->is_dropping = TRUE;
14065
14066   player->drop_pressed_delay = 0;
14067   player->is_dropping_pressed = FALSE;
14068
14069   player->drop_x = dropx;
14070   player->drop_y = dropy;
14071
14072   return TRUE;
14073 }
14074
14075 /* ------------------------------------------------------------------------- */
14076 /* game sound playing functions                                              */
14077 /* ------------------------------------------------------------------------- */
14078
14079 static int *loop_sound_frame = NULL;
14080 static int *loop_sound_volume = NULL;
14081
14082 void InitPlayLevelSound()
14083 {
14084   int num_sounds = getSoundListSize();
14085
14086   checked_free(loop_sound_frame);
14087   checked_free(loop_sound_volume);
14088
14089   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14090   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14091 }
14092
14093 static void PlayLevelSound(int x, int y, int nr)
14094 {
14095   int sx = SCREENX(x), sy = SCREENY(y);
14096   int volume, stereo_position;
14097   int max_distance = 8;
14098   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14099
14100   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14101       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14102     return;
14103
14104   if (!IN_LEV_FIELD(x, y) ||
14105       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14106       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14107     return;
14108
14109   volume = SOUND_MAX_VOLUME;
14110
14111   if (!IN_SCR_FIELD(sx, sy))
14112   {
14113     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14114     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14115
14116     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14117   }
14118
14119   stereo_position = (SOUND_MAX_LEFT +
14120                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14121                      (SCR_FIELDX + 2 * max_distance));
14122
14123   if (IS_LOOP_SOUND(nr))
14124   {
14125     /* This assures that quieter loop sounds do not overwrite louder ones,
14126        while restarting sound volume comparison with each new game frame. */
14127
14128     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14129       return;
14130
14131     loop_sound_volume[nr] = volume;
14132     loop_sound_frame[nr] = FrameCounter;
14133   }
14134
14135   PlaySoundExt(nr, volume, stereo_position, type);
14136 }
14137
14138 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14139 {
14140   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14141                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14142                  y < LEVELY(BY1) ? LEVELY(BY1) :
14143                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14144                  sound_action);
14145 }
14146
14147 static void PlayLevelSoundAction(int x, int y, int action)
14148 {
14149   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14150 }
14151
14152 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14153 {
14154   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14155
14156   if (sound_effect != SND_UNDEFINED)
14157     PlayLevelSound(x, y, sound_effect);
14158 }
14159
14160 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14161                                               int action)
14162 {
14163   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14164
14165   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14166     PlayLevelSound(x, y, sound_effect);
14167 }
14168
14169 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14170 {
14171   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14172
14173   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14174     PlayLevelSound(x, y, sound_effect);
14175 }
14176
14177 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14178 {
14179   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14180
14181   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14182     StopSound(sound_effect);
14183 }
14184
14185 static void PlayLevelMusic()
14186 {
14187   if (levelset.music[level_nr] != MUS_UNDEFINED)
14188     PlayMusic(levelset.music[level_nr]);        /* from config file */
14189   else
14190     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14191 }
14192
14193 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14194 {
14195   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14196   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14197   int x = xx - 1 - offset;
14198   int y = yy - 1 - offset;
14199
14200   switch (sample)
14201   {
14202     case SAMPLE_blank:
14203       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14204       break;
14205
14206     case SAMPLE_roll:
14207       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14208       break;
14209
14210     case SAMPLE_stone:
14211       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14212       break;
14213
14214     case SAMPLE_nut:
14215       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14216       break;
14217
14218     case SAMPLE_crack:
14219       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14220       break;
14221
14222     case SAMPLE_bug:
14223       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14224       break;
14225
14226     case SAMPLE_tank:
14227       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14228       break;
14229
14230     case SAMPLE_android_clone:
14231       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14232       break;
14233
14234     case SAMPLE_android_move:
14235       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14236       break;
14237
14238     case SAMPLE_spring:
14239       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14240       break;
14241
14242     case SAMPLE_slurp:
14243       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14244       break;
14245
14246     case SAMPLE_eater:
14247       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14248       break;
14249
14250     case SAMPLE_eater_eat:
14251       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14252       break;
14253
14254     case SAMPLE_alien:
14255       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14256       break;
14257
14258     case SAMPLE_collect:
14259       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14260       break;
14261
14262     case SAMPLE_diamond:
14263       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14264       break;
14265
14266     case SAMPLE_squash:
14267       /* !!! CHECK THIS !!! */
14268 #if 1
14269       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14270 #else
14271       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14272 #endif
14273       break;
14274
14275     case SAMPLE_wonderfall:
14276       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14277       break;
14278
14279     case SAMPLE_drip:
14280       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14281       break;
14282
14283     case SAMPLE_push:
14284       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14285       break;
14286
14287     case SAMPLE_dirt:
14288       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14289       break;
14290
14291     case SAMPLE_acid:
14292       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14293       break;
14294
14295     case SAMPLE_ball:
14296       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14297       break;
14298
14299     case SAMPLE_grow:
14300       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14301       break;
14302
14303     case SAMPLE_wonder:
14304       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14305       break;
14306
14307     case SAMPLE_door:
14308       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14309       break;
14310
14311     case SAMPLE_exit_open:
14312       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14313       break;
14314
14315     case SAMPLE_exit_leave:
14316       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14317       break;
14318
14319     case SAMPLE_dynamite:
14320       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14321       break;
14322
14323     case SAMPLE_tick:
14324       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14325       break;
14326
14327     case SAMPLE_press:
14328       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14329       break;
14330
14331     case SAMPLE_wheel:
14332       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14333       break;
14334
14335     case SAMPLE_boom:
14336       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14337       break;
14338
14339     case SAMPLE_die:
14340       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14341       break;
14342
14343     case SAMPLE_time:
14344       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14345       break;
14346
14347     default:
14348       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14349       break;
14350   }
14351 }
14352
14353 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14354 {
14355   int element = map_element_SP_to_RND(element_sp);
14356   int action = map_action_SP_to_RND(action_sp);
14357   int offset = (setup.sp_show_border_elements ? 0 : 1);
14358   int x = xx - offset;
14359   int y = yy - offset;
14360
14361   PlayLevelSoundElementAction(x, y, element, action);
14362 }
14363
14364 void RaiseScore(int value)
14365 {
14366   local_player->score += value;
14367
14368   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14369
14370   DisplayGameControlValues();
14371 }
14372
14373 void RaiseScoreElement(int element)
14374 {
14375   switch (element)
14376   {
14377     case EL_EMERALD:
14378     case EL_BD_DIAMOND:
14379     case EL_EMERALD_YELLOW:
14380     case EL_EMERALD_RED:
14381     case EL_EMERALD_PURPLE:
14382     case EL_SP_INFOTRON:
14383       RaiseScore(level.score[SC_EMERALD]);
14384       break;
14385     case EL_DIAMOND:
14386       RaiseScore(level.score[SC_DIAMOND]);
14387       break;
14388     case EL_CRYSTAL:
14389       RaiseScore(level.score[SC_CRYSTAL]);
14390       break;
14391     case EL_PEARL:
14392       RaiseScore(level.score[SC_PEARL]);
14393       break;
14394     case EL_BUG:
14395     case EL_BD_BUTTERFLY:
14396     case EL_SP_ELECTRON:
14397       RaiseScore(level.score[SC_BUG]);
14398       break;
14399     case EL_SPACESHIP:
14400     case EL_BD_FIREFLY:
14401     case EL_SP_SNIKSNAK:
14402       RaiseScore(level.score[SC_SPACESHIP]);
14403       break;
14404     case EL_YAMYAM:
14405     case EL_DARK_YAMYAM:
14406       RaiseScore(level.score[SC_YAMYAM]);
14407       break;
14408     case EL_ROBOT:
14409       RaiseScore(level.score[SC_ROBOT]);
14410       break;
14411     case EL_PACMAN:
14412       RaiseScore(level.score[SC_PACMAN]);
14413       break;
14414     case EL_NUT:
14415       RaiseScore(level.score[SC_NUT]);
14416       break;
14417     case EL_DYNAMITE:
14418     case EL_EM_DYNAMITE:
14419     case EL_SP_DISK_RED:
14420     case EL_DYNABOMB_INCREASE_NUMBER:
14421     case EL_DYNABOMB_INCREASE_SIZE:
14422     case EL_DYNABOMB_INCREASE_POWER:
14423       RaiseScore(level.score[SC_DYNAMITE]);
14424       break;
14425     case EL_SHIELD_NORMAL:
14426     case EL_SHIELD_DEADLY:
14427       RaiseScore(level.score[SC_SHIELD]);
14428       break;
14429     case EL_EXTRA_TIME:
14430       RaiseScore(level.extra_time_score);
14431       break;
14432     case EL_KEY_1:
14433     case EL_KEY_2:
14434     case EL_KEY_3:
14435     case EL_KEY_4:
14436     case EL_EM_KEY_1:
14437     case EL_EM_KEY_2:
14438     case EL_EM_KEY_3:
14439     case EL_EM_KEY_4:
14440     case EL_EMC_KEY_5:
14441     case EL_EMC_KEY_6:
14442     case EL_EMC_KEY_7:
14443     case EL_EMC_KEY_8:
14444     case EL_DC_KEY_WHITE:
14445       RaiseScore(level.score[SC_KEY]);
14446       break;
14447     default:
14448       RaiseScore(element_info[element].collect_score);
14449       break;
14450   }
14451 }
14452
14453 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14454 {
14455   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14456   {
14457     /* closing door required in case of envelope style request dialogs */
14458     if (!skip_request)
14459       CloseDoor(DOOR_CLOSE_1);
14460
14461 #if defined(NETWORK_AVALIABLE)
14462     if (options.network)
14463       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14464     else
14465 #endif
14466     {
14467       if (quick_quit)
14468       {
14469         FadeSkipNextFadeIn();
14470
14471         game_status = GAME_MODE_MAIN;
14472
14473         DrawAndFadeInMainMenu(REDRAW_FIELD);
14474       }
14475       else
14476       {
14477         game_status = GAME_MODE_MAIN;
14478
14479         DrawAndFadeInMainMenu(REDRAW_FIELD);
14480       }
14481     }
14482   }
14483   else          /* continue playing the game */
14484   {
14485     if (tape.playing && tape.deactivate_display)
14486       TapeDeactivateDisplayOff(TRUE);
14487
14488     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14489
14490     if (tape.playing && tape.deactivate_display)
14491       TapeDeactivateDisplayOn();
14492   }
14493 }
14494
14495 void RequestQuitGame(boolean ask_if_really_quit)
14496 {
14497   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14498   boolean skip_request = AllPlayersGone || quick_quit;
14499
14500   RequestQuitGameExt(skip_request, quick_quit,
14501                      "Do you really want to quit the game?");
14502 }
14503
14504
14505 /* ------------------------------------------------------------------------- */
14506 /* random generator functions                                                */
14507 /* ------------------------------------------------------------------------- */
14508
14509 unsigned int InitEngineRandom_RND(int seed)
14510 {
14511   game.num_random_calls = 0;
14512
14513   return InitEngineRandom(seed);
14514 }
14515
14516 unsigned int RND(int max)
14517 {
14518   if (max > 0)
14519   {
14520     game.num_random_calls++;
14521
14522     return GetEngineRandom(max);
14523   }
14524
14525   return 0;
14526 }
14527
14528
14529 /* ------------------------------------------------------------------------- */
14530 /* game engine snapshot handling functions                                   */
14531 /* ------------------------------------------------------------------------- */
14532
14533 struct EngineSnapshotInfo
14534 {
14535   /* runtime values for custom element collect score */
14536   int collect_score[NUM_CUSTOM_ELEMENTS];
14537
14538   /* runtime values for group element choice position */
14539   int choice_pos[NUM_GROUP_ELEMENTS];
14540
14541   /* runtime values for belt position animations */
14542   int belt_graphic[4][NUM_BELT_PARTS];
14543   int belt_anim_mode[4][NUM_BELT_PARTS];
14544 };
14545
14546 static struct EngineSnapshotInfo engine_snapshot_rnd;
14547 static char *snapshot_level_identifier = NULL;
14548 static int snapshot_level_nr = -1;
14549
14550 static void SaveEngineSnapshotValues_RND()
14551 {
14552   static int belt_base_active_element[4] =
14553   {
14554     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14555     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14556     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14557     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14558   };
14559   int i, j;
14560
14561   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14562   {
14563     int element = EL_CUSTOM_START + i;
14564
14565     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14566   }
14567
14568   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14569   {
14570     int element = EL_GROUP_START + i;
14571
14572     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14573   }
14574
14575   for (i = 0; i < 4; i++)
14576   {
14577     for (j = 0; j < NUM_BELT_PARTS; j++)
14578     {
14579       int element = belt_base_active_element[i] + j;
14580       int graphic = el2img(element);
14581       int anim_mode = graphic_info[graphic].anim_mode;
14582
14583       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14584       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14585     }
14586   }
14587 }
14588
14589 static void LoadEngineSnapshotValues_RND()
14590 {
14591   unsigned int num_random_calls = game.num_random_calls;
14592   int i, j;
14593
14594   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14595   {
14596     int element = EL_CUSTOM_START + i;
14597
14598     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14599   }
14600
14601   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14602   {
14603     int element = EL_GROUP_START + i;
14604
14605     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14606   }
14607
14608   for (i = 0; i < 4; i++)
14609   {
14610     for (j = 0; j < NUM_BELT_PARTS; j++)
14611     {
14612       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14613       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14614
14615       graphic_info[graphic].anim_mode = anim_mode;
14616     }
14617   }
14618
14619   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14620   {
14621     InitRND(tape.random_seed);
14622     for (i = 0; i < num_random_calls; i++)
14623       RND(1);
14624   }
14625
14626   if (game.num_random_calls != num_random_calls)
14627   {
14628     Error(ERR_INFO, "number of random calls out of sync");
14629     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14630     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14631     Error(ERR_EXIT, "this should not happen -- please debug");
14632   }
14633 }
14634
14635 void FreeEngineSnapshotSingle()
14636 {
14637   FreeSnapshotSingle();
14638
14639   setString(&snapshot_level_identifier, NULL);
14640   snapshot_level_nr = -1;
14641 }
14642
14643 void FreeEngineSnapshotList()
14644 {
14645   FreeSnapshotList();
14646 }
14647
14648 ListNode *SaveEngineSnapshotBuffers()
14649 {
14650   ListNode *buffers = NULL;
14651
14652   /* copy some special values to a structure better suited for the snapshot */
14653
14654   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14655     SaveEngineSnapshotValues_RND();
14656   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14657     SaveEngineSnapshotValues_EM();
14658   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14659     SaveEngineSnapshotValues_SP(&buffers);
14660
14661   /* save values stored in special snapshot structure */
14662
14663   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14664     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14665   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14666     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14667   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14668     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14669
14670   /* save further RND engine values */
14671
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14675
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14680
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14686
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14690
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14692
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14694
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14697
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14716
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14719
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14722   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14723
14724   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14725   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14730   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14732
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14735
14736 #if 0
14737   ListNode *node = engine_snapshot_list_rnd;
14738   int num_bytes = 0;
14739
14740   while (node != NULL)
14741   {
14742     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14743
14744     node = node->next;
14745   }
14746
14747   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14748 #endif
14749
14750   return buffers;
14751 }
14752
14753 void SaveEngineSnapshotSingle()
14754 {
14755   ListNode *buffers = SaveEngineSnapshotBuffers();
14756
14757   /* finally save all snapshot buffers to single snapshot */
14758   SaveSnapshotSingle(buffers);
14759
14760   /* save level identification information */
14761   setString(&snapshot_level_identifier, leveldir_current->identifier);
14762   snapshot_level_nr = level_nr;
14763 }
14764
14765 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14766 {
14767   boolean save_snapshot =
14768     (initial_snapshot ||
14769      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14770      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14771       game.snapshot.changed_action));
14772
14773   game.snapshot.changed_action = FALSE;
14774
14775   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14776       tape.quick_resume ||
14777       !save_snapshot)
14778     return FALSE;
14779
14780   ListNode *buffers = SaveEngineSnapshotBuffers();
14781
14782   /* finally save all snapshot buffers to snapshot list */
14783   SaveSnapshotToList(buffers);
14784
14785   return TRUE;
14786 }
14787
14788 boolean SaveEngineSnapshotToList()
14789 {
14790   return SaveEngineSnapshotToListExt(FALSE);
14791 }
14792
14793 void SaveEngineSnapshotToListInitial()
14794 {
14795   FreeEngineSnapshotList();
14796
14797   SaveEngineSnapshotToListExt(TRUE);
14798 }
14799
14800 void LoadEngineSnapshotValues()
14801 {
14802   /* restore special values from snapshot structure */
14803
14804   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14805     LoadEngineSnapshotValues_RND();
14806   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14807     LoadEngineSnapshotValues_EM();
14808   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14809     LoadEngineSnapshotValues_SP();
14810 }
14811
14812 void LoadEngineSnapshotSingle()
14813 {
14814   LoadSnapshotSingle();
14815
14816   LoadEngineSnapshotValues();
14817 }
14818
14819 void LoadEngineSnapshot_Undo(int steps)
14820 {
14821   LoadSnapshotFromList_Older(steps);
14822
14823   LoadEngineSnapshotValues();
14824 }
14825
14826 void LoadEngineSnapshot_Redo(int steps)
14827 {
14828   LoadSnapshotFromList_Newer(steps);
14829
14830   LoadEngineSnapshotValues();
14831 }
14832
14833 boolean CheckEngineSnapshotSingle()
14834 {
14835   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14836           snapshot_level_nr == level_nr);
14837 }
14838
14839 boolean CheckEngineSnapshotList()
14840 {
14841   return CheckSnapshotList();
14842 }
14843
14844
14845 /* ---------- new game button stuff ---------------------------------------- */
14846
14847 static struct
14848 {
14849   int graphic;
14850   struct XY *pos;
14851   int gadget_id;
14852   char *infotext;
14853 } gamebutton_info[NUM_GAME_BUTTONS] =
14854 {
14855   {
14856     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14857     GAME_CTRL_ID_STOP,                  "stop game"
14858   },
14859   {
14860     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14861     GAME_CTRL_ID_PAUSE,                 "pause game"
14862   },
14863   {
14864     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14865     GAME_CTRL_ID_PLAY,                  "play game"
14866   },
14867   {
14868     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14869     GAME_CTRL_ID_UNDO,                  "undo step"
14870   },
14871   {
14872     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14873     GAME_CTRL_ID_REDO,                  "redo step"
14874   },
14875   {
14876     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14877     GAME_CTRL_ID_SAVE,                  "save game"
14878   },
14879   {
14880     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14881     GAME_CTRL_ID_PAUSE2,                "pause game"
14882   },
14883   {
14884     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14885     GAME_CTRL_ID_LOAD,                  "load game"
14886   },
14887   {
14888     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14889     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14890   },
14891   {
14892     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14893     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14894   },
14895   {
14896     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14897     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14898   }
14899 };
14900
14901 void CreateGameButtons()
14902 {
14903   int i;
14904
14905   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14906   {
14907     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14908     struct XY *pos = gamebutton_info[i].pos;
14909     struct GadgetInfo *gi;
14910     int button_type;
14911     boolean checked;
14912     unsigned int event_mask;
14913     int base_x = (tape.show_game_buttons ? VX : DX);
14914     int base_y = (tape.show_game_buttons ? VY : DY);
14915     int gd_x   = gfx->src_x;
14916     int gd_y   = gfx->src_y;
14917     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14918     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14919     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14920     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14921     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14922     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14923     int id = i;
14924
14925     if (gfx->bitmap == NULL)
14926     {
14927       game_gadget[id] = NULL;
14928
14929       continue;
14930     }
14931
14932     if (id == GAME_CTRL_ID_STOP ||
14933         id == GAME_CTRL_ID_PLAY ||
14934         id == GAME_CTRL_ID_SAVE ||
14935         id == GAME_CTRL_ID_LOAD)
14936     {
14937       button_type = GD_TYPE_NORMAL_BUTTON;
14938       checked = FALSE;
14939       event_mask = GD_EVENT_RELEASED;
14940     }
14941     else if (id == GAME_CTRL_ID_UNDO ||
14942              id == GAME_CTRL_ID_REDO)
14943     {
14944       button_type = GD_TYPE_NORMAL_BUTTON;
14945       checked = FALSE;
14946       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14947     }
14948     else
14949     {
14950       button_type = GD_TYPE_CHECK_BUTTON;
14951       checked =
14952         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14953          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14954          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14955       event_mask = GD_EVENT_PRESSED;
14956     }
14957
14958     gi = CreateGadget(GDI_CUSTOM_ID, id,
14959                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14960                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14961                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14962                       GDI_WIDTH, gfx->width,
14963                       GDI_HEIGHT, gfx->height,
14964                       GDI_TYPE, button_type,
14965                       GDI_STATE, GD_BUTTON_UNPRESSED,
14966                       GDI_CHECKED, checked,
14967                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14968                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14969                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14970                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14971                       GDI_DIRECT_DRAW, FALSE,
14972                       GDI_EVENT_MASK, event_mask,
14973                       GDI_CALLBACK_ACTION, HandleGameButtons,
14974                       GDI_END);
14975
14976     if (gi == NULL)
14977       Error(ERR_EXIT, "cannot create gadget");
14978
14979     game_gadget[id] = gi;
14980   }
14981 }
14982
14983 void FreeGameButtons()
14984 {
14985   int i;
14986
14987   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14988     FreeGadget(game_gadget[i]);
14989 }
14990
14991 static void MapGameButtonsAtSamePosition(int id)
14992 {
14993   int i;
14994
14995   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14996     if (i != id &&
14997         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14998         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14999       MapGadget(game_gadget[i]);
15000 }
15001
15002 static void UnmapGameButtonsAtSamePosition(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       UnmapGadget(game_gadget[i]);
15011 }
15012
15013 void MapUndoRedoButtons()
15014 {
15015   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15016   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15017
15018   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15019   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15020 }
15021
15022 void UnmapUndoRedoButtons()
15023 {
15024   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15025   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15026
15027   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15028   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15029 }
15030
15031 void MapGameButtons()
15032 {
15033   int i;
15034
15035   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15036     if (i != GAME_CTRL_ID_UNDO &&
15037         i != GAME_CTRL_ID_REDO)
15038       MapGadget(game_gadget[i]);
15039
15040   if (setup.show_snapshot_buttons)
15041   {
15042     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15043     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15044     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15045   }
15046   else
15047   {
15048     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15049     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15050     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15051   }
15052
15053   RedrawGameButtons();
15054 }
15055
15056 void UnmapGameButtons()
15057 {
15058   int i;
15059
15060   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15061     UnmapGadget(game_gadget[i]);
15062 }
15063
15064 void RedrawGameButtons()
15065 {
15066   int i;
15067
15068   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15069     RedrawGadget(game_gadget[i]);
15070
15071   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15072   redraw_mask &= ~REDRAW_ALL;
15073 }
15074
15075 void GameUndoRedoExt()
15076 {
15077   ClearPlayerAction();
15078
15079   tape.pausing = TRUE;
15080
15081   RedrawPlayfield();
15082   UpdateAndDisplayGameControlValues();
15083
15084   DrawCompleteVideoDisplay();
15085   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15086   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15087   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15088                     VIDEO_STATE_1STEP_OFF), 0);
15089
15090   BackToFront();
15091 }
15092
15093 void GameUndo(int steps)
15094 {
15095   if (!CheckEngineSnapshotList())
15096     return;
15097
15098   LoadEngineSnapshot_Undo(steps);
15099
15100   GameUndoRedoExt();
15101 }
15102
15103 void GameRedo(int steps)
15104 {
15105   if (!CheckEngineSnapshotList())
15106     return;
15107
15108   LoadEngineSnapshot_Redo(steps);
15109
15110   GameUndoRedoExt();
15111 }
15112
15113 static void HandleGameButtonsExt(int id, int button)
15114 {
15115   int steps = BUTTON_STEPSIZE(button);
15116   boolean handle_game_buttons =
15117     (game_status == GAME_MODE_PLAYING ||
15118      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15119
15120   if (!handle_game_buttons)
15121     return;
15122
15123   switch (id)
15124   {
15125     case GAME_CTRL_ID_STOP:
15126       if (game_status == GAME_MODE_MAIN)
15127         break;
15128
15129       if (tape.playing)
15130         TapeStop();
15131       else
15132         RequestQuitGame(TRUE);
15133
15134       break;
15135
15136     case GAME_CTRL_ID_PAUSE:
15137     case GAME_CTRL_ID_PAUSE2:
15138       if (options.network && game_status == GAME_MODE_PLAYING)
15139       {
15140 #if defined(NETWORK_AVALIABLE)
15141         if (tape.pausing)
15142           SendToServer_ContinuePlaying();
15143         else
15144           SendToServer_PausePlaying();
15145 #endif
15146       }
15147       else
15148         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15149       break;
15150
15151     case GAME_CTRL_ID_PLAY:
15152       if (game_status == GAME_MODE_MAIN)
15153       {
15154         StartGameActions(options.network, setup.autorecord, level.random_seed);
15155       }
15156       else if (tape.pausing)
15157       {
15158 #if defined(NETWORK_AVALIABLE)
15159         if (options.network)
15160           SendToServer_ContinuePlaying();
15161         else
15162 #endif
15163           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15164       }
15165       break;
15166
15167     case GAME_CTRL_ID_UNDO:
15168       GameUndo(steps);
15169       break;
15170
15171     case GAME_CTRL_ID_REDO:
15172       GameRedo(steps);
15173       break;
15174
15175     case GAME_CTRL_ID_SAVE:
15176       TapeQuickSave();
15177       break;
15178
15179     case GAME_CTRL_ID_LOAD:
15180       TapeQuickLoad();
15181       break;
15182
15183     case SOUND_CTRL_ID_MUSIC:
15184       if (setup.sound_music)
15185       { 
15186         setup.sound_music = FALSE;
15187
15188         FadeMusic();
15189       }
15190       else if (audio.music_available)
15191       { 
15192         setup.sound = setup.sound_music = TRUE;
15193
15194         SetAudioMode(setup.sound);
15195
15196         PlayLevelMusic();
15197       }
15198       break;
15199
15200     case SOUND_CTRL_ID_LOOPS:
15201       if (setup.sound_loops)
15202         setup.sound_loops = FALSE;
15203       else if (audio.loops_available)
15204       {
15205         setup.sound = setup.sound_loops = TRUE;
15206
15207         SetAudioMode(setup.sound);
15208       }
15209       break;
15210
15211     case SOUND_CTRL_ID_SIMPLE:
15212       if (setup.sound_simple)
15213         setup.sound_simple = FALSE;
15214       else if (audio.sound_available)
15215       {
15216         setup.sound = setup.sound_simple = TRUE;
15217
15218         SetAudioMode(setup.sound);
15219       }
15220       break;
15221
15222     default:
15223       break;
15224   }
15225 }
15226
15227 static void HandleGameButtons(struct GadgetInfo *gi)
15228 {
15229   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15230 }
15231
15232 void HandleSoundButtonKeys(Key key)
15233 {
15234
15235   if (key == setup.shortcut.sound_simple)
15236     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15237   else if (key == setup.shortcut.sound_loops)
15238     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15239   else if (key == setup.shortcut.sound_music)
15240     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15241 }