replaced stop/play buttons in game panel with save/load buttons
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode =
3043     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3044      SNAPSHOT_MODE_EVERY_STEP :
3045      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3046      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3047 }
3048
3049 int get_num_special_action(int element, int action_first, int action_last)
3050 {
3051   int num_special_action = 0;
3052   int i, j;
3053
3054   for (i = action_first; i <= action_last; i++)
3055   {
3056     boolean found = FALSE;
3057
3058     for (j = 0; j < NUM_DIRECTIONS; j++)
3059       if (el_act_dir2img(element, i, j) !=
3060           el_act_dir2img(element, ACTION_DEFAULT, j))
3061         found = TRUE;
3062
3063     if (found)
3064       num_special_action++;
3065     else
3066       break;
3067   }
3068
3069   return num_special_action;
3070 }
3071
3072
3073 /*
3074   =============================================================================
3075   InitGame()
3076   -----------------------------------------------------------------------------
3077   initialize and start new game
3078   =============================================================================
3079 */
3080
3081 void InitGame()
3082 {
3083   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3084   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3085
3086   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3087   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3088   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3089   int initial_move_dir = MV_DOWN;
3090   int i, j, x, y;
3091
3092   game_status = GAME_MODE_PLAYING;
3093
3094   StopAnimation();
3095
3096   if (!game.restart_level)
3097     CloseDoor(DOOR_CLOSE_1);
3098
3099   if (level_editor_test_game)
3100     FadeSkipNextFadeIn();
3101   else
3102     FadeSetEnterScreen();
3103
3104   FadeOut(REDRAW_FIELD);
3105
3106   /* needed if different viewport properties defined for playing */
3107   ChangeViewportPropertiesIfNeeded();
3108
3109   DrawCompleteVideoDisplay();
3110
3111   InitGameEngine();
3112   InitGameControlValues();
3113
3114   /* don't play tapes over network */
3115   network_playing = (options.network && !tape.playing);
3116
3117   for (i = 0; i < MAX_PLAYERS; i++)
3118   {
3119     struct PlayerInfo *player = &stored_player[i];
3120
3121     player->index_nr = i;
3122     player->index_bit = (1 << i);
3123     player->element_nr = EL_PLAYER_1 + i;
3124
3125     player->present = FALSE;
3126     player->active = FALSE;
3127     player->mapped = FALSE;
3128
3129     player->killed = FALSE;
3130     player->reanimated = FALSE;
3131
3132     player->action = 0;
3133     player->effective_action = 0;
3134     player->programmed_action = 0;
3135
3136     player->score = 0;
3137     player->score_final = 0;
3138
3139     player->gems_still_needed = level.gems_needed;
3140     player->sokobanfields_still_needed = 0;
3141     player->lights_still_needed = 0;
3142     player->friends_still_needed = 0;
3143
3144     for (j = 0; j < MAX_NUM_KEYS; j++)
3145       player->key[j] = FALSE;
3146
3147     player->num_white_keys = 0;
3148
3149     player->dynabomb_count = 0;
3150     player->dynabomb_size = 1;
3151     player->dynabombs_left = 0;
3152     player->dynabomb_xl = FALSE;
3153
3154     player->MovDir = initial_move_dir;
3155     player->MovPos = 0;
3156     player->GfxPos = 0;
3157     player->GfxDir = initial_move_dir;
3158     player->GfxAction = ACTION_DEFAULT;
3159     player->Frame = 0;
3160     player->StepFrame = 0;
3161
3162     player->initial_element = player->element_nr;
3163     player->artwork_element =
3164       (level.use_artwork_element[i] ? level.artwork_element[i] :
3165        player->element_nr);
3166     player->use_murphy = FALSE;
3167
3168     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3169     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3170
3171     player->gravity = level.initial_player_gravity[i];
3172
3173     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3174
3175     player->actual_frame_counter = 0;
3176
3177     player->step_counter = 0;
3178
3179     player->last_move_dir = initial_move_dir;
3180
3181     player->is_active = FALSE;
3182
3183     player->is_waiting = FALSE;
3184     player->is_moving = FALSE;
3185     player->is_auto_moving = FALSE;
3186     player->is_digging = FALSE;
3187     player->is_snapping = FALSE;
3188     player->is_collecting = FALSE;
3189     player->is_pushing = FALSE;
3190     player->is_switching = FALSE;
3191     player->is_dropping = FALSE;
3192     player->is_dropping_pressed = FALSE;
3193
3194     player->is_bored = FALSE;
3195     player->is_sleeping = FALSE;
3196
3197     player->frame_counter_bored = -1;
3198     player->frame_counter_sleeping = -1;
3199
3200     player->anim_delay_counter = 0;
3201     player->post_delay_counter = 0;
3202
3203     player->dir_waiting = initial_move_dir;
3204     player->action_waiting = ACTION_DEFAULT;
3205     player->last_action_waiting = ACTION_DEFAULT;
3206     player->special_action_bored = ACTION_DEFAULT;
3207     player->special_action_sleeping = ACTION_DEFAULT;
3208
3209     player->switch_x = -1;
3210     player->switch_y = -1;
3211
3212     player->drop_x = -1;
3213     player->drop_y = -1;
3214
3215     player->show_envelope = 0;
3216
3217     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3218
3219     player->push_delay       = -1;      /* initialized when pushing starts */
3220     player->push_delay_value = game.initial_push_delay_value;
3221
3222     player->drop_delay = 0;
3223     player->drop_pressed_delay = 0;
3224
3225     player->last_jx = -1;
3226     player->last_jy = -1;
3227     player->jx = -1;
3228     player->jy = -1;
3229
3230     player->shield_normal_time_left = 0;
3231     player->shield_deadly_time_left = 0;
3232
3233     player->inventory_infinite_element = EL_UNDEFINED;
3234     player->inventory_size = 0;
3235
3236     if (level.use_initial_inventory[i])
3237     {
3238       for (j = 0; j < level.initial_inventory_size[i]; j++)
3239       {
3240         int element = level.initial_inventory_content[i][j];
3241         int collect_count = element_info[element].collect_count_initial;
3242         int k;
3243
3244         if (!IS_CUSTOM_ELEMENT(element))
3245           collect_count = 1;
3246
3247         if (collect_count == 0)
3248           player->inventory_infinite_element = element;
3249         else
3250           for (k = 0; k < collect_count; k++)
3251             if (player->inventory_size < MAX_INVENTORY_SIZE)
3252               player->inventory_element[player->inventory_size++] = element;
3253       }
3254     }
3255
3256     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3257     SnapField(player, 0, 0);
3258
3259     player->LevelSolved = FALSE;
3260     player->GameOver = FALSE;
3261
3262     player->LevelSolved_GameWon = FALSE;
3263     player->LevelSolved_GameEnd = FALSE;
3264     player->LevelSolved_PanelOff = FALSE;
3265     player->LevelSolved_SaveTape = FALSE;
3266     player->LevelSolved_SaveScore = FALSE;
3267     player->LevelSolved_CountingTime = 0;
3268     player->LevelSolved_CountingScore = 0;
3269
3270     map_player_action[i] = i;
3271   }
3272
3273   network_player_action_received = FALSE;
3274
3275 #if defined(NETWORK_AVALIABLE)
3276   /* initial null action */
3277   if (network_playing)
3278     SendToServer_MovePlayer(MV_NONE);
3279 #endif
3280
3281   ZX = ZY = -1;
3282   ExitX = ExitY = -1;
3283
3284   FrameCounter = 0;
3285   TimeFrames = 0;
3286   TimePlayed = 0;
3287   TimeLeft = level.time;
3288   TapeTime = 0;
3289
3290   ScreenMovDir = MV_NONE;
3291   ScreenMovPos = 0;
3292   ScreenGfxPos = 0;
3293
3294   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3295
3296   AllPlayersGone = FALSE;
3297
3298   game.no_time_limit = (level.time == 0);
3299
3300   game.yamyam_content_nr = 0;
3301   game.robot_wheel_active = FALSE;
3302   game.magic_wall_active = FALSE;
3303   game.magic_wall_time_left = 0;
3304   game.light_time_left = 0;
3305   game.timegate_time_left = 0;
3306   game.switchgate_pos = 0;
3307   game.wind_direction = level.wind_direction_initial;
3308
3309   game.lenses_time_left = 0;
3310   game.magnify_time_left = 0;
3311
3312   game.ball_state = level.ball_state_initial;
3313   game.ball_content_nr = 0;
3314
3315   game.envelope_active = FALSE;
3316
3317   /* set focus to local player for network games, else to all players */
3318   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3319   game.centered_player_nr_next = game.centered_player_nr;
3320   game.set_centered_player = FALSE;
3321
3322   if (network_playing && tape.recording)
3323   {
3324     /* store client dependent player focus when recording network games */
3325     tape.centered_player_nr_next = game.centered_player_nr_next;
3326     tape.set_centered_player = TRUE;
3327   }
3328
3329   for (i = 0; i < NUM_BELTS; i++)
3330   {
3331     game.belt_dir[i] = MV_NONE;
3332     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3333   }
3334
3335   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3336     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3337
3338 #if DEBUG_INIT_PLAYER
3339   if (options.debug)
3340   {
3341     printf("Player status at level initialization:\n");
3342   }
3343 #endif
3344
3345   SCAN_PLAYFIELD(x, y)
3346   {
3347     Feld[x][y] = level.field[x][y];
3348     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3349     ChangeDelay[x][y] = 0;
3350     ChangePage[x][y] = -1;
3351     CustomValue[x][y] = 0;              /* initialized in InitField() */
3352     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3353     AmoebaNr[x][y] = 0;
3354     WasJustMoving[x][y] = 0;
3355     WasJustFalling[x][y] = 0;
3356     CheckCollision[x][y] = 0;
3357     CheckImpact[x][y] = 0;
3358     Stop[x][y] = FALSE;
3359     Pushed[x][y] = FALSE;
3360
3361     ChangeCount[x][y] = 0;
3362     ChangeEvent[x][y] = -1;
3363
3364     ExplodePhase[x][y] = 0;
3365     ExplodeDelay[x][y] = 0;
3366     ExplodeField[x][y] = EX_TYPE_NONE;
3367
3368     RunnerVisit[x][y] = 0;
3369     PlayerVisit[x][y] = 0;
3370
3371     GfxFrame[x][y] = 0;
3372     GfxRandom[x][y] = INIT_GFX_RANDOM();
3373     GfxElement[x][y] = EL_UNDEFINED;
3374     GfxAction[x][y] = ACTION_DEFAULT;
3375     GfxDir[x][y] = MV_NONE;
3376     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3377   }
3378
3379   SCAN_PLAYFIELD(x, y)
3380   {
3381     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3382       emulate_bd = FALSE;
3383     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3384       emulate_sb = FALSE;
3385     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3386       emulate_sp = FALSE;
3387
3388     InitField(x, y, TRUE);
3389
3390     ResetGfxAnimation(x, y);
3391   }
3392
3393   InitBeltMovement();
3394
3395   for (i = 0; i < MAX_PLAYERS; i++)
3396   {
3397     struct PlayerInfo *player = &stored_player[i];
3398
3399     /* set number of special actions for bored and sleeping animation */
3400     player->num_special_action_bored =
3401       get_num_special_action(player->artwork_element,
3402                              ACTION_BORING_1, ACTION_BORING_LAST);
3403     player->num_special_action_sleeping =
3404       get_num_special_action(player->artwork_element,
3405                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3406   }
3407
3408   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3409                     emulate_sb ? EMU_SOKOBAN :
3410                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3411
3412   /* initialize type of slippery elements */
3413   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3414   {
3415     if (!IS_CUSTOM_ELEMENT(i))
3416     {
3417       /* default: elements slip down either to the left or right randomly */
3418       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3419
3420       /* SP style elements prefer to slip down on the left side */
3421       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3422         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3423
3424       /* BD style elements prefer to slip down on the left side */
3425       if (game.emulation == EMU_BOULDERDASH)
3426         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3427     }
3428   }
3429
3430   /* initialize explosion and ignition delay */
3431   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3432   {
3433     if (!IS_CUSTOM_ELEMENT(i))
3434     {
3435       int num_phase = 8;
3436       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3437                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3438                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3439       int last_phase = (num_phase + 1) * delay;
3440       int half_phase = (num_phase / 2) * delay;
3441
3442       element_info[i].explosion_delay = last_phase - 1;
3443       element_info[i].ignition_delay = half_phase;
3444
3445       if (i == EL_BLACK_ORB)
3446         element_info[i].ignition_delay = 1;
3447     }
3448   }
3449
3450   /* correct non-moving belts to start moving left */
3451   for (i = 0; i < NUM_BELTS; i++)
3452     if (game.belt_dir[i] == MV_NONE)
3453       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3454
3455 #if USE_NEW_PLAYER_ASSIGNMENTS
3456   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3457   /* choose default local player */
3458   local_player = &stored_player[0];
3459
3460   for (i = 0; i < MAX_PLAYERS; i++)
3461     stored_player[i].connected = FALSE;
3462
3463   local_player->connected = TRUE;
3464   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3465
3466   if (tape.playing)
3467   {
3468     for (i = 0; i < MAX_PLAYERS; i++)
3469       stored_player[i].connected = tape.player_participates[i];
3470   }
3471   else if (game.team_mode && !options.network)
3472   {
3473     /* try to guess locally connected team mode players (needed for correct
3474        assignment of player figures from level to locally playing players) */
3475
3476     for (i = 0; i < MAX_PLAYERS; i++)
3477       if (setup.input[i].use_joystick ||
3478           setup.input[i].key.left != KSYM_UNDEFINED)
3479         stored_player[i].connected = TRUE;
3480   }
3481
3482 #if DEBUG_INIT_PLAYER
3483   if (options.debug)
3484   {
3485     printf("Player status after level initialization:\n");
3486
3487     for (i = 0; i < MAX_PLAYERS; i++)
3488     {
3489       struct PlayerInfo *player = &stored_player[i];
3490
3491       printf("- player %d: present == %d, connected == %d, active == %d",
3492              i + 1,
3493              player->present,
3494              player->connected,
3495              player->active);
3496
3497       if (local_player == player)
3498         printf(" (local player)");
3499
3500       printf("\n");
3501     }
3502   }
3503 #endif
3504
3505 #if DEBUG_INIT_PLAYER
3506   if (options.debug)
3507     printf("Reassigning players ...\n");
3508 #endif
3509
3510   /* check if any connected player was not found in playfield */
3511   for (i = 0; i < MAX_PLAYERS; i++)
3512   {
3513     struct PlayerInfo *player = &stored_player[i];
3514
3515     if (player->connected && !player->present)
3516     {
3517       struct PlayerInfo *field_player = NULL;
3518
3519 #if DEBUG_INIT_PLAYER
3520       if (options.debug)
3521         printf("- looking for field player for player %d ...\n", i + 1);
3522 #endif
3523
3524       /* assign first free player found that is present in the playfield */
3525
3526       /* first try: look for unmapped playfield player that is not connected */
3527       for (j = 0; j < MAX_PLAYERS; j++)
3528         if (field_player == NULL &&
3529             stored_player[j].present &&
3530             !stored_player[j].mapped &&
3531             !stored_player[j].connected)
3532           field_player = &stored_player[j];
3533
3534       /* second try: look for *any* unmapped playfield player */
3535       for (j = 0; j < MAX_PLAYERS; j++)
3536         if (field_player == NULL &&
3537             stored_player[j].present &&
3538             !stored_player[j].mapped)
3539           field_player = &stored_player[j];
3540
3541       if (field_player != NULL)
3542       {
3543         int jx = field_player->jx, jy = field_player->jy;
3544
3545 #if DEBUG_INIT_PLAYER
3546         if (options.debug)
3547           printf("- found player %d\n", field_player->index_nr + 1);
3548 #endif
3549
3550         player->present = FALSE;
3551         player->active = FALSE;
3552
3553         field_player->present = TRUE;
3554         field_player->active = TRUE;
3555
3556         /*
3557         player->initial_element = field_player->initial_element;
3558         player->artwork_element = field_player->artwork_element;
3559
3560         player->block_last_field       = field_player->block_last_field;
3561         player->block_delay_adjustment = field_player->block_delay_adjustment;
3562         */
3563
3564         StorePlayer[jx][jy] = field_player->element_nr;
3565
3566         field_player->jx = field_player->last_jx = jx;
3567         field_player->jy = field_player->last_jy = jy;
3568
3569         if (local_player == player)
3570           local_player = field_player;
3571
3572         map_player_action[field_player->index_nr] = i;
3573
3574         field_player->mapped = TRUE;
3575
3576 #if DEBUG_INIT_PLAYER
3577         if (options.debug)
3578           printf("- map_player_action[%d] == %d\n",
3579                  field_player->index_nr + 1, i + 1);
3580 #endif
3581       }
3582     }
3583
3584     if (player->connected && player->present)
3585       player->mapped = TRUE;
3586   }
3587
3588 #if DEBUG_INIT_PLAYER
3589   if (options.debug)
3590   {
3591     printf("Player status after player assignment (first stage):\n");
3592
3593     for (i = 0; i < MAX_PLAYERS; i++)
3594     {
3595       struct PlayerInfo *player = &stored_player[i];
3596
3597       printf("- player %d: present == %d, connected == %d, active == %d",
3598              i + 1,
3599              player->present,
3600              player->connected,
3601              player->active);
3602
3603       if (local_player == player)
3604         printf(" (local player)");
3605
3606       printf("\n");
3607     }
3608   }
3609 #endif
3610
3611 #else
3612
3613   /* check if any connected player was not found in playfield */
3614   for (i = 0; i < MAX_PLAYERS; i++)
3615   {
3616     struct PlayerInfo *player = &stored_player[i];
3617
3618     if (player->connected && !player->present)
3619     {
3620       for (j = 0; j < MAX_PLAYERS; j++)
3621       {
3622         struct PlayerInfo *field_player = &stored_player[j];
3623         int jx = field_player->jx, jy = field_player->jy;
3624
3625         /* assign first free player found that is present in the playfield */
3626         if (field_player->present && !field_player->connected)
3627         {
3628           player->present = TRUE;
3629           player->active = TRUE;
3630
3631           field_player->present = FALSE;
3632           field_player->active = FALSE;
3633
3634           player->initial_element = field_player->initial_element;
3635           player->artwork_element = field_player->artwork_element;
3636
3637           player->block_last_field       = field_player->block_last_field;
3638           player->block_delay_adjustment = field_player->block_delay_adjustment;
3639
3640           StorePlayer[jx][jy] = player->element_nr;
3641
3642           player->jx = player->last_jx = jx;
3643           player->jy = player->last_jy = jy;
3644
3645           break;
3646         }
3647       }
3648     }
3649   }
3650 #endif
3651
3652 #if 0
3653   printf("::: local_player->present == %d\n", local_player->present);
3654 #endif
3655
3656   if (tape.playing)
3657   {
3658     /* when playing a tape, eliminate all players who do not participate */
3659
3660 #if USE_NEW_PLAYER_ASSIGNMENTS
3661
3662     if (!game.team_mode)
3663     {
3664       for (i = 0; i < MAX_PLAYERS; i++)
3665       {
3666         if (stored_player[i].active &&
3667             !tape.player_participates[map_player_action[i]])
3668         {
3669           struct PlayerInfo *player = &stored_player[i];
3670           int jx = player->jx, jy = player->jy;
3671
3672 #if DEBUG_INIT_PLAYER
3673           if (options.debug)
3674             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3675 #endif
3676
3677           player->active = FALSE;
3678           StorePlayer[jx][jy] = 0;
3679           Feld[jx][jy] = EL_EMPTY;
3680         }
3681       }
3682     }
3683
3684 #else
3685
3686     for (i = 0; i < MAX_PLAYERS; i++)
3687     {
3688       if (stored_player[i].active &&
3689           !tape.player_participates[i])
3690       {
3691         struct PlayerInfo *player = &stored_player[i];
3692         int jx = player->jx, jy = player->jy;
3693
3694         player->active = FALSE;
3695         StorePlayer[jx][jy] = 0;
3696         Feld[jx][jy] = EL_EMPTY;
3697       }
3698     }
3699 #endif
3700   }
3701   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3702   {
3703     /* when in single player mode, eliminate all but the first active player */
3704
3705     for (i = 0; i < MAX_PLAYERS; i++)
3706     {
3707       if (stored_player[i].active)
3708       {
3709         for (j = i + 1; j < MAX_PLAYERS; j++)
3710         {
3711           if (stored_player[j].active)
3712           {
3713             struct PlayerInfo *player = &stored_player[j];
3714             int jx = player->jx, jy = player->jy;
3715
3716             player->active = FALSE;
3717             player->present = FALSE;
3718
3719             StorePlayer[jx][jy] = 0;
3720             Feld[jx][jy] = EL_EMPTY;
3721           }
3722         }
3723       }
3724     }
3725   }
3726
3727   /* when recording the game, store which players take part in the game */
3728   if (tape.recording)
3729   {
3730 #if USE_NEW_PLAYER_ASSIGNMENTS
3731     for (i = 0; i < MAX_PLAYERS; i++)
3732       if (stored_player[i].connected)
3733         tape.player_participates[i] = TRUE;
3734 #else
3735     for (i = 0; i < MAX_PLAYERS; i++)
3736       if (stored_player[i].active)
3737         tape.player_participates[i] = TRUE;
3738 #endif
3739   }
3740
3741 #if DEBUG_INIT_PLAYER
3742   if (options.debug)
3743   {
3744     printf("Player status after player assignment (final stage):\n");
3745
3746     for (i = 0; i < MAX_PLAYERS; i++)
3747     {
3748       struct PlayerInfo *player = &stored_player[i];
3749
3750       printf("- player %d: present == %d, connected == %d, active == %d",
3751              i + 1,
3752              player->present,
3753              player->connected,
3754              player->active);
3755
3756       if (local_player == player)
3757         printf(" (local player)");
3758
3759       printf("\n");
3760     }
3761   }
3762 #endif
3763
3764   if (BorderElement == EL_EMPTY)
3765   {
3766     SBX_Left = 0;
3767     SBX_Right = lev_fieldx - SCR_FIELDX;
3768     SBY_Upper = 0;
3769     SBY_Lower = lev_fieldy - SCR_FIELDY;
3770   }
3771   else
3772   {
3773     SBX_Left = -1;
3774     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3775     SBY_Upper = -1;
3776     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3777   }
3778
3779   if (full_lev_fieldx <= SCR_FIELDX)
3780     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3781   if (full_lev_fieldy <= SCR_FIELDY)
3782     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3783
3784   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3785     SBX_Left--;
3786   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3787     SBY_Upper--;
3788
3789   /* if local player not found, look for custom element that might create
3790      the player (make some assumptions about the right custom element) */
3791   if (!local_player->present)
3792   {
3793     int start_x = 0, start_y = 0;
3794     int found_rating = 0;
3795     int found_element = EL_UNDEFINED;
3796     int player_nr = local_player->index_nr;
3797
3798     SCAN_PLAYFIELD(x, y)
3799     {
3800       int element = Feld[x][y];
3801       int content;
3802       int xx, yy;
3803       boolean is_player;
3804
3805       if (level.use_start_element[player_nr] &&
3806           level.start_element[player_nr] == element &&
3807           found_rating < 4)
3808       {
3809         start_x = x;
3810         start_y = y;
3811
3812         found_rating = 4;
3813         found_element = element;
3814       }
3815
3816       if (!IS_CUSTOM_ELEMENT(element))
3817         continue;
3818
3819       if (CAN_CHANGE(element))
3820       {
3821         for (i = 0; i < element_info[element].num_change_pages; i++)
3822         {
3823           /* check for player created from custom element as single target */
3824           content = element_info[element].change_page[i].target_element;
3825           is_player = ELEM_IS_PLAYER(content);
3826
3827           if (is_player && (found_rating < 3 ||
3828                             (found_rating == 3 && element < found_element)))
3829           {
3830             start_x = x;
3831             start_y = y;
3832
3833             found_rating = 3;
3834             found_element = element;
3835           }
3836         }
3837       }
3838
3839       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3840       {
3841         /* check for player created from custom element as explosion content */
3842         content = element_info[element].content.e[xx][yy];
3843         is_player = ELEM_IS_PLAYER(content);
3844
3845         if (is_player && (found_rating < 2 ||
3846                           (found_rating == 2 && element < found_element)))
3847         {
3848           start_x = x + xx - 1;
3849           start_y = y + yy - 1;
3850
3851           found_rating = 2;
3852           found_element = element;
3853         }
3854
3855         if (!CAN_CHANGE(element))
3856           continue;
3857
3858         for (i = 0; i < element_info[element].num_change_pages; i++)
3859         {
3860           /* check for player created from custom element as extended target */
3861           content =
3862             element_info[element].change_page[i].target_content.e[xx][yy];
3863
3864           is_player = ELEM_IS_PLAYER(content);
3865
3866           if (is_player && (found_rating < 1 ||
3867                             (found_rating == 1 && element < found_element)))
3868           {
3869             start_x = x + xx - 1;
3870             start_y = y + yy - 1;
3871
3872             found_rating = 1;
3873             found_element = element;
3874           }
3875         }
3876       }
3877     }
3878
3879     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3880                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3881                 start_x - MIDPOSX);
3882
3883     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3884                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3885                 start_y - MIDPOSY);
3886   }
3887   else
3888   {
3889     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3891                 local_player->jx - MIDPOSX);
3892
3893     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 local_player->jy - MIDPOSY);
3896   }
3897
3898   /* !!! FIX THIS (START) !!! */
3899   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3900   {
3901     InitGameEngine_EM();
3902   }
3903   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3904   {
3905     InitGameEngine_SP();
3906   }
3907   else
3908   {
3909     DrawLevel(REDRAW_FIELD);
3910     DrawAllPlayers();
3911
3912     /* after drawing the level, correct some elements */
3913     if (game.timegate_time_left == 0)
3914       CloseAllOpenTimegates();
3915   }
3916
3917   /* blit playfield from scroll buffer to normal back buffer for fading in */
3918   BlitScreenToBitmap(backbuffer);
3919
3920   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3921   /* !!! FIX THIS (END) !!! */
3922
3923   FadeIn(REDRAW_FIELD);
3924
3925 #if 1
3926   // full screen redraw is required at this point in the following cases:
3927   // - special editor door undrawn when game was started from level editor
3928   // - drawing area (playfield) was changed and has to be removed completely
3929   redraw_mask = REDRAW_ALL;
3930   BackToFront();
3931 #endif
3932
3933   if (!game.restart_level)
3934   {
3935     /* copy default game door content to main double buffer */
3936
3937     /* !!! CHECK AGAIN !!! */
3938     SetPanelBackground();
3939     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3940     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3941   }
3942
3943   SetPanelBackground();
3944   SetDrawBackgroundMask(REDRAW_DOOR_1);
3945
3946   UpdateAndDisplayGameControlValues();
3947
3948   if (!game.restart_level)
3949   {
3950     UnmapGameButtons();
3951     UnmapTapeButtons();
3952     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3953     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3954     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3955     MapGameButtons();
3956     MapTapeButtons();
3957
3958     /* copy actual game door content to door double buffer for OpenDoor() */
3959     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3960
3961     OpenDoor(DOOR_OPEN_ALL);
3962
3963     PlaySound(SND_GAME_STARTING);
3964
3965     if (setup.sound_music)
3966       PlayLevelMusic();
3967
3968     KeyboardAutoRepeatOffUnlessAutoplay();
3969
3970 #if DEBUG_INIT_PLAYER
3971     if (options.debug)
3972     {
3973       printf("Player status (final):\n");
3974
3975       for (i = 0; i < MAX_PLAYERS; i++)
3976       {
3977         struct PlayerInfo *player = &stored_player[i];
3978
3979         printf("- player %d: present == %d, connected == %d, active == %d",
3980                i + 1,
3981                player->present,
3982                player->connected,
3983                player->active);
3984
3985         if (local_player == player)
3986           printf(" (local player)");
3987
3988         printf("\n");
3989       }
3990     }
3991 #endif
3992   }
3993
3994   UnmapAllGadgets();
3995
3996   MapGameButtons();
3997   MapTapeButtons();
3998
3999   if (!game.restart_level && !tape.playing)
4000   {
4001     LevelStats_incPlayed(level_nr);
4002
4003     SaveLevelSetup_SeriesInfo();
4004   }
4005
4006   game.restart_level = FALSE;
4007
4008   SaveEngineSnapshotToListInitial();
4009 }
4010
4011 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4012 {
4013   /* this is used for non-R'n'D game engines to update certain engine values */
4014
4015   /* needed to determine if sounds are played within the visible screen area */
4016   scroll_x = actual_scroll_x;
4017   scroll_y = actual_scroll_y;
4018 }
4019
4020 void InitMovDir(int x, int y)
4021 {
4022   int i, element = Feld[x][y];
4023   static int xy[4][2] =
4024   {
4025     {  0, +1 },
4026     { +1,  0 },
4027     {  0, -1 },
4028     { -1,  0 }
4029   };
4030   static int direction[3][4] =
4031   {
4032     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4033     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4034     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4035   };
4036
4037   switch (element)
4038   {
4039     case EL_BUG_RIGHT:
4040     case EL_BUG_UP:
4041     case EL_BUG_LEFT:
4042     case EL_BUG_DOWN:
4043       Feld[x][y] = EL_BUG;
4044       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4045       break;
4046
4047     case EL_SPACESHIP_RIGHT:
4048     case EL_SPACESHIP_UP:
4049     case EL_SPACESHIP_LEFT:
4050     case EL_SPACESHIP_DOWN:
4051       Feld[x][y] = EL_SPACESHIP;
4052       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4053       break;
4054
4055     case EL_BD_BUTTERFLY_RIGHT:
4056     case EL_BD_BUTTERFLY_UP:
4057     case EL_BD_BUTTERFLY_LEFT:
4058     case EL_BD_BUTTERFLY_DOWN:
4059       Feld[x][y] = EL_BD_BUTTERFLY;
4060       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4061       break;
4062
4063     case EL_BD_FIREFLY_RIGHT:
4064     case EL_BD_FIREFLY_UP:
4065     case EL_BD_FIREFLY_LEFT:
4066     case EL_BD_FIREFLY_DOWN:
4067       Feld[x][y] = EL_BD_FIREFLY;
4068       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4069       break;
4070
4071     case EL_PACMAN_RIGHT:
4072     case EL_PACMAN_UP:
4073     case EL_PACMAN_LEFT:
4074     case EL_PACMAN_DOWN:
4075       Feld[x][y] = EL_PACMAN;
4076       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4077       break;
4078
4079     case EL_YAMYAM_LEFT:
4080     case EL_YAMYAM_RIGHT:
4081     case EL_YAMYAM_UP:
4082     case EL_YAMYAM_DOWN:
4083       Feld[x][y] = EL_YAMYAM;
4084       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4085       break;
4086
4087     case EL_SP_SNIKSNAK:
4088       MovDir[x][y] = MV_UP;
4089       break;
4090
4091     case EL_SP_ELECTRON:
4092       MovDir[x][y] = MV_LEFT;
4093       break;
4094
4095     case EL_MOLE_LEFT:
4096     case EL_MOLE_RIGHT:
4097     case EL_MOLE_UP:
4098     case EL_MOLE_DOWN:
4099       Feld[x][y] = EL_MOLE;
4100       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4101       break;
4102
4103     default:
4104       if (IS_CUSTOM_ELEMENT(element))
4105       {
4106         struct ElementInfo *ei = &element_info[element];
4107         int move_direction_initial = ei->move_direction_initial;
4108         int move_pattern = ei->move_pattern;
4109
4110         if (move_direction_initial == MV_START_PREVIOUS)
4111         {
4112           if (MovDir[x][y] != MV_NONE)
4113             return;
4114
4115           move_direction_initial = MV_START_AUTOMATIC;
4116         }
4117
4118         if (move_direction_initial == MV_START_RANDOM)
4119           MovDir[x][y] = 1 << RND(4);
4120         else if (move_direction_initial & MV_ANY_DIRECTION)
4121           MovDir[x][y] = move_direction_initial;
4122         else if (move_pattern == MV_ALL_DIRECTIONS ||
4123                  move_pattern == MV_TURNING_LEFT ||
4124                  move_pattern == MV_TURNING_RIGHT ||
4125                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4126                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4127                  move_pattern == MV_TURNING_RANDOM)
4128           MovDir[x][y] = 1 << RND(4);
4129         else if (move_pattern == MV_HORIZONTAL)
4130           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4131         else if (move_pattern == MV_VERTICAL)
4132           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4133         else if (move_pattern & MV_ANY_DIRECTION)
4134           MovDir[x][y] = element_info[element].move_pattern;
4135         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4136                  move_pattern == MV_ALONG_RIGHT_SIDE)
4137         {
4138           /* use random direction as default start direction */
4139           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4140             MovDir[x][y] = 1 << RND(4);
4141
4142           for (i = 0; i < NUM_DIRECTIONS; i++)
4143           {
4144             int x1 = x + xy[i][0];
4145             int y1 = y + xy[i][1];
4146
4147             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4148             {
4149               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4150                 MovDir[x][y] = direction[0][i];
4151               else
4152                 MovDir[x][y] = direction[1][i];
4153
4154               break;
4155             }
4156           }
4157         }                
4158       }
4159       else
4160       {
4161         MovDir[x][y] = 1 << RND(4);
4162
4163         if (element != EL_BUG &&
4164             element != EL_SPACESHIP &&
4165             element != EL_BD_BUTTERFLY &&
4166             element != EL_BD_FIREFLY)
4167           break;
4168
4169         for (i = 0; i < NUM_DIRECTIONS; i++)
4170         {
4171           int x1 = x + xy[i][0];
4172           int y1 = y + xy[i][1];
4173
4174           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4175           {
4176             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4177             {
4178               MovDir[x][y] = direction[0][i];
4179               break;
4180             }
4181             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4182                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4183             {
4184               MovDir[x][y] = direction[1][i];
4185               break;
4186             }
4187           }
4188         }
4189       }
4190       break;
4191   }
4192
4193   GfxDir[x][y] = MovDir[x][y];
4194 }
4195
4196 void InitAmoebaNr(int x, int y)
4197 {
4198   int i;
4199   int group_nr = AmoebeNachbarNr(x, y);
4200
4201   if (group_nr == 0)
4202   {
4203     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4204     {
4205       if (AmoebaCnt[i] == 0)
4206       {
4207         group_nr = i;
4208         break;
4209       }
4210     }
4211   }
4212
4213   AmoebaNr[x][y] = group_nr;
4214   AmoebaCnt[group_nr]++;
4215   AmoebaCnt2[group_nr]++;
4216 }
4217
4218 static void PlayerWins(struct PlayerInfo *player)
4219 {
4220   player->LevelSolved = TRUE;
4221   player->GameOver = TRUE;
4222
4223   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4224                          level.native_em_level->lev->score : player->score);
4225
4226   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4227                                       TimeLeft);
4228   player->LevelSolved_CountingScore = player->score_final;
4229 }
4230
4231 void GameWon()
4232 {
4233   static int time, time_final;
4234   static int score, score_final;
4235   static int game_over_delay_1 = 0;
4236   static int game_over_delay_2 = 0;
4237   int game_over_delay_value_1 = 50;
4238   int game_over_delay_value_2 = 50;
4239
4240   if (!local_player->LevelSolved_GameWon)
4241   {
4242     int i;
4243
4244     /* do not start end game actions before the player stops moving (to exit) */
4245     if (local_player->MovPos)
4246       return;
4247
4248     local_player->LevelSolved_GameWon = TRUE;
4249     local_player->LevelSolved_SaveTape = tape.recording;
4250     local_player->LevelSolved_SaveScore = !tape.playing;
4251
4252     if (!tape.playing)
4253     {
4254       LevelStats_incSolved(level_nr);
4255
4256       SaveLevelSetup_SeriesInfo();
4257     }
4258
4259     if (tape.auto_play)         /* tape might already be stopped here */
4260       tape.auto_play_level_solved = TRUE;
4261
4262     TapeStop();
4263
4264     game_over_delay_1 = game_over_delay_value_1;
4265     game_over_delay_2 = game_over_delay_value_2;
4266
4267     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4268     score = score_final = local_player->score_final;
4269
4270     if (TimeLeft > 0)
4271     {
4272       time_final = 0;
4273       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4274     }
4275     else if (game.no_time_limit && TimePlayed < 999)
4276     {
4277       time_final = 999;
4278       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4279     }
4280
4281     local_player->score_final = score_final;
4282
4283     if (level_editor_test_game)
4284     {
4285       time = time_final;
4286       score = score_final;
4287
4288       local_player->LevelSolved_CountingTime = time;
4289       local_player->LevelSolved_CountingScore = score;
4290
4291       game_panel_controls[GAME_PANEL_TIME].value = time;
4292       game_panel_controls[GAME_PANEL_SCORE].value = score;
4293
4294       DisplayGameControlValues();
4295     }
4296
4297     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4298     {
4299       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4300       {
4301         /* close exit door after last player */
4302         if ((AllPlayersGone &&
4303              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4304               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4305               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4306             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4307             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4308         {
4309           int element = Feld[ExitX][ExitY];
4310
4311           Feld[ExitX][ExitY] =
4312             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4313              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4314              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4315              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4316              EL_EM_STEEL_EXIT_CLOSING);
4317
4318           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4319         }
4320
4321         /* player disappears */
4322         DrawLevelField(ExitX, ExitY);
4323       }
4324
4325       for (i = 0; i < MAX_PLAYERS; i++)
4326       {
4327         struct PlayerInfo *player = &stored_player[i];
4328
4329         if (player->present)
4330         {
4331           RemovePlayer(player);
4332
4333           /* player disappears */
4334           DrawLevelField(player->jx, player->jy);
4335         }
4336       }
4337     }
4338
4339     PlaySound(SND_GAME_WINNING);
4340   }
4341
4342   if (game_over_delay_1 > 0)
4343   {
4344     game_over_delay_1--;
4345
4346     return;
4347   }
4348
4349   if (time != time_final)
4350   {
4351     int time_to_go = ABS(time_final - time);
4352     int time_count_dir = (time < time_final ? +1 : -1);
4353     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4354
4355     time  += time_count_steps * time_count_dir;
4356     score += time_count_steps * level.score[SC_TIME_BONUS];
4357
4358     local_player->LevelSolved_CountingTime = time;
4359     local_player->LevelSolved_CountingScore = score;
4360
4361     game_panel_controls[GAME_PANEL_TIME].value = time;
4362     game_panel_controls[GAME_PANEL_SCORE].value = score;
4363
4364     DisplayGameControlValues();
4365
4366     if (time == time_final)
4367       StopSound(SND_GAME_LEVELTIME_BONUS);
4368     else if (setup.sound_loops)
4369       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4370     else
4371       PlaySound(SND_GAME_LEVELTIME_BONUS);
4372
4373     return;
4374   }
4375
4376   local_player->LevelSolved_PanelOff = TRUE;
4377
4378   if (game_over_delay_2 > 0)
4379   {
4380     game_over_delay_2--;
4381
4382     return;
4383   }
4384
4385   GameEnd();
4386 }
4387
4388 void GameEnd()
4389 {
4390   int hi_pos;
4391   boolean raise_level = FALSE;
4392
4393   local_player->LevelSolved_GameEnd = TRUE;
4394
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   if (level_editor_test_game)
4403   {
4404     game_status = GAME_MODE_MAIN;
4405
4406     DrawAndFadeInMainMenu(REDRAW_FIELD);
4407
4408     return;
4409   }
4410
4411   if (!local_player->LevelSolved_SaveScore)
4412   {
4413     FadeOut(REDRAW_FIELD);
4414
4415     game_status = GAME_MODE_MAIN;
4416
4417     DrawAndFadeInMainMenu(REDRAW_FIELD);
4418
4419     return;
4420   }
4421
4422   if (level_nr == leveldir_current->handicap_level)
4423   {
4424     leveldir_current->handicap_level++;
4425
4426     SaveLevelSetup_SeriesInfo();
4427   }
4428
4429   if (level_nr < leveldir_current->last_level)
4430     raise_level = TRUE;                 /* advance to next level */
4431
4432   if ((hi_pos = NewHiScore()) >= 0) 
4433   {
4434     game_status = GAME_MODE_SCORES;
4435
4436     DrawHallOfFame(hi_pos);
4437
4438     if (raise_level)
4439     {
4440       level_nr++;
4441       TapeErase();
4442     }
4443   }
4444   else
4445   {
4446     FadeOut(REDRAW_FIELD);
4447
4448     game_status = GAME_MODE_MAIN;
4449
4450     if (raise_level)
4451     {
4452       level_nr++;
4453       TapeErase();
4454     }
4455
4456     DrawAndFadeInMainMenu(REDRAW_FIELD);
4457   }
4458 }
4459
4460 int NewHiScore()
4461 {
4462   int k, l;
4463   int position = -1;
4464
4465   LoadScore(level_nr);
4466
4467   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4468       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4469     return -1;
4470
4471   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4472   {
4473     if (local_player->score_final > highscore[k].Score)
4474     {
4475       /* player has made it to the hall of fame */
4476
4477       if (k < MAX_SCORE_ENTRIES - 1)
4478       {
4479         int m = MAX_SCORE_ENTRIES - 1;
4480
4481 #ifdef ONE_PER_NAME
4482         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4483           if (strEqual(setup.player_name, highscore[l].Name))
4484             m = l;
4485         if (m == k)     /* player's new highscore overwrites his old one */
4486           goto put_into_list;
4487 #endif
4488
4489         for (l = m; l > k; l--)
4490         {
4491           strcpy(highscore[l].Name, highscore[l - 1].Name);
4492           highscore[l].Score = highscore[l - 1].Score;
4493         }
4494       }
4495
4496 #ifdef ONE_PER_NAME
4497       put_into_list:
4498 #endif
4499       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4500       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4501       highscore[k].Score = local_player->score_final; 
4502       position = k;
4503       break;
4504     }
4505
4506 #ifdef ONE_PER_NAME
4507     else if (!strncmp(setup.player_name, highscore[k].Name,
4508                       MAX_PLAYER_NAME_LEN))
4509       break;    /* player already there with a higher score */
4510 #endif
4511
4512   }
4513
4514   if (position >= 0) 
4515     SaveScore(level_nr);
4516
4517   return position;
4518 }
4519
4520 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4521 {
4522   int element = Feld[x][y];
4523   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4524   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4525   int horiz_move = (dx != 0);
4526   int sign = (horiz_move ? dx : dy);
4527   int step = sign * element_info[element].move_stepsize;
4528
4529   /* special values for move stepsize for spring and things on conveyor belt */
4530   if (horiz_move)
4531   {
4532     if (CAN_FALL(element) &&
4533         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4534       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4535     else if (element == EL_SPRING)
4536       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4537   }
4538
4539   return step;
4540 }
4541
4542 inline static int getElementMoveStepsize(int x, int y)
4543 {
4544   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4545 }
4546
4547 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4548 {
4549   if (player->GfxAction != action || player->GfxDir != dir)
4550   {
4551     player->GfxAction = action;
4552     player->GfxDir = dir;
4553     player->Frame = 0;
4554     player->StepFrame = 0;
4555   }
4556 }
4557
4558 static void ResetGfxFrame(int x, int y, boolean redraw)
4559 {
4560   int element = Feld[x][y];
4561   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4562   int last_gfx_frame = GfxFrame[x][y];
4563
4564   if (graphic_info[graphic].anim_global_sync)
4565     GfxFrame[x][y] = FrameCounter;
4566   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4567     GfxFrame[x][y] = CustomValue[x][y];
4568   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4569     GfxFrame[x][y] = element_info[element].collect_score;
4570   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4571     GfxFrame[x][y] = ChangeDelay[x][y];
4572
4573   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4574     DrawLevelGraphicAnimation(x, y, graphic);
4575 }
4576
4577 static void ResetGfxAnimation(int x, int y)
4578 {
4579   GfxAction[x][y] = ACTION_DEFAULT;
4580   GfxDir[x][y] = MovDir[x][y];
4581   GfxFrame[x][y] = 0;
4582
4583   ResetGfxFrame(x, y, FALSE);
4584 }
4585
4586 static void ResetRandomAnimationValue(int x, int y)
4587 {
4588   GfxRandom[x][y] = INIT_GFX_RANDOM();
4589 }
4590
4591 void InitMovingField(int x, int y, int direction)
4592 {
4593   int element = Feld[x][y];
4594   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4595   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4596   int newx = x + dx;
4597   int newy = y + dy;
4598   boolean is_moving_before, is_moving_after;
4599
4600   /* check if element was/is moving or being moved before/after mode change */
4601   is_moving_before = (WasJustMoving[x][y] != 0);
4602   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4603
4604   /* reset animation only for moving elements which change direction of moving
4605      or which just started or stopped moving
4606      (else CEs with property "can move" / "not moving" are reset each frame) */
4607   if (is_moving_before != is_moving_after ||
4608       direction != MovDir[x][y])
4609     ResetGfxAnimation(x, y);
4610
4611   MovDir[x][y] = direction;
4612   GfxDir[x][y] = direction;
4613
4614   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4615                      direction == MV_DOWN && CAN_FALL(element) ?
4616                      ACTION_FALLING : ACTION_MOVING);
4617
4618   /* this is needed for CEs with property "can move" / "not moving" */
4619
4620   if (is_moving_after)
4621   {
4622     if (Feld[newx][newy] == EL_EMPTY)
4623       Feld[newx][newy] = EL_BLOCKED;
4624
4625     MovDir[newx][newy] = MovDir[x][y];
4626
4627     CustomValue[newx][newy] = CustomValue[x][y];
4628
4629     GfxFrame[newx][newy] = GfxFrame[x][y];
4630     GfxRandom[newx][newy] = GfxRandom[x][y];
4631     GfxAction[newx][newy] = GfxAction[x][y];
4632     GfxDir[newx][newy] = GfxDir[x][y];
4633   }
4634 }
4635
4636 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4637 {
4638   int direction = MovDir[x][y];
4639   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4640   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4641
4642   *goes_to_x = newx;
4643   *goes_to_y = newy;
4644 }
4645
4646 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4647 {
4648   int oldx = x, oldy = y;
4649   int direction = MovDir[x][y];
4650
4651   if (direction == MV_LEFT)
4652     oldx++;
4653   else if (direction == MV_RIGHT)
4654     oldx--;
4655   else if (direction == MV_UP)
4656     oldy++;
4657   else if (direction == MV_DOWN)
4658     oldy--;
4659
4660   *comes_from_x = oldx;
4661   *comes_from_y = oldy;
4662 }
4663
4664 int MovingOrBlocked2Element(int x, int y)
4665 {
4666   int element = Feld[x][y];
4667
4668   if (element == EL_BLOCKED)
4669   {
4670     int oldx, oldy;
4671
4672     Blocked2Moving(x, y, &oldx, &oldy);
4673     return Feld[oldx][oldy];
4674   }
4675   else
4676     return element;
4677 }
4678
4679 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4680 {
4681   /* like MovingOrBlocked2Element(), but if element is moving
4682      and (x,y) is the field the moving element is just leaving,
4683      return EL_BLOCKED instead of the element value */
4684   int element = Feld[x][y];
4685
4686   if (IS_MOVING(x, y))
4687   {
4688     if (element == EL_BLOCKED)
4689     {
4690       int oldx, oldy;
4691
4692       Blocked2Moving(x, y, &oldx, &oldy);
4693       return Feld[oldx][oldy];
4694     }
4695     else
4696       return EL_BLOCKED;
4697   }
4698   else
4699     return element;
4700 }
4701
4702 static void RemoveField(int x, int y)
4703 {
4704   Feld[x][y] = EL_EMPTY;
4705
4706   MovPos[x][y] = 0;
4707   MovDir[x][y] = 0;
4708   MovDelay[x][y] = 0;
4709
4710   CustomValue[x][y] = 0;
4711
4712   AmoebaNr[x][y] = 0;
4713   ChangeDelay[x][y] = 0;
4714   ChangePage[x][y] = -1;
4715   Pushed[x][y] = FALSE;
4716
4717   GfxElement[x][y] = EL_UNDEFINED;
4718   GfxAction[x][y] = ACTION_DEFAULT;
4719   GfxDir[x][y] = MV_NONE;
4720 }
4721
4722 void RemoveMovingField(int x, int y)
4723 {
4724   int oldx = x, oldy = y, newx = x, newy = y;
4725   int element = Feld[x][y];
4726   int next_element = EL_UNDEFINED;
4727
4728   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4729     return;
4730
4731   if (IS_MOVING(x, y))
4732   {
4733     Moving2Blocked(x, y, &newx, &newy);
4734
4735     if (Feld[newx][newy] != EL_BLOCKED)
4736     {
4737       /* element is moving, but target field is not free (blocked), but
4738          already occupied by something different (example: acid pool);
4739          in this case, only remove the moving field, but not the target */
4740
4741       RemoveField(oldx, oldy);
4742
4743       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4744
4745       TEST_DrawLevelField(oldx, oldy);
4746
4747       return;
4748     }
4749   }
4750   else if (element == EL_BLOCKED)
4751   {
4752     Blocked2Moving(x, y, &oldx, &oldy);
4753     if (!IS_MOVING(oldx, oldy))
4754       return;
4755   }
4756
4757   if (element == EL_BLOCKED &&
4758       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4759        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4760        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4761        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4762        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4763        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4764     next_element = get_next_element(Feld[oldx][oldy]);
4765
4766   RemoveField(oldx, oldy);
4767   RemoveField(newx, newy);
4768
4769   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4770
4771   if (next_element != EL_UNDEFINED)
4772     Feld[oldx][oldy] = next_element;
4773
4774   TEST_DrawLevelField(oldx, oldy);
4775   TEST_DrawLevelField(newx, newy);
4776 }
4777
4778 void DrawDynamite(int x, int y)
4779 {
4780   int sx = SCREENX(x), sy = SCREENY(y);
4781   int graphic = el2img(Feld[x][y]);
4782   int frame;
4783
4784   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4785     return;
4786
4787   if (IS_WALKABLE_INSIDE(Back[x][y]))
4788     return;
4789
4790   if (Back[x][y])
4791     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4792   else if (Store[x][y])
4793     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4794
4795   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4796
4797   if (Back[x][y] || Store[x][y])
4798     DrawGraphicThruMask(sx, sy, graphic, frame);
4799   else
4800     DrawGraphic(sx, sy, graphic, frame);
4801 }
4802
4803 void CheckDynamite(int x, int y)
4804 {
4805   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4806   {
4807     MovDelay[x][y]--;
4808
4809     if (MovDelay[x][y] != 0)
4810     {
4811       DrawDynamite(x, y);
4812       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4813
4814       return;
4815     }
4816   }
4817
4818   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4819
4820   Bang(x, y);
4821 }
4822
4823 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4824 {
4825   boolean num_checked_players = 0;
4826   int i;
4827
4828   for (i = 0; i < MAX_PLAYERS; i++)
4829   {
4830     if (stored_player[i].active)
4831     {
4832       int sx = stored_player[i].jx;
4833       int sy = stored_player[i].jy;
4834
4835       if (num_checked_players == 0)
4836       {
4837         *sx1 = *sx2 = sx;
4838         *sy1 = *sy2 = sy;
4839       }
4840       else
4841       {
4842         *sx1 = MIN(*sx1, sx);
4843         *sy1 = MIN(*sy1, sy);
4844         *sx2 = MAX(*sx2, sx);
4845         *sy2 = MAX(*sy2, sy);
4846       }
4847
4848       num_checked_players++;
4849     }
4850   }
4851 }
4852
4853 static boolean checkIfAllPlayersFitToScreen_RND()
4854 {
4855   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4856
4857   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4858
4859   return (sx2 - sx1 < SCR_FIELDX &&
4860           sy2 - sy1 < SCR_FIELDY);
4861 }
4862
4863 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4864 {
4865   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4866
4867   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4868
4869   *sx = (sx1 + sx2) / 2;
4870   *sy = (sy1 + sy2) / 2;
4871 }
4872
4873 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4874                         boolean center_screen, boolean quick_relocation)
4875 {
4876   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4877   boolean no_delay = (tape.warp_forward);
4878   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4879   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4880
4881   if (quick_relocation)
4882   {
4883     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4884     {
4885       if (!level.shifted_relocation || center_screen)
4886       {
4887         /* quick relocation (without scrolling), with centering of screen */
4888
4889         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4890                     x > SBX_Right + MIDPOSX ? SBX_Right :
4891                     x - MIDPOSX);
4892
4893         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4894                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4895                     y - MIDPOSY);
4896       }
4897       else
4898       {
4899         /* quick relocation (without scrolling), but do not center screen */
4900
4901         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4902                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4903                                old_x - MIDPOSX);
4904
4905         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4906                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4907                                old_y - MIDPOSY);
4908
4909         int offset_x = x + (scroll_x - center_scroll_x);
4910         int offset_y = y + (scroll_y - center_scroll_y);
4911
4912         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4913                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4914                     offset_x - MIDPOSX);
4915
4916         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4917                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4918                     offset_y - MIDPOSY);
4919       }
4920     }
4921     else
4922     {
4923       if (!level.shifted_relocation || center_screen)
4924       {
4925         /* quick relocation (without scrolling), with centering of screen */
4926
4927         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4928                     x > SBX_Right + MIDPOSX ? SBX_Right :
4929                     x - MIDPOSX);
4930
4931         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4932                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4933                     y - MIDPOSY);
4934       }
4935       else
4936       {
4937         /* quick relocation (without scrolling), but do not center screen */
4938
4939         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4940                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4941                                old_x - MIDPOSX);
4942
4943         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4944                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4945                                old_y - MIDPOSY);
4946
4947         int offset_x = x + (scroll_x - center_scroll_x);
4948         int offset_y = y + (scroll_y - center_scroll_y);
4949
4950         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4951                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4952                     offset_x - MIDPOSX);
4953
4954         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4955                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4956                     offset_y - MIDPOSY);
4957       }
4958     }
4959
4960     RedrawPlayfield(TRUE, 0,0,0,0);
4961   }
4962   else
4963   {
4964     int scroll_xx, scroll_yy;
4965
4966     if (!level.shifted_relocation || center_screen)
4967     {
4968       /* visible relocation (with scrolling), with centering of screen */
4969
4970       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4971                    x > SBX_Right + MIDPOSX ? SBX_Right :
4972                    x - MIDPOSX);
4973
4974       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4975                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4976                    y - MIDPOSY);
4977     }
4978     else
4979     {
4980       /* visible relocation (with scrolling), but do not center screen */
4981
4982       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4983                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4984                              old_x - MIDPOSX);
4985
4986       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4987                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4988                              old_y - MIDPOSY);
4989
4990       int offset_x = x + (scroll_x - center_scroll_x);
4991       int offset_y = y + (scroll_y - center_scroll_y);
4992
4993       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4994                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4995                    offset_x - MIDPOSX);
4996
4997       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4998                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4999                    offset_y - MIDPOSY);
5000     }
5001
5002
5003     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5004
5005     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5006     {
5007       int dx = 0, dy = 0;
5008       int fx = FX, fy = FY;
5009
5010       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5011       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5012
5013       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5014         break;
5015
5016       scroll_x -= dx;
5017       scroll_y -= dy;
5018
5019       fx += dx * TILEX / 2;
5020       fy += dy * TILEY / 2;
5021
5022       ScrollLevel(dx, dy);
5023       DrawAllPlayers();
5024
5025       /* scroll in two steps of half tile size to make things smoother */
5026       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5027       Delay(wait_delay_value);
5028
5029       /* scroll second step to align at full tile size */
5030       BackToFront();
5031       Delay(wait_delay_value);
5032     }
5033
5034     DrawAllPlayers();
5035     BackToFront();
5036     Delay(wait_delay_value);
5037   }
5038 }
5039
5040 void RelocatePlayer(int jx, int jy, int el_player_raw)
5041 {
5042   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5043   int player_nr = GET_PLAYER_NR(el_player);
5044   struct PlayerInfo *player = &stored_player[player_nr];
5045   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5046   boolean no_delay = (tape.warp_forward);
5047   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5048   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5049   int old_jx = player->jx;
5050   int old_jy = player->jy;
5051   int old_element = Feld[old_jx][old_jy];
5052   int element = Feld[jx][jy];
5053   boolean player_relocated = (old_jx != jx || old_jy != jy);
5054
5055   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5056   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5057   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5058   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5059   int leave_side_horiz = move_dir_horiz;
5060   int leave_side_vert  = move_dir_vert;
5061   int enter_side = enter_side_horiz | enter_side_vert;
5062   int leave_side = leave_side_horiz | leave_side_vert;
5063
5064   if (player->GameOver)         /* do not reanimate dead player */
5065     return;
5066
5067   if (!player_relocated)        /* no need to relocate the player */
5068     return;
5069
5070   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5071   {
5072     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5073     DrawLevelField(jx, jy);
5074   }
5075
5076   if (player->present)
5077   {
5078     while (player->MovPos)
5079     {
5080       ScrollPlayer(player, SCROLL_GO_ON);
5081       ScrollScreen(NULL, SCROLL_GO_ON);
5082
5083       AdvanceFrameAndPlayerCounters(player->index_nr);
5084
5085       DrawPlayer(player);
5086
5087       BackToFront();
5088       Delay(wait_delay_value);
5089     }
5090
5091     DrawPlayer(player);         /* needed here only to cleanup last field */
5092     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5093
5094     player->is_moving = FALSE;
5095   }
5096
5097   if (IS_CUSTOM_ELEMENT(old_element))
5098     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5099                                CE_LEFT_BY_PLAYER,
5100                                player->index_bit, leave_side);
5101
5102   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5103                                       CE_PLAYER_LEAVES_X,
5104                                       player->index_bit, leave_side);
5105
5106   Feld[jx][jy] = el_player;
5107   InitPlayerField(jx, jy, el_player, TRUE);
5108
5109   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5110      possible that the relocation target field did not contain a player element,
5111      but a walkable element, to which the new player was relocated -- in this
5112      case, restore that (already initialized!) element on the player field */
5113   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5114   {
5115     Feld[jx][jy] = element;     /* restore previously existing element */
5116   }
5117
5118   /* only visually relocate centered player */
5119   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5120                      FALSE, level.instant_relocation);
5121
5122   TestIfPlayerTouchesBadThing(jx, jy);
5123   TestIfPlayerTouchesCustomElement(jx, jy);
5124
5125   if (IS_CUSTOM_ELEMENT(element))
5126     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5127                                player->index_bit, enter_side);
5128
5129   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5130                                       player->index_bit, enter_side);
5131
5132   if (player->is_switching)
5133   {
5134     /* ensure that relocation while still switching an element does not cause
5135        a new element to be treated as also switched directly after relocation
5136        (this is important for teleporter switches that teleport the player to
5137        a place where another teleporter switch is in the same direction, which
5138        would then incorrectly be treated as immediately switched before the
5139        direction key that caused the switch was released) */
5140
5141     player->switch_x += jx - old_jx;
5142     player->switch_y += jy - old_jy;
5143   }
5144 }
5145
5146 void Explode(int ex, int ey, int phase, int mode)
5147 {
5148   int x, y;
5149   int last_phase;
5150   int border_element;
5151
5152   /* !!! eliminate this variable !!! */
5153   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5154
5155   if (game.explosions_delayed)
5156   {
5157     ExplodeField[ex][ey] = mode;
5158     return;
5159   }
5160
5161   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5162   {
5163     int center_element = Feld[ex][ey];
5164     int artwork_element, explosion_element;     /* set these values later */
5165
5166     /* remove things displayed in background while burning dynamite */
5167     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5168       Back[ex][ey] = 0;
5169
5170     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5171     {
5172       /* put moving element to center field (and let it explode there) */
5173       center_element = MovingOrBlocked2Element(ex, ey);
5174       RemoveMovingField(ex, ey);
5175       Feld[ex][ey] = center_element;
5176     }
5177
5178     /* now "center_element" is finally determined -- set related values now */
5179     artwork_element = center_element;           /* for custom player artwork */
5180     explosion_element = center_element;         /* for custom player artwork */
5181
5182     if (IS_PLAYER(ex, ey))
5183     {
5184       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5185
5186       artwork_element = stored_player[player_nr].artwork_element;
5187
5188       if (level.use_explosion_element[player_nr])
5189       {
5190         explosion_element = level.explosion_element[player_nr];
5191         artwork_element = explosion_element;
5192       }
5193     }
5194
5195     if (mode == EX_TYPE_NORMAL ||
5196         mode == EX_TYPE_CENTER ||
5197         mode == EX_TYPE_CROSS)
5198       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5199
5200     last_phase = element_info[explosion_element].explosion_delay + 1;
5201
5202     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5203     {
5204       int xx = x - ex + 1;
5205       int yy = y - ey + 1;
5206       int element;
5207
5208       if (!IN_LEV_FIELD(x, y) ||
5209           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5210           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5211         continue;
5212
5213       element = Feld[x][y];
5214
5215       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5216       {
5217         element = MovingOrBlocked2Element(x, y);
5218
5219         if (!IS_EXPLOSION_PROOF(element))
5220           RemoveMovingField(x, y);
5221       }
5222
5223       /* indestructible elements can only explode in center (but not flames) */
5224       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5225                                            mode == EX_TYPE_BORDER)) ||
5226           element == EL_FLAMES)
5227         continue;
5228
5229       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5230          behaviour, for example when touching a yamyam that explodes to rocks
5231          with active deadly shield, a rock is created under the player !!! */
5232       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5233 #if 0
5234       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5235           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5236            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5237 #else
5238       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5239 #endif
5240       {
5241         if (IS_ACTIVE_BOMB(element))
5242         {
5243           /* re-activate things under the bomb like gate or penguin */
5244           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5245           Back[x][y] = 0;
5246         }
5247
5248         continue;
5249       }
5250
5251       /* save walkable background elements while explosion on same tile */
5252       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5253           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5254         Back[x][y] = element;
5255
5256       /* ignite explodable elements reached by other explosion */
5257       if (element == EL_EXPLOSION)
5258         element = Store2[x][y];
5259
5260       if (AmoebaNr[x][y] &&
5261           (element == EL_AMOEBA_FULL ||
5262            element == EL_BD_AMOEBA ||
5263            element == EL_AMOEBA_GROWING))
5264       {
5265         AmoebaCnt[AmoebaNr[x][y]]--;
5266         AmoebaCnt2[AmoebaNr[x][y]]--;
5267       }
5268
5269       RemoveField(x, y);
5270
5271       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5272       {
5273         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5274
5275         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5276
5277         if (PLAYERINFO(ex, ey)->use_murphy)
5278           Store[x][y] = EL_EMPTY;
5279       }
5280
5281       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5282          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5283       else if (ELEM_IS_PLAYER(center_element))
5284         Store[x][y] = EL_EMPTY;
5285       else if (center_element == EL_YAMYAM)
5286         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5287       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5288         Store[x][y] = element_info[center_element].content.e[xx][yy];
5289 #if 1
5290       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5291          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5292          otherwise) -- FIX THIS !!! */
5293       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5294         Store[x][y] = element_info[element].content.e[1][1];
5295 #else
5296       else if (!CAN_EXPLODE(element))
5297         Store[x][y] = element_info[element].content.e[1][1];
5298 #endif
5299       else
5300         Store[x][y] = EL_EMPTY;
5301
5302       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5303           center_element == EL_AMOEBA_TO_DIAMOND)
5304         Store2[x][y] = element;
5305
5306       Feld[x][y] = EL_EXPLOSION;
5307       GfxElement[x][y] = artwork_element;
5308
5309       ExplodePhase[x][y] = 1;
5310       ExplodeDelay[x][y] = last_phase;
5311
5312       Stop[x][y] = TRUE;
5313     }
5314
5315     if (center_element == EL_YAMYAM)
5316       game.yamyam_content_nr =
5317         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5318
5319     return;
5320   }
5321
5322   if (Stop[ex][ey])
5323     return;
5324
5325   x = ex;
5326   y = ey;
5327
5328   if (phase == 1)
5329     GfxFrame[x][y] = 0;         /* restart explosion animation */
5330
5331   last_phase = ExplodeDelay[x][y];
5332
5333   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5334
5335   /* this can happen if the player leaves an explosion just in time */
5336   if (GfxElement[x][y] == EL_UNDEFINED)
5337     GfxElement[x][y] = EL_EMPTY;
5338
5339   border_element = Store2[x][y];
5340   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5341     border_element = StorePlayer[x][y];
5342
5343   if (phase == element_info[border_element].ignition_delay ||
5344       phase == last_phase)
5345   {
5346     boolean border_explosion = FALSE;
5347
5348     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5349         !PLAYER_EXPLOSION_PROTECTED(x, y))
5350     {
5351       KillPlayerUnlessExplosionProtected(x, y);
5352       border_explosion = TRUE;
5353     }
5354     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5355     {
5356       Feld[x][y] = Store2[x][y];
5357       Store2[x][y] = 0;
5358       Bang(x, y);
5359       border_explosion = TRUE;
5360     }
5361     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5362     {
5363       AmoebeUmwandeln(x, y);
5364       Store2[x][y] = 0;
5365       border_explosion = TRUE;
5366     }
5367
5368     /* if an element just explodes due to another explosion (chain-reaction),
5369        do not immediately end the new explosion when it was the last frame of
5370        the explosion (as it would be done in the following "if"-statement!) */
5371     if (border_explosion && phase == last_phase)
5372       return;
5373   }
5374
5375   if (phase == last_phase)
5376   {
5377     int element;
5378
5379     element = Feld[x][y] = Store[x][y];
5380     Store[x][y] = Store2[x][y] = 0;
5381     GfxElement[x][y] = EL_UNDEFINED;
5382
5383     /* player can escape from explosions and might therefore be still alive */
5384     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5385         element <= EL_PLAYER_IS_EXPLODING_4)
5386     {
5387       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5388       int explosion_element = EL_PLAYER_1 + player_nr;
5389       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5390       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5391
5392       if (level.use_explosion_element[player_nr])
5393         explosion_element = level.explosion_element[player_nr];
5394
5395       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5396                     element_info[explosion_element].content.e[xx][yy]);
5397     }
5398
5399     /* restore probably existing indestructible background element */
5400     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5401       element = Feld[x][y] = Back[x][y];
5402     Back[x][y] = 0;
5403
5404     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5405     GfxDir[x][y] = MV_NONE;
5406     ChangeDelay[x][y] = 0;
5407     ChangePage[x][y] = -1;
5408
5409     CustomValue[x][y] = 0;
5410
5411     InitField_WithBug2(x, y, FALSE);
5412
5413     TEST_DrawLevelField(x, y);
5414
5415     TestIfElementTouchesCustomElement(x, y);
5416
5417     if (GFX_CRUMBLED(element))
5418       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5419
5420     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5421       StorePlayer[x][y] = 0;
5422
5423     if (ELEM_IS_PLAYER(element))
5424       RelocatePlayer(x, y, element);
5425   }
5426   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5427   {
5428     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5429     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5430
5431     if (phase == delay)
5432       TEST_DrawLevelFieldCrumbled(x, y);
5433
5434     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5435     {
5436       DrawLevelElement(x, y, Back[x][y]);
5437       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5438     }
5439     else if (IS_WALKABLE_UNDER(Back[x][y]))
5440     {
5441       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5442       DrawLevelElementThruMask(x, y, Back[x][y]);
5443     }
5444     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5445       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5446   }
5447 }
5448
5449 void DynaExplode(int ex, int ey)
5450 {
5451   int i, j;
5452   int dynabomb_element = Feld[ex][ey];
5453   int dynabomb_size = 1;
5454   boolean dynabomb_xl = FALSE;
5455   struct PlayerInfo *player;
5456   static int xy[4][2] =
5457   {
5458     { 0, -1 },
5459     { -1, 0 },
5460     { +1, 0 },
5461     { 0, +1 }
5462   };
5463
5464   if (IS_ACTIVE_BOMB(dynabomb_element))
5465   {
5466     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5467     dynabomb_size = player->dynabomb_size;
5468     dynabomb_xl = player->dynabomb_xl;
5469     player->dynabombs_left++;
5470   }
5471
5472   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5473
5474   for (i = 0; i < NUM_DIRECTIONS; i++)
5475   {
5476     for (j = 1; j <= dynabomb_size; j++)
5477     {
5478       int x = ex + j * xy[i][0];
5479       int y = ey + j * xy[i][1];
5480       int element;
5481
5482       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5483         break;
5484
5485       element = Feld[x][y];
5486
5487       /* do not restart explosions of fields with active bombs */
5488       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5489         continue;
5490
5491       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5492
5493       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5494           !IS_DIGGABLE(element) && !dynabomb_xl)
5495         break;
5496     }
5497   }
5498 }
5499
5500 void Bang(int x, int y)
5501 {
5502   int element = MovingOrBlocked2Element(x, y);
5503   int explosion_type = EX_TYPE_NORMAL;
5504
5505   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5506   {
5507     struct PlayerInfo *player = PLAYERINFO(x, y);
5508
5509     element = Feld[x][y] = player->initial_element;
5510
5511     if (level.use_explosion_element[player->index_nr])
5512     {
5513       int explosion_element = level.explosion_element[player->index_nr];
5514
5515       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5516         explosion_type = EX_TYPE_CROSS;
5517       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5518         explosion_type = EX_TYPE_CENTER;
5519     }
5520   }
5521
5522   switch (element)
5523   {
5524     case EL_BUG:
5525     case EL_SPACESHIP:
5526     case EL_BD_BUTTERFLY:
5527     case EL_BD_FIREFLY:
5528     case EL_YAMYAM:
5529     case EL_DARK_YAMYAM:
5530     case EL_ROBOT:
5531     case EL_PACMAN:
5532     case EL_MOLE:
5533       RaiseScoreElement(element);
5534       break;
5535
5536     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5537     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5538     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5539     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5540     case EL_DYNABOMB_INCREASE_NUMBER:
5541     case EL_DYNABOMB_INCREASE_SIZE:
5542     case EL_DYNABOMB_INCREASE_POWER:
5543       explosion_type = EX_TYPE_DYNA;
5544       break;
5545
5546     case EL_DC_LANDMINE:
5547       explosion_type = EX_TYPE_CENTER;
5548       break;
5549
5550     case EL_PENGUIN:
5551     case EL_LAMP:
5552     case EL_LAMP_ACTIVE:
5553     case EL_AMOEBA_TO_DIAMOND:
5554       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5555         explosion_type = EX_TYPE_CENTER;
5556       break;
5557
5558     default:
5559       if (element_info[element].explosion_type == EXPLODES_CROSS)
5560         explosion_type = EX_TYPE_CROSS;
5561       else if (element_info[element].explosion_type == EXPLODES_1X1)
5562         explosion_type = EX_TYPE_CENTER;
5563       break;
5564   }
5565
5566   if (explosion_type == EX_TYPE_DYNA)
5567     DynaExplode(x, y);
5568   else
5569     Explode(x, y, EX_PHASE_START, explosion_type);
5570
5571   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5572 }
5573
5574 void SplashAcid(int x, int y)
5575 {
5576   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5577       (!IN_LEV_FIELD(x - 1, y - 2) ||
5578        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5579     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
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_RIGHT;
5585
5586   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5587 }
5588
5589 static void InitBeltMovement()
5590 {
5591   static int belt_base_element[4] =
5592   {
5593     EL_CONVEYOR_BELT_1_LEFT,
5594     EL_CONVEYOR_BELT_2_LEFT,
5595     EL_CONVEYOR_BELT_3_LEFT,
5596     EL_CONVEYOR_BELT_4_LEFT
5597   };
5598   static int belt_base_active_element[4] =
5599   {
5600     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5601     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5602     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5603     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5604   };
5605
5606   int x, y, i, j;
5607
5608   /* set frame order for belt animation graphic according to belt direction */
5609   for (i = 0; i < NUM_BELTS; i++)
5610   {
5611     int belt_nr = i;
5612
5613     for (j = 0; j < NUM_BELT_PARTS; j++)
5614     {
5615       int element = belt_base_active_element[belt_nr] + j;
5616       int graphic_1 = el2img(element);
5617       int graphic_2 = el2panelimg(element);
5618
5619       if (game.belt_dir[i] == MV_LEFT)
5620       {
5621         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5622         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5623       }
5624       else
5625       {
5626         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5627         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5628       }
5629     }
5630   }
5631
5632   SCAN_PLAYFIELD(x, y)
5633   {
5634     int element = Feld[x][y];
5635
5636     for (i = 0; i < NUM_BELTS; i++)
5637     {
5638       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5639       {
5640         int e_belt_nr = getBeltNrFromBeltElement(element);
5641         int belt_nr = i;
5642
5643         if (e_belt_nr == belt_nr)
5644         {
5645           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5646
5647           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5648         }
5649       }
5650     }
5651   }
5652 }
5653
5654 static void ToggleBeltSwitch(int x, int y)
5655 {
5656   static int belt_base_element[4] =
5657   {
5658     EL_CONVEYOR_BELT_1_LEFT,
5659     EL_CONVEYOR_BELT_2_LEFT,
5660     EL_CONVEYOR_BELT_3_LEFT,
5661     EL_CONVEYOR_BELT_4_LEFT
5662   };
5663   static int belt_base_active_element[4] =
5664   {
5665     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5666     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5667     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5668     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5669   };
5670   static int belt_base_switch_element[4] =
5671   {
5672     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5673     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5674     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5675     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5676   };
5677   static int belt_move_dir[4] =
5678   {
5679     MV_LEFT,
5680     MV_NONE,
5681     MV_RIGHT,
5682     MV_NONE,
5683   };
5684
5685   int element = Feld[x][y];
5686   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5687   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5688   int belt_dir = belt_move_dir[belt_dir_nr];
5689   int xx, yy, i;
5690
5691   if (!IS_BELT_SWITCH(element))
5692     return;
5693
5694   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5695   game.belt_dir[belt_nr] = belt_dir;
5696
5697   if (belt_dir_nr == 3)
5698     belt_dir_nr = 1;
5699
5700   /* set frame order for belt animation graphic according to belt direction */
5701   for (i = 0; i < NUM_BELT_PARTS; i++)
5702   {
5703     int element = belt_base_active_element[belt_nr] + i;
5704     int graphic_1 = el2img(element);
5705     int graphic_2 = el2panelimg(element);
5706
5707     if (belt_dir == MV_LEFT)
5708     {
5709       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5710       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5711     }
5712     else
5713     {
5714       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5715       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5716     }
5717   }
5718
5719   SCAN_PLAYFIELD(xx, yy)
5720   {
5721     int element = Feld[xx][yy];
5722
5723     if (IS_BELT_SWITCH(element))
5724     {
5725       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5726
5727       if (e_belt_nr == belt_nr)
5728       {
5729         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5730         TEST_DrawLevelField(xx, yy);
5731       }
5732     }
5733     else if (IS_BELT(element) && belt_dir != MV_NONE)
5734     {
5735       int e_belt_nr = getBeltNrFromBeltElement(element);
5736
5737       if (e_belt_nr == belt_nr)
5738       {
5739         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5740
5741         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5742         TEST_DrawLevelField(xx, yy);
5743       }
5744     }
5745     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5746     {
5747       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5748
5749       if (e_belt_nr == belt_nr)
5750       {
5751         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5752
5753         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5754         TEST_DrawLevelField(xx, yy);
5755       }
5756     }
5757   }
5758 }
5759
5760 static void ToggleSwitchgateSwitch(int x, int y)
5761 {
5762   int xx, yy;
5763
5764   game.switchgate_pos = !game.switchgate_pos;
5765
5766   SCAN_PLAYFIELD(xx, yy)
5767   {
5768     int element = Feld[xx][yy];
5769
5770     if (element == EL_SWITCHGATE_SWITCH_UP)
5771     {
5772       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5773       TEST_DrawLevelField(xx, yy);
5774     }
5775     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5776     {
5777       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5778       TEST_DrawLevelField(xx, yy);
5779     }
5780     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5781     {
5782       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5783       TEST_DrawLevelField(xx, yy);
5784     }
5785     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5786     {
5787       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5788       TEST_DrawLevelField(xx, yy);
5789     }
5790     else if (element == EL_SWITCHGATE_OPEN ||
5791              element == EL_SWITCHGATE_OPENING)
5792     {
5793       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5794
5795       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5796     }
5797     else if (element == EL_SWITCHGATE_CLOSED ||
5798              element == EL_SWITCHGATE_CLOSING)
5799     {
5800       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5801
5802       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5803     }
5804   }
5805 }
5806
5807 static int getInvisibleActiveFromInvisibleElement(int element)
5808 {
5809   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5810           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5811           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5812           element);
5813 }
5814
5815 static int getInvisibleFromInvisibleActiveElement(int element)
5816 {
5817   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5818           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5819           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5820           element);
5821 }
5822
5823 static void RedrawAllLightSwitchesAndInvisibleElements()
5824 {
5825   int x, y;
5826
5827   SCAN_PLAYFIELD(x, y)
5828   {
5829     int element = Feld[x][y];
5830
5831     if (element == EL_LIGHT_SWITCH &&
5832         game.light_time_left > 0)
5833     {
5834       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5835       TEST_DrawLevelField(x, y);
5836     }
5837     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5838              game.light_time_left == 0)
5839     {
5840       Feld[x][y] = EL_LIGHT_SWITCH;
5841       TEST_DrawLevelField(x, y);
5842     }
5843     else if (element == EL_EMC_DRIPPER &&
5844              game.light_time_left > 0)
5845     {
5846       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5847       TEST_DrawLevelField(x, y);
5848     }
5849     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5850              game.light_time_left == 0)
5851     {
5852       Feld[x][y] = EL_EMC_DRIPPER;
5853       TEST_DrawLevelField(x, y);
5854     }
5855     else if (element == EL_INVISIBLE_STEELWALL ||
5856              element == EL_INVISIBLE_WALL ||
5857              element == EL_INVISIBLE_SAND)
5858     {
5859       if (game.light_time_left > 0)
5860         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5861
5862       TEST_DrawLevelField(x, y);
5863
5864       /* uncrumble neighbour fields, if needed */
5865       if (element == EL_INVISIBLE_SAND)
5866         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5867     }
5868     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5869              element == EL_INVISIBLE_WALL_ACTIVE ||
5870              element == EL_INVISIBLE_SAND_ACTIVE)
5871     {
5872       if (game.light_time_left == 0)
5873         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5874
5875       TEST_DrawLevelField(x, y);
5876
5877       /* re-crumble neighbour fields, if needed */
5878       if (element == EL_INVISIBLE_SAND)
5879         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5880     }
5881   }
5882 }
5883
5884 static void RedrawAllInvisibleElementsForLenses()
5885 {
5886   int x, y;
5887
5888   SCAN_PLAYFIELD(x, y)
5889   {
5890     int element = Feld[x][y];
5891
5892     if (element == EL_EMC_DRIPPER &&
5893         game.lenses_time_left > 0)
5894     {
5895       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5896       TEST_DrawLevelField(x, y);
5897     }
5898     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5899              game.lenses_time_left == 0)
5900     {
5901       Feld[x][y] = EL_EMC_DRIPPER;
5902       TEST_DrawLevelField(x, y);
5903     }
5904     else if (element == EL_INVISIBLE_STEELWALL ||
5905              element == EL_INVISIBLE_WALL ||
5906              element == EL_INVISIBLE_SAND)
5907     {
5908       if (game.lenses_time_left > 0)
5909         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5910
5911       TEST_DrawLevelField(x, y);
5912
5913       /* uncrumble neighbour fields, if needed */
5914       if (element == EL_INVISIBLE_SAND)
5915         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5916     }
5917     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5918              element == EL_INVISIBLE_WALL_ACTIVE ||
5919              element == EL_INVISIBLE_SAND_ACTIVE)
5920     {
5921       if (game.lenses_time_left == 0)
5922         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5923
5924       TEST_DrawLevelField(x, y);
5925
5926       /* re-crumble neighbour fields, if needed */
5927       if (element == EL_INVISIBLE_SAND)
5928         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5929     }
5930   }
5931 }
5932
5933 static void RedrawAllInvisibleElementsForMagnifier()
5934 {
5935   int x, y;
5936
5937   SCAN_PLAYFIELD(x, y)
5938   {
5939     int element = Feld[x][y];
5940
5941     if (element == EL_EMC_FAKE_GRASS &&
5942         game.magnify_time_left > 0)
5943     {
5944       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5945       TEST_DrawLevelField(x, y);
5946     }
5947     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5948              game.magnify_time_left == 0)
5949     {
5950       Feld[x][y] = EL_EMC_FAKE_GRASS;
5951       TEST_DrawLevelField(x, y);
5952     }
5953     else if (IS_GATE_GRAY(element) &&
5954              game.magnify_time_left > 0)
5955     {
5956       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5957                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5958                     IS_EM_GATE_GRAY(element) ?
5959                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5960                     IS_EMC_GATE_GRAY(element) ?
5961                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5962                     IS_DC_GATE_GRAY(element) ?
5963                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5964                     element);
5965       TEST_DrawLevelField(x, y);
5966     }
5967     else if (IS_GATE_GRAY_ACTIVE(element) &&
5968              game.magnify_time_left == 0)
5969     {
5970       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5971                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5972                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5973                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5974                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5975                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5976                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5977                     EL_DC_GATE_WHITE_GRAY :
5978                     element);
5979       TEST_DrawLevelField(x, y);
5980     }
5981   }
5982 }
5983
5984 static void ToggleLightSwitch(int x, int y)
5985 {
5986   int element = Feld[x][y];
5987
5988   game.light_time_left =
5989     (element == EL_LIGHT_SWITCH ?
5990      level.time_light * FRAMES_PER_SECOND : 0);
5991
5992   RedrawAllLightSwitchesAndInvisibleElements();
5993 }
5994
5995 static void ActivateTimegateSwitch(int x, int y)
5996 {
5997   int xx, yy;
5998
5999   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6000
6001   SCAN_PLAYFIELD(xx, yy)
6002   {
6003     int element = Feld[xx][yy];
6004
6005     if (element == EL_TIMEGATE_CLOSED ||
6006         element == EL_TIMEGATE_CLOSING)
6007     {
6008       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6009       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6010     }
6011
6012     /*
6013     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6014     {
6015       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6016       TEST_DrawLevelField(xx, yy);
6017     }
6018     */
6019
6020   }
6021
6022   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6023                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6024 }
6025
6026 void Impact(int x, int y)
6027 {
6028   boolean last_line = (y == lev_fieldy - 1);
6029   boolean object_hit = FALSE;
6030   boolean impact = (last_line || object_hit);
6031   int element = Feld[x][y];
6032   int smashed = EL_STEELWALL;
6033
6034   if (!last_line)       /* check if element below was hit */
6035   {
6036     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6037       return;
6038
6039     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6040                                          MovDir[x][y + 1] != MV_DOWN ||
6041                                          MovPos[x][y + 1] <= TILEY / 2));
6042
6043     /* do not smash moving elements that left the smashed field in time */
6044     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6045         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6046       object_hit = FALSE;
6047
6048 #if USE_QUICKSAND_IMPACT_BUGFIX
6049     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6050     {
6051       RemoveMovingField(x, y + 1);
6052       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6053       Feld[x][y + 2] = EL_ROCK;
6054       TEST_DrawLevelField(x, y + 2);
6055
6056       object_hit = TRUE;
6057     }
6058
6059     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6060     {
6061       RemoveMovingField(x, y + 1);
6062       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6063       Feld[x][y + 2] = EL_ROCK;
6064       TEST_DrawLevelField(x, y + 2);
6065
6066       object_hit = TRUE;
6067     }
6068 #endif
6069
6070     if (object_hit)
6071       smashed = MovingOrBlocked2Element(x, y + 1);
6072
6073     impact = (last_line || object_hit);
6074   }
6075
6076   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6077   {
6078     SplashAcid(x, y + 1);
6079     return;
6080   }
6081
6082   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6083   /* only reset graphic animation if graphic really changes after impact */
6084   if (impact &&
6085       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6086   {
6087     ResetGfxAnimation(x, y);
6088     TEST_DrawLevelField(x, y);
6089   }
6090
6091   if (impact && CAN_EXPLODE_IMPACT(element))
6092   {
6093     Bang(x, y);
6094     return;
6095   }
6096   else if (impact && element == EL_PEARL &&
6097            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6098   {
6099     ResetGfxAnimation(x, y);
6100
6101     Feld[x][y] = EL_PEARL_BREAKING;
6102     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6103     return;
6104   }
6105   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6106   {
6107     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6108
6109     return;
6110   }
6111
6112   if (impact && element == EL_AMOEBA_DROP)
6113   {
6114     if (object_hit && IS_PLAYER(x, y + 1))
6115       KillPlayerUnlessEnemyProtected(x, y + 1);
6116     else if (object_hit && smashed == EL_PENGUIN)
6117       Bang(x, y + 1);
6118     else
6119     {
6120       Feld[x][y] = EL_AMOEBA_GROWING;
6121       Store[x][y] = EL_AMOEBA_WET;
6122
6123       ResetRandomAnimationValue(x, y);
6124     }
6125     return;
6126   }
6127
6128   if (object_hit)               /* check which object was hit */
6129   {
6130     if ((CAN_PASS_MAGIC_WALL(element) && 
6131          (smashed == EL_MAGIC_WALL ||
6132           smashed == EL_BD_MAGIC_WALL)) ||
6133         (CAN_PASS_DC_MAGIC_WALL(element) &&
6134          smashed == EL_DC_MAGIC_WALL))
6135     {
6136       int xx, yy;
6137       int activated_magic_wall =
6138         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6139          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6140          EL_DC_MAGIC_WALL_ACTIVE);
6141
6142       /* activate magic wall / mill */
6143       SCAN_PLAYFIELD(xx, yy)
6144       {
6145         if (Feld[xx][yy] == smashed)
6146           Feld[xx][yy] = activated_magic_wall;
6147       }
6148
6149       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6150       game.magic_wall_active = TRUE;
6151
6152       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6153                             SND_MAGIC_WALL_ACTIVATING :
6154                             smashed == EL_BD_MAGIC_WALL ?
6155                             SND_BD_MAGIC_WALL_ACTIVATING :
6156                             SND_DC_MAGIC_WALL_ACTIVATING));
6157     }
6158
6159     if (IS_PLAYER(x, y + 1))
6160     {
6161       if (CAN_SMASH_PLAYER(element))
6162       {
6163         KillPlayerUnlessEnemyProtected(x, y + 1);
6164         return;
6165       }
6166     }
6167     else if (smashed == EL_PENGUIN)
6168     {
6169       if (CAN_SMASH_PLAYER(element))
6170       {
6171         Bang(x, y + 1);
6172         return;
6173       }
6174     }
6175     else if (element == EL_BD_DIAMOND)
6176     {
6177       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6178       {
6179         Bang(x, y + 1);
6180         return;
6181       }
6182     }
6183     else if (((element == EL_SP_INFOTRON ||
6184                element == EL_SP_ZONK) &&
6185               (smashed == EL_SP_SNIKSNAK ||
6186                smashed == EL_SP_ELECTRON ||
6187                smashed == EL_SP_DISK_ORANGE)) ||
6188              (element == EL_SP_INFOTRON &&
6189               smashed == EL_SP_DISK_YELLOW))
6190     {
6191       Bang(x, y + 1);
6192       return;
6193     }
6194     else if (CAN_SMASH_EVERYTHING(element))
6195     {
6196       if (IS_CLASSIC_ENEMY(smashed) ||
6197           CAN_EXPLODE_SMASHED(smashed))
6198       {
6199         Bang(x, y + 1);
6200         return;
6201       }
6202       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6203       {
6204         if (smashed == EL_LAMP ||
6205             smashed == EL_LAMP_ACTIVE)
6206         {
6207           Bang(x, y + 1);
6208           return;
6209         }
6210         else if (smashed == EL_NUT)
6211         {
6212           Feld[x][y + 1] = EL_NUT_BREAKING;
6213           PlayLevelSound(x, y, SND_NUT_BREAKING);
6214           RaiseScoreElement(EL_NUT);
6215           return;
6216         }
6217         else if (smashed == EL_PEARL)
6218         {
6219           ResetGfxAnimation(x, y);
6220
6221           Feld[x][y + 1] = EL_PEARL_BREAKING;
6222           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6223           return;
6224         }
6225         else if (smashed == EL_DIAMOND)
6226         {
6227           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6228           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6229           return;
6230         }
6231         else if (IS_BELT_SWITCH(smashed))
6232         {
6233           ToggleBeltSwitch(x, y + 1);
6234         }
6235         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6236                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6237                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6238                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6239         {
6240           ToggleSwitchgateSwitch(x, y + 1);
6241         }
6242         else if (smashed == EL_LIGHT_SWITCH ||
6243                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6244         {
6245           ToggleLightSwitch(x, y + 1);
6246         }
6247         else
6248         {
6249           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6250
6251           CheckElementChangeBySide(x, y + 1, smashed, element,
6252                                    CE_SWITCHED, CH_SIDE_TOP);
6253           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6254                                             CH_SIDE_TOP);
6255         }
6256       }
6257       else
6258       {
6259         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6260       }
6261     }
6262   }
6263
6264   /* play sound of magic wall / mill */
6265   if (!last_line &&
6266       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6267        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6268        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6269   {
6270     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6271       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6272     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6273       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6274     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6275       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6276
6277     return;
6278   }
6279
6280   /* play sound of object that hits the ground */
6281   if (last_line || object_hit)
6282     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6283 }
6284
6285 inline static void TurnRoundExt(int x, int y)
6286 {
6287   static struct
6288   {
6289     int dx, dy;
6290   } move_xy[] =
6291   {
6292     {  0,  0 },
6293     { -1,  0 },
6294     { +1,  0 },
6295     {  0,  0 },
6296     {  0, -1 },
6297     {  0,  0 }, { 0, 0 }, { 0, 0 },
6298     {  0, +1 }
6299   };
6300   static struct
6301   {
6302     int left, right, back;
6303   } turn[] =
6304   {
6305     { 0,        0,              0        },
6306     { MV_DOWN,  MV_UP,          MV_RIGHT },
6307     { MV_UP,    MV_DOWN,        MV_LEFT  },
6308     { 0,        0,              0        },
6309     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6310     { 0,        0,              0        },
6311     { 0,        0,              0        },
6312     { 0,        0,              0        },
6313     { MV_RIGHT, MV_LEFT,        MV_UP    }
6314   };
6315
6316   int element = Feld[x][y];
6317   int move_pattern = element_info[element].move_pattern;
6318
6319   int old_move_dir = MovDir[x][y];
6320   int left_dir  = turn[old_move_dir].left;
6321   int right_dir = turn[old_move_dir].right;
6322   int back_dir  = turn[old_move_dir].back;
6323
6324   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6325   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6326   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6327   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6328
6329   int left_x  = x + left_dx,  left_y  = y + left_dy;
6330   int right_x = x + right_dx, right_y = y + right_dy;
6331   int move_x  = x + move_dx,  move_y  = y + move_dy;
6332
6333   int xx, yy;
6334
6335   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6336   {
6337     TestIfBadThingTouchesOtherBadThing(x, y);
6338
6339     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6340       MovDir[x][y] = right_dir;
6341     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6342       MovDir[x][y] = left_dir;
6343
6344     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6345       MovDelay[x][y] = 9;
6346     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6347       MovDelay[x][y] = 1;
6348   }
6349   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6350   {
6351     TestIfBadThingTouchesOtherBadThing(x, y);
6352
6353     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6354       MovDir[x][y] = left_dir;
6355     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6356       MovDir[x][y] = right_dir;
6357
6358     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6359       MovDelay[x][y] = 9;
6360     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6361       MovDelay[x][y] = 1;
6362   }
6363   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6364   {
6365     TestIfBadThingTouchesOtherBadThing(x, y);
6366
6367     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6368       MovDir[x][y] = left_dir;
6369     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6370       MovDir[x][y] = right_dir;
6371
6372     if (MovDir[x][y] != old_move_dir)
6373       MovDelay[x][y] = 9;
6374   }
6375   else if (element == EL_YAMYAM)
6376   {
6377     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6378     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6379
6380     if (can_turn_left && can_turn_right)
6381       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6382     else if (can_turn_left)
6383       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6384     else if (can_turn_right)
6385       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6386     else
6387       MovDir[x][y] = back_dir;
6388
6389     MovDelay[x][y] = 16 + 16 * RND(3);
6390   }
6391   else if (element == EL_DARK_YAMYAM)
6392   {
6393     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6394                                                          left_x, left_y);
6395     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6396                                                          right_x, right_y);
6397
6398     if (can_turn_left && can_turn_right)
6399       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6400     else if (can_turn_left)
6401       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6402     else if (can_turn_right)
6403       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6404     else
6405       MovDir[x][y] = back_dir;
6406
6407     MovDelay[x][y] = 16 + 16 * RND(3);
6408   }
6409   else if (element == EL_PACMAN)
6410   {
6411     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6412     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6413
6414     if (can_turn_left && can_turn_right)
6415       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6416     else if (can_turn_left)
6417       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6418     else if (can_turn_right)
6419       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6420     else
6421       MovDir[x][y] = back_dir;
6422
6423     MovDelay[x][y] = 6 + RND(40);
6424   }
6425   else if (element == EL_PIG)
6426   {
6427     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6428     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6429     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6430     boolean should_turn_left, should_turn_right, should_move_on;
6431     int rnd_value = 24;
6432     int rnd = RND(rnd_value);
6433
6434     should_turn_left = (can_turn_left &&
6435                         (!can_move_on ||
6436                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6437                                                    y + back_dy + left_dy)));
6438     should_turn_right = (can_turn_right &&
6439                          (!can_move_on ||
6440                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6441                                                     y + back_dy + right_dy)));
6442     should_move_on = (can_move_on &&
6443                       (!can_turn_left ||
6444                        !can_turn_right ||
6445                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6446                                                  y + move_dy + left_dy) ||
6447                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6448                                                  y + move_dy + right_dy)));
6449
6450     if (should_turn_left || should_turn_right || should_move_on)
6451     {
6452       if (should_turn_left && should_turn_right && should_move_on)
6453         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6454                         rnd < 2 * rnd_value / 3 ? right_dir :
6455                         old_move_dir);
6456       else if (should_turn_left && should_turn_right)
6457         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6458       else if (should_turn_left && should_move_on)
6459         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6460       else if (should_turn_right && should_move_on)
6461         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6462       else if (should_turn_left)
6463         MovDir[x][y] = left_dir;
6464       else if (should_turn_right)
6465         MovDir[x][y] = right_dir;
6466       else if (should_move_on)
6467         MovDir[x][y] = old_move_dir;
6468     }
6469     else if (can_move_on && rnd > rnd_value / 8)
6470       MovDir[x][y] = old_move_dir;
6471     else if (can_turn_left && can_turn_right)
6472       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6473     else if (can_turn_left && rnd > rnd_value / 8)
6474       MovDir[x][y] = left_dir;
6475     else if (can_turn_right && rnd > rnd_value/8)
6476       MovDir[x][y] = right_dir;
6477     else
6478       MovDir[x][y] = back_dir;
6479
6480     xx = x + move_xy[MovDir[x][y]].dx;
6481     yy = y + move_xy[MovDir[x][y]].dy;
6482
6483     if (!IN_LEV_FIELD(xx, yy) ||
6484         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6485       MovDir[x][y] = old_move_dir;
6486
6487     MovDelay[x][y] = 0;
6488   }
6489   else if (element == EL_DRAGON)
6490   {
6491     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6492     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6493     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6494     int rnd_value = 24;
6495     int rnd = RND(rnd_value);
6496
6497     if (can_move_on && rnd > rnd_value / 8)
6498       MovDir[x][y] = old_move_dir;
6499     else if (can_turn_left && can_turn_right)
6500       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6501     else if (can_turn_left && rnd > rnd_value / 8)
6502       MovDir[x][y] = left_dir;
6503     else if (can_turn_right && rnd > rnd_value / 8)
6504       MovDir[x][y] = right_dir;
6505     else
6506       MovDir[x][y] = back_dir;
6507
6508     xx = x + move_xy[MovDir[x][y]].dx;
6509     yy = y + move_xy[MovDir[x][y]].dy;
6510
6511     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6512       MovDir[x][y] = old_move_dir;
6513
6514     MovDelay[x][y] = 0;
6515   }
6516   else if (element == EL_MOLE)
6517   {
6518     boolean can_move_on =
6519       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6520                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6521                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6522     if (!can_move_on)
6523     {
6524       boolean can_turn_left =
6525         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6526                               IS_AMOEBOID(Feld[left_x][left_y])));
6527
6528       boolean can_turn_right =
6529         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6530                               IS_AMOEBOID(Feld[right_x][right_y])));
6531
6532       if (can_turn_left && can_turn_right)
6533         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6534       else if (can_turn_left)
6535         MovDir[x][y] = left_dir;
6536       else
6537         MovDir[x][y] = right_dir;
6538     }
6539
6540     if (MovDir[x][y] != old_move_dir)
6541       MovDelay[x][y] = 9;
6542   }
6543   else if (element == EL_BALLOON)
6544   {
6545     MovDir[x][y] = game.wind_direction;
6546     MovDelay[x][y] = 0;
6547   }
6548   else if (element == EL_SPRING)
6549   {
6550     if (MovDir[x][y] & MV_HORIZONTAL)
6551     {
6552       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6553           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6554       {
6555         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6556         ResetGfxAnimation(move_x, move_y);
6557         TEST_DrawLevelField(move_x, move_y);
6558
6559         MovDir[x][y] = back_dir;
6560       }
6561       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6562                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6563         MovDir[x][y] = MV_NONE;
6564     }
6565
6566     MovDelay[x][y] = 0;
6567   }
6568   else if (element == EL_ROBOT ||
6569            element == EL_SATELLITE ||
6570            element == EL_PENGUIN ||
6571            element == EL_EMC_ANDROID)
6572   {
6573     int attr_x = -1, attr_y = -1;
6574
6575     if (AllPlayersGone)
6576     {
6577       attr_x = ExitX;
6578       attr_y = ExitY;
6579     }
6580     else
6581     {
6582       int i;
6583
6584       for (i = 0; i < MAX_PLAYERS; i++)
6585       {
6586         struct PlayerInfo *player = &stored_player[i];
6587         int jx = player->jx, jy = player->jy;
6588
6589         if (!player->active)
6590           continue;
6591
6592         if (attr_x == -1 ||
6593             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6594         {
6595           attr_x = jx;
6596           attr_y = jy;
6597         }
6598       }
6599     }
6600
6601     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6602         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6603          game.engine_version < VERSION_IDENT(3,1,0,0)))
6604     {
6605       attr_x = ZX;
6606       attr_y = ZY;
6607     }
6608
6609     if (element == EL_PENGUIN)
6610     {
6611       int i;
6612       static int xy[4][2] =
6613       {
6614         { 0, -1 },
6615         { -1, 0 },
6616         { +1, 0 },
6617         { 0, +1 }
6618       };
6619
6620       for (i = 0; i < NUM_DIRECTIONS; i++)
6621       {
6622         int ex = x + xy[i][0];
6623         int ey = y + xy[i][1];
6624
6625         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6626                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6627                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6628                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6629         {
6630           attr_x = ex;
6631           attr_y = ey;
6632           break;
6633         }
6634       }
6635     }
6636
6637     MovDir[x][y] = MV_NONE;
6638     if (attr_x < x)
6639       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6640     else if (attr_x > x)
6641       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6642     if (attr_y < y)
6643       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6644     else if (attr_y > y)
6645       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6646
6647     if (element == EL_ROBOT)
6648     {
6649       int newx, newy;
6650
6651       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6652         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6653       Moving2Blocked(x, y, &newx, &newy);
6654
6655       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6656         MovDelay[x][y] = 8 + 8 * !RND(3);
6657       else
6658         MovDelay[x][y] = 16;
6659     }
6660     else if (element == EL_PENGUIN)
6661     {
6662       int newx, newy;
6663
6664       MovDelay[x][y] = 1;
6665
6666       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6667       {
6668         boolean first_horiz = RND(2);
6669         int new_move_dir = MovDir[x][y];
6670
6671         MovDir[x][y] =
6672           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6673         Moving2Blocked(x, y, &newx, &newy);
6674
6675         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6676           return;
6677
6678         MovDir[x][y] =
6679           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6680         Moving2Blocked(x, y, &newx, &newy);
6681
6682         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6683           return;
6684
6685         MovDir[x][y] = old_move_dir;
6686         return;
6687       }
6688     }
6689     else if (element == EL_SATELLITE)
6690     {
6691       int newx, newy;
6692
6693       MovDelay[x][y] = 1;
6694
6695       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6696       {
6697         boolean first_horiz = RND(2);
6698         int new_move_dir = MovDir[x][y];
6699
6700         MovDir[x][y] =
6701           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6702         Moving2Blocked(x, y, &newx, &newy);
6703
6704         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6705           return;
6706
6707         MovDir[x][y] =
6708           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6709         Moving2Blocked(x, y, &newx, &newy);
6710
6711         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6712           return;
6713
6714         MovDir[x][y] = old_move_dir;
6715         return;
6716       }
6717     }
6718     else if (element == EL_EMC_ANDROID)
6719     {
6720       static int check_pos[16] =
6721       {
6722         -1,             /*  0 => (invalid)          */
6723         7,              /*  1 => MV_LEFT            */
6724         3,              /*  2 => MV_RIGHT           */
6725         -1,             /*  3 => (invalid)          */
6726         1,              /*  4 =>            MV_UP   */
6727         0,              /*  5 => MV_LEFT  | MV_UP   */
6728         2,              /*  6 => MV_RIGHT | MV_UP   */
6729         -1,             /*  7 => (invalid)          */
6730         5,              /*  8 =>            MV_DOWN */
6731         6,              /*  9 => MV_LEFT  | MV_DOWN */
6732         4,              /* 10 => MV_RIGHT | MV_DOWN */
6733         -1,             /* 11 => (invalid)          */
6734         -1,             /* 12 => (invalid)          */
6735         -1,             /* 13 => (invalid)          */
6736         -1,             /* 14 => (invalid)          */
6737         -1,             /* 15 => (invalid)          */
6738       };
6739       static struct
6740       {
6741         int dx, dy;
6742         int dir;
6743       } check_xy[8] =
6744       {
6745         { -1, -1,       MV_LEFT  | MV_UP   },
6746         {  0, -1,                  MV_UP   },
6747         { +1, -1,       MV_RIGHT | MV_UP   },
6748         { +1,  0,       MV_RIGHT           },
6749         { +1, +1,       MV_RIGHT | MV_DOWN },
6750         {  0, +1,                  MV_DOWN },
6751         { -1, +1,       MV_LEFT  | MV_DOWN },
6752         { -1,  0,       MV_LEFT            },
6753       };
6754       int start_pos, check_order;
6755       boolean can_clone = FALSE;
6756       int i;
6757
6758       /* check if there is any free field around current position */
6759       for (i = 0; i < 8; i++)
6760       {
6761         int newx = x + check_xy[i].dx;
6762         int newy = y + check_xy[i].dy;
6763
6764         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6765         {
6766           can_clone = TRUE;
6767
6768           break;
6769         }
6770       }
6771
6772       if (can_clone)            /* randomly find an element to clone */
6773       {
6774         can_clone = FALSE;
6775
6776         start_pos = check_pos[RND(8)];
6777         check_order = (RND(2) ? -1 : +1);
6778
6779         for (i = 0; i < 8; i++)
6780         {
6781           int pos_raw = start_pos + i * check_order;
6782           int pos = (pos_raw + 8) % 8;
6783           int newx = x + check_xy[pos].dx;
6784           int newy = y + check_xy[pos].dy;
6785
6786           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6787           {
6788             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6789             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6790
6791             Store[x][y] = Feld[newx][newy];
6792
6793             can_clone = TRUE;
6794
6795             break;
6796           }
6797         }
6798       }
6799
6800       if (can_clone)            /* randomly find a direction to move */
6801       {
6802         can_clone = FALSE;
6803
6804         start_pos = check_pos[RND(8)];
6805         check_order = (RND(2) ? -1 : +1);
6806
6807         for (i = 0; i < 8; i++)
6808         {
6809           int pos_raw = start_pos + i * check_order;
6810           int pos = (pos_raw + 8) % 8;
6811           int newx = x + check_xy[pos].dx;
6812           int newy = y + check_xy[pos].dy;
6813           int new_move_dir = check_xy[pos].dir;
6814
6815           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6816           {
6817             MovDir[x][y] = new_move_dir;
6818             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6819
6820             can_clone = TRUE;
6821
6822             break;
6823           }
6824         }
6825       }
6826
6827       if (can_clone)            /* cloning and moving successful */
6828         return;
6829
6830       /* cannot clone -- try to move towards player */
6831
6832       start_pos = check_pos[MovDir[x][y] & 0x0f];
6833       check_order = (RND(2) ? -1 : +1);
6834
6835       for (i = 0; i < 3; i++)
6836       {
6837         /* first check start_pos, then previous/next or (next/previous) pos */
6838         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6839         int pos = (pos_raw + 8) % 8;
6840         int newx = x + check_xy[pos].dx;
6841         int newy = y + check_xy[pos].dy;
6842         int new_move_dir = check_xy[pos].dir;
6843
6844         if (IS_PLAYER(newx, newy))
6845           break;
6846
6847         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6848         {
6849           MovDir[x][y] = new_move_dir;
6850           MovDelay[x][y] = level.android_move_time * 8 + 1;
6851
6852           break;
6853         }
6854       }
6855     }
6856   }
6857   else if (move_pattern == MV_TURNING_LEFT ||
6858            move_pattern == MV_TURNING_RIGHT ||
6859            move_pattern == MV_TURNING_LEFT_RIGHT ||
6860            move_pattern == MV_TURNING_RIGHT_LEFT ||
6861            move_pattern == MV_TURNING_RANDOM ||
6862            move_pattern == MV_ALL_DIRECTIONS)
6863   {
6864     boolean can_turn_left =
6865       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6866     boolean can_turn_right =
6867       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6868
6869     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6870       return;
6871
6872     if (move_pattern == MV_TURNING_LEFT)
6873       MovDir[x][y] = left_dir;
6874     else if (move_pattern == MV_TURNING_RIGHT)
6875       MovDir[x][y] = right_dir;
6876     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6877       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6878     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6879       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6880     else if (move_pattern == MV_TURNING_RANDOM)
6881       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6882                       can_turn_right && !can_turn_left ? right_dir :
6883                       RND(2) ? left_dir : right_dir);
6884     else if (can_turn_left && can_turn_right)
6885       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6886     else if (can_turn_left)
6887       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6888     else if (can_turn_right)
6889       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6890     else
6891       MovDir[x][y] = back_dir;
6892
6893     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6894   }
6895   else if (move_pattern == MV_HORIZONTAL ||
6896            move_pattern == MV_VERTICAL)
6897   {
6898     if (move_pattern & old_move_dir)
6899       MovDir[x][y] = back_dir;
6900     else if (move_pattern == MV_HORIZONTAL)
6901       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6902     else if (move_pattern == MV_VERTICAL)
6903       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6904
6905     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6906   }
6907   else if (move_pattern & MV_ANY_DIRECTION)
6908   {
6909     MovDir[x][y] = move_pattern;
6910     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6911   }
6912   else if (move_pattern & MV_WIND_DIRECTION)
6913   {
6914     MovDir[x][y] = game.wind_direction;
6915     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6916   }
6917   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6918   {
6919     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6920       MovDir[x][y] = left_dir;
6921     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6922       MovDir[x][y] = right_dir;
6923
6924     if (MovDir[x][y] != old_move_dir)
6925       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6926   }
6927   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6928   {
6929     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6930       MovDir[x][y] = right_dir;
6931     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6932       MovDir[x][y] = left_dir;
6933
6934     if (MovDir[x][y] != old_move_dir)
6935       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6936   }
6937   else if (move_pattern == MV_TOWARDS_PLAYER ||
6938            move_pattern == MV_AWAY_FROM_PLAYER)
6939   {
6940     int attr_x = -1, attr_y = -1;
6941     int newx, newy;
6942     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6943
6944     if (AllPlayersGone)
6945     {
6946       attr_x = ExitX;
6947       attr_y = ExitY;
6948     }
6949     else
6950     {
6951       int i;
6952
6953       for (i = 0; i < MAX_PLAYERS; i++)
6954       {
6955         struct PlayerInfo *player = &stored_player[i];
6956         int jx = player->jx, jy = player->jy;
6957
6958         if (!player->active)
6959           continue;
6960
6961         if (attr_x == -1 ||
6962             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6963         {
6964           attr_x = jx;
6965           attr_y = jy;
6966         }
6967       }
6968     }
6969
6970     MovDir[x][y] = MV_NONE;
6971     if (attr_x < x)
6972       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6973     else if (attr_x > x)
6974       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6975     if (attr_y < y)
6976       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6977     else if (attr_y > y)
6978       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6979
6980     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6981
6982     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6983     {
6984       boolean first_horiz = RND(2);
6985       int new_move_dir = MovDir[x][y];
6986
6987       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6988       {
6989         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6990         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6991
6992         return;
6993       }
6994
6995       MovDir[x][y] =
6996         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6997       Moving2Blocked(x, y, &newx, &newy);
6998
6999       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7000         return;
7001
7002       MovDir[x][y] =
7003         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7004       Moving2Blocked(x, y, &newx, &newy);
7005
7006       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7007         return;
7008
7009       MovDir[x][y] = old_move_dir;
7010     }
7011   }
7012   else if (move_pattern == MV_WHEN_PUSHED ||
7013            move_pattern == MV_WHEN_DROPPED)
7014   {
7015     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7016       MovDir[x][y] = MV_NONE;
7017
7018     MovDelay[x][y] = 0;
7019   }
7020   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7021   {
7022     static int test_xy[7][2] =
7023     {
7024       { 0, -1 },
7025       { -1, 0 },
7026       { +1, 0 },
7027       { 0, +1 },
7028       { 0, -1 },
7029       { -1, 0 },
7030       { +1, 0 },
7031     };
7032     static int test_dir[7] =
7033     {
7034       MV_UP,
7035       MV_LEFT,
7036       MV_RIGHT,
7037       MV_DOWN,
7038       MV_UP,
7039       MV_LEFT,
7040       MV_RIGHT,
7041     };
7042     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7043     int move_preference = -1000000;     /* start with very low preference */
7044     int new_move_dir = MV_NONE;
7045     int start_test = RND(4);
7046     int i;
7047
7048     for (i = 0; i < NUM_DIRECTIONS; i++)
7049     {
7050       int move_dir = test_dir[start_test + i];
7051       int move_dir_preference;
7052
7053       xx = x + test_xy[start_test + i][0];
7054       yy = y + test_xy[start_test + i][1];
7055
7056       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7057           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7058       {
7059         new_move_dir = move_dir;
7060
7061         break;
7062       }
7063
7064       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7065         continue;
7066
7067       move_dir_preference = -1 * RunnerVisit[xx][yy];
7068       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7069         move_dir_preference = PlayerVisit[xx][yy];
7070
7071       if (move_dir_preference > move_preference)
7072       {
7073         /* prefer field that has not been visited for the longest time */
7074         move_preference = move_dir_preference;
7075         new_move_dir = move_dir;
7076       }
7077       else if (move_dir_preference == move_preference &&
7078                move_dir == old_move_dir)
7079       {
7080         /* prefer last direction when all directions are preferred equally */
7081         move_preference = move_dir_preference;
7082         new_move_dir = move_dir;
7083       }
7084     }
7085
7086     MovDir[x][y] = new_move_dir;
7087     if (old_move_dir != new_move_dir)
7088       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7089   }
7090 }
7091
7092 static void TurnRound(int x, int y)
7093 {
7094   int direction = MovDir[x][y];
7095
7096   TurnRoundExt(x, y);
7097
7098   GfxDir[x][y] = MovDir[x][y];
7099
7100   if (direction != MovDir[x][y])
7101     GfxFrame[x][y] = 0;
7102
7103   if (MovDelay[x][y])
7104     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7105
7106   ResetGfxFrame(x, y, FALSE);
7107 }
7108
7109 static boolean JustBeingPushed(int x, int y)
7110 {
7111   int i;
7112
7113   for (i = 0; i < MAX_PLAYERS; i++)
7114   {
7115     struct PlayerInfo *player = &stored_player[i];
7116
7117     if (player->active && player->is_pushing && player->MovPos)
7118     {
7119       int next_jx = player->jx + (player->jx - player->last_jx);
7120       int next_jy = player->jy + (player->jy - player->last_jy);
7121
7122       if (x == next_jx && y == next_jy)
7123         return TRUE;
7124     }
7125   }
7126
7127   return FALSE;
7128 }
7129
7130 void StartMoving(int x, int y)
7131 {
7132   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7133   int element = Feld[x][y];
7134
7135   if (Stop[x][y])
7136     return;
7137
7138   if (MovDelay[x][y] == 0)
7139     GfxAction[x][y] = ACTION_DEFAULT;
7140
7141   if (CAN_FALL(element) && y < lev_fieldy - 1)
7142   {
7143     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7144         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7145       if (JustBeingPushed(x, y))
7146         return;
7147
7148     if (element == EL_QUICKSAND_FULL)
7149     {
7150       if (IS_FREE(x, y + 1))
7151       {
7152         InitMovingField(x, y, MV_DOWN);
7153         started_moving = TRUE;
7154
7155         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7156 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7157         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7158           Store[x][y] = EL_ROCK;
7159 #else
7160         Store[x][y] = EL_ROCK;
7161 #endif
7162
7163         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7164       }
7165       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7166       {
7167         if (!MovDelay[x][y])
7168         {
7169           MovDelay[x][y] = TILEY + 1;
7170
7171           ResetGfxAnimation(x, y);
7172           ResetGfxAnimation(x, y + 1);
7173         }
7174
7175         if (MovDelay[x][y])
7176         {
7177           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7178           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7179
7180           MovDelay[x][y]--;
7181           if (MovDelay[x][y])
7182             return;
7183         }
7184
7185         Feld[x][y] = EL_QUICKSAND_EMPTY;
7186         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7187         Store[x][y + 1] = Store[x][y];
7188         Store[x][y] = 0;
7189
7190         PlayLevelSoundAction(x, y, ACTION_FILLING);
7191       }
7192       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7193       {
7194         if (!MovDelay[x][y])
7195         {
7196           MovDelay[x][y] = TILEY + 1;
7197
7198           ResetGfxAnimation(x, y);
7199           ResetGfxAnimation(x, y + 1);
7200         }
7201
7202         if (MovDelay[x][y])
7203         {
7204           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7205           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7206
7207           MovDelay[x][y]--;
7208           if (MovDelay[x][y])
7209             return;
7210         }
7211
7212         Feld[x][y] = EL_QUICKSAND_EMPTY;
7213         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7214         Store[x][y + 1] = Store[x][y];
7215         Store[x][y] = 0;
7216
7217         PlayLevelSoundAction(x, y, ACTION_FILLING);
7218       }
7219     }
7220     else if (element == EL_QUICKSAND_FAST_FULL)
7221     {
7222       if (IS_FREE(x, y + 1))
7223       {
7224         InitMovingField(x, y, MV_DOWN);
7225         started_moving = TRUE;
7226
7227         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7228 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7229         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7230           Store[x][y] = EL_ROCK;
7231 #else
7232         Store[x][y] = EL_ROCK;
7233 #endif
7234
7235         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7236       }
7237       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7238       {
7239         if (!MovDelay[x][y])
7240         {
7241           MovDelay[x][y] = TILEY + 1;
7242
7243           ResetGfxAnimation(x, y);
7244           ResetGfxAnimation(x, y + 1);
7245         }
7246
7247         if (MovDelay[x][y])
7248         {
7249           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7250           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7251
7252           MovDelay[x][y]--;
7253           if (MovDelay[x][y])
7254             return;
7255         }
7256
7257         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7258         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7259         Store[x][y + 1] = Store[x][y];
7260         Store[x][y] = 0;
7261
7262         PlayLevelSoundAction(x, y, ACTION_FILLING);
7263       }
7264       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7265       {
7266         if (!MovDelay[x][y])
7267         {
7268           MovDelay[x][y] = TILEY + 1;
7269
7270           ResetGfxAnimation(x, y);
7271           ResetGfxAnimation(x, y + 1);
7272         }
7273
7274         if (MovDelay[x][y])
7275         {
7276           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7277           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7278
7279           MovDelay[x][y]--;
7280           if (MovDelay[x][y])
7281             return;
7282         }
7283
7284         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7285         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7286         Store[x][y + 1] = Store[x][y];
7287         Store[x][y] = 0;
7288
7289         PlayLevelSoundAction(x, y, ACTION_FILLING);
7290       }
7291     }
7292     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7293              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7294     {
7295       InitMovingField(x, y, MV_DOWN);
7296       started_moving = TRUE;
7297
7298       Feld[x][y] = EL_QUICKSAND_FILLING;
7299       Store[x][y] = element;
7300
7301       PlayLevelSoundAction(x, y, ACTION_FILLING);
7302     }
7303     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7304              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7305     {
7306       InitMovingField(x, y, MV_DOWN);
7307       started_moving = TRUE;
7308
7309       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7310       Store[x][y] = element;
7311
7312       PlayLevelSoundAction(x, y, ACTION_FILLING);
7313     }
7314     else if (element == EL_MAGIC_WALL_FULL)
7315     {
7316       if (IS_FREE(x, y + 1))
7317       {
7318         InitMovingField(x, y, MV_DOWN);
7319         started_moving = TRUE;
7320
7321         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7322         Store[x][y] = EL_CHANGED(Store[x][y]);
7323       }
7324       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7325       {
7326         if (!MovDelay[x][y])
7327           MovDelay[x][y] = TILEY / 4 + 1;
7328
7329         if (MovDelay[x][y])
7330         {
7331           MovDelay[x][y]--;
7332           if (MovDelay[x][y])
7333             return;
7334         }
7335
7336         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7337         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7338         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7339         Store[x][y] = 0;
7340       }
7341     }
7342     else if (element == EL_BD_MAGIC_WALL_FULL)
7343     {
7344       if (IS_FREE(x, y + 1))
7345       {
7346         InitMovingField(x, y, MV_DOWN);
7347         started_moving = TRUE;
7348
7349         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7350         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7351       }
7352       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7353       {
7354         if (!MovDelay[x][y])
7355           MovDelay[x][y] = TILEY / 4 + 1;
7356
7357         if (MovDelay[x][y])
7358         {
7359           MovDelay[x][y]--;
7360           if (MovDelay[x][y])
7361             return;
7362         }
7363
7364         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7365         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7366         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7367         Store[x][y] = 0;
7368       }
7369     }
7370     else if (element == EL_DC_MAGIC_WALL_FULL)
7371     {
7372       if (IS_FREE(x, y + 1))
7373       {
7374         InitMovingField(x, y, MV_DOWN);
7375         started_moving = TRUE;
7376
7377         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7378         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7379       }
7380       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7381       {
7382         if (!MovDelay[x][y])
7383           MovDelay[x][y] = TILEY / 4 + 1;
7384
7385         if (MovDelay[x][y])
7386         {
7387           MovDelay[x][y]--;
7388           if (MovDelay[x][y])
7389             return;
7390         }
7391
7392         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7393         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7394         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7395         Store[x][y] = 0;
7396       }
7397     }
7398     else if ((CAN_PASS_MAGIC_WALL(element) &&
7399               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7400                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7401              (CAN_PASS_DC_MAGIC_WALL(element) &&
7402               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7403
7404     {
7405       InitMovingField(x, y, MV_DOWN);
7406       started_moving = TRUE;
7407
7408       Feld[x][y] =
7409         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7410          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7411          EL_DC_MAGIC_WALL_FILLING);
7412       Store[x][y] = element;
7413     }
7414     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7415     {
7416       SplashAcid(x, y + 1);
7417
7418       InitMovingField(x, y, MV_DOWN);
7419       started_moving = TRUE;
7420
7421       Store[x][y] = EL_ACID;
7422     }
7423     else if (
7424              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7425               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7426              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7427               CAN_FALL(element) && WasJustFalling[x][y] &&
7428               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7429
7430              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7431               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7432               (Feld[x][y + 1] == EL_BLOCKED)))
7433     {
7434       /* this is needed for a special case not covered by calling "Impact()"
7435          from "ContinueMoving()": if an element moves to a tile directly below
7436          another element which was just falling on that tile (which was empty
7437          in the previous frame), the falling element above would just stop
7438          instead of smashing the element below (in previous version, the above
7439          element was just checked for "moving" instead of "falling", resulting
7440          in incorrect smashes caused by horizontal movement of the above
7441          element; also, the case of the player being the element to smash was
7442          simply not covered here... :-/ ) */
7443
7444       CheckCollision[x][y] = 0;
7445       CheckImpact[x][y] = 0;
7446
7447       Impact(x, y);
7448     }
7449     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7450     {
7451       if (MovDir[x][y] == MV_NONE)
7452       {
7453         InitMovingField(x, y, MV_DOWN);
7454         started_moving = TRUE;
7455       }
7456     }
7457     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7458     {
7459       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7460         MovDir[x][y] = MV_DOWN;
7461
7462       InitMovingField(x, y, MV_DOWN);
7463       started_moving = TRUE;
7464     }
7465     else if (element == EL_AMOEBA_DROP)
7466     {
7467       Feld[x][y] = EL_AMOEBA_GROWING;
7468       Store[x][y] = EL_AMOEBA_WET;
7469     }
7470     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7471               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7472              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7473              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7474     {
7475       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7476                                 (IS_FREE(x - 1, y + 1) ||
7477                                  Feld[x - 1][y + 1] == EL_ACID));
7478       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7479                                 (IS_FREE(x + 1, y + 1) ||
7480                                  Feld[x + 1][y + 1] == EL_ACID));
7481       boolean can_fall_any  = (can_fall_left || can_fall_right);
7482       boolean can_fall_both = (can_fall_left && can_fall_right);
7483       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7484
7485       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7486       {
7487         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7488           can_fall_right = FALSE;
7489         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7490           can_fall_left = FALSE;
7491         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7492           can_fall_right = FALSE;
7493         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7494           can_fall_left = FALSE;
7495
7496         can_fall_any  = (can_fall_left || can_fall_right);
7497         can_fall_both = FALSE;
7498       }
7499
7500       if (can_fall_both)
7501       {
7502         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7503           can_fall_right = FALSE;       /* slip down on left side */
7504         else
7505           can_fall_left = !(can_fall_right = RND(2));
7506
7507         can_fall_both = FALSE;
7508       }
7509
7510       if (can_fall_any)
7511       {
7512         /* if not determined otherwise, prefer left side for slipping down */
7513         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7514         started_moving = TRUE;
7515       }
7516     }
7517     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7518     {
7519       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7520       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7521       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7522       int belt_dir = game.belt_dir[belt_nr];
7523
7524       if ((belt_dir == MV_LEFT  && left_is_free) ||
7525           (belt_dir == MV_RIGHT && right_is_free))
7526       {
7527         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7528
7529         InitMovingField(x, y, belt_dir);
7530         started_moving = TRUE;
7531
7532         Pushed[x][y] = TRUE;
7533         Pushed[nextx][y] = TRUE;
7534
7535         GfxAction[x][y] = ACTION_DEFAULT;
7536       }
7537       else
7538       {
7539         MovDir[x][y] = 0;       /* if element was moving, stop it */
7540       }
7541     }
7542   }
7543
7544   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7545   if (CAN_MOVE(element) && !started_moving)
7546   {
7547     int move_pattern = element_info[element].move_pattern;
7548     int newx, newy;
7549
7550     Moving2Blocked(x, y, &newx, &newy);
7551
7552     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7553       return;
7554
7555     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7556         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7557     {
7558       WasJustMoving[x][y] = 0;
7559       CheckCollision[x][y] = 0;
7560
7561       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7562
7563       if (Feld[x][y] != element)        /* element has changed */
7564         return;
7565     }
7566
7567     if (!MovDelay[x][y])        /* start new movement phase */
7568     {
7569       /* all objects that can change their move direction after each step
7570          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7571
7572       if (element != EL_YAMYAM &&
7573           element != EL_DARK_YAMYAM &&
7574           element != EL_PACMAN &&
7575           !(move_pattern & MV_ANY_DIRECTION) &&
7576           move_pattern != MV_TURNING_LEFT &&
7577           move_pattern != MV_TURNING_RIGHT &&
7578           move_pattern != MV_TURNING_LEFT_RIGHT &&
7579           move_pattern != MV_TURNING_RIGHT_LEFT &&
7580           move_pattern != MV_TURNING_RANDOM)
7581       {
7582         TurnRound(x, y);
7583
7584         if (MovDelay[x][y] && (element == EL_BUG ||
7585                                element == EL_SPACESHIP ||
7586                                element == EL_SP_SNIKSNAK ||
7587                                element == EL_SP_ELECTRON ||
7588                                element == EL_MOLE))
7589           TEST_DrawLevelField(x, y);
7590       }
7591     }
7592
7593     if (MovDelay[x][y])         /* wait some time before next movement */
7594     {
7595       MovDelay[x][y]--;
7596
7597       if (element == EL_ROBOT ||
7598           element == EL_YAMYAM ||
7599           element == EL_DARK_YAMYAM)
7600       {
7601         DrawLevelElementAnimationIfNeeded(x, y, element);
7602         PlayLevelSoundAction(x, y, ACTION_WAITING);
7603       }
7604       else if (element == EL_SP_ELECTRON)
7605         DrawLevelElementAnimationIfNeeded(x, y, element);
7606       else if (element == EL_DRAGON)
7607       {
7608         int i;
7609         int dir = MovDir[x][y];
7610         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7611         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7612         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7613                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7614                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7615                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7616         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7617
7618         GfxAction[x][y] = ACTION_ATTACKING;
7619
7620         if (IS_PLAYER(x, y))
7621           DrawPlayerField(x, y);
7622         else
7623           TEST_DrawLevelField(x, y);
7624
7625         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7626
7627         for (i = 1; i <= 3; i++)
7628         {
7629           int xx = x + i * dx;
7630           int yy = y + i * dy;
7631           int sx = SCREENX(xx);
7632           int sy = SCREENY(yy);
7633           int flame_graphic = graphic + (i - 1);
7634
7635           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7636             break;
7637
7638           if (MovDelay[x][y])
7639           {
7640             int flamed = MovingOrBlocked2Element(xx, yy);
7641
7642             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7643               Bang(xx, yy);
7644             else
7645               RemoveMovingField(xx, yy);
7646
7647             ChangeDelay[xx][yy] = 0;
7648
7649             Feld[xx][yy] = EL_FLAMES;
7650
7651             if (IN_SCR_FIELD(sx, sy))
7652             {
7653               TEST_DrawLevelFieldCrumbled(xx, yy);
7654               DrawGraphic(sx, sy, flame_graphic, frame);
7655             }
7656           }
7657           else
7658           {
7659             if (Feld[xx][yy] == EL_FLAMES)
7660               Feld[xx][yy] = EL_EMPTY;
7661             TEST_DrawLevelField(xx, yy);
7662           }
7663         }
7664       }
7665
7666       if (MovDelay[x][y])       /* element still has to wait some time */
7667       {
7668         PlayLevelSoundAction(x, y, ACTION_WAITING);
7669
7670         return;
7671       }
7672     }
7673
7674     /* now make next step */
7675
7676     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7677
7678     if (DONT_COLLIDE_WITH(element) &&
7679         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7680         !PLAYER_ENEMY_PROTECTED(newx, newy))
7681     {
7682       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7683
7684       return;
7685     }
7686
7687     else if (CAN_MOVE_INTO_ACID(element) &&
7688              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7689              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7690              (MovDir[x][y] == MV_DOWN ||
7691               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7692     {
7693       SplashAcid(newx, newy);
7694       Store[x][y] = EL_ACID;
7695     }
7696     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7697     {
7698       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7699           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7700           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7701           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7702       {
7703         RemoveField(x, y);
7704         TEST_DrawLevelField(x, y);
7705
7706         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7707         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7708           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7709
7710         local_player->friends_still_needed--;
7711         if (!local_player->friends_still_needed &&
7712             !local_player->GameOver && AllPlayersGone)
7713           PlayerWins(local_player);
7714
7715         return;
7716       }
7717       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7718       {
7719         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7720           TEST_DrawLevelField(newx, newy);
7721         else
7722           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7723       }
7724       else if (!IS_FREE(newx, newy))
7725       {
7726         GfxAction[x][y] = ACTION_WAITING;
7727
7728         if (IS_PLAYER(x, y))
7729           DrawPlayerField(x, y);
7730         else
7731           TEST_DrawLevelField(x, y);
7732
7733         return;
7734       }
7735     }
7736     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7737     {
7738       if (IS_FOOD_PIG(Feld[newx][newy]))
7739       {
7740         if (IS_MOVING(newx, newy))
7741           RemoveMovingField(newx, newy);
7742         else
7743         {
7744           Feld[newx][newy] = EL_EMPTY;
7745           TEST_DrawLevelField(newx, newy);
7746         }
7747
7748         PlayLevelSound(x, y, SND_PIG_DIGGING);
7749       }
7750       else if (!IS_FREE(newx, newy))
7751       {
7752         if (IS_PLAYER(x, y))
7753           DrawPlayerField(x, y);
7754         else
7755           TEST_DrawLevelField(x, y);
7756
7757         return;
7758       }
7759     }
7760     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7761     {
7762       if (Store[x][y] != EL_EMPTY)
7763       {
7764         boolean can_clone = FALSE;
7765         int xx, yy;
7766
7767         /* check if element to clone is still there */
7768         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7769         {
7770           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7771           {
7772             can_clone = TRUE;
7773
7774             break;
7775           }
7776         }
7777
7778         /* cannot clone or target field not free anymore -- do not clone */
7779         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7780           Store[x][y] = EL_EMPTY;
7781       }
7782
7783       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7784       {
7785         if (IS_MV_DIAGONAL(MovDir[x][y]))
7786         {
7787           int diagonal_move_dir = MovDir[x][y];
7788           int stored = Store[x][y];
7789           int change_delay = 8;
7790           int graphic;
7791
7792           /* android is moving diagonally */
7793
7794           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7795
7796           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7797           GfxElement[x][y] = EL_EMC_ANDROID;
7798           GfxAction[x][y] = ACTION_SHRINKING;
7799           GfxDir[x][y] = diagonal_move_dir;
7800           ChangeDelay[x][y] = change_delay;
7801
7802           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7803                                    GfxDir[x][y]);
7804
7805           DrawLevelGraphicAnimation(x, y, graphic);
7806           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7807
7808           if (Feld[newx][newy] == EL_ACID)
7809           {
7810             SplashAcid(newx, newy);
7811
7812             return;
7813           }
7814
7815           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7816
7817           Store[newx][newy] = EL_EMC_ANDROID;
7818           GfxElement[newx][newy] = EL_EMC_ANDROID;
7819           GfxAction[newx][newy] = ACTION_GROWING;
7820           GfxDir[newx][newy] = diagonal_move_dir;
7821           ChangeDelay[newx][newy] = change_delay;
7822
7823           graphic = el_act_dir2img(GfxElement[newx][newy],
7824                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7825
7826           DrawLevelGraphicAnimation(newx, newy, graphic);
7827           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7828
7829           return;
7830         }
7831         else
7832         {
7833           Feld[newx][newy] = EL_EMPTY;
7834           TEST_DrawLevelField(newx, newy);
7835
7836           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7837         }
7838       }
7839       else if (!IS_FREE(newx, newy))
7840       {
7841         return;
7842       }
7843     }
7844     else if (IS_CUSTOM_ELEMENT(element) &&
7845              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7846     {
7847       if (!DigFieldByCE(newx, newy, element))
7848         return;
7849
7850       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7851       {
7852         RunnerVisit[x][y] = FrameCounter;
7853         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7854       }
7855     }
7856     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7857     {
7858       if (!IS_FREE(newx, newy))
7859       {
7860         if (IS_PLAYER(x, y))
7861           DrawPlayerField(x, y);
7862         else
7863           TEST_DrawLevelField(x, y);
7864
7865         return;
7866       }
7867       else
7868       {
7869         boolean wanna_flame = !RND(10);
7870         int dx = newx - x, dy = newy - y;
7871         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7872         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7873         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7874                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7875         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7876                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7877
7878         if ((wanna_flame ||
7879              IS_CLASSIC_ENEMY(element1) ||
7880              IS_CLASSIC_ENEMY(element2)) &&
7881             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7882             element1 != EL_FLAMES && element2 != EL_FLAMES)
7883         {
7884           ResetGfxAnimation(x, y);
7885           GfxAction[x][y] = ACTION_ATTACKING;
7886
7887           if (IS_PLAYER(x, y))
7888             DrawPlayerField(x, y);
7889           else
7890             TEST_DrawLevelField(x, y);
7891
7892           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7893
7894           MovDelay[x][y] = 50;
7895
7896           Feld[newx][newy] = EL_FLAMES;
7897           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7898             Feld[newx1][newy1] = EL_FLAMES;
7899           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7900             Feld[newx2][newy2] = EL_FLAMES;
7901
7902           return;
7903         }
7904       }
7905     }
7906     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7907              Feld[newx][newy] == EL_DIAMOND)
7908     {
7909       if (IS_MOVING(newx, newy))
7910         RemoveMovingField(newx, newy);
7911       else
7912       {
7913         Feld[newx][newy] = EL_EMPTY;
7914         TEST_DrawLevelField(newx, newy);
7915       }
7916
7917       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7918     }
7919     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7920              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7921     {
7922       if (AmoebaNr[newx][newy])
7923       {
7924         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7925         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7926             Feld[newx][newy] == EL_BD_AMOEBA)
7927           AmoebaCnt[AmoebaNr[newx][newy]]--;
7928       }
7929
7930       if (IS_MOVING(newx, newy))
7931       {
7932         RemoveMovingField(newx, newy);
7933       }
7934       else
7935       {
7936         Feld[newx][newy] = EL_EMPTY;
7937         TEST_DrawLevelField(newx, newy);
7938       }
7939
7940       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7941     }
7942     else if ((element == EL_PACMAN || element == EL_MOLE)
7943              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7944     {
7945       if (AmoebaNr[newx][newy])
7946       {
7947         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7948         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7949             Feld[newx][newy] == EL_BD_AMOEBA)
7950           AmoebaCnt[AmoebaNr[newx][newy]]--;
7951       }
7952
7953       if (element == EL_MOLE)
7954       {
7955         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7956         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7957
7958         ResetGfxAnimation(x, y);
7959         GfxAction[x][y] = ACTION_DIGGING;
7960         TEST_DrawLevelField(x, y);
7961
7962         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7963
7964         return;                         /* wait for shrinking amoeba */
7965       }
7966       else      /* element == EL_PACMAN */
7967       {
7968         Feld[newx][newy] = EL_EMPTY;
7969         TEST_DrawLevelField(newx, newy);
7970         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7971       }
7972     }
7973     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7974              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7975               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7976     {
7977       /* wait for shrinking amoeba to completely disappear */
7978       return;
7979     }
7980     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7981     {
7982       /* object was running against a wall */
7983
7984       TurnRound(x, y);
7985
7986       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7987         DrawLevelElementAnimation(x, y, element);
7988
7989       if (DONT_TOUCH(element))
7990         TestIfBadThingTouchesPlayer(x, y);
7991
7992       return;
7993     }
7994
7995     InitMovingField(x, y, MovDir[x][y]);
7996
7997     PlayLevelSoundAction(x, y, ACTION_MOVING);
7998   }
7999
8000   if (MovDir[x][y])
8001     ContinueMoving(x, y);
8002 }
8003
8004 void ContinueMoving(int x, int y)
8005 {
8006   int element = Feld[x][y];
8007   struct ElementInfo *ei = &element_info[element];
8008   int direction = MovDir[x][y];
8009   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8010   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8011   int newx = x + dx, newy = y + dy;
8012   int stored = Store[x][y];
8013   int stored_new = Store[newx][newy];
8014   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8015   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8016   boolean last_line = (newy == lev_fieldy - 1);
8017
8018   MovPos[x][y] += getElementMoveStepsize(x, y);
8019
8020   if (pushed_by_player) /* special case: moving object pushed by player */
8021     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8022
8023   if (ABS(MovPos[x][y]) < TILEX)
8024   {
8025     TEST_DrawLevelField(x, y);
8026
8027     return;     /* element is still moving */
8028   }
8029
8030   /* element reached destination field */
8031
8032   Feld[x][y] = EL_EMPTY;
8033   Feld[newx][newy] = element;
8034   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8035
8036   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8037   {
8038     element = Feld[newx][newy] = EL_ACID;
8039   }
8040   else if (element == EL_MOLE)
8041   {
8042     Feld[x][y] = EL_SAND;
8043
8044     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8045   }
8046   else if (element == EL_QUICKSAND_FILLING)
8047   {
8048     element = Feld[newx][newy] = get_next_element(element);
8049     Store[newx][newy] = Store[x][y];
8050   }
8051   else if (element == EL_QUICKSAND_EMPTYING)
8052   {
8053     Feld[x][y] = get_next_element(element);
8054     element = Feld[newx][newy] = Store[x][y];
8055   }
8056   else if (element == EL_QUICKSAND_FAST_FILLING)
8057   {
8058     element = Feld[newx][newy] = get_next_element(element);
8059     Store[newx][newy] = Store[x][y];
8060   }
8061   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8062   {
8063     Feld[x][y] = get_next_element(element);
8064     element = Feld[newx][newy] = Store[x][y];
8065   }
8066   else if (element == EL_MAGIC_WALL_FILLING)
8067   {
8068     element = Feld[newx][newy] = get_next_element(element);
8069     if (!game.magic_wall_active)
8070       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8071     Store[newx][newy] = Store[x][y];
8072   }
8073   else if (element == EL_MAGIC_WALL_EMPTYING)
8074   {
8075     Feld[x][y] = get_next_element(element);
8076     if (!game.magic_wall_active)
8077       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8078     element = Feld[newx][newy] = Store[x][y];
8079
8080     InitField(newx, newy, FALSE);
8081   }
8082   else if (element == EL_BD_MAGIC_WALL_FILLING)
8083   {
8084     element = Feld[newx][newy] = get_next_element(element);
8085     if (!game.magic_wall_active)
8086       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8087     Store[newx][newy] = Store[x][y];
8088   }
8089   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8090   {
8091     Feld[x][y] = get_next_element(element);
8092     if (!game.magic_wall_active)
8093       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8094     element = Feld[newx][newy] = Store[x][y];
8095
8096     InitField(newx, newy, FALSE);
8097   }
8098   else if (element == EL_DC_MAGIC_WALL_FILLING)
8099   {
8100     element = Feld[newx][newy] = get_next_element(element);
8101     if (!game.magic_wall_active)
8102       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8103     Store[newx][newy] = Store[x][y];
8104   }
8105   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8106   {
8107     Feld[x][y] = get_next_element(element);
8108     if (!game.magic_wall_active)
8109       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8110     element = Feld[newx][newy] = Store[x][y];
8111
8112     InitField(newx, newy, FALSE);
8113   }
8114   else if (element == EL_AMOEBA_DROPPING)
8115   {
8116     Feld[x][y] = get_next_element(element);
8117     element = Feld[newx][newy] = Store[x][y];
8118   }
8119   else if (element == EL_SOKOBAN_OBJECT)
8120   {
8121     if (Back[x][y])
8122       Feld[x][y] = Back[x][y];
8123
8124     if (Back[newx][newy])
8125       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8126
8127     Back[x][y] = Back[newx][newy] = 0;
8128   }
8129
8130   Store[x][y] = EL_EMPTY;
8131   MovPos[x][y] = 0;
8132   MovDir[x][y] = 0;
8133   MovDelay[x][y] = 0;
8134
8135   MovDelay[newx][newy] = 0;
8136
8137   if (CAN_CHANGE_OR_HAS_ACTION(element))
8138   {
8139     /* copy element change control values to new field */
8140     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8141     ChangePage[newx][newy]  = ChangePage[x][y];
8142     ChangeCount[newx][newy] = ChangeCount[x][y];
8143     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8144   }
8145
8146   CustomValue[newx][newy] = CustomValue[x][y];
8147
8148   ChangeDelay[x][y] = 0;
8149   ChangePage[x][y] = -1;
8150   ChangeCount[x][y] = 0;
8151   ChangeEvent[x][y] = -1;
8152
8153   CustomValue[x][y] = 0;
8154
8155   /* copy animation control values to new field */
8156   GfxFrame[newx][newy]  = GfxFrame[x][y];
8157   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8158   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8159   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8160
8161   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8162
8163   /* some elements can leave other elements behind after moving */
8164   if (ei->move_leave_element != EL_EMPTY &&
8165       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8166       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8167   {
8168     int move_leave_element = ei->move_leave_element;
8169
8170     /* this makes it possible to leave the removed element again */
8171     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8172       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8173
8174     Feld[x][y] = move_leave_element;
8175
8176     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8177       MovDir[x][y] = direction;
8178
8179     InitField(x, y, FALSE);
8180
8181     if (GFX_CRUMBLED(Feld[x][y]))
8182       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8183
8184     if (ELEM_IS_PLAYER(move_leave_element))
8185       RelocatePlayer(x, y, move_leave_element);
8186   }
8187
8188   /* do this after checking for left-behind element */
8189   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8190
8191   if (!CAN_MOVE(element) ||
8192       (CAN_FALL(element) && direction == MV_DOWN &&
8193        (element == EL_SPRING ||
8194         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8195         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8196     GfxDir[x][y] = MovDir[newx][newy] = 0;
8197
8198   TEST_DrawLevelField(x, y);
8199   TEST_DrawLevelField(newx, newy);
8200
8201   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8202
8203   /* prevent pushed element from moving on in pushed direction */
8204   if (pushed_by_player && CAN_MOVE(element) &&
8205       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8206       !(element_info[element].move_pattern & direction))
8207     TurnRound(newx, newy);
8208
8209   /* prevent elements on conveyor belt from moving on in last direction */
8210   if (pushed_by_conveyor && CAN_FALL(element) &&
8211       direction & MV_HORIZONTAL)
8212     MovDir[newx][newy] = 0;
8213
8214   if (!pushed_by_player)
8215   {
8216     int nextx = newx + dx, nexty = newy + dy;
8217     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8218
8219     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8220
8221     if (CAN_FALL(element) && direction == MV_DOWN)
8222       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8223
8224     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8225       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8226
8227     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8228       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8229   }
8230
8231   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8232   {
8233     TestIfBadThingTouchesPlayer(newx, newy);
8234     TestIfBadThingTouchesFriend(newx, newy);
8235
8236     if (!IS_CUSTOM_ELEMENT(element))
8237       TestIfBadThingTouchesOtherBadThing(newx, newy);
8238   }
8239   else if (element == EL_PENGUIN)
8240     TestIfFriendTouchesBadThing(newx, newy);
8241
8242   if (DONT_GET_HIT_BY(element))
8243   {
8244     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8245   }
8246
8247   /* give the player one last chance (one more frame) to move away */
8248   if (CAN_FALL(element) && direction == MV_DOWN &&
8249       (last_line || (!IS_FREE(x, newy + 1) &&
8250                      (!IS_PLAYER(x, newy + 1) ||
8251                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8252     Impact(x, newy);
8253
8254   if (pushed_by_player && !game.use_change_when_pushing_bug)
8255   {
8256     int push_side = MV_DIR_OPPOSITE(direction);
8257     struct PlayerInfo *player = PLAYERINFO(x, y);
8258
8259     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8260                                player->index_bit, push_side);
8261     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8262                                         player->index_bit, push_side);
8263   }
8264
8265   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8266     MovDelay[newx][newy] = 1;
8267
8268   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8269
8270   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8271   TestIfElementHitsCustomElement(newx, newy, direction);
8272   TestIfPlayerTouchesCustomElement(newx, newy);
8273   TestIfElementTouchesCustomElement(newx, newy);
8274
8275   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8276       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8277     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8278                              MV_DIR_OPPOSITE(direction));
8279 }
8280
8281 int AmoebeNachbarNr(int ax, int ay)
8282 {
8283   int i;
8284   int element = Feld[ax][ay];
8285   int group_nr = 0;
8286   static int xy[4][2] =
8287   {
8288     { 0, -1 },
8289     { -1, 0 },
8290     { +1, 0 },
8291     { 0, +1 }
8292   };
8293
8294   for (i = 0; i < NUM_DIRECTIONS; i++)
8295   {
8296     int x = ax + xy[i][0];
8297     int y = ay + xy[i][1];
8298
8299     if (!IN_LEV_FIELD(x, y))
8300       continue;
8301
8302     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8303       group_nr = AmoebaNr[x][y];
8304   }
8305
8306   return group_nr;
8307 }
8308
8309 void AmoebenVereinigen(int ax, int ay)
8310 {
8311   int i, x, y, xx, yy;
8312   int new_group_nr = AmoebaNr[ax][ay];
8313   static int xy[4][2] =
8314   {
8315     { 0, -1 },
8316     { -1, 0 },
8317     { +1, 0 },
8318     { 0, +1 }
8319   };
8320
8321   if (new_group_nr == 0)
8322     return;
8323
8324   for (i = 0; i < NUM_DIRECTIONS; i++)
8325   {
8326     x = ax + xy[i][0];
8327     y = ay + xy[i][1];
8328
8329     if (!IN_LEV_FIELD(x, y))
8330       continue;
8331
8332     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8333          Feld[x][y] == EL_BD_AMOEBA ||
8334          Feld[x][y] == EL_AMOEBA_DEAD) &&
8335         AmoebaNr[x][y] != new_group_nr)
8336     {
8337       int old_group_nr = AmoebaNr[x][y];
8338
8339       if (old_group_nr == 0)
8340         return;
8341
8342       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8343       AmoebaCnt[old_group_nr] = 0;
8344       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8345       AmoebaCnt2[old_group_nr] = 0;
8346
8347       SCAN_PLAYFIELD(xx, yy)
8348       {
8349         if (AmoebaNr[xx][yy] == old_group_nr)
8350           AmoebaNr[xx][yy] = new_group_nr;
8351       }
8352     }
8353   }
8354 }
8355
8356 void AmoebeUmwandeln(int ax, int ay)
8357 {
8358   int i, x, y;
8359
8360   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8361   {
8362     int group_nr = AmoebaNr[ax][ay];
8363
8364 #ifdef DEBUG
8365     if (group_nr == 0)
8366     {
8367       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8368       printf("AmoebeUmwandeln(): This should never happen!\n");
8369       return;
8370     }
8371 #endif
8372
8373     SCAN_PLAYFIELD(x, y)
8374     {
8375       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8376       {
8377         AmoebaNr[x][y] = 0;
8378         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8379       }
8380     }
8381
8382     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8383                             SND_AMOEBA_TURNING_TO_GEM :
8384                             SND_AMOEBA_TURNING_TO_ROCK));
8385     Bang(ax, ay);
8386   }
8387   else
8388   {
8389     static int xy[4][2] =
8390     {
8391       { 0, -1 },
8392       { -1, 0 },
8393       { +1, 0 },
8394       { 0, +1 }
8395     };
8396
8397     for (i = 0; i < NUM_DIRECTIONS; i++)
8398     {
8399       x = ax + xy[i][0];
8400       y = ay + xy[i][1];
8401
8402       if (!IN_LEV_FIELD(x, y))
8403         continue;
8404
8405       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8406       {
8407         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8408                               SND_AMOEBA_TURNING_TO_GEM :
8409                               SND_AMOEBA_TURNING_TO_ROCK));
8410         Bang(x, y);
8411       }
8412     }
8413   }
8414 }
8415
8416 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8417 {
8418   int x, y;
8419   int group_nr = AmoebaNr[ax][ay];
8420   boolean done = FALSE;
8421
8422 #ifdef DEBUG
8423   if (group_nr == 0)
8424   {
8425     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8426     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8427     return;
8428   }
8429 #endif
8430
8431   SCAN_PLAYFIELD(x, y)
8432   {
8433     if (AmoebaNr[x][y] == group_nr &&
8434         (Feld[x][y] == EL_AMOEBA_DEAD ||
8435          Feld[x][y] == EL_BD_AMOEBA ||
8436          Feld[x][y] == EL_AMOEBA_GROWING))
8437     {
8438       AmoebaNr[x][y] = 0;
8439       Feld[x][y] = new_element;
8440       InitField(x, y, FALSE);
8441       TEST_DrawLevelField(x, y);
8442       done = TRUE;
8443     }
8444   }
8445
8446   if (done)
8447     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8448                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8449                             SND_BD_AMOEBA_TURNING_TO_GEM));
8450 }
8451
8452 void AmoebeWaechst(int x, int y)
8453 {
8454   static unsigned int sound_delay = 0;
8455   static unsigned int sound_delay_value = 0;
8456
8457   if (!MovDelay[x][y])          /* start new growing cycle */
8458   {
8459     MovDelay[x][y] = 7;
8460
8461     if (DelayReached(&sound_delay, sound_delay_value))
8462     {
8463       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8464       sound_delay_value = 30;
8465     }
8466   }
8467
8468   if (MovDelay[x][y])           /* wait some time before growing bigger */
8469   {
8470     MovDelay[x][y]--;
8471     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8472     {
8473       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8474                                            6 - MovDelay[x][y]);
8475
8476       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8477     }
8478
8479     if (!MovDelay[x][y])
8480     {
8481       Feld[x][y] = Store[x][y];
8482       Store[x][y] = 0;
8483       TEST_DrawLevelField(x, y);
8484     }
8485   }
8486 }
8487
8488 void AmoebaDisappearing(int x, int y)
8489 {
8490   static unsigned int sound_delay = 0;
8491   static unsigned int sound_delay_value = 0;
8492
8493   if (!MovDelay[x][y])          /* start new shrinking cycle */
8494   {
8495     MovDelay[x][y] = 7;
8496
8497     if (DelayReached(&sound_delay, sound_delay_value))
8498       sound_delay_value = 30;
8499   }
8500
8501   if (MovDelay[x][y])           /* wait some time before shrinking */
8502   {
8503     MovDelay[x][y]--;
8504     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8505     {
8506       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8507                                            6 - MovDelay[x][y]);
8508
8509       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8510     }
8511
8512     if (!MovDelay[x][y])
8513     {
8514       Feld[x][y] = EL_EMPTY;
8515       TEST_DrawLevelField(x, y);
8516
8517       /* don't let mole enter this field in this cycle;
8518          (give priority to objects falling to this field from above) */
8519       Stop[x][y] = TRUE;
8520     }
8521   }
8522 }
8523
8524 void AmoebeAbleger(int ax, int ay)
8525 {
8526   int i;
8527   int element = Feld[ax][ay];
8528   int graphic = el2img(element);
8529   int newax = ax, neway = ay;
8530   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8531   static int xy[4][2] =
8532   {
8533     { 0, -1 },
8534     { -1, 0 },
8535     { +1, 0 },
8536     { 0, +1 }
8537   };
8538
8539   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8540   {
8541     Feld[ax][ay] = EL_AMOEBA_DEAD;
8542     TEST_DrawLevelField(ax, ay);
8543     return;
8544   }
8545
8546   if (IS_ANIMATED(graphic))
8547     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8548
8549   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8550     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8551
8552   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8553   {
8554     MovDelay[ax][ay]--;
8555     if (MovDelay[ax][ay])
8556       return;
8557   }
8558
8559   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8560   {
8561     int start = RND(4);
8562     int x = ax + xy[start][0];
8563     int y = ay + xy[start][1];
8564
8565     if (!IN_LEV_FIELD(x, y))
8566       return;
8567
8568     if (IS_FREE(x, y) ||
8569         CAN_GROW_INTO(Feld[x][y]) ||
8570         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8571         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8572     {
8573       newax = x;
8574       neway = y;
8575     }
8576
8577     if (newax == ax && neway == ay)
8578       return;
8579   }
8580   else                          /* normal or "filled" (BD style) amoeba */
8581   {
8582     int start = RND(4);
8583     boolean waiting_for_player = FALSE;
8584
8585     for (i = 0; i < NUM_DIRECTIONS; i++)
8586     {
8587       int j = (start + i) % 4;
8588       int x = ax + xy[j][0];
8589       int y = ay + xy[j][1];
8590
8591       if (!IN_LEV_FIELD(x, y))
8592         continue;
8593
8594       if (IS_FREE(x, y) ||
8595           CAN_GROW_INTO(Feld[x][y]) ||
8596           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8597           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8598       {
8599         newax = x;
8600         neway = y;
8601         break;
8602       }
8603       else if (IS_PLAYER(x, y))
8604         waiting_for_player = TRUE;
8605     }
8606
8607     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8608     {
8609       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8610       {
8611         Feld[ax][ay] = EL_AMOEBA_DEAD;
8612         TEST_DrawLevelField(ax, ay);
8613         AmoebaCnt[AmoebaNr[ax][ay]]--;
8614
8615         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8616         {
8617           if (element == EL_AMOEBA_FULL)
8618             AmoebeUmwandeln(ax, ay);
8619           else if (element == EL_BD_AMOEBA)
8620             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8621         }
8622       }
8623       return;
8624     }
8625     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8626     {
8627       /* amoeba gets larger by growing in some direction */
8628
8629       int new_group_nr = AmoebaNr[ax][ay];
8630
8631 #ifdef DEBUG
8632   if (new_group_nr == 0)
8633   {
8634     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8635     printf("AmoebeAbleger(): This should never happen!\n");
8636     return;
8637   }
8638 #endif
8639
8640       AmoebaNr[newax][neway] = new_group_nr;
8641       AmoebaCnt[new_group_nr]++;
8642       AmoebaCnt2[new_group_nr]++;
8643
8644       /* if amoeba touches other amoeba(s) after growing, unify them */
8645       AmoebenVereinigen(newax, neway);
8646
8647       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8648       {
8649         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8650         return;
8651       }
8652     }
8653   }
8654
8655   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8656       (neway == lev_fieldy - 1 && newax != ax))
8657   {
8658     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8659     Store[newax][neway] = element;
8660   }
8661   else if (neway == ay || element == EL_EMC_DRIPPER)
8662   {
8663     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8664
8665     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8666   }
8667   else
8668   {
8669     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8670     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8671     Store[ax][ay] = EL_AMOEBA_DROP;
8672     ContinueMoving(ax, ay);
8673     return;
8674   }
8675
8676   TEST_DrawLevelField(newax, neway);
8677 }
8678
8679 void Life(int ax, int ay)
8680 {
8681   int x1, y1, x2, y2;
8682   int life_time = 40;
8683   int element = Feld[ax][ay];
8684   int graphic = el2img(element);
8685   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8686                          level.biomaze);
8687   boolean changed = FALSE;
8688
8689   if (IS_ANIMATED(graphic))
8690     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8691
8692   if (Stop[ax][ay])
8693     return;
8694
8695   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8696     MovDelay[ax][ay] = life_time;
8697
8698   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8699   {
8700     MovDelay[ax][ay]--;
8701     if (MovDelay[ax][ay])
8702       return;
8703   }
8704
8705   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8706   {
8707     int xx = ax+x1, yy = ay+y1;
8708     int nachbarn = 0;
8709
8710     if (!IN_LEV_FIELD(xx, yy))
8711       continue;
8712
8713     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8714     {
8715       int x = xx+x2, y = yy+y2;
8716
8717       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8718         continue;
8719
8720       if (((Feld[x][y] == element ||
8721             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8722            !Stop[x][y]) ||
8723           (IS_FREE(x, y) && Stop[x][y]))
8724         nachbarn++;
8725     }
8726
8727     if (xx == ax && yy == ay)           /* field in the middle */
8728     {
8729       if (nachbarn < life_parameter[0] ||
8730           nachbarn > life_parameter[1])
8731       {
8732         Feld[xx][yy] = EL_EMPTY;
8733         if (!Stop[xx][yy])
8734           TEST_DrawLevelField(xx, yy);
8735         Stop[xx][yy] = TRUE;
8736         changed = TRUE;
8737       }
8738     }
8739     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8740     {                                   /* free border field */
8741       if (nachbarn >= life_parameter[2] &&
8742           nachbarn <= life_parameter[3])
8743       {
8744         Feld[xx][yy] = element;
8745         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8746         if (!Stop[xx][yy])
8747           TEST_DrawLevelField(xx, yy);
8748         Stop[xx][yy] = TRUE;
8749         changed = TRUE;
8750       }
8751     }
8752   }
8753
8754   if (changed)
8755     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8756                    SND_GAME_OF_LIFE_GROWING);
8757 }
8758
8759 static void InitRobotWheel(int x, int y)
8760 {
8761   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8762 }
8763
8764 static void RunRobotWheel(int x, int y)
8765 {
8766   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8767 }
8768
8769 static void StopRobotWheel(int x, int y)
8770 {
8771   if (ZX == x && ZY == y)
8772   {
8773     ZX = ZY = -1;
8774
8775     game.robot_wheel_active = FALSE;
8776   }
8777 }
8778
8779 static void InitTimegateWheel(int x, int y)
8780 {
8781   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8782 }
8783
8784 static void RunTimegateWheel(int x, int y)
8785 {
8786   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8787 }
8788
8789 static void InitMagicBallDelay(int x, int y)
8790 {
8791   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8792 }
8793
8794 static void ActivateMagicBall(int bx, int by)
8795 {
8796   int x, y;
8797
8798   if (level.ball_random)
8799   {
8800     int pos_border = RND(8);    /* select one of the eight border elements */
8801     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8802     int xx = pos_content % 3;
8803     int yy = pos_content / 3;
8804
8805     x = bx - 1 + xx;
8806     y = by - 1 + yy;
8807
8808     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8809       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8810   }
8811   else
8812   {
8813     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8814     {
8815       int xx = x - bx + 1;
8816       int yy = y - by + 1;
8817
8818       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8819         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8820     }
8821   }
8822
8823   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8824 }
8825
8826 void CheckExit(int x, int y)
8827 {
8828   if (local_player->gems_still_needed > 0 ||
8829       local_player->sokobanfields_still_needed > 0 ||
8830       local_player->lights_still_needed > 0)
8831   {
8832     int element = Feld[x][y];
8833     int graphic = el2img(element);
8834
8835     if (IS_ANIMATED(graphic))
8836       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8837
8838     return;
8839   }
8840
8841   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8842     return;
8843
8844   Feld[x][y] = EL_EXIT_OPENING;
8845
8846   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8847 }
8848
8849 void CheckExitEM(int x, int y)
8850 {
8851   if (local_player->gems_still_needed > 0 ||
8852       local_player->sokobanfields_still_needed > 0 ||
8853       local_player->lights_still_needed > 0)
8854   {
8855     int element = Feld[x][y];
8856     int graphic = el2img(element);
8857
8858     if (IS_ANIMATED(graphic))
8859       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8860
8861     return;
8862   }
8863
8864   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8865     return;
8866
8867   Feld[x][y] = EL_EM_EXIT_OPENING;
8868
8869   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8870 }
8871
8872 void CheckExitSteel(int x, int y)
8873 {
8874   if (local_player->gems_still_needed > 0 ||
8875       local_player->sokobanfields_still_needed > 0 ||
8876       local_player->lights_still_needed > 0)
8877   {
8878     int element = Feld[x][y];
8879     int graphic = el2img(element);
8880
8881     if (IS_ANIMATED(graphic))
8882       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8883
8884     return;
8885   }
8886
8887   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8888     return;
8889
8890   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8891
8892   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8893 }
8894
8895 void CheckExitSteelEM(int x, int y)
8896 {
8897   if (local_player->gems_still_needed > 0 ||
8898       local_player->sokobanfields_still_needed > 0 ||
8899       local_player->lights_still_needed > 0)
8900   {
8901     int element = Feld[x][y];
8902     int graphic = el2img(element);
8903
8904     if (IS_ANIMATED(graphic))
8905       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8906
8907     return;
8908   }
8909
8910   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8911     return;
8912
8913   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8914
8915   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8916 }
8917
8918 void CheckExitSP(int x, int y)
8919 {
8920   if (local_player->gems_still_needed > 0)
8921   {
8922     int element = Feld[x][y];
8923     int graphic = el2img(element);
8924
8925     if (IS_ANIMATED(graphic))
8926       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8927
8928     return;
8929   }
8930
8931   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8932     return;
8933
8934   Feld[x][y] = EL_SP_EXIT_OPENING;
8935
8936   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8937 }
8938
8939 static void CloseAllOpenTimegates()
8940 {
8941   int x, y;
8942
8943   SCAN_PLAYFIELD(x, y)
8944   {
8945     int element = Feld[x][y];
8946
8947     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8948     {
8949       Feld[x][y] = EL_TIMEGATE_CLOSING;
8950
8951       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8952     }
8953   }
8954 }
8955
8956 void DrawTwinkleOnField(int x, int y)
8957 {
8958   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8959     return;
8960
8961   if (Feld[x][y] == EL_BD_DIAMOND)
8962     return;
8963
8964   if (MovDelay[x][y] == 0)      /* next animation frame */
8965     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8966
8967   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8968   {
8969     MovDelay[x][y]--;
8970
8971     DrawLevelElementAnimation(x, y, Feld[x][y]);
8972
8973     if (MovDelay[x][y] != 0)
8974     {
8975       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8976                                            10 - MovDelay[x][y]);
8977
8978       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8979     }
8980   }
8981 }
8982
8983 void MauerWaechst(int x, int y)
8984 {
8985   int delay = 6;
8986
8987   if (!MovDelay[x][y])          /* next animation frame */
8988     MovDelay[x][y] = 3 * delay;
8989
8990   if (MovDelay[x][y])           /* wait some time before next frame */
8991   {
8992     MovDelay[x][y]--;
8993
8994     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8995     {
8996       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8997       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8998
8999       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9000     }
9001
9002     if (!MovDelay[x][y])
9003     {
9004       if (MovDir[x][y] == MV_LEFT)
9005       {
9006         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9007           TEST_DrawLevelField(x - 1, y);
9008       }
9009       else if (MovDir[x][y] == MV_RIGHT)
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_UP)
9015       {
9016         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9017           TEST_DrawLevelField(x, y - 1);
9018       }
9019       else
9020       {
9021         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9022           TEST_DrawLevelField(x, y + 1);
9023       }
9024
9025       Feld[x][y] = Store[x][y];
9026       Store[x][y] = 0;
9027       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9028       TEST_DrawLevelField(x, y);
9029     }
9030   }
9031 }
9032
9033 void MauerAbleger(int ax, int ay)
9034 {
9035   int element = Feld[ax][ay];
9036   int graphic = el2img(element);
9037   boolean oben_frei = FALSE, unten_frei = FALSE;
9038   boolean links_frei = FALSE, rechts_frei = FALSE;
9039   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9040   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9041   boolean new_wall = FALSE;
9042
9043   if (IS_ANIMATED(graphic))
9044     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9045
9046   if (!MovDelay[ax][ay])        /* start building new wall */
9047     MovDelay[ax][ay] = 6;
9048
9049   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9050   {
9051     MovDelay[ax][ay]--;
9052     if (MovDelay[ax][ay])
9053       return;
9054   }
9055
9056   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9057     oben_frei = TRUE;
9058   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9059     unten_frei = TRUE;
9060   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9061     links_frei = TRUE;
9062   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9063     rechts_frei = TRUE;
9064
9065   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9066       element == EL_EXPANDABLE_WALL_ANY)
9067   {
9068     if (oben_frei)
9069     {
9070       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9071       Store[ax][ay-1] = element;
9072       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9073       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9074         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9075                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9076       new_wall = TRUE;
9077     }
9078     if (unten_frei)
9079     {
9080       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9081       Store[ax][ay+1] = element;
9082       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9083       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9084         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9085                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9086       new_wall = TRUE;
9087     }
9088   }
9089
9090   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9091       element == EL_EXPANDABLE_WALL_ANY ||
9092       element == EL_EXPANDABLE_WALL ||
9093       element == EL_BD_EXPANDABLE_WALL)
9094   {
9095     if (links_frei)
9096     {
9097       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9098       Store[ax-1][ay] = element;
9099       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9100       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9101         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9102                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9103       new_wall = TRUE;
9104     }
9105
9106     if (rechts_frei)
9107     {
9108       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9109       Store[ax+1][ay] = element;
9110       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9111       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9112         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9113                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9114       new_wall = TRUE;
9115     }
9116   }
9117
9118   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9119     TEST_DrawLevelField(ax, ay);
9120
9121   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9122     oben_massiv = TRUE;
9123   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9124     unten_massiv = TRUE;
9125   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9126     links_massiv = TRUE;
9127   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9128     rechts_massiv = TRUE;
9129
9130   if (((oben_massiv && unten_massiv) ||
9131        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9132        element == EL_EXPANDABLE_WALL) &&
9133       ((links_massiv && rechts_massiv) ||
9134        element == EL_EXPANDABLE_WALL_VERTICAL))
9135     Feld[ax][ay] = EL_WALL;
9136
9137   if (new_wall)
9138     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9139 }
9140
9141 void MauerAblegerStahl(int ax, int ay)
9142 {
9143   int element = Feld[ax][ay];
9144   int graphic = el2img(element);
9145   boolean oben_frei = FALSE, unten_frei = FALSE;
9146   boolean links_frei = FALSE, rechts_frei = FALSE;
9147   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9148   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9149   boolean new_wall = FALSE;
9150
9151   if (IS_ANIMATED(graphic))
9152     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9153
9154   if (!MovDelay[ax][ay])        /* start building new wall */
9155     MovDelay[ax][ay] = 6;
9156
9157   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9158   {
9159     MovDelay[ax][ay]--;
9160     if (MovDelay[ax][ay])
9161       return;
9162   }
9163
9164   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9165     oben_frei = TRUE;
9166   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9167     unten_frei = TRUE;
9168   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9169     links_frei = TRUE;
9170   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9171     rechts_frei = TRUE;
9172
9173   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9174       element == EL_EXPANDABLE_STEELWALL_ANY)
9175   {
9176     if (oben_frei)
9177     {
9178       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9179       Store[ax][ay-1] = element;
9180       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9181       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9182         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9183                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9184       new_wall = TRUE;
9185     }
9186     if (unten_frei)
9187     {
9188       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9189       Store[ax][ay+1] = element;
9190       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9191       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9192         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9193                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9194       new_wall = TRUE;
9195     }
9196   }
9197
9198   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9199       element == EL_EXPANDABLE_STEELWALL_ANY)
9200   {
9201     if (links_frei)
9202     {
9203       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9204       Store[ax-1][ay] = element;
9205       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9206       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9207         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9208                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9209       new_wall = TRUE;
9210     }
9211
9212     if (rechts_frei)
9213     {
9214       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9215       Store[ax+1][ay] = element;
9216       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9217       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9218         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9219                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9220       new_wall = TRUE;
9221     }
9222   }
9223
9224   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9225     oben_massiv = TRUE;
9226   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9227     unten_massiv = TRUE;
9228   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9229     links_massiv = TRUE;
9230   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9231     rechts_massiv = TRUE;
9232
9233   if (((oben_massiv && unten_massiv) ||
9234        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9235       ((links_massiv && rechts_massiv) ||
9236        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9237     Feld[ax][ay] = EL_STEELWALL;
9238
9239   if (new_wall)
9240     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9241 }
9242
9243 void CheckForDragon(int x, int y)
9244 {
9245   int i, j;
9246   boolean dragon_found = FALSE;
9247   static int xy[4][2] =
9248   {
9249     { 0, -1 },
9250     { -1, 0 },
9251     { +1, 0 },
9252     { 0, +1 }
9253   };
9254
9255   for (i = 0; i < NUM_DIRECTIONS; i++)
9256   {
9257     for (j = 0; j < 4; j++)
9258     {
9259       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9260
9261       if (IN_LEV_FIELD(xx, yy) &&
9262           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9263       {
9264         if (Feld[xx][yy] == EL_DRAGON)
9265           dragon_found = TRUE;
9266       }
9267       else
9268         break;
9269     }
9270   }
9271
9272   if (!dragon_found)
9273   {
9274     for (i = 0; i < NUM_DIRECTIONS; i++)
9275     {
9276       for (j = 0; j < 3; j++)
9277       {
9278         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9279   
9280         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9281         {
9282           Feld[xx][yy] = EL_EMPTY;
9283           TEST_DrawLevelField(xx, yy);
9284         }
9285         else
9286           break;
9287       }
9288     }
9289   }
9290 }
9291
9292 static void InitBuggyBase(int x, int y)
9293 {
9294   int element = Feld[x][y];
9295   int activating_delay = FRAMES_PER_SECOND / 4;
9296
9297   ChangeDelay[x][y] =
9298     (element == EL_SP_BUGGY_BASE ?
9299      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9300      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9301      activating_delay :
9302      element == EL_SP_BUGGY_BASE_ACTIVE ?
9303      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9304 }
9305
9306 static void WarnBuggyBase(int x, int y)
9307 {
9308   int i;
9309   static int xy[4][2] =
9310   {
9311     { 0, -1 },
9312     { -1, 0 },
9313     { +1, 0 },
9314     { 0, +1 }
9315   };
9316
9317   for (i = 0; i < NUM_DIRECTIONS; i++)
9318   {
9319     int xx = x + xy[i][0];
9320     int yy = y + xy[i][1];
9321
9322     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9323     {
9324       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9325
9326       break;
9327     }
9328   }
9329 }
9330
9331 static void InitTrap(int x, int y)
9332 {
9333   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9334 }
9335
9336 static void ActivateTrap(int x, int y)
9337 {
9338   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9339 }
9340
9341 static void ChangeActiveTrap(int x, int y)
9342 {
9343   int graphic = IMG_TRAP_ACTIVE;
9344
9345   /* if new animation frame was drawn, correct crumbled sand border */
9346   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9347     TEST_DrawLevelFieldCrumbled(x, y);
9348 }
9349
9350 static int getSpecialActionElement(int element, int number, int base_element)
9351 {
9352   return (element != EL_EMPTY ? element :
9353           number != -1 ? base_element + number - 1 :
9354           EL_EMPTY);
9355 }
9356
9357 static int getModifiedActionNumber(int value_old, int operator, int operand,
9358                                    int value_min, int value_max)
9359 {
9360   int value_new = (operator == CA_MODE_SET      ? operand :
9361                    operator == CA_MODE_ADD      ? value_old + operand :
9362                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9363                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9364                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9365                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9366                    value_old);
9367
9368   return (value_new < value_min ? value_min :
9369           value_new > value_max ? value_max :
9370           value_new);
9371 }
9372
9373 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9374 {
9375   struct ElementInfo *ei = &element_info[element];
9376   struct ElementChangeInfo *change = &ei->change_page[page];
9377   int target_element = change->target_element;
9378   int action_type = change->action_type;
9379   int action_mode = change->action_mode;
9380   int action_arg = change->action_arg;
9381   int action_element = change->action_element;
9382   int i;
9383
9384   if (!change->has_action)
9385     return;
9386
9387   /* ---------- determine action paramater values -------------------------- */
9388
9389   int level_time_value =
9390     (level.time > 0 ? TimeLeft :
9391      TimePlayed);
9392
9393   int action_arg_element_raw =
9394     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9395      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9396      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9397      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9398      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9399      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9400      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9401      EL_EMPTY);
9402   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9403
9404   int action_arg_direction =
9405     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9406      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9407      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9408      change->actual_trigger_side :
9409      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9410      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9411      MV_NONE);
9412
9413   int action_arg_number_min =
9414     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9415      CA_ARG_MIN);
9416
9417   int action_arg_number_max =
9418     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9419      action_type == CA_SET_LEVEL_GEMS ? 999 :
9420      action_type == CA_SET_LEVEL_TIME ? 9999 :
9421      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9422      action_type == CA_SET_CE_VALUE ? 9999 :
9423      action_type == CA_SET_CE_SCORE ? 9999 :
9424      CA_ARG_MAX);
9425
9426   int action_arg_number_reset =
9427     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9428      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9429      action_type == CA_SET_LEVEL_TIME ? level.time :
9430      action_type == CA_SET_LEVEL_SCORE ? 0 :
9431      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9432      action_type == CA_SET_CE_SCORE ? 0 :
9433      0);
9434
9435   int action_arg_number =
9436     (action_arg <= CA_ARG_MAX ? action_arg :
9437      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9438      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9439      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9440      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9441      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9442      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9443      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9444      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9445      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9446      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9447      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9448      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9449      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9450      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9451      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9452      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9453      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9454      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9455      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9456      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9457      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9458      -1);
9459
9460   int action_arg_number_old =
9461     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9462      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9463      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9464      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9465      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9466      0);
9467
9468   int action_arg_number_new =
9469     getModifiedActionNumber(action_arg_number_old,
9470                             action_mode, action_arg_number,
9471                             action_arg_number_min, action_arg_number_max);
9472
9473   int trigger_player_bits =
9474     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9475      change->actual_trigger_player_bits : change->trigger_player);
9476
9477   int action_arg_player_bits =
9478     (action_arg >= CA_ARG_PLAYER_1 &&
9479      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9480      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9481      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9482      PLAYER_BITS_ANY);
9483
9484   /* ---------- execute action  -------------------------------------------- */
9485
9486   switch (action_type)
9487   {
9488     case CA_NO_ACTION:
9489     {
9490       return;
9491     }
9492
9493     /* ---------- level actions  ------------------------------------------- */
9494
9495     case CA_RESTART_LEVEL:
9496     {
9497       game.restart_level = TRUE;
9498
9499       break;
9500     }
9501
9502     case CA_SHOW_ENVELOPE:
9503     {
9504       int element = getSpecialActionElement(action_arg_element,
9505                                             action_arg_number, EL_ENVELOPE_1);
9506
9507       if (IS_ENVELOPE(element))
9508         local_player->show_envelope = element;
9509
9510       break;
9511     }
9512
9513     case CA_SET_LEVEL_TIME:
9514     {
9515       if (level.time > 0)       /* only modify limited time value */
9516       {
9517         TimeLeft = action_arg_number_new;
9518
9519         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9520
9521         DisplayGameControlValues();
9522
9523         if (!TimeLeft && setup.time_limit)
9524           for (i = 0; i < MAX_PLAYERS; i++)
9525             KillPlayer(&stored_player[i]);
9526       }
9527
9528       break;
9529     }
9530
9531     case CA_SET_LEVEL_SCORE:
9532     {
9533       local_player->score = action_arg_number_new;
9534
9535       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9536
9537       DisplayGameControlValues();
9538
9539       break;
9540     }
9541
9542     case CA_SET_LEVEL_GEMS:
9543     {
9544       local_player->gems_still_needed = action_arg_number_new;
9545
9546       game_panel_controls[GAME_PANEL_GEMS].value =
9547         local_player->gems_still_needed;
9548
9549       DisplayGameControlValues();
9550
9551       break;
9552     }
9553
9554     case CA_SET_LEVEL_WIND:
9555     {
9556       game.wind_direction = action_arg_direction;
9557
9558       break;
9559     }
9560
9561     case CA_SET_LEVEL_RANDOM_SEED:
9562     {
9563       /* ensure that setting a new random seed while playing is predictable */
9564       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9565
9566       break;
9567     }
9568
9569     /* ---------- player actions  ------------------------------------------ */
9570
9571     case CA_MOVE_PLAYER:
9572     {
9573       /* automatically move to the next field in specified direction */
9574       for (i = 0; i < MAX_PLAYERS; i++)
9575         if (trigger_player_bits & (1 << i))
9576           stored_player[i].programmed_action = action_arg_direction;
9577
9578       break;
9579     }
9580
9581     case CA_EXIT_PLAYER:
9582     {
9583       for (i = 0; i < MAX_PLAYERS; i++)
9584         if (action_arg_player_bits & (1 << i))
9585           PlayerWins(&stored_player[i]);
9586
9587       break;
9588     }
9589
9590     case CA_KILL_PLAYER:
9591     {
9592       for (i = 0; i < MAX_PLAYERS; i++)
9593         if (action_arg_player_bits & (1 << i))
9594           KillPlayer(&stored_player[i]);
9595
9596       break;
9597     }
9598
9599     case CA_SET_PLAYER_KEYS:
9600     {
9601       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9602       int element = getSpecialActionElement(action_arg_element,
9603                                             action_arg_number, EL_KEY_1);
9604
9605       if (IS_KEY(element))
9606       {
9607         for (i = 0; i < MAX_PLAYERS; i++)
9608         {
9609           if (trigger_player_bits & (1 << i))
9610           {
9611             stored_player[i].key[KEY_NR(element)] = key_state;
9612
9613             DrawGameDoorValues();
9614           }
9615         }
9616       }
9617
9618       break;
9619     }
9620
9621     case CA_SET_PLAYER_SPEED:
9622     {
9623       for (i = 0; i < MAX_PLAYERS; i++)
9624       {
9625         if (trigger_player_bits & (1 << i))
9626         {
9627           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9628
9629           if (action_arg == CA_ARG_SPEED_FASTER &&
9630               stored_player[i].cannot_move)
9631           {
9632             action_arg_number = STEPSIZE_VERY_SLOW;
9633           }
9634           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9635                    action_arg == CA_ARG_SPEED_FASTER)
9636           {
9637             action_arg_number = 2;
9638             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9639                            CA_MODE_MULTIPLY);
9640           }
9641           else if (action_arg == CA_ARG_NUMBER_RESET)
9642           {
9643             action_arg_number = level.initial_player_stepsize[i];
9644           }
9645
9646           move_stepsize =
9647             getModifiedActionNumber(move_stepsize,
9648                                     action_mode,
9649                                     action_arg_number,
9650                                     action_arg_number_min,
9651                                     action_arg_number_max);
9652
9653           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9654         }
9655       }
9656
9657       break;
9658     }
9659
9660     case CA_SET_PLAYER_SHIELD:
9661     {
9662       for (i = 0; i < MAX_PLAYERS; i++)
9663       {
9664         if (trigger_player_bits & (1 << i))
9665         {
9666           if (action_arg == CA_ARG_SHIELD_OFF)
9667           {
9668             stored_player[i].shield_normal_time_left = 0;
9669             stored_player[i].shield_deadly_time_left = 0;
9670           }
9671           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9672           {
9673             stored_player[i].shield_normal_time_left = 999999;
9674           }
9675           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9676           {
9677             stored_player[i].shield_normal_time_left = 999999;
9678             stored_player[i].shield_deadly_time_left = 999999;
9679           }
9680         }
9681       }
9682
9683       break;
9684     }
9685
9686     case CA_SET_PLAYER_GRAVITY:
9687     {
9688       for (i = 0; i < MAX_PLAYERS; i++)
9689       {
9690         if (trigger_player_bits & (1 << i))
9691         {
9692           stored_player[i].gravity =
9693             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9694              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9695              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9696              stored_player[i].gravity);
9697         }
9698       }
9699
9700       break;
9701     }
9702
9703     case CA_SET_PLAYER_ARTWORK:
9704     {
9705       for (i = 0; i < MAX_PLAYERS; i++)
9706       {
9707         if (trigger_player_bits & (1 << i))
9708         {
9709           int artwork_element = action_arg_element;
9710
9711           if (action_arg == CA_ARG_ELEMENT_RESET)
9712             artwork_element =
9713               (level.use_artwork_element[i] ? level.artwork_element[i] :
9714                stored_player[i].element_nr);
9715
9716           if (stored_player[i].artwork_element != artwork_element)
9717             stored_player[i].Frame = 0;
9718
9719           stored_player[i].artwork_element = artwork_element;
9720
9721           SetPlayerWaiting(&stored_player[i], FALSE);
9722
9723           /* set number of special actions for bored and sleeping animation */
9724           stored_player[i].num_special_action_bored =
9725             get_num_special_action(artwork_element,
9726                                    ACTION_BORING_1, ACTION_BORING_LAST);
9727           stored_player[i].num_special_action_sleeping =
9728             get_num_special_action(artwork_element,
9729                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9730         }
9731       }
9732
9733       break;
9734     }
9735
9736     case CA_SET_PLAYER_INVENTORY:
9737     {
9738       for (i = 0; i < MAX_PLAYERS; i++)
9739       {
9740         struct PlayerInfo *player = &stored_player[i];
9741         int j, k;
9742
9743         if (trigger_player_bits & (1 << i))
9744         {
9745           int inventory_element = action_arg_element;
9746
9747           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9748               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9749               action_arg == CA_ARG_ELEMENT_ACTION)
9750           {
9751             int element = inventory_element;
9752             int collect_count = element_info[element].collect_count_initial;
9753
9754             if (!IS_CUSTOM_ELEMENT(element))
9755               collect_count = 1;
9756
9757             if (collect_count == 0)
9758               player->inventory_infinite_element = element;
9759             else
9760               for (k = 0; k < collect_count; k++)
9761                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9762                   player->inventory_element[player->inventory_size++] =
9763                     element;
9764           }
9765           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9766                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9767                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9768           {
9769             if (player->inventory_infinite_element != EL_UNDEFINED &&
9770                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9771                                      action_arg_element_raw))
9772               player->inventory_infinite_element = EL_UNDEFINED;
9773
9774             for (k = 0, j = 0; j < player->inventory_size; j++)
9775             {
9776               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9777                                         action_arg_element_raw))
9778                 player->inventory_element[k++] = player->inventory_element[j];
9779             }
9780
9781             player->inventory_size = k;
9782           }
9783           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9784           {
9785             if (player->inventory_size > 0)
9786             {
9787               for (j = 0; j < player->inventory_size - 1; j++)
9788                 player->inventory_element[j] = player->inventory_element[j + 1];
9789
9790               player->inventory_size--;
9791             }
9792           }
9793           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9794           {
9795             if (player->inventory_size > 0)
9796               player->inventory_size--;
9797           }
9798           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9799           {
9800             player->inventory_infinite_element = EL_UNDEFINED;
9801             player->inventory_size = 0;
9802           }
9803           else if (action_arg == CA_ARG_INVENTORY_RESET)
9804           {
9805             player->inventory_infinite_element = EL_UNDEFINED;
9806             player->inventory_size = 0;
9807
9808             if (level.use_initial_inventory[i])
9809             {
9810               for (j = 0; j < level.initial_inventory_size[i]; j++)
9811               {
9812                 int element = level.initial_inventory_content[i][j];
9813                 int collect_count = element_info[element].collect_count_initial;
9814
9815                 if (!IS_CUSTOM_ELEMENT(element))
9816                   collect_count = 1;
9817
9818                 if (collect_count == 0)
9819                   player->inventory_infinite_element = element;
9820                 else
9821                   for (k = 0; k < collect_count; k++)
9822                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9823                       player->inventory_element[player->inventory_size++] =
9824                         element;
9825               }
9826             }
9827           }
9828         }
9829       }
9830
9831       break;
9832     }
9833
9834     /* ---------- CE actions  ---------------------------------------------- */
9835
9836     case CA_SET_CE_VALUE:
9837     {
9838       int last_ce_value = CustomValue[x][y];
9839
9840       CustomValue[x][y] = action_arg_number_new;
9841
9842       if (CustomValue[x][y] != last_ce_value)
9843       {
9844         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9845         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9846
9847         if (CustomValue[x][y] == 0)
9848         {
9849           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9850           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9851         }
9852       }
9853
9854       break;
9855     }
9856
9857     case CA_SET_CE_SCORE:
9858     {
9859       int last_ce_score = ei->collect_score;
9860
9861       ei->collect_score = action_arg_number_new;
9862
9863       if (ei->collect_score != last_ce_score)
9864       {
9865         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9866         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9867
9868         if (ei->collect_score == 0)
9869         {
9870           int xx, yy;
9871
9872           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9873           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9874
9875           /*
9876             This is a very special case that seems to be a mixture between
9877             CheckElementChange() and CheckTriggeredElementChange(): while
9878             the first one only affects single elements that are triggered
9879             directly, the second one affects multiple elements in the playfield
9880             that are triggered indirectly by another element. This is a third
9881             case: Changing the CE score always affects multiple identical CEs,
9882             so every affected CE must be checked, not only the single CE for
9883             which the CE score was changed in the first place (as every instance
9884             of that CE shares the same CE score, and therefore also can change)!
9885           */
9886           SCAN_PLAYFIELD(xx, yy)
9887           {
9888             if (Feld[xx][yy] == element)
9889               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9890                                  CE_SCORE_GETS_ZERO);
9891           }
9892         }
9893       }
9894
9895       break;
9896     }
9897
9898     case CA_SET_CE_ARTWORK:
9899     {
9900       int artwork_element = action_arg_element;
9901       boolean reset_frame = FALSE;
9902       int xx, yy;
9903
9904       if (action_arg == CA_ARG_ELEMENT_RESET)
9905         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9906                            element);
9907
9908       if (ei->gfx_element != artwork_element)
9909         reset_frame = TRUE;
9910
9911       ei->gfx_element = artwork_element;
9912
9913       SCAN_PLAYFIELD(xx, yy)
9914       {
9915         if (Feld[xx][yy] == element)
9916         {
9917           if (reset_frame)
9918           {
9919             ResetGfxAnimation(xx, yy);
9920             ResetRandomAnimationValue(xx, yy);
9921           }
9922
9923           TEST_DrawLevelField(xx, yy);
9924         }
9925       }
9926
9927       break;
9928     }
9929
9930     /* ---------- engine actions  ------------------------------------------ */
9931
9932     case CA_SET_ENGINE_SCAN_MODE:
9933     {
9934       InitPlayfieldScanMode(action_arg);
9935
9936       break;
9937     }
9938
9939     default:
9940       break;
9941   }
9942 }
9943
9944 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9945 {
9946   int old_element = Feld[x][y];
9947   int new_element = GetElementFromGroupElement(element);
9948   int previous_move_direction = MovDir[x][y];
9949   int last_ce_value = CustomValue[x][y];
9950   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9951   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9952   boolean add_player_onto_element = (new_element_is_player &&
9953                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9954                                      IS_WALKABLE(old_element));
9955
9956   if (!add_player_onto_element)
9957   {
9958     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9959       RemoveMovingField(x, y);
9960     else
9961       RemoveField(x, y);
9962
9963     Feld[x][y] = new_element;
9964
9965     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9966       MovDir[x][y] = previous_move_direction;
9967
9968     if (element_info[new_element].use_last_ce_value)
9969       CustomValue[x][y] = last_ce_value;
9970
9971     InitField_WithBug1(x, y, FALSE);
9972
9973     new_element = Feld[x][y];   /* element may have changed */
9974
9975     ResetGfxAnimation(x, y);
9976     ResetRandomAnimationValue(x, y);
9977
9978     TEST_DrawLevelField(x, y);
9979
9980     if (GFX_CRUMBLED(new_element))
9981       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9982   }
9983
9984   /* check if element under the player changes from accessible to unaccessible
9985      (needed for special case of dropping element which then changes) */
9986   /* (must be checked after creating new element for walkable group elements) */
9987   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9988       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9989   {
9990     Bang(x, y);
9991
9992     return;
9993   }
9994
9995   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9996   if (new_element_is_player)
9997     RelocatePlayer(x, y, new_element);
9998
9999   if (is_change)
10000     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10001
10002   TestIfBadThingTouchesPlayer(x, y);
10003   TestIfPlayerTouchesCustomElement(x, y);
10004   TestIfElementTouchesCustomElement(x, y);
10005 }
10006
10007 static void CreateField(int x, int y, int element)
10008 {
10009   CreateFieldExt(x, y, element, FALSE);
10010 }
10011
10012 static void CreateElementFromChange(int x, int y, int element)
10013 {
10014   element = GET_VALID_RUNTIME_ELEMENT(element);
10015
10016   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10017   {
10018     int old_element = Feld[x][y];
10019
10020     /* prevent changed element from moving in same engine frame
10021        unless both old and new element can either fall or move */
10022     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10023         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10024       Stop[x][y] = TRUE;
10025   }
10026
10027   CreateFieldExt(x, y, element, TRUE);
10028 }
10029
10030 static boolean ChangeElement(int x, int y, int element, int page)
10031 {
10032   struct ElementInfo *ei = &element_info[element];
10033   struct ElementChangeInfo *change = &ei->change_page[page];
10034   int ce_value = CustomValue[x][y];
10035   int ce_score = ei->collect_score;
10036   int target_element;
10037   int old_element = Feld[x][y];
10038
10039   /* always use default change event to prevent running into a loop */
10040   if (ChangeEvent[x][y] == -1)
10041     ChangeEvent[x][y] = CE_DELAY;
10042
10043   if (ChangeEvent[x][y] == CE_DELAY)
10044   {
10045     /* reset actual trigger element, trigger player and action element */
10046     change->actual_trigger_element = EL_EMPTY;
10047     change->actual_trigger_player = EL_EMPTY;
10048     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10049     change->actual_trigger_side = CH_SIDE_NONE;
10050     change->actual_trigger_ce_value = 0;
10051     change->actual_trigger_ce_score = 0;
10052   }
10053
10054   /* do not change elements more than a specified maximum number of changes */
10055   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10056     return FALSE;
10057
10058   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10059
10060   if (change->explode)
10061   {
10062     Bang(x, y);
10063
10064     return TRUE;
10065   }
10066
10067   if (change->use_target_content)
10068   {
10069     boolean complete_replace = TRUE;
10070     boolean can_replace[3][3];
10071     int xx, yy;
10072
10073     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10074     {
10075       boolean is_empty;
10076       boolean is_walkable;
10077       boolean is_diggable;
10078       boolean is_collectible;
10079       boolean is_removable;
10080       boolean is_destructible;
10081       int ex = x + xx - 1;
10082       int ey = y + yy - 1;
10083       int content_element = change->target_content.e[xx][yy];
10084       int e;
10085
10086       can_replace[xx][yy] = TRUE;
10087
10088       if (ex == x && ey == y)   /* do not check changing element itself */
10089         continue;
10090
10091       if (content_element == EL_EMPTY_SPACE)
10092       {
10093         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10094
10095         continue;
10096       }
10097
10098       if (!IN_LEV_FIELD(ex, ey))
10099       {
10100         can_replace[xx][yy] = FALSE;
10101         complete_replace = FALSE;
10102
10103         continue;
10104       }
10105
10106       e = Feld[ex][ey];
10107
10108       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10109         e = MovingOrBlocked2Element(ex, ey);
10110
10111       is_empty = (IS_FREE(ex, ey) ||
10112                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10113
10114       is_walkable     = (is_empty || IS_WALKABLE(e));
10115       is_diggable     = (is_empty || IS_DIGGABLE(e));
10116       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10117       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10118       is_removable    = (is_diggable || is_collectible);
10119
10120       can_replace[xx][yy] =
10121         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10122           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10123           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10124           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10125           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10126           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10127          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10128
10129       if (!can_replace[xx][yy])
10130         complete_replace = FALSE;
10131     }
10132
10133     if (!change->only_if_complete || complete_replace)
10134     {
10135       boolean something_has_changed = FALSE;
10136
10137       if (change->only_if_complete && change->use_random_replace &&
10138           RND(100) < change->random_percentage)
10139         return FALSE;
10140
10141       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10142       {
10143         int ex = x + xx - 1;
10144         int ey = y + yy - 1;
10145         int content_element;
10146
10147         if (can_replace[xx][yy] && (!change->use_random_replace ||
10148                                     RND(100) < change->random_percentage))
10149         {
10150           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10151             RemoveMovingField(ex, ey);
10152
10153           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10154
10155           content_element = change->target_content.e[xx][yy];
10156           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10157                                               ce_value, ce_score);
10158
10159           CreateElementFromChange(ex, ey, target_element);
10160
10161           something_has_changed = TRUE;
10162
10163           /* for symmetry reasons, freeze newly created border elements */
10164           if (ex != x || ey != y)
10165             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10166         }
10167       }
10168
10169       if (something_has_changed)
10170       {
10171         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10172         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10173       }
10174     }
10175   }
10176   else
10177   {
10178     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10179                                         ce_value, ce_score);
10180
10181     if (element == EL_DIAGONAL_GROWING ||
10182         element == EL_DIAGONAL_SHRINKING)
10183     {
10184       target_element = Store[x][y];
10185
10186       Store[x][y] = EL_EMPTY;
10187     }
10188
10189     CreateElementFromChange(x, y, target_element);
10190
10191     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10192     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10193   }
10194
10195   /* this uses direct change before indirect change */
10196   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10197
10198   return TRUE;
10199 }
10200
10201 static void HandleElementChange(int x, int y, int page)
10202 {
10203   int element = MovingOrBlocked2Element(x, y);
10204   struct ElementInfo *ei = &element_info[element];
10205   struct ElementChangeInfo *change = &ei->change_page[page];
10206   boolean handle_action_before_change = FALSE;
10207
10208 #ifdef DEBUG
10209   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10210       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10211   {
10212     printf("\n\n");
10213     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10214            x, y, element, element_info[element].token_name);
10215     printf("HandleElementChange(): This should never happen!\n");
10216     printf("\n\n");
10217   }
10218 #endif
10219
10220   /* this can happen with classic bombs on walkable, changing elements */
10221   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10222   {
10223     return;
10224   }
10225
10226   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10227   {
10228     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10229
10230     if (change->can_change)
10231     {
10232       /* !!! not clear why graphic animation should be reset at all here !!! */
10233       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10234       /* when a custom element is about to change (for example by change delay),
10235          do not reset graphic animation when the custom element is moving */
10236       if (!IS_MOVING(x, y))
10237       {
10238         ResetGfxAnimation(x, y);
10239         ResetRandomAnimationValue(x, y);
10240       }
10241
10242       if (change->pre_change_function)
10243         change->pre_change_function(x, y);
10244     }
10245   }
10246
10247   ChangeDelay[x][y]--;
10248
10249   if (ChangeDelay[x][y] != 0)           /* continue element change */
10250   {
10251     if (change->can_change)
10252     {
10253       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10254
10255       if (IS_ANIMATED(graphic))
10256         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10257
10258       if (change->change_function)
10259         change->change_function(x, y);
10260     }
10261   }
10262   else                                  /* finish element change */
10263   {
10264     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10265     {
10266       page = ChangePage[x][y];
10267       ChangePage[x][y] = -1;
10268
10269       change = &ei->change_page[page];
10270     }
10271
10272     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10273     {
10274       ChangeDelay[x][y] = 1;            /* try change after next move step */
10275       ChangePage[x][y] = page;          /* remember page to use for change */
10276
10277       return;
10278     }
10279
10280     /* special case: set new level random seed before changing element */
10281     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10282       handle_action_before_change = TRUE;
10283
10284     if (change->has_action && handle_action_before_change)
10285       ExecuteCustomElementAction(x, y, element, page);
10286
10287     if (change->can_change)
10288     {
10289       if (ChangeElement(x, y, element, page))
10290       {
10291         if (change->post_change_function)
10292           change->post_change_function(x, y);
10293       }
10294     }
10295
10296     if (change->has_action && !handle_action_before_change)
10297       ExecuteCustomElementAction(x, y, element, page);
10298   }
10299 }
10300
10301 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10302                                               int trigger_element,
10303                                               int trigger_event,
10304                                               int trigger_player,
10305                                               int trigger_side,
10306                                               int trigger_page)
10307 {
10308   boolean change_done_any = FALSE;
10309   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10310   int i;
10311
10312   if (!(trigger_events[trigger_element][trigger_event]))
10313     return FALSE;
10314
10315   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10316
10317   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10318   {
10319     int element = EL_CUSTOM_START + i;
10320     boolean change_done = FALSE;
10321     int p;
10322
10323     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10324         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10325       continue;
10326
10327     for (p = 0; p < element_info[element].num_change_pages; p++)
10328     {
10329       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10330
10331       if (change->can_change_or_has_action &&
10332           change->has_event[trigger_event] &&
10333           change->trigger_side & trigger_side &&
10334           change->trigger_player & trigger_player &&
10335           change->trigger_page & trigger_page_bits &&
10336           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10337       {
10338         change->actual_trigger_element = trigger_element;
10339         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10340         change->actual_trigger_player_bits = trigger_player;
10341         change->actual_trigger_side = trigger_side;
10342         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10343         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10344
10345         if ((change->can_change && !change_done) || change->has_action)
10346         {
10347           int x, y;
10348
10349           SCAN_PLAYFIELD(x, y)
10350           {
10351             if (Feld[x][y] == element)
10352             {
10353               if (change->can_change && !change_done)
10354               {
10355                 /* if element already changed in this frame, not only prevent
10356                    another element change (checked in ChangeElement()), but
10357                    also prevent additional element actions for this element */
10358
10359                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10360                     !level.use_action_after_change_bug)
10361                   continue;
10362
10363                 ChangeDelay[x][y] = 1;
10364                 ChangeEvent[x][y] = trigger_event;
10365
10366                 HandleElementChange(x, y, p);
10367               }
10368               else if (change->has_action)
10369               {
10370                 /* if element already changed in this frame, not only prevent
10371                    another element change (checked in ChangeElement()), but
10372                    also prevent additional element actions for this element */
10373
10374                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10375                     !level.use_action_after_change_bug)
10376                   continue;
10377
10378                 ExecuteCustomElementAction(x, y, element, p);
10379                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10380               }
10381             }
10382           }
10383
10384           if (change->can_change)
10385           {
10386             change_done = TRUE;
10387             change_done_any = TRUE;
10388           }
10389         }
10390       }
10391     }
10392   }
10393
10394   RECURSION_LOOP_DETECTION_END();
10395
10396   return change_done_any;
10397 }
10398
10399 static boolean CheckElementChangeExt(int x, int y,
10400                                      int element,
10401                                      int trigger_element,
10402                                      int trigger_event,
10403                                      int trigger_player,
10404                                      int trigger_side)
10405 {
10406   boolean change_done = FALSE;
10407   int p;
10408
10409   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10410       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10411     return FALSE;
10412
10413   if (Feld[x][y] == EL_BLOCKED)
10414   {
10415     Blocked2Moving(x, y, &x, &y);
10416     element = Feld[x][y];
10417   }
10418
10419   /* check if element has already changed or is about to change after moving */
10420   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10421        Feld[x][y] != element) ||
10422
10423       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10424        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10425         ChangePage[x][y] != -1)))
10426     return FALSE;
10427
10428   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10429
10430   for (p = 0; p < element_info[element].num_change_pages; p++)
10431   {
10432     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10433
10434     /* check trigger element for all events where the element that is checked
10435        for changing interacts with a directly adjacent element -- this is
10436        different to element changes that affect other elements to change on the
10437        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10438     boolean check_trigger_element =
10439       (trigger_event == CE_TOUCHING_X ||
10440        trigger_event == CE_HITTING_X ||
10441        trigger_event == CE_HIT_BY_X ||
10442        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10443
10444     if (change->can_change_or_has_action &&
10445         change->has_event[trigger_event] &&
10446         change->trigger_side & trigger_side &&
10447         change->trigger_player & trigger_player &&
10448         (!check_trigger_element ||
10449          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10450     {
10451       change->actual_trigger_element = trigger_element;
10452       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10453       change->actual_trigger_player_bits = trigger_player;
10454       change->actual_trigger_side = trigger_side;
10455       change->actual_trigger_ce_value = CustomValue[x][y];
10456       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10457
10458       /* special case: trigger element not at (x,y) position for some events */
10459       if (check_trigger_element)
10460       {
10461         static struct
10462         {
10463           int dx, dy;
10464         } move_xy[] =
10465           {
10466             {  0,  0 },
10467             { -1,  0 },
10468             { +1,  0 },
10469             {  0,  0 },
10470             {  0, -1 },
10471             {  0,  0 }, { 0, 0 }, { 0, 0 },
10472             {  0, +1 }
10473           };
10474
10475         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10476         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10477
10478         change->actual_trigger_ce_value = CustomValue[xx][yy];
10479         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10480       }
10481
10482       if (change->can_change && !change_done)
10483       {
10484         ChangeDelay[x][y] = 1;
10485         ChangeEvent[x][y] = trigger_event;
10486
10487         HandleElementChange(x, y, p);
10488
10489         change_done = TRUE;
10490       }
10491       else if (change->has_action)
10492       {
10493         ExecuteCustomElementAction(x, y, element, p);
10494         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10495       }
10496     }
10497   }
10498
10499   RECURSION_LOOP_DETECTION_END();
10500
10501   return change_done;
10502 }
10503
10504 static void PlayPlayerSound(struct PlayerInfo *player)
10505 {
10506   int jx = player->jx, jy = player->jy;
10507   int sound_element = player->artwork_element;
10508   int last_action = player->last_action_waiting;
10509   int action = player->action_waiting;
10510
10511   if (player->is_waiting)
10512   {
10513     if (action != last_action)
10514       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10515     else
10516       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10517   }
10518   else
10519   {
10520     if (action != last_action)
10521       StopSound(element_info[sound_element].sound[last_action]);
10522
10523     if (last_action == ACTION_SLEEPING)
10524       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10525   }
10526 }
10527
10528 static void PlayAllPlayersSound()
10529 {
10530   int i;
10531
10532   for (i = 0; i < MAX_PLAYERS; i++)
10533     if (stored_player[i].active)
10534       PlayPlayerSound(&stored_player[i]);
10535 }
10536
10537 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10538 {
10539   boolean last_waiting = player->is_waiting;
10540   int move_dir = player->MovDir;
10541
10542   player->dir_waiting = move_dir;
10543   player->last_action_waiting = player->action_waiting;
10544
10545   if (is_waiting)
10546   {
10547     if (!last_waiting)          /* not waiting -> waiting */
10548     {
10549       player->is_waiting = TRUE;
10550
10551       player->frame_counter_bored =
10552         FrameCounter +
10553         game.player_boring_delay_fixed +
10554         GetSimpleRandom(game.player_boring_delay_random);
10555       player->frame_counter_sleeping =
10556         FrameCounter +
10557         game.player_sleeping_delay_fixed +
10558         GetSimpleRandom(game.player_sleeping_delay_random);
10559
10560       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10561     }
10562
10563     if (game.player_sleeping_delay_fixed +
10564         game.player_sleeping_delay_random > 0 &&
10565         player->anim_delay_counter == 0 &&
10566         player->post_delay_counter == 0 &&
10567         FrameCounter >= player->frame_counter_sleeping)
10568       player->is_sleeping = TRUE;
10569     else if (game.player_boring_delay_fixed +
10570              game.player_boring_delay_random > 0 &&
10571              FrameCounter >= player->frame_counter_bored)
10572       player->is_bored = TRUE;
10573
10574     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10575                               player->is_bored ? ACTION_BORING :
10576                               ACTION_WAITING);
10577
10578     if (player->is_sleeping && player->use_murphy)
10579     {
10580       /* special case for sleeping Murphy when leaning against non-free tile */
10581
10582       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10583           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10584            !IS_MOVING(player->jx - 1, player->jy)))
10585         move_dir = MV_LEFT;
10586       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10587                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10588                 !IS_MOVING(player->jx + 1, player->jy)))
10589         move_dir = MV_RIGHT;
10590       else
10591         player->is_sleeping = FALSE;
10592
10593       player->dir_waiting = move_dir;
10594     }
10595
10596     if (player->is_sleeping)
10597     {
10598       if (player->num_special_action_sleeping > 0)
10599       {
10600         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10601         {
10602           int last_special_action = player->special_action_sleeping;
10603           int num_special_action = player->num_special_action_sleeping;
10604           int special_action =
10605             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10606              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10607              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10608              last_special_action + 1 : ACTION_SLEEPING);
10609           int special_graphic =
10610             el_act_dir2img(player->artwork_element, special_action, move_dir);
10611
10612           player->anim_delay_counter =
10613             graphic_info[special_graphic].anim_delay_fixed +
10614             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10615           player->post_delay_counter =
10616             graphic_info[special_graphic].post_delay_fixed +
10617             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10618
10619           player->special_action_sleeping = special_action;
10620         }
10621
10622         if (player->anim_delay_counter > 0)
10623         {
10624           player->action_waiting = player->special_action_sleeping;
10625           player->anim_delay_counter--;
10626         }
10627         else if (player->post_delay_counter > 0)
10628         {
10629           player->post_delay_counter--;
10630         }
10631       }
10632     }
10633     else if (player->is_bored)
10634     {
10635       if (player->num_special_action_bored > 0)
10636       {
10637         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10638         {
10639           int special_action =
10640             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10641           int special_graphic =
10642             el_act_dir2img(player->artwork_element, special_action, move_dir);
10643
10644           player->anim_delay_counter =
10645             graphic_info[special_graphic].anim_delay_fixed +
10646             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10647           player->post_delay_counter =
10648             graphic_info[special_graphic].post_delay_fixed +
10649             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10650
10651           player->special_action_bored = special_action;
10652         }
10653
10654         if (player->anim_delay_counter > 0)
10655         {
10656           player->action_waiting = player->special_action_bored;
10657           player->anim_delay_counter--;
10658         }
10659         else if (player->post_delay_counter > 0)
10660         {
10661           player->post_delay_counter--;
10662         }
10663       }
10664     }
10665   }
10666   else if (last_waiting)        /* waiting -> not waiting */
10667   {
10668     player->is_waiting = FALSE;
10669     player->is_bored = FALSE;
10670     player->is_sleeping = FALSE;
10671
10672     player->frame_counter_bored = -1;
10673     player->frame_counter_sleeping = -1;
10674
10675     player->anim_delay_counter = 0;
10676     player->post_delay_counter = 0;
10677
10678     player->dir_waiting = player->MovDir;
10679     player->action_waiting = ACTION_DEFAULT;
10680
10681     player->special_action_bored = ACTION_DEFAULT;
10682     player->special_action_sleeping = ACTION_DEFAULT;
10683   }
10684 }
10685
10686 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10687 {
10688   static boolean player_was_moving = FALSE;
10689   static boolean player_was_snapping = FALSE;
10690   static boolean player_was_dropping = FALSE;
10691
10692   if ((!player->is_moving  && player_was_moving) ||
10693       (player->MovPos == 0 && player_was_moving) ||
10694       (player->is_snapping && !player_was_snapping) ||
10695       (player->is_dropping && !player_was_dropping))
10696   {
10697     if (!SaveEngineSnapshotToList())
10698       return;
10699
10700     player_was_moving = FALSE;
10701     player_was_snapping = TRUE;
10702     player_was_dropping = TRUE;
10703   }
10704   else
10705   {
10706     if (player->is_moving)
10707       player_was_moving = TRUE;
10708
10709     if (!player->is_snapping)
10710       player_was_snapping = FALSE;
10711
10712     if (!player->is_dropping)
10713       player_was_dropping = FALSE;
10714   }
10715 }
10716
10717 static void CheckSingleStepMode(struct PlayerInfo *player)
10718 {
10719   if (tape.single_step && tape.recording && !tape.pausing)
10720   {
10721     /* as it is called "single step mode", just return to pause mode when the
10722        player stopped moving after one tile (or never starts moving at all) */
10723     if (!player->is_moving && !player->is_pushing)
10724     {
10725       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10726       SnapField(player, 0, 0);                  /* stop snapping */
10727     }
10728   }
10729
10730   CheckSaveEngineSnapshot(player);
10731 }
10732
10733 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10734 {
10735   int left      = player_action & JOY_LEFT;
10736   int right     = player_action & JOY_RIGHT;
10737   int up        = player_action & JOY_UP;
10738   int down      = player_action & JOY_DOWN;
10739   int button1   = player_action & JOY_BUTTON_1;
10740   int button2   = player_action & JOY_BUTTON_2;
10741   int dx        = (left ? -1 : right ? 1 : 0);
10742   int dy        = (up   ? -1 : down  ? 1 : 0);
10743
10744   if (!player->active || tape.pausing)
10745     return 0;
10746
10747   if (player_action)
10748   {
10749     if (button1)
10750       SnapField(player, dx, dy);
10751     else
10752     {
10753       if (button2)
10754         DropElement(player);
10755
10756       MovePlayer(player, dx, dy);
10757     }
10758
10759     CheckSingleStepMode(player);
10760
10761     SetPlayerWaiting(player, FALSE);
10762
10763     return player_action;
10764   }
10765   else
10766   {
10767     /* no actions for this player (no input at player's configured device) */
10768
10769     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10770     SnapField(player, 0, 0);
10771     CheckGravityMovementWhenNotMoving(player);
10772
10773     if (player->MovPos == 0)
10774       SetPlayerWaiting(player, TRUE);
10775
10776     if (player->MovPos == 0)    /* needed for tape.playing */
10777       player->is_moving = FALSE;
10778
10779     player->is_dropping = FALSE;
10780     player->is_dropping_pressed = FALSE;
10781     player->drop_pressed_delay = 0;
10782
10783     CheckSingleStepMode(player);
10784
10785     return 0;
10786   }
10787 }
10788
10789 static void CheckLevelTime()
10790 {
10791   int i;
10792
10793   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10794   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10795   {
10796     if (level.native_em_level->lev->home == 0)  /* all players at home */
10797     {
10798       PlayerWins(local_player);
10799
10800       AllPlayersGone = TRUE;
10801
10802       level.native_em_level->lev->home = -1;
10803     }
10804
10805     if (level.native_em_level->ply[0]->alive == 0 &&
10806         level.native_em_level->ply[1]->alive == 0 &&
10807         level.native_em_level->ply[2]->alive == 0 &&
10808         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10809       AllPlayersGone = TRUE;
10810   }
10811   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10812   {
10813     if (game_sp.LevelSolved &&
10814         !game_sp.GameOver)                              /* game won */
10815     {
10816       PlayerWins(local_player);
10817
10818       game_sp.GameOver = TRUE;
10819
10820       AllPlayersGone = TRUE;
10821     }
10822
10823     if (game_sp.GameOver)                               /* game lost */
10824       AllPlayersGone = TRUE;
10825   }
10826
10827   if (TimeFrames >= FRAMES_PER_SECOND)
10828   {
10829     TimeFrames = 0;
10830     TapeTime++;
10831
10832     for (i = 0; i < MAX_PLAYERS; i++)
10833     {
10834       struct PlayerInfo *player = &stored_player[i];
10835
10836       if (SHIELD_ON(player))
10837       {
10838         player->shield_normal_time_left--;
10839
10840         if (player->shield_deadly_time_left > 0)
10841           player->shield_deadly_time_left--;
10842       }
10843     }
10844
10845     if (!local_player->LevelSolved && !level.use_step_counter)
10846     {
10847       TimePlayed++;
10848
10849       if (TimeLeft > 0)
10850       {
10851         TimeLeft--;
10852
10853         if (TimeLeft <= 10 && setup.time_limit)
10854           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10855
10856         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10857            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10858
10859         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10860
10861         if (!TimeLeft && setup.time_limit)
10862         {
10863           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10864             level.native_em_level->lev->killed_out_of_time = TRUE;
10865           else
10866             for (i = 0; i < MAX_PLAYERS; i++)
10867               KillPlayer(&stored_player[i]);
10868         }
10869       }
10870       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10871       {
10872         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10873       }
10874
10875       level.native_em_level->lev->time =
10876         (game.no_time_limit ? TimePlayed : TimeLeft);
10877     }
10878
10879     if (tape.recording || tape.playing)
10880       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10881   }
10882
10883   if (tape.recording || tape.playing)
10884     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10885
10886   UpdateAndDisplayGameControlValues();
10887 }
10888
10889 void AdvanceFrameAndPlayerCounters(int player_nr)
10890 {
10891   int i;
10892
10893   /* advance frame counters (global frame counter and time frame counter) */
10894   FrameCounter++;
10895   TimeFrames++;
10896
10897   /* advance player counters (counters for move delay, move animation etc.) */
10898   for (i = 0; i < MAX_PLAYERS; i++)
10899   {
10900     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10901     int move_delay_value = stored_player[i].move_delay_value;
10902     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10903
10904     if (!advance_player_counters)       /* not all players may be affected */
10905       continue;
10906
10907     if (move_frames == 0)       /* less than one move per game frame */
10908     {
10909       int stepsize = TILEX / move_delay_value;
10910       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10911       int count = (stored_player[i].is_moving ?
10912                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10913
10914       if (count % delay == 0)
10915         move_frames = 1;
10916     }
10917
10918     stored_player[i].Frame += move_frames;
10919
10920     if (stored_player[i].MovPos != 0)
10921       stored_player[i].StepFrame += move_frames;
10922
10923     if (stored_player[i].move_delay > 0)
10924       stored_player[i].move_delay--;
10925
10926     /* due to bugs in previous versions, counter must count up, not down */
10927     if (stored_player[i].push_delay != -1)
10928       stored_player[i].push_delay++;
10929
10930     if (stored_player[i].drop_delay > 0)
10931       stored_player[i].drop_delay--;
10932
10933     if (stored_player[i].is_dropping_pressed)
10934       stored_player[i].drop_pressed_delay++;
10935   }
10936 }
10937
10938 void StartGameActions(boolean init_network_game, boolean record_tape,
10939                       int random_seed)
10940 {
10941   unsigned int new_random_seed = InitRND(random_seed);
10942
10943   if (record_tape)
10944     TapeStartRecording(new_random_seed);
10945
10946 #if defined(NETWORK_AVALIABLE)
10947   if (init_network_game)
10948   {
10949     SendToServer_StartPlaying();
10950
10951     return;
10952   }
10953 #endif
10954
10955   InitGame();
10956 }
10957
10958 void GameActions()
10959 {
10960   static unsigned int game_frame_delay = 0;
10961   unsigned int game_frame_delay_value;
10962   byte *recorded_player_action;
10963   byte summarized_player_action = 0;
10964   byte tape_action[MAX_PLAYERS];
10965   int i;
10966
10967   for (i = 0; i < MAX_PLAYERS; i++)
10968   {
10969     struct PlayerInfo *player = &stored_player[i];
10970
10971     // allow engine snapshot if movement attempt was stopped
10972     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10973         (player->action & KEY_MOTION) == 0)
10974       game.snapshot.changed_action = TRUE;
10975
10976     // allow engine snapshot in case of snapping/dropping attempt
10977     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10978         (player->action & KEY_BUTTON) != 0)
10979       game.snapshot.changed_action = TRUE;
10980
10981     game.snapshot.last_action[i] = player->action;
10982   }
10983
10984   /* detect endless loops, caused by custom element programming */
10985   if (recursion_loop_detected && recursion_loop_depth == 0)
10986   {
10987     char *message = getStringCat3("Internal Error! Element ",
10988                                   EL_NAME(recursion_loop_element),
10989                                   " caused endless loop! Quit the game?");
10990
10991     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10992           EL_NAME(recursion_loop_element));
10993
10994     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10995
10996     recursion_loop_detected = FALSE;    /* if game should be continued */
10997
10998     free(message);
10999
11000     return;
11001   }
11002
11003   if (game.restart_level)
11004     StartGameActions(options.network, setup.autorecord, level.random_seed);
11005
11006   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11007   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11008   {
11009     if (level.native_em_level->lev->home == 0)  /* all players at home */
11010     {
11011       PlayerWins(local_player);
11012
11013       AllPlayersGone = TRUE;
11014
11015       level.native_em_level->lev->home = -1;
11016     }
11017
11018     if (level.native_em_level->ply[0]->alive == 0 &&
11019         level.native_em_level->ply[1]->alive == 0 &&
11020         level.native_em_level->ply[2]->alive == 0 &&
11021         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11022       AllPlayersGone = TRUE;
11023   }
11024   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11025   {
11026     if (game_sp.LevelSolved &&
11027         !game_sp.GameOver)                              /* game won */
11028     {
11029       PlayerWins(local_player);
11030
11031       game_sp.GameOver = TRUE;
11032
11033       AllPlayersGone = TRUE;
11034     }
11035
11036     if (game_sp.GameOver)                               /* game lost */
11037       AllPlayersGone = TRUE;
11038   }
11039
11040   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11041     GameWon();
11042
11043   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11044     TapeStop();
11045
11046   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11047     return;
11048
11049   game_frame_delay_value =
11050     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11051
11052   if (tape.playing && tape.warp_forward && !tape.pausing)
11053     game_frame_delay_value = 0;
11054
11055   /* ---------- main game synchronization point ---------- */
11056
11057   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11058
11059   if (network_playing && !network_player_action_received)
11060   {
11061     /* try to get network player actions in time */
11062
11063 #if defined(NETWORK_AVALIABLE)
11064     /* last chance to get network player actions without main loop delay */
11065     HandleNetworking();
11066 #endif
11067
11068     /* game was quit by network peer */
11069     if (game_status != GAME_MODE_PLAYING)
11070       return;
11071
11072     if (!network_player_action_received)
11073       return;           /* failed to get network player actions in time */
11074
11075     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11076   }
11077
11078   if (tape.pausing)
11079     return;
11080
11081   /* at this point we know that we really continue executing the game */
11082
11083   network_player_action_received = FALSE;
11084
11085   /* when playing tape, read previously recorded player input from tape data */
11086   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11087
11088   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11089   if (tape.pausing)
11090     return;
11091
11092   if (tape.set_centered_player)
11093   {
11094     game.centered_player_nr_next = tape.centered_player_nr_next;
11095     game.set_centered_player = TRUE;
11096   }
11097
11098   for (i = 0; i < MAX_PLAYERS; i++)
11099   {
11100     summarized_player_action |= stored_player[i].action;
11101
11102     if (!network_playing && (game.team_mode || tape.playing))
11103       stored_player[i].effective_action = stored_player[i].action;
11104   }
11105
11106 #if defined(NETWORK_AVALIABLE)
11107   if (network_playing)
11108     SendToServer_MovePlayer(summarized_player_action);
11109 #endif
11110
11111   if (!options.network && !game.team_mode)
11112     local_player->effective_action = summarized_player_action;
11113
11114   if (tape.recording &&
11115       setup.team_mode &&
11116       setup.input_on_focus &&
11117       game.centered_player_nr != -1)
11118   {
11119     for (i = 0; i < MAX_PLAYERS; i++)
11120       stored_player[i].effective_action =
11121         (i == game.centered_player_nr ? summarized_player_action : 0);
11122   }
11123
11124   if (recorded_player_action != NULL)
11125     for (i = 0; i < MAX_PLAYERS; i++)
11126       stored_player[i].effective_action = recorded_player_action[i];
11127
11128   for (i = 0; i < MAX_PLAYERS; i++)
11129   {
11130     tape_action[i] = stored_player[i].effective_action;
11131
11132     /* (this may happen in the RND game engine if a player was not present on
11133        the playfield on level start, but appeared later from a custom element */
11134     if (setup.team_mode &&
11135         tape.recording &&
11136         tape_action[i] &&
11137         !tape.player_participates[i])
11138       tape.player_participates[i] = TRUE;
11139   }
11140
11141   /* only record actions from input devices, but not programmed actions */
11142   if (tape.recording)
11143     TapeRecordAction(tape_action);
11144
11145 #if USE_NEW_PLAYER_ASSIGNMENTS
11146   // !!! also map player actions in single player mode !!!
11147   // if (game.team_mode)
11148   {
11149     byte mapped_action[MAX_PLAYERS];
11150
11151 #if DEBUG_PLAYER_ACTIONS
11152     printf(":::");
11153     for (i = 0; i < MAX_PLAYERS; i++)
11154       printf(" %d, ", stored_player[i].effective_action);
11155 #endif
11156
11157     for (i = 0; i < MAX_PLAYERS; i++)
11158       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11159
11160     for (i = 0; i < MAX_PLAYERS; i++)
11161       stored_player[i].effective_action = mapped_action[i];
11162
11163 #if DEBUG_PLAYER_ACTIONS
11164     printf(" =>");
11165     for (i = 0; i < MAX_PLAYERS; i++)
11166       printf(" %d, ", stored_player[i].effective_action);
11167     printf("\n");
11168 #endif
11169   }
11170 #if DEBUG_PLAYER_ACTIONS
11171   else
11172   {
11173     printf(":::");
11174     for (i = 0; i < MAX_PLAYERS; i++)
11175       printf(" %d, ", stored_player[i].effective_action);
11176     printf("\n");
11177   }
11178 #endif
11179 #endif
11180
11181   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11182   {
11183     GameActions_EM_Main();
11184   }
11185   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11186   {
11187     GameActions_SP_Main();
11188   }
11189   else
11190   {
11191     GameActions_RND();
11192   }
11193 }
11194
11195 void GameActions_EM_Main()
11196 {
11197   byte effective_action[MAX_PLAYERS];
11198   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11199   int i;
11200
11201   for (i = 0; i < MAX_PLAYERS; i++)
11202     effective_action[i] = stored_player[i].effective_action;
11203
11204   GameActions_EM(effective_action, warp_mode);
11205
11206   CheckLevelTime();
11207
11208   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11209 }
11210
11211 void GameActions_SP_Main()
11212 {
11213   byte effective_action[MAX_PLAYERS];
11214   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11215   int i;
11216
11217   for (i = 0; i < MAX_PLAYERS; i++)
11218     effective_action[i] = stored_player[i].effective_action;
11219
11220   GameActions_SP(effective_action, warp_mode);
11221
11222   CheckLevelTime();
11223
11224   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11225 }
11226
11227 void GameActions_RND()
11228 {
11229   int magic_wall_x = 0, magic_wall_y = 0;
11230   int i, x, y, element, graphic;
11231
11232   InitPlayfieldScanModeVars();
11233
11234   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11235   {
11236     SCAN_PLAYFIELD(x, y)
11237     {
11238       ChangeCount[x][y] = 0;
11239       ChangeEvent[x][y] = -1;
11240     }
11241   }
11242
11243   if (game.set_centered_player)
11244   {
11245     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11246
11247     /* switching to "all players" only possible if all players fit to screen */
11248     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11249     {
11250       game.centered_player_nr_next = game.centered_player_nr;
11251       game.set_centered_player = FALSE;
11252     }
11253
11254     /* do not switch focus to non-existing (or non-active) player */
11255     if (game.centered_player_nr_next >= 0 &&
11256         !stored_player[game.centered_player_nr_next].active)
11257     {
11258       game.centered_player_nr_next = game.centered_player_nr;
11259       game.set_centered_player = FALSE;
11260     }
11261   }
11262
11263   if (game.set_centered_player &&
11264       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11265   {
11266     int sx, sy;
11267
11268     if (game.centered_player_nr_next == -1)
11269     {
11270       setScreenCenteredToAllPlayers(&sx, &sy);
11271     }
11272     else
11273     {
11274       sx = stored_player[game.centered_player_nr_next].jx;
11275       sy = stored_player[game.centered_player_nr_next].jy;
11276     }
11277
11278     game.centered_player_nr = game.centered_player_nr_next;
11279     game.set_centered_player = FALSE;
11280
11281     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11282     DrawGameDoorValues();
11283   }
11284
11285   for (i = 0; i < MAX_PLAYERS; i++)
11286   {
11287     int actual_player_action = stored_player[i].effective_action;
11288
11289 #if 1
11290     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11291        - rnd_equinox_tetrachloride 048
11292        - rnd_equinox_tetrachloride_ii 096
11293        - rnd_emanuel_schmieg 002
11294        - doctor_sloan_ww 001, 020
11295     */
11296     if (stored_player[i].MovPos == 0)
11297       CheckGravityMovement(&stored_player[i]);
11298 #endif
11299
11300     /* overwrite programmed action with tape action */
11301     if (stored_player[i].programmed_action)
11302       actual_player_action = stored_player[i].programmed_action;
11303
11304     PlayerActions(&stored_player[i], actual_player_action);
11305
11306     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11307   }
11308
11309   ScrollScreen(NULL, SCROLL_GO_ON);
11310
11311   /* for backwards compatibility, the following code emulates a fixed bug that
11312      occured when pushing elements (causing elements that just made their last
11313      pushing step to already (if possible) make their first falling step in the
11314      same game frame, which is bad); this code is also needed to use the famous
11315      "spring push bug" which is used in older levels and might be wanted to be
11316      used also in newer levels, but in this case the buggy pushing code is only
11317      affecting the "spring" element and no other elements */
11318
11319   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11320   {
11321     for (i = 0; i < MAX_PLAYERS; i++)
11322     {
11323       struct PlayerInfo *player = &stored_player[i];
11324       int x = player->jx;
11325       int y = player->jy;
11326
11327       if (player->active && player->is_pushing && player->is_moving &&
11328           IS_MOVING(x, y) &&
11329           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11330            Feld[x][y] == EL_SPRING))
11331       {
11332         ContinueMoving(x, y);
11333
11334         /* continue moving after pushing (this is actually a bug) */
11335         if (!IS_MOVING(x, y))
11336           Stop[x][y] = FALSE;
11337       }
11338     }
11339   }
11340
11341   SCAN_PLAYFIELD(x, y)
11342   {
11343     ChangeCount[x][y] = 0;
11344     ChangeEvent[x][y] = -1;
11345
11346     /* this must be handled before main playfield loop */
11347     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11348     {
11349       MovDelay[x][y]--;
11350       if (MovDelay[x][y] <= 0)
11351         RemoveField(x, y);
11352     }
11353
11354     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11355     {
11356       MovDelay[x][y]--;
11357       if (MovDelay[x][y] <= 0)
11358       {
11359         RemoveField(x, y);
11360         TEST_DrawLevelField(x, y);
11361
11362         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11363       }
11364     }
11365
11366 #if DEBUG
11367     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11368     {
11369       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11370       printf("GameActions(): This should never happen!\n");
11371
11372       ChangePage[x][y] = -1;
11373     }
11374 #endif
11375
11376     Stop[x][y] = FALSE;
11377     if (WasJustMoving[x][y] > 0)
11378       WasJustMoving[x][y]--;
11379     if (WasJustFalling[x][y] > 0)
11380       WasJustFalling[x][y]--;
11381     if (CheckCollision[x][y] > 0)
11382       CheckCollision[x][y]--;
11383     if (CheckImpact[x][y] > 0)
11384       CheckImpact[x][y]--;
11385
11386     GfxFrame[x][y]++;
11387
11388     /* reset finished pushing action (not done in ContinueMoving() to allow
11389        continuous pushing animation for elements with zero push delay) */
11390     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11391     {
11392       ResetGfxAnimation(x, y);
11393       TEST_DrawLevelField(x, y);
11394     }
11395
11396 #if DEBUG
11397     if (IS_BLOCKED(x, y))
11398     {
11399       int oldx, oldy;
11400
11401       Blocked2Moving(x, y, &oldx, &oldy);
11402       if (!IS_MOVING(oldx, oldy))
11403       {
11404         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11405         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11406         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11407         printf("GameActions(): This should never happen!\n");
11408       }
11409     }
11410 #endif
11411   }
11412
11413   SCAN_PLAYFIELD(x, y)
11414   {
11415     element = Feld[x][y];
11416     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11417
11418     ResetGfxFrame(x, y, TRUE);
11419
11420     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11421         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11422       ResetRandomAnimationValue(x, y);
11423
11424     SetRandomAnimationValue(x, y);
11425
11426     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11427
11428     if (IS_INACTIVE(element))
11429     {
11430       if (IS_ANIMATED(graphic))
11431         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11432
11433       continue;
11434     }
11435
11436     /* this may take place after moving, so 'element' may have changed */
11437     if (IS_CHANGING(x, y) &&
11438         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11439     {
11440       int page = element_info[element].event_page_nr[CE_DELAY];
11441
11442       HandleElementChange(x, y, page);
11443
11444       element = Feld[x][y];
11445       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11446     }
11447
11448     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11449     {
11450       StartMoving(x, y);
11451
11452       element = Feld[x][y];
11453       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11454
11455       if (IS_ANIMATED(graphic) &&
11456           !IS_MOVING(x, y) &&
11457           !Stop[x][y])
11458         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11459
11460       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11461         TEST_DrawTwinkleOnField(x, y);
11462     }
11463     else if ((element == EL_ACID ||
11464               element == EL_EXIT_OPEN ||
11465               element == EL_EM_EXIT_OPEN ||
11466               element == EL_SP_EXIT_OPEN ||
11467               element == EL_STEEL_EXIT_OPEN ||
11468               element == EL_EM_STEEL_EXIT_OPEN ||
11469               element == EL_SP_TERMINAL ||
11470               element == EL_SP_TERMINAL_ACTIVE ||
11471               element == EL_EXTRA_TIME ||
11472               element == EL_SHIELD_NORMAL ||
11473               element == EL_SHIELD_DEADLY) &&
11474              IS_ANIMATED(graphic))
11475       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11476     else if (IS_MOVING(x, y))
11477       ContinueMoving(x, y);
11478     else if (IS_ACTIVE_BOMB(element))
11479       CheckDynamite(x, y);
11480     else if (element == EL_AMOEBA_GROWING)
11481       AmoebeWaechst(x, y);
11482     else if (element == EL_AMOEBA_SHRINKING)
11483       AmoebaDisappearing(x, y);
11484
11485 #if !USE_NEW_AMOEBA_CODE
11486     else if (IS_AMOEBALIVE(element))
11487       AmoebeAbleger(x, y);
11488 #endif
11489
11490     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11491       Life(x, y);
11492     else if (element == EL_EXIT_CLOSED)
11493       CheckExit(x, y);
11494     else if (element == EL_EM_EXIT_CLOSED)
11495       CheckExitEM(x, y);
11496     else if (element == EL_STEEL_EXIT_CLOSED)
11497       CheckExitSteel(x, y);
11498     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11499       CheckExitSteelEM(x, y);
11500     else if (element == EL_SP_EXIT_CLOSED)
11501       CheckExitSP(x, y);
11502     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11503              element == EL_EXPANDABLE_STEELWALL_GROWING)
11504       MauerWaechst(x, y);
11505     else if (element == EL_EXPANDABLE_WALL ||
11506              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11507              element == EL_EXPANDABLE_WALL_VERTICAL ||
11508              element == EL_EXPANDABLE_WALL_ANY ||
11509              element == EL_BD_EXPANDABLE_WALL)
11510       MauerAbleger(x, y);
11511     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11512              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11513              element == EL_EXPANDABLE_STEELWALL_ANY)
11514       MauerAblegerStahl(x, y);
11515     else if (element == EL_FLAMES)
11516       CheckForDragon(x, y);
11517     else if (element == EL_EXPLOSION)
11518       ; /* drawing of correct explosion animation is handled separately */
11519     else if (element == EL_ELEMENT_SNAPPING ||
11520              element == EL_DIAGONAL_SHRINKING ||
11521              element == EL_DIAGONAL_GROWING)
11522     {
11523       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11524
11525       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11526     }
11527     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11528       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11529
11530     if (IS_BELT_ACTIVE(element))
11531       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11532
11533     if (game.magic_wall_active)
11534     {
11535       int jx = local_player->jx, jy = local_player->jy;
11536
11537       /* play the element sound at the position nearest to the player */
11538       if ((element == EL_MAGIC_WALL_FULL ||
11539            element == EL_MAGIC_WALL_ACTIVE ||
11540            element == EL_MAGIC_WALL_EMPTYING ||
11541            element == EL_BD_MAGIC_WALL_FULL ||
11542            element == EL_BD_MAGIC_WALL_ACTIVE ||
11543            element == EL_BD_MAGIC_WALL_EMPTYING ||
11544            element == EL_DC_MAGIC_WALL_FULL ||
11545            element == EL_DC_MAGIC_WALL_ACTIVE ||
11546            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11547           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11548       {
11549         magic_wall_x = x;
11550         magic_wall_y = y;
11551       }
11552     }
11553   }
11554
11555 #if USE_NEW_AMOEBA_CODE
11556   /* new experimental amoeba growth stuff */
11557   if (!(FrameCounter % 8))
11558   {
11559     static unsigned int random = 1684108901;
11560
11561     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11562     {
11563       x = RND(lev_fieldx);
11564       y = RND(lev_fieldy);
11565       element = Feld[x][y];
11566
11567       if (!IS_PLAYER(x,y) &&
11568           (element == EL_EMPTY ||
11569            CAN_GROW_INTO(element) ||
11570            element == EL_QUICKSAND_EMPTY ||
11571            element == EL_QUICKSAND_FAST_EMPTY ||
11572            element == EL_ACID_SPLASH_LEFT ||
11573            element == EL_ACID_SPLASH_RIGHT))
11574       {
11575         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11576             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11577             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11578             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11579           Feld[x][y] = EL_AMOEBA_DROP;
11580       }
11581
11582       random = random * 129 + 1;
11583     }
11584   }
11585 #endif
11586
11587   game.explosions_delayed = FALSE;
11588
11589   SCAN_PLAYFIELD(x, y)
11590   {
11591     element = Feld[x][y];
11592
11593     if (ExplodeField[x][y])
11594       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11595     else if (element == EL_EXPLOSION)
11596       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11597
11598     ExplodeField[x][y] = EX_TYPE_NONE;
11599   }
11600
11601   game.explosions_delayed = TRUE;
11602
11603   if (game.magic_wall_active)
11604   {
11605     if (!(game.magic_wall_time_left % 4))
11606     {
11607       int element = Feld[magic_wall_x][magic_wall_y];
11608
11609       if (element == EL_BD_MAGIC_WALL_FULL ||
11610           element == EL_BD_MAGIC_WALL_ACTIVE ||
11611           element == EL_BD_MAGIC_WALL_EMPTYING)
11612         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11613       else if (element == EL_DC_MAGIC_WALL_FULL ||
11614                element == EL_DC_MAGIC_WALL_ACTIVE ||
11615                element == EL_DC_MAGIC_WALL_EMPTYING)
11616         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11617       else
11618         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11619     }
11620
11621     if (game.magic_wall_time_left > 0)
11622     {
11623       game.magic_wall_time_left--;
11624
11625       if (!game.magic_wall_time_left)
11626       {
11627         SCAN_PLAYFIELD(x, y)
11628         {
11629           element = Feld[x][y];
11630
11631           if (element == EL_MAGIC_WALL_ACTIVE ||
11632               element == EL_MAGIC_WALL_FULL)
11633           {
11634             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11635             TEST_DrawLevelField(x, y);
11636           }
11637           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11638                    element == EL_BD_MAGIC_WALL_FULL)
11639           {
11640             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11641             TEST_DrawLevelField(x, y);
11642           }
11643           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11644                    element == EL_DC_MAGIC_WALL_FULL)
11645           {
11646             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11647             TEST_DrawLevelField(x, y);
11648           }
11649         }
11650
11651         game.magic_wall_active = FALSE;
11652       }
11653     }
11654   }
11655
11656   if (game.light_time_left > 0)
11657   {
11658     game.light_time_left--;
11659
11660     if (game.light_time_left == 0)
11661       RedrawAllLightSwitchesAndInvisibleElements();
11662   }
11663
11664   if (game.timegate_time_left > 0)
11665   {
11666     game.timegate_time_left--;
11667
11668     if (game.timegate_time_left == 0)
11669       CloseAllOpenTimegates();
11670   }
11671
11672   if (game.lenses_time_left > 0)
11673   {
11674     game.lenses_time_left--;
11675
11676     if (game.lenses_time_left == 0)
11677       RedrawAllInvisibleElementsForLenses();
11678   }
11679
11680   if (game.magnify_time_left > 0)
11681   {
11682     game.magnify_time_left--;
11683
11684     if (game.magnify_time_left == 0)
11685       RedrawAllInvisibleElementsForMagnifier();
11686   }
11687
11688   for (i = 0; i < MAX_PLAYERS; i++)
11689   {
11690     struct PlayerInfo *player = &stored_player[i];
11691
11692     if (SHIELD_ON(player))
11693     {
11694       if (player->shield_deadly_time_left)
11695         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11696       else if (player->shield_normal_time_left)
11697         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11698     }
11699   }
11700
11701 #if USE_DELAYED_GFX_REDRAW
11702   SCAN_PLAYFIELD(x, y)
11703   {
11704     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11705     {
11706       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11707          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11708
11709       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11710         DrawLevelField(x, y);
11711
11712       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11713         DrawLevelFieldCrumbled(x, y);
11714
11715       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11716         DrawLevelFieldCrumbledNeighbours(x, y);
11717
11718       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11719         DrawTwinkleOnField(x, y);
11720     }
11721
11722     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11723   }
11724 #endif
11725
11726   CheckLevelTime();
11727
11728   DrawAllPlayers();
11729   PlayAllPlayersSound();
11730
11731   if (options.debug)                    /* calculate frames per second */
11732   {
11733     static unsigned int fps_counter = 0;
11734     static int fps_frames = 0;
11735     unsigned int fps_delay_ms = Counter() - fps_counter;
11736
11737     fps_frames++;
11738
11739     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11740     {
11741       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11742
11743       fps_frames = 0;
11744       fps_counter = Counter();
11745     }
11746
11747     redraw_mask |= REDRAW_FPS;
11748   }
11749
11750   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11751
11752   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11753   {
11754     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11755
11756     local_player->show_envelope = 0;
11757   }
11758
11759   /* use random number generator in every frame to make it less predictable */
11760   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11761     RND(1);
11762 }
11763
11764 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11765 {
11766   int min_x = x, min_y = y, max_x = x, max_y = y;
11767   int i;
11768
11769   for (i = 0; i < MAX_PLAYERS; i++)
11770   {
11771     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11772
11773     if (!stored_player[i].active || &stored_player[i] == player)
11774       continue;
11775
11776     min_x = MIN(min_x, jx);
11777     min_y = MIN(min_y, jy);
11778     max_x = MAX(max_x, jx);
11779     max_y = MAX(max_y, jy);
11780   }
11781
11782   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11783 }
11784
11785 static boolean AllPlayersInVisibleScreen()
11786 {
11787   int i;
11788
11789   for (i = 0; i < MAX_PLAYERS; i++)
11790   {
11791     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11792
11793     if (!stored_player[i].active)
11794       continue;
11795
11796     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11797       return FALSE;
11798   }
11799
11800   return TRUE;
11801 }
11802
11803 void ScrollLevel(int dx, int dy)
11804 {
11805   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11806   int x, y;
11807
11808   BlitBitmap(drawto_field, drawto_field,
11809              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11810              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11811              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11812              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11813              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11814              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11815
11816   if (dx != 0)
11817   {
11818     x = (dx == 1 ? BX1 : BX2);
11819     for (y = BY1; y <= BY2; y++)
11820       DrawScreenField(x, y);
11821   }
11822
11823   if (dy != 0)
11824   {
11825     y = (dy == 1 ? BY1 : BY2);
11826     for (x = BX1; x <= BX2; x++)
11827       DrawScreenField(x, y);
11828   }
11829
11830   redraw_mask |= REDRAW_FIELD;
11831 }
11832
11833 static boolean canFallDown(struct PlayerInfo *player)
11834 {
11835   int jx = player->jx, jy = player->jy;
11836
11837   return (IN_LEV_FIELD(jx, jy + 1) &&
11838           (IS_FREE(jx, jy + 1) ||
11839            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11840           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11841           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11842 }
11843
11844 static boolean canPassField(int x, int y, int move_dir)
11845 {
11846   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11847   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11848   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11849   int nextx = x + dx;
11850   int nexty = y + dy;
11851   int element = Feld[x][y];
11852
11853   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11854           !CAN_MOVE(element) &&
11855           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11856           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11857           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11858 }
11859
11860 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11861 {
11862   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11863   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11864   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11865   int newx = x + dx;
11866   int newy = y + dy;
11867
11868   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11869           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11870           (IS_DIGGABLE(Feld[newx][newy]) ||
11871            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11872            canPassField(newx, newy, move_dir)));
11873 }
11874
11875 static void CheckGravityMovement(struct PlayerInfo *player)
11876 {
11877   if (player->gravity && !player->programmed_action)
11878   {
11879     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11880     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11881     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11882     int jx = player->jx, jy = player->jy;
11883     boolean player_is_moving_to_valid_field =
11884       (!player_is_snapping &&
11885        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11886         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11887     boolean player_can_fall_down = canFallDown(player);
11888
11889     if (player_can_fall_down &&
11890         !player_is_moving_to_valid_field)
11891       player->programmed_action = MV_DOWN;
11892   }
11893 }
11894
11895 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11896 {
11897   return CheckGravityMovement(player);
11898
11899   if (player->gravity && !player->programmed_action)
11900   {
11901     int jx = player->jx, jy = player->jy;
11902     boolean field_under_player_is_free =
11903       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11904     boolean player_is_standing_on_valid_field =
11905       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11906        (IS_WALKABLE(Feld[jx][jy]) &&
11907         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11908
11909     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11910       player->programmed_action = MV_DOWN;
11911   }
11912 }
11913
11914 /*
11915   MovePlayerOneStep()
11916   -----------------------------------------------------------------------------
11917   dx, dy:               direction (non-diagonal) to try to move the player to
11918   real_dx, real_dy:     direction as read from input device (can be diagonal)
11919 */
11920
11921 boolean MovePlayerOneStep(struct PlayerInfo *player,
11922                           int dx, int dy, int real_dx, int real_dy)
11923 {
11924   int jx = player->jx, jy = player->jy;
11925   int new_jx = jx + dx, new_jy = jy + dy;
11926   int can_move;
11927   boolean player_can_move = !player->cannot_move;
11928
11929   if (!player->active || (!dx && !dy))
11930     return MP_NO_ACTION;
11931
11932   player->MovDir = (dx < 0 ? MV_LEFT :
11933                     dx > 0 ? MV_RIGHT :
11934                     dy < 0 ? MV_UP :
11935                     dy > 0 ? MV_DOWN :  MV_NONE);
11936
11937   if (!IN_LEV_FIELD(new_jx, new_jy))
11938     return MP_NO_ACTION;
11939
11940   if (!player_can_move)
11941   {
11942     if (player->MovPos == 0)
11943     {
11944       player->is_moving = FALSE;
11945       player->is_digging = FALSE;
11946       player->is_collecting = FALSE;
11947       player->is_snapping = FALSE;
11948       player->is_pushing = FALSE;
11949     }
11950   }
11951
11952   if (!options.network && game.centered_player_nr == -1 &&
11953       !AllPlayersInSight(player, new_jx, new_jy))
11954     return MP_NO_ACTION;
11955
11956   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11957   if (can_move != MP_MOVING)
11958     return can_move;
11959
11960   /* check if DigField() has caused relocation of the player */
11961   if (player->jx != jx || player->jy != jy)
11962     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11963
11964   StorePlayer[jx][jy] = 0;
11965   player->last_jx = jx;
11966   player->last_jy = jy;
11967   player->jx = new_jx;
11968   player->jy = new_jy;
11969   StorePlayer[new_jx][new_jy] = player->element_nr;
11970
11971   if (player->move_delay_value_next != -1)
11972   {
11973     player->move_delay_value = player->move_delay_value_next;
11974     player->move_delay_value_next = -1;
11975   }
11976
11977   player->MovPos =
11978     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11979
11980   player->step_counter++;
11981
11982   PlayerVisit[jx][jy] = FrameCounter;
11983
11984   player->is_moving = TRUE;
11985
11986 #if 1
11987   /* should better be called in MovePlayer(), but this breaks some tapes */
11988   ScrollPlayer(player, SCROLL_INIT);
11989 #endif
11990
11991   return MP_MOVING;
11992 }
11993
11994 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11995 {
11996   int jx = player->jx, jy = player->jy;
11997   int old_jx = jx, old_jy = jy;
11998   int moved = MP_NO_ACTION;
11999
12000   if (!player->active)
12001     return FALSE;
12002
12003   if (!dx && !dy)
12004   {
12005     if (player->MovPos == 0)
12006     {
12007       player->is_moving = FALSE;
12008       player->is_digging = FALSE;
12009       player->is_collecting = FALSE;
12010       player->is_snapping = FALSE;
12011       player->is_pushing = FALSE;
12012     }
12013
12014     return FALSE;
12015   }
12016
12017   if (player->move_delay > 0)
12018     return FALSE;
12019
12020   player->move_delay = -1;              /* set to "uninitialized" value */
12021
12022   /* store if player is automatically moved to next field */
12023   player->is_auto_moving = (player->programmed_action != MV_NONE);
12024
12025   /* remove the last programmed player action */
12026   player->programmed_action = 0;
12027
12028   if (player->MovPos)
12029   {
12030     /* should only happen if pre-1.2 tape recordings are played */
12031     /* this is only for backward compatibility */
12032
12033     int original_move_delay_value = player->move_delay_value;
12034
12035 #if DEBUG
12036     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12037            tape.counter);
12038 #endif
12039
12040     /* scroll remaining steps with finest movement resolution */
12041     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12042
12043     while (player->MovPos)
12044     {
12045       ScrollPlayer(player, SCROLL_GO_ON);
12046       ScrollScreen(NULL, SCROLL_GO_ON);
12047
12048       AdvanceFrameAndPlayerCounters(player->index_nr);
12049
12050       DrawAllPlayers();
12051       BackToFront();
12052     }
12053
12054     player->move_delay_value = original_move_delay_value;
12055   }
12056
12057   player->is_active = FALSE;
12058
12059   if (player->last_move_dir & MV_HORIZONTAL)
12060   {
12061     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12062       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12063   }
12064   else
12065   {
12066     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12067       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12068   }
12069
12070   if (!moved && !player->is_active)
12071   {
12072     player->is_moving = FALSE;
12073     player->is_digging = FALSE;
12074     player->is_collecting = FALSE;
12075     player->is_snapping = FALSE;
12076     player->is_pushing = FALSE;
12077   }
12078
12079   jx = player->jx;
12080   jy = player->jy;
12081
12082   if (moved & MP_MOVING && !ScreenMovPos &&
12083       (player->index_nr == game.centered_player_nr ||
12084        game.centered_player_nr == -1))
12085   {
12086     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12087     int offset = game.scroll_delay_value;
12088
12089     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12090     {
12091       /* actual player has left the screen -- scroll in that direction */
12092       if (jx != old_jx)         /* player has moved horizontally */
12093         scroll_x += (jx - old_jx);
12094       else                      /* player has moved vertically */
12095         scroll_y += (jy - old_jy);
12096     }
12097     else
12098     {
12099       if (jx != old_jx)         /* player has moved horizontally */
12100       {
12101         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12102             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12103           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12104
12105         /* don't scroll over playfield boundaries */
12106         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12107           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12108
12109         /* don't scroll more than one field at a time */
12110         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12111
12112         /* don't scroll against the player's moving direction */
12113         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12114             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12115           scroll_x = old_scroll_x;
12116       }
12117       else                      /* player has moved vertically */
12118       {
12119         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12120             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12121           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12122
12123         /* don't scroll over playfield boundaries */
12124         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12125           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12126
12127         /* don't scroll more than one field at a time */
12128         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12129
12130         /* don't scroll against the player's moving direction */
12131         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12132             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12133           scroll_y = old_scroll_y;
12134       }
12135     }
12136
12137     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12138     {
12139       if (!options.network && game.centered_player_nr == -1 &&
12140           !AllPlayersInVisibleScreen())
12141       {
12142         scroll_x = old_scroll_x;
12143         scroll_y = old_scroll_y;
12144       }
12145       else
12146       {
12147         ScrollScreen(player, SCROLL_INIT);
12148         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12149       }
12150     }
12151   }
12152
12153   player->StepFrame = 0;
12154
12155   if (moved & MP_MOVING)
12156   {
12157     if (old_jx != jx && old_jy == jy)
12158       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12159     else if (old_jx == jx && old_jy != jy)
12160       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12161
12162     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12163
12164     player->last_move_dir = player->MovDir;
12165     player->is_moving = TRUE;
12166     player->is_snapping = FALSE;
12167     player->is_switching = FALSE;
12168     player->is_dropping = FALSE;
12169     player->is_dropping_pressed = FALSE;
12170     player->drop_pressed_delay = 0;
12171
12172 #if 0
12173     /* should better be called here than above, but this breaks some tapes */
12174     ScrollPlayer(player, SCROLL_INIT);
12175 #endif
12176   }
12177   else
12178   {
12179     CheckGravityMovementWhenNotMoving(player);
12180
12181     player->is_moving = FALSE;
12182
12183     /* at this point, the player is allowed to move, but cannot move right now
12184        (e.g. because of something blocking the way) -- ensure that the player
12185        is also allowed to move in the next frame (in old versions before 3.1.1,
12186        the player was forced to wait again for eight frames before next try) */
12187
12188     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12189       player->move_delay = 0;   /* allow direct movement in the next frame */
12190   }
12191
12192   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12193     player->move_delay = player->move_delay_value;
12194
12195   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12196   {
12197     TestIfPlayerTouchesBadThing(jx, jy);
12198     TestIfPlayerTouchesCustomElement(jx, jy);
12199   }
12200
12201   if (!player->active)
12202     RemovePlayer(player);
12203
12204   return moved;
12205 }
12206
12207 void ScrollPlayer(struct PlayerInfo *player, int mode)
12208 {
12209   int jx = player->jx, jy = player->jy;
12210   int last_jx = player->last_jx, last_jy = player->last_jy;
12211   int move_stepsize = TILEX / player->move_delay_value;
12212
12213   if (!player->active)
12214     return;
12215
12216   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12217     return;
12218
12219   if (mode == SCROLL_INIT)
12220   {
12221     player->actual_frame_counter = FrameCounter;
12222     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12223
12224     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12225         Feld[last_jx][last_jy] == EL_EMPTY)
12226     {
12227       int last_field_block_delay = 0;   /* start with no blocking at all */
12228       int block_delay_adjustment = player->block_delay_adjustment;
12229
12230       /* if player blocks last field, add delay for exactly one move */
12231       if (player->block_last_field)
12232       {
12233         last_field_block_delay += player->move_delay_value;
12234
12235         /* when blocking enabled, prevent moving up despite gravity */
12236         if (player->gravity && player->MovDir == MV_UP)
12237           block_delay_adjustment = -1;
12238       }
12239
12240       /* add block delay adjustment (also possible when not blocking) */
12241       last_field_block_delay += block_delay_adjustment;
12242
12243       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12244       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12245     }
12246
12247     if (player->MovPos != 0)    /* player has not yet reached destination */
12248       return;
12249   }
12250   else if (!FrameReached(&player->actual_frame_counter, 1))
12251     return;
12252
12253   if (player->MovPos != 0)
12254   {
12255     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12256     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12257
12258     /* before DrawPlayer() to draw correct player graphic for this case */
12259     if (player->MovPos == 0)
12260       CheckGravityMovement(player);
12261   }
12262
12263   if (player->MovPos == 0)      /* player reached destination field */
12264   {
12265     if (player->move_delay_reset_counter > 0)
12266     {
12267       player->move_delay_reset_counter--;
12268
12269       if (player->move_delay_reset_counter == 0)
12270       {
12271         /* continue with normal speed after quickly moving through gate */
12272         HALVE_PLAYER_SPEED(player);
12273
12274         /* be able to make the next move without delay */
12275         player->move_delay = 0;
12276       }
12277     }
12278
12279     player->last_jx = jx;
12280     player->last_jy = jy;
12281
12282     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12283         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12284         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12285         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12286         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12287         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12288         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12289         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12290     {
12291       DrawPlayer(player);       /* needed here only to cleanup last field */
12292       RemovePlayer(player);
12293
12294       if (local_player->friends_still_needed == 0 ||
12295           IS_SP_ELEMENT(Feld[jx][jy]))
12296         PlayerWins(player);
12297     }
12298
12299     /* this breaks one level: "machine", level 000 */
12300     {
12301       int move_direction = player->MovDir;
12302       int enter_side = MV_DIR_OPPOSITE(move_direction);
12303       int leave_side = move_direction;
12304       int old_jx = last_jx;
12305       int old_jy = last_jy;
12306       int old_element = Feld[old_jx][old_jy];
12307       int new_element = Feld[jx][jy];
12308
12309       if (IS_CUSTOM_ELEMENT(old_element))
12310         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12311                                    CE_LEFT_BY_PLAYER,
12312                                    player->index_bit, leave_side);
12313
12314       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12315                                           CE_PLAYER_LEAVES_X,
12316                                           player->index_bit, leave_side);
12317
12318       if (IS_CUSTOM_ELEMENT(new_element))
12319         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12320                                    player->index_bit, enter_side);
12321
12322       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12323                                           CE_PLAYER_ENTERS_X,
12324                                           player->index_bit, enter_side);
12325
12326       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12327                                         CE_MOVE_OF_X, move_direction);
12328     }
12329
12330     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12331     {
12332       TestIfPlayerTouchesBadThing(jx, jy);
12333       TestIfPlayerTouchesCustomElement(jx, jy);
12334
12335       /* needed because pushed element has not yet reached its destination,
12336          so it would trigger a change event at its previous field location */
12337       if (!player->is_pushing)
12338         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12339
12340       if (!player->active)
12341         RemovePlayer(player);
12342     }
12343
12344     if (!local_player->LevelSolved && level.use_step_counter)
12345     {
12346       int i;
12347
12348       TimePlayed++;
12349
12350       if (TimeLeft > 0)
12351       {
12352         TimeLeft--;
12353
12354         if (TimeLeft <= 10 && setup.time_limit)
12355           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12356
12357         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12358
12359         DisplayGameControlValues();
12360
12361         if (!TimeLeft && setup.time_limit)
12362           for (i = 0; i < MAX_PLAYERS; i++)
12363             KillPlayer(&stored_player[i]);
12364       }
12365       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12366       {
12367         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12368
12369         DisplayGameControlValues();
12370       }
12371     }
12372
12373     if (tape.single_step && tape.recording && !tape.pausing &&
12374         !player->programmed_action)
12375       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12376
12377     if (!player->programmed_action)
12378       CheckSaveEngineSnapshot(player);
12379   }
12380 }
12381
12382 void ScrollScreen(struct PlayerInfo *player, int mode)
12383 {
12384   static unsigned int screen_frame_counter = 0;
12385
12386   if (mode == SCROLL_INIT)
12387   {
12388     /* set scrolling step size according to actual player's moving speed */
12389     ScrollStepSize = TILEX / player->move_delay_value;
12390
12391     screen_frame_counter = FrameCounter;
12392     ScreenMovDir = player->MovDir;
12393     ScreenMovPos = player->MovPos;
12394     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12395     return;
12396   }
12397   else if (!FrameReached(&screen_frame_counter, 1))
12398     return;
12399
12400   if (ScreenMovPos)
12401   {
12402     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12403     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12404     redraw_mask |= REDRAW_FIELD;
12405   }
12406   else
12407     ScreenMovDir = MV_NONE;
12408 }
12409
12410 void TestIfPlayerTouchesCustomElement(int x, int y)
12411 {
12412   static int xy[4][2] =
12413   {
12414     { 0, -1 },
12415     { -1, 0 },
12416     { +1, 0 },
12417     { 0, +1 }
12418   };
12419   static int trigger_sides[4][2] =
12420   {
12421     /* center side       border side */
12422     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12423     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12424     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12425     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12426   };
12427   static int touch_dir[4] =
12428   {
12429     MV_LEFT | MV_RIGHT,
12430     MV_UP   | MV_DOWN,
12431     MV_UP   | MV_DOWN,
12432     MV_LEFT | MV_RIGHT
12433   };
12434   int center_element = Feld[x][y];      /* should always be non-moving! */
12435   int i;
12436
12437   for (i = 0; i < NUM_DIRECTIONS; i++)
12438   {
12439     int xx = x + xy[i][0];
12440     int yy = y + xy[i][1];
12441     int center_side = trigger_sides[i][0];
12442     int border_side = trigger_sides[i][1];
12443     int border_element;
12444
12445     if (!IN_LEV_FIELD(xx, yy))
12446       continue;
12447
12448     if (IS_PLAYER(x, y))                /* player found at center element */
12449     {
12450       struct PlayerInfo *player = PLAYERINFO(x, y);
12451
12452       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12453         border_element = Feld[xx][yy];          /* may be moving! */
12454       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12455         border_element = Feld[xx][yy];
12456       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12457         border_element = MovingOrBlocked2Element(xx, yy);
12458       else
12459         continue;               /* center and border element do not touch */
12460
12461       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12462                                  player->index_bit, border_side);
12463       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12464                                           CE_PLAYER_TOUCHES_X,
12465                                           player->index_bit, border_side);
12466
12467       {
12468         /* use player element that is initially defined in the level playfield,
12469            not the player element that corresponds to the runtime player number
12470            (example: a level that contains EL_PLAYER_3 as the only player would
12471            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12472         int player_element = PLAYERINFO(x, y)->initial_element;
12473
12474         CheckElementChangeBySide(xx, yy, border_element, player_element,
12475                                  CE_TOUCHING_X, border_side);
12476       }
12477     }
12478     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12479     {
12480       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12481
12482       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12483       {
12484         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12485           continue;             /* center and border element do not touch */
12486       }
12487
12488       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12489                                  player->index_bit, center_side);
12490       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12491                                           CE_PLAYER_TOUCHES_X,
12492                                           player->index_bit, center_side);
12493
12494       {
12495         /* use player element that is initially defined in the level playfield,
12496            not the player element that corresponds to the runtime player number
12497            (example: a level that contains EL_PLAYER_3 as the only player would
12498            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12499         int player_element = PLAYERINFO(xx, yy)->initial_element;
12500
12501         CheckElementChangeBySide(x, y, center_element, player_element,
12502                                  CE_TOUCHING_X, center_side);
12503       }
12504
12505       break;
12506     }
12507   }
12508 }
12509
12510 void TestIfElementTouchesCustomElement(int x, int y)
12511 {
12512   static int xy[4][2] =
12513   {
12514     { 0, -1 },
12515     { -1, 0 },
12516     { +1, 0 },
12517     { 0, +1 }
12518   };
12519   static int trigger_sides[4][2] =
12520   {
12521     /* center side      border side */
12522     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12523     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12524     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12525     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12526   };
12527   static int touch_dir[4] =
12528   {
12529     MV_LEFT | MV_RIGHT,
12530     MV_UP   | MV_DOWN,
12531     MV_UP   | MV_DOWN,
12532     MV_LEFT | MV_RIGHT
12533   };
12534   boolean change_center_element = FALSE;
12535   int center_element = Feld[x][y];      /* should always be non-moving! */
12536   int border_element_old[NUM_DIRECTIONS];
12537   int i;
12538
12539   for (i = 0; i < NUM_DIRECTIONS; i++)
12540   {
12541     int xx = x + xy[i][0];
12542     int yy = y + xy[i][1];
12543     int border_element;
12544
12545     border_element_old[i] = -1;
12546
12547     if (!IN_LEV_FIELD(xx, yy))
12548       continue;
12549
12550     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12551       border_element = Feld[xx][yy];    /* may be moving! */
12552     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12553       border_element = Feld[xx][yy];
12554     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12555       border_element = MovingOrBlocked2Element(xx, yy);
12556     else
12557       continue;                 /* center and border element do not touch */
12558
12559     border_element_old[i] = border_element;
12560   }
12561
12562   for (i = 0; i < NUM_DIRECTIONS; i++)
12563   {
12564     int xx = x + xy[i][0];
12565     int yy = y + xy[i][1];
12566     int center_side = trigger_sides[i][0];
12567     int border_element = border_element_old[i];
12568
12569     if (border_element == -1)
12570       continue;
12571
12572     /* check for change of border element */
12573     CheckElementChangeBySide(xx, yy, border_element, center_element,
12574                              CE_TOUCHING_X, center_side);
12575
12576     /* (center element cannot be player, so we dont have to check this here) */
12577   }
12578
12579   for (i = 0; i < NUM_DIRECTIONS; i++)
12580   {
12581     int xx = x + xy[i][0];
12582     int yy = y + xy[i][1];
12583     int border_side = trigger_sides[i][1];
12584     int border_element = border_element_old[i];
12585
12586     if (border_element == -1)
12587       continue;
12588
12589     /* check for change of center element (but change it only once) */
12590     if (!change_center_element)
12591       change_center_element =
12592         CheckElementChangeBySide(x, y, center_element, border_element,
12593                                  CE_TOUCHING_X, border_side);
12594
12595     if (IS_PLAYER(xx, yy))
12596     {
12597       /* use player element that is initially defined in the level playfield,
12598          not the player element that corresponds to the runtime player number
12599          (example: a level that contains EL_PLAYER_3 as the only player would
12600          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12601       int player_element = PLAYERINFO(xx, yy)->initial_element;
12602
12603       CheckElementChangeBySide(x, y, center_element, player_element,
12604                                CE_TOUCHING_X, border_side);
12605     }
12606   }
12607 }
12608
12609 void TestIfElementHitsCustomElement(int x, int y, int direction)
12610 {
12611   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12612   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12613   int hitx = x + dx, hity = y + dy;
12614   int hitting_element = Feld[x][y];
12615   int touched_element;
12616
12617   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12618     return;
12619
12620   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12621                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12622
12623   if (IN_LEV_FIELD(hitx, hity))
12624   {
12625     int opposite_direction = MV_DIR_OPPOSITE(direction);
12626     int hitting_side = direction;
12627     int touched_side = opposite_direction;
12628     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12629                           MovDir[hitx][hity] != direction ||
12630                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12631
12632     object_hit = TRUE;
12633
12634     if (object_hit)
12635     {
12636       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12637                                CE_HITTING_X, touched_side);
12638
12639       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12640                                CE_HIT_BY_X, hitting_side);
12641
12642       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12643                                CE_HIT_BY_SOMETHING, opposite_direction);
12644
12645       if (IS_PLAYER(hitx, hity))
12646       {
12647         /* use player element that is initially defined in the level playfield,
12648            not the player element that corresponds to the runtime player number
12649            (example: a level that contains EL_PLAYER_3 as the only player would
12650            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12651         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12652
12653         CheckElementChangeBySide(x, y, hitting_element, player_element,
12654                                  CE_HITTING_X, touched_side);
12655       }
12656     }
12657   }
12658
12659   /* "hitting something" is also true when hitting the playfield border */
12660   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12661                            CE_HITTING_SOMETHING, direction);
12662 }
12663
12664 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12665 {
12666   int i, kill_x = -1, kill_y = -1;
12667
12668   int bad_element = -1;
12669   static int test_xy[4][2] =
12670   {
12671     { 0, -1 },
12672     { -1, 0 },
12673     { +1, 0 },
12674     { 0, +1 }
12675   };
12676   static int test_dir[4] =
12677   {
12678     MV_UP,
12679     MV_LEFT,
12680     MV_RIGHT,
12681     MV_DOWN
12682   };
12683
12684   for (i = 0; i < NUM_DIRECTIONS; i++)
12685   {
12686     int test_x, test_y, test_move_dir, test_element;
12687
12688     test_x = good_x + test_xy[i][0];
12689     test_y = good_y + test_xy[i][1];
12690
12691     if (!IN_LEV_FIELD(test_x, test_y))
12692       continue;
12693
12694     test_move_dir =
12695       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12696
12697     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12698
12699     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12700        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12701     */
12702     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12703         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12704     {
12705       kill_x = test_x;
12706       kill_y = test_y;
12707       bad_element = test_element;
12708
12709       break;
12710     }
12711   }
12712
12713   if (kill_x != -1 || kill_y != -1)
12714   {
12715     if (IS_PLAYER(good_x, good_y))
12716     {
12717       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12718
12719       if (player->shield_deadly_time_left > 0 &&
12720           !IS_INDESTRUCTIBLE(bad_element))
12721         Bang(kill_x, kill_y);
12722       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12723         KillPlayer(player);
12724     }
12725     else
12726       Bang(good_x, good_y);
12727   }
12728 }
12729
12730 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12731 {
12732   int i, kill_x = -1, kill_y = -1;
12733   int bad_element = Feld[bad_x][bad_y];
12734   static int test_xy[4][2] =
12735   {
12736     { 0, -1 },
12737     { -1, 0 },
12738     { +1, 0 },
12739     { 0, +1 }
12740   };
12741   static int touch_dir[4] =
12742   {
12743     MV_LEFT | MV_RIGHT,
12744     MV_UP   | MV_DOWN,
12745     MV_UP   | MV_DOWN,
12746     MV_LEFT | MV_RIGHT
12747   };
12748   static int test_dir[4] =
12749   {
12750     MV_UP,
12751     MV_LEFT,
12752     MV_RIGHT,
12753     MV_DOWN
12754   };
12755
12756   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12757     return;
12758
12759   for (i = 0; i < NUM_DIRECTIONS; i++)
12760   {
12761     int test_x, test_y, test_move_dir, test_element;
12762
12763     test_x = bad_x + test_xy[i][0];
12764     test_y = bad_y + test_xy[i][1];
12765
12766     if (!IN_LEV_FIELD(test_x, test_y))
12767       continue;
12768
12769     test_move_dir =
12770       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12771
12772     test_element = Feld[test_x][test_y];
12773
12774     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12775        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12776     */
12777     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12778         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12779     {
12780       /* good thing is player or penguin that does not move away */
12781       if (IS_PLAYER(test_x, test_y))
12782       {
12783         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12784
12785         if (bad_element == EL_ROBOT && player->is_moving)
12786           continue;     /* robot does not kill player if he is moving */
12787
12788         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12789         {
12790           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12791             continue;           /* center and border element do not touch */
12792         }
12793
12794         kill_x = test_x;
12795         kill_y = test_y;
12796
12797         break;
12798       }
12799       else if (test_element == EL_PENGUIN)
12800       {
12801         kill_x = test_x;
12802         kill_y = test_y;
12803
12804         break;
12805       }
12806     }
12807   }
12808
12809   if (kill_x != -1 || kill_y != -1)
12810   {
12811     if (IS_PLAYER(kill_x, kill_y))
12812     {
12813       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12814
12815       if (player->shield_deadly_time_left > 0 &&
12816           !IS_INDESTRUCTIBLE(bad_element))
12817         Bang(bad_x, bad_y);
12818       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12819         KillPlayer(player);
12820     }
12821     else
12822       Bang(kill_x, kill_y);
12823   }
12824 }
12825
12826 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12827 {
12828   int bad_element = Feld[bad_x][bad_y];
12829   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12830   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12831   int test_x = bad_x + dx, test_y = bad_y + dy;
12832   int test_move_dir, test_element;
12833   int kill_x = -1, kill_y = -1;
12834
12835   if (!IN_LEV_FIELD(test_x, test_y))
12836     return;
12837
12838   test_move_dir =
12839     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12840
12841   test_element = Feld[test_x][test_y];
12842
12843   if (test_move_dir != bad_move_dir)
12844   {
12845     /* good thing can be player or penguin that does not move away */
12846     if (IS_PLAYER(test_x, test_y))
12847     {
12848       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12849
12850       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12851          player as being hit when he is moving towards the bad thing, because
12852          the "get hit by" condition would be lost after the player stops) */
12853       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12854         return;         /* player moves away from bad thing */
12855
12856       kill_x = test_x;
12857       kill_y = test_y;
12858     }
12859     else if (test_element == EL_PENGUIN)
12860     {
12861       kill_x = test_x;
12862       kill_y = test_y;
12863     }
12864   }
12865
12866   if (kill_x != -1 || kill_y != -1)
12867   {
12868     if (IS_PLAYER(kill_x, kill_y))
12869     {
12870       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12871
12872       if (player->shield_deadly_time_left > 0 &&
12873           !IS_INDESTRUCTIBLE(bad_element))
12874         Bang(bad_x, bad_y);
12875       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12876         KillPlayer(player);
12877     }
12878     else
12879       Bang(kill_x, kill_y);
12880   }
12881 }
12882
12883 void TestIfPlayerTouchesBadThing(int x, int y)
12884 {
12885   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12886 }
12887
12888 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12889 {
12890   TestIfGoodThingHitsBadThing(x, y, move_dir);
12891 }
12892
12893 void TestIfBadThingTouchesPlayer(int x, int y)
12894 {
12895   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12896 }
12897
12898 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12899 {
12900   TestIfBadThingHitsGoodThing(x, y, move_dir);
12901 }
12902
12903 void TestIfFriendTouchesBadThing(int x, int y)
12904 {
12905   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12906 }
12907
12908 void TestIfBadThingTouchesFriend(int x, int y)
12909 {
12910   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12911 }
12912
12913 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12914 {
12915   int i, kill_x = bad_x, kill_y = bad_y;
12916   static int xy[4][2] =
12917   {
12918     { 0, -1 },
12919     { -1, 0 },
12920     { +1, 0 },
12921     { 0, +1 }
12922   };
12923
12924   for (i = 0; i < NUM_DIRECTIONS; i++)
12925   {
12926     int x, y, element;
12927
12928     x = bad_x + xy[i][0];
12929     y = bad_y + xy[i][1];
12930     if (!IN_LEV_FIELD(x, y))
12931       continue;
12932
12933     element = Feld[x][y];
12934     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12935         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12936     {
12937       kill_x = x;
12938       kill_y = y;
12939       break;
12940     }
12941   }
12942
12943   if (kill_x != bad_x || kill_y != bad_y)
12944     Bang(bad_x, bad_y);
12945 }
12946
12947 void KillPlayer(struct PlayerInfo *player)
12948 {
12949   int jx = player->jx, jy = player->jy;
12950
12951   if (!player->active)
12952     return;
12953
12954 #if 0
12955   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12956          player->killed, player->active, player->reanimated);
12957 #endif
12958
12959   /* the following code was introduced to prevent an infinite loop when calling
12960      -> Bang()
12961      -> CheckTriggeredElementChangeExt()
12962      -> ExecuteCustomElementAction()
12963      -> KillPlayer()
12964      -> (infinitely repeating the above sequence of function calls)
12965      which occurs when killing the player while having a CE with the setting
12966      "kill player X when explosion of <player X>"; the solution using a new
12967      field "player->killed" was chosen for backwards compatibility, although
12968      clever use of the fields "player->active" etc. would probably also work */
12969 #if 1
12970   if (player->killed)
12971     return;
12972 #endif
12973
12974   player->killed = TRUE;
12975
12976   /* remove accessible field at the player's position */
12977   Feld[jx][jy] = EL_EMPTY;
12978
12979   /* deactivate shield (else Bang()/Explode() would not work right) */
12980   player->shield_normal_time_left = 0;
12981   player->shield_deadly_time_left = 0;
12982
12983 #if 0
12984   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12985          player->killed, player->active, player->reanimated);
12986 #endif
12987
12988   Bang(jx, jy);
12989
12990 #if 0
12991   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12992          player->killed, player->active, player->reanimated);
12993 #endif
12994
12995   if (player->reanimated)       /* killed player may have been reanimated */
12996     player->killed = player->reanimated = FALSE;
12997   else
12998     BuryPlayer(player);
12999 }
13000
13001 static void KillPlayerUnlessEnemyProtected(int x, int y)
13002 {
13003   if (!PLAYER_ENEMY_PROTECTED(x, y))
13004     KillPlayer(PLAYERINFO(x, y));
13005 }
13006
13007 static void KillPlayerUnlessExplosionProtected(int x, int y)
13008 {
13009   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13010     KillPlayer(PLAYERINFO(x, y));
13011 }
13012
13013 void BuryPlayer(struct PlayerInfo *player)
13014 {
13015   int jx = player->jx, jy = player->jy;
13016
13017   if (!player->active)
13018     return;
13019
13020   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13021   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13022
13023   player->GameOver = TRUE;
13024   RemovePlayer(player);
13025 }
13026
13027 void RemovePlayer(struct PlayerInfo *player)
13028 {
13029   int jx = player->jx, jy = player->jy;
13030   int i, found = FALSE;
13031
13032   player->present = FALSE;
13033   player->active = FALSE;
13034
13035   if (!ExplodeField[jx][jy])
13036     StorePlayer[jx][jy] = 0;
13037
13038   if (player->is_moving)
13039     TEST_DrawLevelField(player->last_jx, player->last_jy);
13040
13041   for (i = 0; i < MAX_PLAYERS; i++)
13042     if (stored_player[i].active)
13043       found = TRUE;
13044
13045   if (!found)
13046     AllPlayersGone = TRUE;
13047
13048   ExitX = ZX = jx;
13049   ExitY = ZY = jy;
13050 }
13051
13052 static void setFieldForSnapping(int x, int y, int element, int direction)
13053 {
13054   struct ElementInfo *ei = &element_info[element];
13055   int direction_bit = MV_DIR_TO_BIT(direction);
13056   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13057   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13058                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13059
13060   Feld[x][y] = EL_ELEMENT_SNAPPING;
13061   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13062
13063   ResetGfxAnimation(x, y);
13064
13065   GfxElement[x][y] = element;
13066   GfxAction[x][y] = action;
13067   GfxDir[x][y] = direction;
13068   GfxFrame[x][y] = -1;
13069 }
13070
13071 /*
13072   =============================================================================
13073   checkDiagonalPushing()
13074   -----------------------------------------------------------------------------
13075   check if diagonal input device direction results in pushing of object
13076   (by checking if the alternative direction is walkable, diggable, ...)
13077   =============================================================================
13078 */
13079
13080 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13081                                     int x, int y, int real_dx, int real_dy)
13082 {
13083   int jx, jy, dx, dy, xx, yy;
13084
13085   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13086     return TRUE;
13087
13088   /* diagonal direction: check alternative direction */
13089   jx = player->jx;
13090   jy = player->jy;
13091   dx = x - jx;
13092   dy = y - jy;
13093   xx = jx + (dx == 0 ? real_dx : 0);
13094   yy = jy + (dy == 0 ? real_dy : 0);
13095
13096   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13097 }
13098
13099 /*
13100   =============================================================================
13101   DigField()
13102   -----------------------------------------------------------------------------
13103   x, y:                 field next to player (non-diagonal) to try to dig to
13104   real_dx, real_dy:     direction as read from input device (can be diagonal)
13105   =============================================================================
13106 */
13107
13108 static int DigField(struct PlayerInfo *player,
13109                     int oldx, int oldy, int x, int y,
13110                     int real_dx, int real_dy, int mode)
13111 {
13112   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13113   boolean player_was_pushing = player->is_pushing;
13114   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13115   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13116   int jx = oldx, jy = oldy;
13117   int dx = x - jx, dy = y - jy;
13118   int nextx = x + dx, nexty = y + dy;
13119   int move_direction = (dx == -1 ? MV_LEFT  :
13120                         dx == +1 ? MV_RIGHT :
13121                         dy == -1 ? MV_UP    :
13122                         dy == +1 ? MV_DOWN  : MV_NONE);
13123   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13124   int dig_side = MV_DIR_OPPOSITE(move_direction);
13125   int old_element = Feld[jx][jy];
13126   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13127   int collect_count;
13128
13129   if (is_player)                /* function can also be called by EL_PENGUIN */
13130   {
13131     if (player->MovPos == 0)
13132     {
13133       player->is_digging = FALSE;
13134       player->is_collecting = FALSE;
13135     }
13136
13137     if (player->MovPos == 0)    /* last pushing move finished */
13138       player->is_pushing = FALSE;
13139
13140     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13141     {
13142       player->is_switching = FALSE;
13143       player->push_delay = -1;
13144
13145       return MP_NO_ACTION;
13146     }
13147   }
13148
13149   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13150     old_element = Back[jx][jy];
13151
13152   /* in case of element dropped at player position, check background */
13153   else if (Back[jx][jy] != EL_EMPTY &&
13154            game.engine_version >= VERSION_IDENT(2,2,0,0))
13155     old_element = Back[jx][jy];
13156
13157   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13158     return MP_NO_ACTION;        /* field has no opening in this direction */
13159
13160   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13161     return MP_NO_ACTION;        /* field has no opening in this direction */
13162
13163   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13164   {
13165     SplashAcid(x, y);
13166
13167     Feld[jx][jy] = player->artwork_element;
13168     InitMovingField(jx, jy, MV_DOWN);
13169     Store[jx][jy] = EL_ACID;
13170     ContinueMoving(jx, jy);
13171     BuryPlayer(player);
13172
13173     return MP_DONT_RUN_INTO;
13174   }
13175
13176   if (player_can_move && DONT_RUN_INTO(element))
13177   {
13178     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13179
13180     return MP_DONT_RUN_INTO;
13181   }
13182
13183   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13184     return MP_NO_ACTION;
13185
13186   collect_count = element_info[element].collect_count_initial;
13187
13188   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13189     return MP_NO_ACTION;
13190
13191   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13192     player_can_move = player_can_move_or_snap;
13193
13194   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13195       game.engine_version >= VERSION_IDENT(2,2,0,0))
13196   {
13197     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13198                                player->index_bit, dig_side);
13199     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13200                                         player->index_bit, dig_side);
13201
13202     if (element == EL_DC_LANDMINE)
13203       Bang(x, y);
13204
13205     if (Feld[x][y] != element)          /* field changed by snapping */
13206       return MP_ACTION;
13207
13208     return MP_NO_ACTION;
13209   }
13210
13211   if (player->gravity && is_player && !player->is_auto_moving &&
13212       canFallDown(player) && move_direction != MV_DOWN &&
13213       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13214     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13215
13216   if (player_can_move &&
13217       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13218   {
13219     int sound_element = SND_ELEMENT(element);
13220     int sound_action = ACTION_WALKING;
13221
13222     if (IS_RND_GATE(element))
13223     {
13224       if (!player->key[RND_GATE_NR(element)])
13225         return MP_NO_ACTION;
13226     }
13227     else if (IS_RND_GATE_GRAY(element))
13228     {
13229       if (!player->key[RND_GATE_GRAY_NR(element)])
13230         return MP_NO_ACTION;
13231     }
13232     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13233     {
13234       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13235         return MP_NO_ACTION;
13236     }
13237     else if (element == EL_EXIT_OPEN ||
13238              element == EL_EM_EXIT_OPEN ||
13239              element == EL_EM_EXIT_OPENING ||
13240              element == EL_STEEL_EXIT_OPEN ||
13241              element == EL_EM_STEEL_EXIT_OPEN ||
13242              element == EL_EM_STEEL_EXIT_OPENING ||
13243              element == EL_SP_EXIT_OPEN ||
13244              element == EL_SP_EXIT_OPENING)
13245     {
13246       sound_action = ACTION_PASSING;    /* player is passing exit */
13247     }
13248     else if (element == EL_EMPTY)
13249     {
13250       sound_action = ACTION_MOVING;             /* nothing to walk on */
13251     }
13252
13253     /* play sound from background or player, whatever is available */
13254     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13255       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13256     else
13257       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13258   }
13259   else if (player_can_move &&
13260            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13261   {
13262     if (!ACCESS_FROM(element, opposite_direction))
13263       return MP_NO_ACTION;      /* field not accessible from this direction */
13264
13265     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13266       return MP_NO_ACTION;
13267
13268     if (IS_EM_GATE(element))
13269     {
13270       if (!player->key[EM_GATE_NR(element)])
13271         return MP_NO_ACTION;
13272     }
13273     else if (IS_EM_GATE_GRAY(element))
13274     {
13275       if (!player->key[EM_GATE_GRAY_NR(element)])
13276         return MP_NO_ACTION;
13277     }
13278     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13279     {
13280       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13281         return MP_NO_ACTION;
13282     }
13283     else if (IS_EMC_GATE(element))
13284     {
13285       if (!player->key[EMC_GATE_NR(element)])
13286         return MP_NO_ACTION;
13287     }
13288     else if (IS_EMC_GATE_GRAY(element))
13289     {
13290       if (!player->key[EMC_GATE_GRAY_NR(element)])
13291         return MP_NO_ACTION;
13292     }
13293     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13294     {
13295       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13296         return MP_NO_ACTION;
13297     }
13298     else if (element == EL_DC_GATE_WHITE ||
13299              element == EL_DC_GATE_WHITE_GRAY ||
13300              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13301     {
13302       if (player->num_white_keys == 0)
13303         return MP_NO_ACTION;
13304
13305       player->num_white_keys--;
13306     }
13307     else if (IS_SP_PORT(element))
13308     {
13309       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13310           element == EL_SP_GRAVITY_PORT_RIGHT ||
13311           element == EL_SP_GRAVITY_PORT_UP ||
13312           element == EL_SP_GRAVITY_PORT_DOWN)
13313         player->gravity = !player->gravity;
13314       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13315                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13316                element == EL_SP_GRAVITY_ON_PORT_UP ||
13317                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13318         player->gravity = TRUE;
13319       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13320                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13321                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13322                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13323         player->gravity = FALSE;
13324     }
13325
13326     /* automatically move to the next field with double speed */
13327     player->programmed_action = move_direction;
13328
13329     if (player->move_delay_reset_counter == 0)
13330     {
13331       player->move_delay_reset_counter = 2;     /* two double speed steps */
13332
13333       DOUBLE_PLAYER_SPEED(player);
13334     }
13335
13336     PlayLevelSoundAction(x, y, ACTION_PASSING);
13337   }
13338   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13339   {
13340     RemoveField(x, y);
13341
13342     if (mode != DF_SNAP)
13343     {
13344       GfxElement[x][y] = GFX_ELEMENT(element);
13345       player->is_digging = TRUE;
13346     }
13347
13348     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13349
13350     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13351                                         player->index_bit, dig_side);
13352
13353     if (mode == DF_SNAP)
13354     {
13355       if (level.block_snap_field)
13356         setFieldForSnapping(x, y, element, move_direction);
13357       else
13358         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13359
13360       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13361                                           player->index_bit, dig_side);
13362     }
13363   }
13364   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13365   {
13366     RemoveField(x, y);
13367
13368     if (is_player && mode != DF_SNAP)
13369     {
13370       GfxElement[x][y] = element;
13371       player->is_collecting = TRUE;
13372     }
13373
13374     if (element == EL_SPEED_PILL)
13375     {
13376       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13377     }
13378     else if (element == EL_EXTRA_TIME && level.time > 0)
13379     {
13380       TimeLeft += level.extra_time;
13381
13382       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13383
13384       DisplayGameControlValues();
13385     }
13386     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13387     {
13388       player->shield_normal_time_left += level.shield_normal_time;
13389       if (element == EL_SHIELD_DEADLY)
13390         player->shield_deadly_time_left += level.shield_deadly_time;
13391     }
13392     else if (element == EL_DYNAMITE ||
13393              element == EL_EM_DYNAMITE ||
13394              element == EL_SP_DISK_RED)
13395     {
13396       if (player->inventory_size < MAX_INVENTORY_SIZE)
13397         player->inventory_element[player->inventory_size++] = element;
13398
13399       DrawGameDoorValues();
13400     }
13401     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13402     {
13403       player->dynabomb_count++;
13404       player->dynabombs_left++;
13405     }
13406     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13407     {
13408       player->dynabomb_size++;
13409     }
13410     else if (element == EL_DYNABOMB_INCREASE_POWER)
13411     {
13412       player->dynabomb_xl = TRUE;
13413     }
13414     else if (IS_KEY(element))
13415     {
13416       player->key[KEY_NR(element)] = TRUE;
13417
13418       DrawGameDoorValues();
13419     }
13420     else if (element == EL_DC_KEY_WHITE)
13421     {
13422       player->num_white_keys++;
13423
13424       /* display white keys? */
13425       /* DrawGameDoorValues(); */
13426     }
13427     else if (IS_ENVELOPE(element))
13428     {
13429       player->show_envelope = element;
13430     }
13431     else if (element == EL_EMC_LENSES)
13432     {
13433       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13434
13435       RedrawAllInvisibleElementsForLenses();
13436     }
13437     else if (element == EL_EMC_MAGNIFIER)
13438     {
13439       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13440
13441       RedrawAllInvisibleElementsForMagnifier();
13442     }
13443     else if (IS_DROPPABLE(element) ||
13444              IS_THROWABLE(element))     /* can be collected and dropped */
13445     {
13446       int i;
13447
13448       if (collect_count == 0)
13449         player->inventory_infinite_element = element;
13450       else
13451         for (i = 0; i < collect_count; i++)
13452           if (player->inventory_size < MAX_INVENTORY_SIZE)
13453             player->inventory_element[player->inventory_size++] = element;
13454
13455       DrawGameDoorValues();
13456     }
13457     else if (collect_count > 0)
13458     {
13459       local_player->gems_still_needed -= collect_count;
13460       if (local_player->gems_still_needed < 0)
13461         local_player->gems_still_needed = 0;
13462
13463       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13464
13465       DisplayGameControlValues();
13466     }
13467
13468     RaiseScoreElement(element);
13469     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13470
13471     if (is_player)
13472       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13473                                           player->index_bit, dig_side);
13474
13475     if (mode == DF_SNAP)
13476     {
13477       if (level.block_snap_field)
13478         setFieldForSnapping(x, y, element, move_direction);
13479       else
13480         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13481
13482       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13483                                           player->index_bit, dig_side);
13484     }
13485   }
13486   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13487   {
13488     if (mode == DF_SNAP && element != EL_BD_ROCK)
13489       return MP_NO_ACTION;
13490
13491     if (CAN_FALL(element) && dy)
13492       return MP_NO_ACTION;
13493
13494     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13495         !(element == EL_SPRING && level.use_spring_bug))
13496       return MP_NO_ACTION;
13497
13498     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13499         ((move_direction & MV_VERTICAL &&
13500           ((element_info[element].move_pattern & MV_LEFT &&
13501             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13502            (element_info[element].move_pattern & MV_RIGHT &&
13503             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13504          (move_direction & MV_HORIZONTAL &&
13505           ((element_info[element].move_pattern & MV_UP &&
13506             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13507            (element_info[element].move_pattern & MV_DOWN &&
13508             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13509       return MP_NO_ACTION;
13510
13511     /* do not push elements already moving away faster than player */
13512     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13513         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13514       return MP_NO_ACTION;
13515
13516     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13517     {
13518       if (player->push_delay_value == -1 || !player_was_pushing)
13519         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13520     }
13521     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13522     {
13523       if (player->push_delay_value == -1)
13524         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13525     }
13526     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13527     {
13528       if (!player->is_pushing)
13529         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13530     }
13531
13532     player->is_pushing = TRUE;
13533     player->is_active = TRUE;
13534
13535     if (!(IN_LEV_FIELD(nextx, nexty) &&
13536           (IS_FREE(nextx, nexty) ||
13537            (IS_SB_ELEMENT(element) &&
13538             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13539            (IS_CUSTOM_ELEMENT(element) &&
13540             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13541       return MP_NO_ACTION;
13542
13543     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13544       return MP_NO_ACTION;
13545
13546     if (player->push_delay == -1)       /* new pushing; restart delay */
13547       player->push_delay = 0;
13548
13549     if (player->push_delay < player->push_delay_value &&
13550         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13551         element != EL_SPRING && element != EL_BALLOON)
13552     {
13553       /* make sure that there is no move delay before next try to push */
13554       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13555         player->move_delay = 0;
13556
13557       return MP_NO_ACTION;
13558     }
13559
13560     if (IS_CUSTOM_ELEMENT(element) &&
13561         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13562     {
13563       if (!DigFieldByCE(nextx, nexty, element))
13564         return MP_NO_ACTION;
13565     }
13566
13567     if (IS_SB_ELEMENT(element))
13568     {
13569       if (element == EL_SOKOBAN_FIELD_FULL)
13570       {
13571         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13572         local_player->sokobanfields_still_needed++;
13573       }
13574
13575       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13576       {
13577         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13578         local_player->sokobanfields_still_needed--;
13579       }
13580
13581       Feld[x][y] = EL_SOKOBAN_OBJECT;
13582
13583       if (Back[x][y] == Back[nextx][nexty])
13584         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13585       else if (Back[x][y] != 0)
13586         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13587                                     ACTION_EMPTYING);
13588       else
13589         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13590                                     ACTION_FILLING);
13591
13592       if (local_player->sokobanfields_still_needed == 0 &&
13593           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13594       {
13595         PlayerWins(player);
13596
13597         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13598       }
13599     }
13600     else
13601       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13602
13603     InitMovingField(x, y, move_direction);
13604     GfxAction[x][y] = ACTION_PUSHING;
13605
13606     if (mode == DF_SNAP)
13607       ContinueMoving(x, y);
13608     else
13609       MovPos[x][y] = (dx != 0 ? dx : dy);
13610
13611     Pushed[x][y] = TRUE;
13612     Pushed[nextx][nexty] = TRUE;
13613
13614     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13615       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13616     else
13617       player->push_delay_value = -1;    /* get new value later */
13618
13619     /* check for element change _after_ element has been pushed */
13620     if (game.use_change_when_pushing_bug)
13621     {
13622       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13623                                  player->index_bit, dig_side);
13624       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13625                                           player->index_bit, dig_side);
13626     }
13627   }
13628   else if (IS_SWITCHABLE(element))
13629   {
13630     if (PLAYER_SWITCHING(player, x, y))
13631     {
13632       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13633                                           player->index_bit, dig_side);
13634
13635       return MP_ACTION;
13636     }
13637
13638     player->is_switching = TRUE;
13639     player->switch_x = x;
13640     player->switch_y = y;
13641
13642     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13643
13644     if (element == EL_ROBOT_WHEEL)
13645     {
13646       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13647       ZX = x;
13648       ZY = y;
13649
13650       game.robot_wheel_active = TRUE;
13651
13652       TEST_DrawLevelField(x, y);
13653     }
13654     else if (element == EL_SP_TERMINAL)
13655     {
13656       int xx, yy;
13657
13658       SCAN_PLAYFIELD(xx, yy)
13659       {
13660         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13661           Bang(xx, yy);
13662         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13663           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13664       }
13665     }
13666     else if (IS_BELT_SWITCH(element))
13667     {
13668       ToggleBeltSwitch(x, y);
13669     }
13670     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13671              element == EL_SWITCHGATE_SWITCH_DOWN ||
13672              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13673              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13674     {
13675       ToggleSwitchgateSwitch(x, y);
13676     }
13677     else if (element == EL_LIGHT_SWITCH ||
13678              element == EL_LIGHT_SWITCH_ACTIVE)
13679     {
13680       ToggleLightSwitch(x, y);
13681     }
13682     else if (element == EL_TIMEGATE_SWITCH ||
13683              element == EL_DC_TIMEGATE_SWITCH)
13684     {
13685       ActivateTimegateSwitch(x, y);
13686     }
13687     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13688              element == EL_BALLOON_SWITCH_RIGHT ||
13689              element == EL_BALLOON_SWITCH_UP    ||
13690              element == EL_BALLOON_SWITCH_DOWN  ||
13691              element == EL_BALLOON_SWITCH_NONE  ||
13692              element == EL_BALLOON_SWITCH_ANY)
13693     {
13694       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13695                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13696                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13697                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13698                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13699                              move_direction);
13700     }
13701     else if (element == EL_LAMP)
13702     {
13703       Feld[x][y] = EL_LAMP_ACTIVE;
13704       local_player->lights_still_needed--;
13705
13706       ResetGfxAnimation(x, y);
13707       TEST_DrawLevelField(x, y);
13708     }
13709     else if (element == EL_TIME_ORB_FULL)
13710     {
13711       Feld[x][y] = EL_TIME_ORB_EMPTY;
13712
13713       if (level.time > 0 || level.use_time_orb_bug)
13714       {
13715         TimeLeft += level.time_orb_time;
13716         game.no_time_limit = FALSE;
13717
13718         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13719
13720         DisplayGameControlValues();
13721       }
13722
13723       ResetGfxAnimation(x, y);
13724       TEST_DrawLevelField(x, y);
13725     }
13726     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13727              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13728     {
13729       int xx, yy;
13730
13731       game.ball_state = !game.ball_state;
13732
13733       SCAN_PLAYFIELD(xx, yy)
13734       {
13735         int e = Feld[xx][yy];
13736
13737         if (game.ball_state)
13738         {
13739           if (e == EL_EMC_MAGIC_BALL)
13740             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13741           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13742             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13743         }
13744         else
13745         {
13746           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13747             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13748           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13749             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13750         }
13751       }
13752     }
13753
13754     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13755                                         player->index_bit, dig_side);
13756
13757     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13758                                         player->index_bit, dig_side);
13759
13760     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13761                                         player->index_bit, dig_side);
13762
13763     return MP_ACTION;
13764   }
13765   else
13766   {
13767     if (!PLAYER_SWITCHING(player, x, y))
13768     {
13769       player->is_switching = TRUE;
13770       player->switch_x = x;
13771       player->switch_y = y;
13772
13773       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13774                                  player->index_bit, dig_side);
13775       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13776                                           player->index_bit, dig_side);
13777
13778       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13779                                  player->index_bit, dig_side);
13780       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13781                                           player->index_bit, dig_side);
13782     }
13783
13784     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13785                                player->index_bit, dig_side);
13786     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13787                                         player->index_bit, dig_side);
13788
13789     return MP_NO_ACTION;
13790   }
13791
13792   player->push_delay = -1;
13793
13794   if (is_player)                /* function can also be called by EL_PENGUIN */
13795   {
13796     if (Feld[x][y] != element)          /* really digged/collected something */
13797     {
13798       player->is_collecting = !player->is_digging;
13799       player->is_active = TRUE;
13800     }
13801   }
13802
13803   return MP_MOVING;
13804 }
13805
13806 static boolean DigFieldByCE(int x, int y, int digging_element)
13807 {
13808   int element = Feld[x][y];
13809
13810   if (!IS_FREE(x, y))
13811   {
13812     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13813                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13814                   ACTION_BREAKING);
13815
13816     /* no element can dig solid indestructible elements */
13817     if (IS_INDESTRUCTIBLE(element) &&
13818         !IS_DIGGABLE(element) &&
13819         !IS_COLLECTIBLE(element))
13820       return FALSE;
13821
13822     if (AmoebaNr[x][y] &&
13823         (element == EL_AMOEBA_FULL ||
13824          element == EL_BD_AMOEBA ||
13825          element == EL_AMOEBA_GROWING))
13826     {
13827       AmoebaCnt[AmoebaNr[x][y]]--;
13828       AmoebaCnt2[AmoebaNr[x][y]]--;
13829     }
13830
13831     if (IS_MOVING(x, y))
13832       RemoveMovingField(x, y);
13833     else
13834     {
13835       RemoveField(x, y);
13836       TEST_DrawLevelField(x, y);
13837     }
13838
13839     /* if digged element was about to explode, prevent the explosion */
13840     ExplodeField[x][y] = EX_TYPE_NONE;
13841
13842     PlayLevelSoundAction(x, y, action);
13843   }
13844
13845   Store[x][y] = EL_EMPTY;
13846
13847   /* this makes it possible to leave the removed element again */
13848   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13849     Store[x][y] = element;
13850
13851   return TRUE;
13852 }
13853
13854 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13855 {
13856   int jx = player->jx, jy = player->jy;
13857   int x = jx + dx, y = jy + dy;
13858   int snap_direction = (dx == -1 ? MV_LEFT  :
13859                         dx == +1 ? MV_RIGHT :
13860                         dy == -1 ? MV_UP    :
13861                         dy == +1 ? MV_DOWN  : MV_NONE);
13862   boolean can_continue_snapping = (level.continuous_snapping &&
13863                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13864
13865   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13866     return FALSE;
13867
13868   if (!player->active || !IN_LEV_FIELD(x, y))
13869     return FALSE;
13870
13871   if (dx && dy)
13872     return FALSE;
13873
13874   if (!dx && !dy)
13875   {
13876     if (player->MovPos == 0)
13877       player->is_pushing = FALSE;
13878
13879     player->is_snapping = FALSE;
13880
13881     if (player->MovPos == 0)
13882     {
13883       player->is_moving = FALSE;
13884       player->is_digging = FALSE;
13885       player->is_collecting = FALSE;
13886     }
13887
13888     return FALSE;
13889   }
13890
13891   /* prevent snapping with already pressed snap key when not allowed */
13892   if (player->is_snapping && !can_continue_snapping)
13893     return FALSE;
13894
13895   player->MovDir = snap_direction;
13896
13897   if (player->MovPos == 0)
13898   {
13899     player->is_moving = FALSE;
13900     player->is_digging = FALSE;
13901     player->is_collecting = FALSE;
13902   }
13903
13904   player->is_dropping = FALSE;
13905   player->is_dropping_pressed = FALSE;
13906   player->drop_pressed_delay = 0;
13907
13908   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13909     return FALSE;
13910
13911   player->is_snapping = TRUE;
13912   player->is_active = TRUE;
13913
13914   if (player->MovPos == 0)
13915   {
13916     player->is_moving = FALSE;
13917     player->is_digging = FALSE;
13918     player->is_collecting = FALSE;
13919   }
13920
13921   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13922     TEST_DrawLevelField(player->last_jx, player->last_jy);
13923
13924   TEST_DrawLevelField(x, y);
13925
13926   return TRUE;
13927 }
13928
13929 static boolean DropElement(struct PlayerInfo *player)
13930 {
13931   int old_element, new_element;
13932   int dropx = player->jx, dropy = player->jy;
13933   int drop_direction = player->MovDir;
13934   int drop_side = drop_direction;
13935   int drop_element = get_next_dropped_element(player);
13936
13937   player->is_dropping_pressed = TRUE;
13938
13939   /* do not drop an element on top of another element; when holding drop key
13940      pressed without moving, dropped element must move away before the next
13941      element can be dropped (this is especially important if the next element
13942      is dynamite, which can be placed on background for historical reasons) */
13943   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13944     return MP_ACTION;
13945
13946   if (IS_THROWABLE(drop_element))
13947   {
13948     dropx += GET_DX_FROM_DIR(drop_direction);
13949     dropy += GET_DY_FROM_DIR(drop_direction);
13950
13951     if (!IN_LEV_FIELD(dropx, dropy))
13952       return FALSE;
13953   }
13954
13955   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13956   new_element = drop_element;           /* default: no change when dropping */
13957
13958   /* check if player is active, not moving and ready to drop */
13959   if (!player->active || player->MovPos || player->drop_delay > 0)
13960     return FALSE;
13961
13962   /* check if player has anything that can be dropped */
13963   if (new_element == EL_UNDEFINED)
13964     return FALSE;
13965
13966   /* check if drop key was pressed long enough for EM style dynamite */
13967   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13968     return FALSE;
13969
13970   /* check if anything can be dropped at the current position */
13971   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13972     return FALSE;
13973
13974   /* collected custom elements can only be dropped on empty fields */
13975   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13976     return FALSE;
13977
13978   if (old_element != EL_EMPTY)
13979     Back[dropx][dropy] = old_element;   /* store old element on this field */
13980
13981   ResetGfxAnimation(dropx, dropy);
13982   ResetRandomAnimationValue(dropx, dropy);
13983
13984   if (player->inventory_size > 0 ||
13985       player->inventory_infinite_element != EL_UNDEFINED)
13986   {
13987     if (player->inventory_size > 0)
13988     {
13989       player->inventory_size--;
13990
13991       DrawGameDoorValues();
13992
13993       if (new_element == EL_DYNAMITE)
13994         new_element = EL_DYNAMITE_ACTIVE;
13995       else if (new_element == EL_EM_DYNAMITE)
13996         new_element = EL_EM_DYNAMITE_ACTIVE;
13997       else if (new_element == EL_SP_DISK_RED)
13998         new_element = EL_SP_DISK_RED_ACTIVE;
13999     }
14000
14001     Feld[dropx][dropy] = new_element;
14002
14003     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14004       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14005                           el2img(Feld[dropx][dropy]), 0);
14006
14007     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14008
14009     /* needed if previous element just changed to "empty" in the last frame */
14010     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14011
14012     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14013                                player->index_bit, drop_side);
14014     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14015                                         CE_PLAYER_DROPS_X,
14016                                         player->index_bit, drop_side);
14017
14018     TestIfElementTouchesCustomElement(dropx, dropy);
14019   }
14020   else          /* player is dropping a dyna bomb */
14021   {
14022     player->dynabombs_left--;
14023
14024     Feld[dropx][dropy] = new_element;
14025
14026     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14027       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14028                           el2img(Feld[dropx][dropy]), 0);
14029
14030     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14031   }
14032
14033   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14034     InitField_WithBug1(dropx, dropy, FALSE);
14035
14036   new_element = Feld[dropx][dropy];     /* element might have changed */
14037
14038   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14039       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14040   {
14041     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14042       MovDir[dropx][dropy] = drop_direction;
14043
14044     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14045
14046     /* do not cause impact style collision by dropping elements that can fall */
14047     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14048   }
14049
14050   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14051   player->is_dropping = TRUE;
14052
14053   player->drop_pressed_delay = 0;
14054   player->is_dropping_pressed = FALSE;
14055
14056   player->drop_x = dropx;
14057   player->drop_y = dropy;
14058
14059   return TRUE;
14060 }
14061
14062 /* ------------------------------------------------------------------------- */
14063 /* game sound playing functions                                              */
14064 /* ------------------------------------------------------------------------- */
14065
14066 static int *loop_sound_frame = NULL;
14067 static int *loop_sound_volume = NULL;
14068
14069 void InitPlayLevelSound()
14070 {
14071   int num_sounds = getSoundListSize();
14072
14073   checked_free(loop_sound_frame);
14074   checked_free(loop_sound_volume);
14075
14076   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14077   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14078 }
14079
14080 static void PlayLevelSound(int x, int y, int nr)
14081 {
14082   int sx = SCREENX(x), sy = SCREENY(y);
14083   int volume, stereo_position;
14084   int max_distance = 8;
14085   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14086
14087   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14088       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14089     return;
14090
14091   if (!IN_LEV_FIELD(x, y) ||
14092       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14093       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14094     return;
14095
14096   volume = SOUND_MAX_VOLUME;
14097
14098   if (!IN_SCR_FIELD(sx, sy))
14099   {
14100     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14101     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14102
14103     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14104   }
14105
14106   stereo_position = (SOUND_MAX_LEFT +
14107                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14108                      (SCR_FIELDX + 2 * max_distance));
14109
14110   if (IS_LOOP_SOUND(nr))
14111   {
14112     /* This assures that quieter loop sounds do not overwrite louder ones,
14113        while restarting sound volume comparison with each new game frame. */
14114
14115     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14116       return;
14117
14118     loop_sound_volume[nr] = volume;
14119     loop_sound_frame[nr] = FrameCounter;
14120   }
14121
14122   PlaySoundExt(nr, volume, stereo_position, type);
14123 }
14124
14125 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14126 {
14127   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14128                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14129                  y < LEVELY(BY1) ? LEVELY(BY1) :
14130                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14131                  sound_action);
14132 }
14133
14134 static void PlayLevelSoundAction(int x, int y, int action)
14135 {
14136   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14137 }
14138
14139 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14140 {
14141   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14142
14143   if (sound_effect != SND_UNDEFINED)
14144     PlayLevelSound(x, y, sound_effect);
14145 }
14146
14147 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14148                                               int action)
14149 {
14150   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14151
14152   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14153     PlayLevelSound(x, y, sound_effect);
14154 }
14155
14156 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14157 {
14158   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14159
14160   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14161     PlayLevelSound(x, y, sound_effect);
14162 }
14163
14164 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14165 {
14166   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14167
14168   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14169     StopSound(sound_effect);
14170 }
14171
14172 static void PlayLevelMusic()
14173 {
14174   if (levelset.music[level_nr] != MUS_UNDEFINED)
14175     PlayMusic(levelset.music[level_nr]);        /* from config file */
14176   else
14177     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14178 }
14179
14180 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14181 {
14182   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14183   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14184   int x = xx - 1 - offset;
14185   int y = yy - 1 - offset;
14186
14187   switch (sample)
14188   {
14189     case SAMPLE_blank:
14190       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14191       break;
14192
14193     case SAMPLE_roll:
14194       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14195       break;
14196
14197     case SAMPLE_stone:
14198       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14199       break;
14200
14201     case SAMPLE_nut:
14202       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14203       break;
14204
14205     case SAMPLE_crack:
14206       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14207       break;
14208
14209     case SAMPLE_bug:
14210       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14211       break;
14212
14213     case SAMPLE_tank:
14214       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14215       break;
14216
14217     case SAMPLE_android_clone:
14218       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14219       break;
14220
14221     case SAMPLE_android_move:
14222       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14223       break;
14224
14225     case SAMPLE_spring:
14226       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14227       break;
14228
14229     case SAMPLE_slurp:
14230       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14231       break;
14232
14233     case SAMPLE_eater:
14234       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14235       break;
14236
14237     case SAMPLE_eater_eat:
14238       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14239       break;
14240
14241     case SAMPLE_alien:
14242       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14243       break;
14244
14245     case SAMPLE_collect:
14246       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14247       break;
14248
14249     case SAMPLE_diamond:
14250       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14251       break;
14252
14253     case SAMPLE_squash:
14254       /* !!! CHECK THIS !!! */
14255 #if 1
14256       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14257 #else
14258       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14259 #endif
14260       break;
14261
14262     case SAMPLE_wonderfall:
14263       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14264       break;
14265
14266     case SAMPLE_drip:
14267       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14268       break;
14269
14270     case SAMPLE_push:
14271       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14272       break;
14273
14274     case SAMPLE_dirt:
14275       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14276       break;
14277
14278     case SAMPLE_acid:
14279       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14280       break;
14281
14282     case SAMPLE_ball:
14283       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14284       break;
14285
14286     case SAMPLE_grow:
14287       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14288       break;
14289
14290     case SAMPLE_wonder:
14291       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14292       break;
14293
14294     case SAMPLE_door:
14295       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14296       break;
14297
14298     case SAMPLE_exit_open:
14299       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14300       break;
14301
14302     case SAMPLE_exit_leave:
14303       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14304       break;
14305
14306     case SAMPLE_dynamite:
14307       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14308       break;
14309
14310     case SAMPLE_tick:
14311       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14312       break;
14313
14314     case SAMPLE_press:
14315       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14316       break;
14317
14318     case SAMPLE_wheel:
14319       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14320       break;
14321
14322     case SAMPLE_boom:
14323       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14324       break;
14325
14326     case SAMPLE_die:
14327       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14328       break;
14329
14330     case SAMPLE_time:
14331       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14332       break;
14333
14334     default:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14336       break;
14337   }
14338 }
14339
14340 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14341 {
14342   int element = map_element_SP_to_RND(element_sp);
14343   int action = map_action_SP_to_RND(action_sp);
14344   int offset = (setup.sp_show_border_elements ? 0 : 1);
14345   int x = xx - offset;
14346   int y = yy - offset;
14347
14348   PlayLevelSoundElementAction(x, y, element, action);
14349 }
14350
14351 void RaiseScore(int value)
14352 {
14353   local_player->score += value;
14354
14355   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14356
14357   DisplayGameControlValues();
14358 }
14359
14360 void RaiseScoreElement(int element)
14361 {
14362   switch (element)
14363   {
14364     case EL_EMERALD:
14365     case EL_BD_DIAMOND:
14366     case EL_EMERALD_YELLOW:
14367     case EL_EMERALD_RED:
14368     case EL_EMERALD_PURPLE:
14369     case EL_SP_INFOTRON:
14370       RaiseScore(level.score[SC_EMERALD]);
14371       break;
14372     case EL_DIAMOND:
14373       RaiseScore(level.score[SC_DIAMOND]);
14374       break;
14375     case EL_CRYSTAL:
14376       RaiseScore(level.score[SC_CRYSTAL]);
14377       break;
14378     case EL_PEARL:
14379       RaiseScore(level.score[SC_PEARL]);
14380       break;
14381     case EL_BUG:
14382     case EL_BD_BUTTERFLY:
14383     case EL_SP_ELECTRON:
14384       RaiseScore(level.score[SC_BUG]);
14385       break;
14386     case EL_SPACESHIP:
14387     case EL_BD_FIREFLY:
14388     case EL_SP_SNIKSNAK:
14389       RaiseScore(level.score[SC_SPACESHIP]);
14390       break;
14391     case EL_YAMYAM:
14392     case EL_DARK_YAMYAM:
14393       RaiseScore(level.score[SC_YAMYAM]);
14394       break;
14395     case EL_ROBOT:
14396       RaiseScore(level.score[SC_ROBOT]);
14397       break;
14398     case EL_PACMAN:
14399       RaiseScore(level.score[SC_PACMAN]);
14400       break;
14401     case EL_NUT:
14402       RaiseScore(level.score[SC_NUT]);
14403       break;
14404     case EL_DYNAMITE:
14405     case EL_EM_DYNAMITE:
14406     case EL_SP_DISK_RED:
14407     case EL_DYNABOMB_INCREASE_NUMBER:
14408     case EL_DYNABOMB_INCREASE_SIZE:
14409     case EL_DYNABOMB_INCREASE_POWER:
14410       RaiseScore(level.score[SC_DYNAMITE]);
14411       break;
14412     case EL_SHIELD_NORMAL:
14413     case EL_SHIELD_DEADLY:
14414       RaiseScore(level.score[SC_SHIELD]);
14415       break;
14416     case EL_EXTRA_TIME:
14417       RaiseScore(level.extra_time_score);
14418       break;
14419     case EL_KEY_1:
14420     case EL_KEY_2:
14421     case EL_KEY_3:
14422     case EL_KEY_4:
14423     case EL_EM_KEY_1:
14424     case EL_EM_KEY_2:
14425     case EL_EM_KEY_3:
14426     case EL_EM_KEY_4:
14427     case EL_EMC_KEY_5:
14428     case EL_EMC_KEY_6:
14429     case EL_EMC_KEY_7:
14430     case EL_EMC_KEY_8:
14431     case EL_DC_KEY_WHITE:
14432       RaiseScore(level.score[SC_KEY]);
14433       break;
14434     default:
14435       RaiseScore(element_info[element].collect_score);
14436       break;
14437   }
14438 }
14439
14440 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14441 {
14442   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14443   {
14444     /* closing door required in case of envelope style request dialogs */
14445     if (!skip_request)
14446       CloseDoor(DOOR_CLOSE_1);
14447
14448 #if defined(NETWORK_AVALIABLE)
14449     if (options.network)
14450       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14451     else
14452 #endif
14453     {
14454       if (quick_quit)
14455       {
14456         FadeSkipNextFadeIn();
14457
14458         game_status = GAME_MODE_MAIN;
14459
14460         DrawAndFadeInMainMenu(REDRAW_FIELD);
14461       }
14462       else
14463       {
14464         game_status = GAME_MODE_MAIN;
14465
14466         DrawAndFadeInMainMenu(REDRAW_FIELD);
14467       }
14468     }
14469   }
14470   else          /* continue playing the game */
14471   {
14472     if (tape.playing && tape.deactivate_display)
14473       TapeDeactivateDisplayOff(TRUE);
14474
14475     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14476
14477     if (tape.playing && tape.deactivate_display)
14478       TapeDeactivateDisplayOn();
14479   }
14480 }
14481
14482 void RequestQuitGame(boolean ask_if_really_quit)
14483 {
14484   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14485   boolean skip_request = AllPlayersGone || quick_quit;
14486
14487   RequestQuitGameExt(skip_request, quick_quit,
14488                      "Do you really want to quit the game?");
14489 }
14490
14491
14492 /* ------------------------------------------------------------------------- */
14493 /* random generator functions                                                */
14494 /* ------------------------------------------------------------------------- */
14495
14496 unsigned int InitEngineRandom_RND(int seed)
14497 {
14498   game.num_random_calls = 0;
14499
14500   return InitEngineRandom(seed);
14501 }
14502
14503 unsigned int RND(int max)
14504 {
14505   if (max > 0)
14506   {
14507     game.num_random_calls++;
14508
14509     return GetEngineRandom(max);
14510   }
14511
14512   return 0;
14513 }
14514
14515
14516 /* ------------------------------------------------------------------------- */
14517 /* game engine snapshot handling functions                                   */
14518 /* ------------------------------------------------------------------------- */
14519
14520 struct EngineSnapshotInfo
14521 {
14522   /* runtime values for custom element collect score */
14523   int collect_score[NUM_CUSTOM_ELEMENTS];
14524
14525   /* runtime values for group element choice position */
14526   int choice_pos[NUM_GROUP_ELEMENTS];
14527
14528   /* runtime values for belt position animations */
14529   int belt_graphic[4][NUM_BELT_PARTS];
14530   int belt_anim_mode[4][NUM_BELT_PARTS];
14531 };
14532
14533 static struct EngineSnapshotInfo engine_snapshot_rnd;
14534 static char *snapshot_level_identifier = NULL;
14535 static int snapshot_level_nr = -1;
14536
14537 static void SaveEngineSnapshotValues_RND()
14538 {
14539   static int belt_base_active_element[4] =
14540   {
14541     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14542     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14543     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14544     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14545   };
14546   int i, j;
14547
14548   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14549   {
14550     int element = EL_CUSTOM_START + i;
14551
14552     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14553   }
14554
14555   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14556   {
14557     int element = EL_GROUP_START + i;
14558
14559     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14560   }
14561
14562   for (i = 0; i < 4; i++)
14563   {
14564     for (j = 0; j < NUM_BELT_PARTS; j++)
14565     {
14566       int element = belt_base_active_element[i] + j;
14567       int graphic = el2img(element);
14568       int anim_mode = graphic_info[graphic].anim_mode;
14569
14570       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14571       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14572     }
14573   }
14574 }
14575
14576 static void LoadEngineSnapshotValues_RND()
14577 {
14578   unsigned int num_random_calls = game.num_random_calls;
14579   int i, j;
14580
14581   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14582   {
14583     int element = EL_CUSTOM_START + i;
14584
14585     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14586   }
14587
14588   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14589   {
14590     int element = EL_GROUP_START + i;
14591
14592     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14593   }
14594
14595   for (i = 0; i < 4; i++)
14596   {
14597     for (j = 0; j < NUM_BELT_PARTS; j++)
14598     {
14599       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14600       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14601
14602       graphic_info[graphic].anim_mode = anim_mode;
14603     }
14604   }
14605
14606   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14607   {
14608     InitRND(tape.random_seed);
14609     for (i = 0; i < num_random_calls; i++)
14610       RND(1);
14611   }
14612
14613   if (game.num_random_calls != num_random_calls)
14614   {
14615     Error(ERR_INFO, "number of random calls out of sync");
14616     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14617     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14618     Error(ERR_EXIT, "this should not happen -- please debug");
14619   }
14620 }
14621
14622 void FreeEngineSnapshotSingle()
14623 {
14624   FreeSnapshotSingle();
14625
14626   setString(&snapshot_level_identifier, NULL);
14627   snapshot_level_nr = -1;
14628 }
14629
14630 void FreeEngineSnapshotList()
14631 {
14632   FreeSnapshotList();
14633 }
14634
14635 ListNode *SaveEngineSnapshotBuffers()
14636 {
14637   ListNode *buffers = NULL;
14638
14639   /* copy some special values to a structure better suited for the snapshot */
14640
14641   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14642     SaveEngineSnapshotValues_RND();
14643   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14644     SaveEngineSnapshotValues_EM();
14645   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14646     SaveEngineSnapshotValues_SP(&buffers);
14647
14648   /* save values stored in special snapshot structure */
14649
14650   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14651     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14652   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14653     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14654   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14655     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14656
14657   /* save further RND engine values */
14658
14659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14662
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14667
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14671   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14673
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14677
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14679
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14681
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14684
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14703
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14706
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14710
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14713
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14717   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14719
14720   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14721   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14722
14723 #if 0
14724   ListNode *node = engine_snapshot_list_rnd;
14725   int num_bytes = 0;
14726
14727   while (node != NULL)
14728   {
14729     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14730
14731     node = node->next;
14732   }
14733
14734   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14735 #endif
14736
14737   return buffers;
14738 }
14739
14740 void SaveEngineSnapshotSingle()
14741 {
14742   ListNode *buffers = SaveEngineSnapshotBuffers();
14743
14744   /* finally save all snapshot buffers to single snapshot */
14745   SaveSnapshotSingle(buffers);
14746
14747   /* save level identification information */
14748   setString(&snapshot_level_identifier, leveldir_current->identifier);
14749   snapshot_level_nr = level_nr;
14750 }
14751
14752 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14753 {
14754   boolean save_snapshot =
14755     (initial_snapshot ||
14756      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14757      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14758       game.snapshot.changed_action));
14759
14760   game.snapshot.changed_action = FALSE;
14761
14762   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14763       tape.quick_resume ||
14764       !save_snapshot)
14765     return FALSE;
14766
14767   ListNode *buffers = SaveEngineSnapshotBuffers();
14768
14769   /* finally save all snapshot buffers to snapshot list */
14770   SaveSnapshotToList(buffers);
14771
14772   return TRUE;
14773 }
14774
14775 boolean SaveEngineSnapshotToList()
14776 {
14777   return SaveEngineSnapshotToListExt(FALSE);
14778 }
14779
14780 void SaveEngineSnapshotToListInitial()
14781 {
14782   FreeEngineSnapshotList();
14783
14784   SaveEngineSnapshotToListExt(TRUE);
14785 }
14786
14787 void LoadEngineSnapshotValues()
14788 {
14789   /* restore special values from snapshot structure */
14790
14791   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14792     LoadEngineSnapshotValues_RND();
14793   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14794     LoadEngineSnapshotValues_EM();
14795   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14796     LoadEngineSnapshotValues_SP();
14797 }
14798
14799 void LoadEngineSnapshotSingle()
14800 {
14801   LoadSnapshotSingle();
14802
14803   LoadEngineSnapshotValues();
14804 }
14805
14806 void LoadEngineSnapshot_Undo(int steps)
14807 {
14808   LoadSnapshotFromList_Older(steps);
14809
14810   LoadEngineSnapshotValues();
14811 }
14812
14813 void LoadEngineSnapshot_Redo(int steps)
14814 {
14815   LoadSnapshotFromList_Newer(steps);
14816
14817   LoadEngineSnapshotValues();
14818 }
14819
14820 boolean CheckEngineSnapshotSingle()
14821 {
14822   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14823           snapshot_level_nr == level_nr);
14824 }
14825
14826 boolean CheckEngineSnapshotList()
14827 {
14828   return CheckSnapshotList();
14829 }
14830
14831
14832 /* ---------- new game button stuff ---------------------------------------- */
14833
14834 static struct
14835 {
14836   int graphic;
14837   struct XY *pos;
14838   int gadget_id;
14839   char *infotext;
14840 } gamebutton_info[NUM_GAME_BUTTONS] =
14841 {
14842   {
14843     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14844     GAME_CTRL_ID_STOP,                  "stop game"
14845   },
14846   {
14847     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14848     GAME_CTRL_ID_PAUSE,                 "pause game"
14849   },
14850   {
14851     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14852     GAME_CTRL_ID_PLAY,                  "play game"
14853   },
14854   {
14855     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14856     GAME_CTRL_ID_UNDO,                  "undo step"
14857   },
14858   {
14859     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14860     GAME_CTRL_ID_REDO,                  "redo step"
14861   },
14862   {
14863     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14864     GAME_CTRL_ID_SAVE,                  "save game"
14865   },
14866   {
14867     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14868     GAME_CTRL_ID_LOAD,                  "load game"
14869   },
14870   {
14871     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14872     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14873   },
14874   {
14875     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14876     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14877   },
14878   {
14879     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14880     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14881   }
14882 };
14883
14884 void CreateGameButtons()
14885 {
14886   int i;
14887
14888   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14889   {
14890     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14891     struct XY *pos = gamebutton_info[i].pos;
14892     struct GadgetInfo *gi;
14893     int button_type;
14894     boolean checked;
14895     unsigned int event_mask;
14896     int base_x = (tape.show_game_buttons ? VX : DX);
14897     int base_y = (tape.show_game_buttons ? VY : DY);
14898     int gd_x   = gfx->src_x;
14899     int gd_y   = gfx->src_y;
14900     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14901     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14902     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14903     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14904     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14905     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14906     int id = i;
14907
14908     if (gfx->bitmap == NULL)
14909     {
14910       game_gadget[id] = NULL;
14911
14912       continue;
14913     }
14914
14915     if (id == GAME_CTRL_ID_STOP ||
14916         id == GAME_CTRL_ID_PAUSE ||
14917         id == GAME_CTRL_ID_PLAY ||
14918         id == GAME_CTRL_ID_SAVE ||
14919         id == GAME_CTRL_ID_LOAD)
14920     {
14921       button_type = GD_TYPE_NORMAL_BUTTON;
14922       checked = FALSE;
14923       event_mask = GD_EVENT_RELEASED;
14924     }
14925     else if (id == GAME_CTRL_ID_UNDO ||
14926              id == GAME_CTRL_ID_REDO)
14927     {
14928       button_type = GD_TYPE_NORMAL_BUTTON;
14929       checked = FALSE;
14930       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14931     }
14932     else
14933     {
14934       button_type = GD_TYPE_CHECK_BUTTON;
14935       checked =
14936         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14937          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14938          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14939       event_mask = GD_EVENT_PRESSED;
14940     }
14941
14942     gi = CreateGadget(GDI_CUSTOM_ID, id,
14943                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14944                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14945                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14946                       GDI_WIDTH, gfx->width,
14947                       GDI_HEIGHT, gfx->height,
14948                       GDI_TYPE, button_type,
14949                       GDI_STATE, GD_BUTTON_UNPRESSED,
14950                       GDI_CHECKED, checked,
14951                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14952                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14953                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14954                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14955                       GDI_DIRECT_DRAW, FALSE,
14956                       GDI_EVENT_MASK, event_mask,
14957                       GDI_CALLBACK_ACTION, HandleGameButtons,
14958                       GDI_END);
14959
14960     if (gi == NULL)
14961       Error(ERR_EXIT, "cannot create gadget");
14962
14963     game_gadget[id] = gi;
14964   }
14965 }
14966
14967 void FreeGameButtons()
14968 {
14969   int i;
14970
14971   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14972     FreeGadget(game_gadget[i]);
14973 }
14974
14975 static void MapGameButtonsAtSamePosition(int id)
14976 {
14977   int i;
14978
14979   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14980     if (i != id &&
14981         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14982         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14983       MapGadget(game_gadget[i]);
14984 }
14985
14986 static void UnmapGameButtonsAtSamePosition(int id)
14987 {
14988   int i;
14989
14990   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14991     if (i != id &&
14992         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14993         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14994       UnmapGadget(game_gadget[i]);
14995 }
14996
14997 void MapUndoRedoButtons()
14998 {
14999   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15000   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15001
15002   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15003   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15004 }
15005
15006 void UnmapUndoRedoButtons()
15007 {
15008   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15009   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15010
15011   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15012   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15013 }
15014
15015 void MapGameButtons()
15016 {
15017   int i;
15018
15019   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15020     if (i != GAME_CTRL_ID_UNDO &&
15021         i != GAME_CTRL_ID_REDO)
15022       MapGadget(game_gadget[i]);
15023 }
15024
15025 void UnmapGameButtons()
15026 {
15027   int i;
15028
15029   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15030     UnmapGadget(game_gadget[i]);
15031 }
15032
15033 void RedrawGameButtons()
15034 {
15035   int i;
15036
15037   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15038     RedrawGadget(game_gadget[i]);
15039
15040   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15041   redraw_mask &= ~REDRAW_ALL;
15042 }
15043
15044 void GameUndoRedoExt()
15045 {
15046   ClearPlayerAction();
15047
15048   tape.pausing = TRUE;
15049
15050   RedrawPlayfield();
15051   UpdateAndDisplayGameControlValues();
15052
15053   DrawCompleteVideoDisplay();
15054   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15055   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15056   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15057                     VIDEO_STATE_1STEP_OFF), 0);
15058
15059   BackToFront();
15060 }
15061
15062 void GameUndo(int steps)
15063 {
15064   if (!CheckEngineSnapshotList())
15065     return;
15066
15067   LoadEngineSnapshot_Undo(steps);
15068
15069   GameUndoRedoExt();
15070 }
15071
15072 void GameRedo(int steps)
15073 {
15074   if (!CheckEngineSnapshotList())
15075     return;
15076
15077   LoadEngineSnapshot_Redo(steps);
15078
15079   GameUndoRedoExt();
15080 }
15081
15082 static void HandleGameButtonsExt(int id, int button)
15083 {
15084   int steps = BUTTON_STEPSIZE(button);
15085   boolean handle_game_buttons =
15086     (game_status == GAME_MODE_PLAYING ||
15087      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15088
15089   if (!handle_game_buttons)
15090     return;
15091
15092   switch (id)
15093   {
15094     case GAME_CTRL_ID_STOP:
15095       if (game_status == GAME_MODE_MAIN)
15096         break;
15097
15098       if (tape.playing)
15099         TapeStop();
15100       else
15101         RequestQuitGame(TRUE);
15102
15103       break;
15104
15105     case GAME_CTRL_ID_PAUSE:
15106       if (options.network && game_status == GAME_MODE_PLAYING)
15107       {
15108 #if defined(NETWORK_AVALIABLE)
15109         if (tape.pausing)
15110           SendToServer_ContinuePlaying();
15111         else
15112           SendToServer_PausePlaying();
15113 #endif
15114       }
15115       else
15116         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15117       break;
15118
15119     case GAME_CTRL_ID_PLAY:
15120       if (game_status == GAME_MODE_MAIN)
15121       {
15122         StartGameActions(options.network, setup.autorecord, level.random_seed);
15123       }
15124       else if (tape.pausing)
15125       {
15126 #if defined(NETWORK_AVALIABLE)
15127         if (options.network)
15128           SendToServer_ContinuePlaying();
15129         else
15130 #endif
15131         {
15132           tape.pausing = FALSE;
15133           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15134         }
15135       }
15136       break;
15137
15138     case GAME_CTRL_ID_UNDO:
15139       GameUndo(steps);
15140       break;
15141
15142     case GAME_CTRL_ID_REDO:
15143       GameRedo(steps);
15144       break;
15145
15146     case GAME_CTRL_ID_SAVE:
15147       TapeQuickSave();
15148       break;
15149
15150     case GAME_CTRL_ID_LOAD:
15151       TapeQuickLoad();
15152       break;
15153
15154     case SOUND_CTRL_ID_MUSIC:
15155       if (setup.sound_music)
15156       { 
15157         setup.sound_music = FALSE;
15158
15159         FadeMusic();
15160       }
15161       else if (audio.music_available)
15162       { 
15163         setup.sound = setup.sound_music = TRUE;
15164
15165         SetAudioMode(setup.sound);
15166
15167         PlayLevelMusic();
15168       }
15169       break;
15170
15171     case SOUND_CTRL_ID_LOOPS:
15172       if (setup.sound_loops)
15173         setup.sound_loops = FALSE;
15174       else if (audio.loops_available)
15175       {
15176         setup.sound = setup.sound_loops = TRUE;
15177
15178         SetAudioMode(setup.sound);
15179       }
15180       break;
15181
15182     case SOUND_CTRL_ID_SIMPLE:
15183       if (setup.sound_simple)
15184         setup.sound_simple = FALSE;
15185       else if (audio.sound_available)
15186       {
15187         setup.sound = setup.sound_simple = TRUE;
15188
15189         SetAudioMode(setup.sound);
15190       }
15191       break;
15192
15193     default:
15194       break;
15195   }
15196 }
15197
15198 static void HandleGameButtons(struct GadgetInfo *gi)
15199 {
15200   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15201 }
15202
15203 void HandleSoundButtonKeys(Key key)
15204 {
15205
15206   if (key == setup.shortcut.sound_simple)
15207     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15208   else if (key == setup.shortcut.sound_loops)
15209     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15210   else if (key == setup.shortcut.sound_music)
15211     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15212 }