added configurability of different window size for title screens
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int fade_mask = REDRAW_FIELD;
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   // required here to update video display before fading (FIX THIS)
3095   DrawMaskedBorder(REDRAW_DOOR_2);
3096
3097   game_status = GAME_MODE_PLAYING;
3098
3099   if (!game.restart_level)
3100     CloseDoor(DOOR_CLOSE_1);
3101
3102   /* needed if different viewport properties defined for playing */
3103   ChangeViewportPropertiesIfNeeded();
3104
3105   if (level_editor_test_game)
3106     FadeSkipNextFadeIn();
3107   else
3108     FadeSetEnterScreen();
3109
3110   if (CheckIfGlobalBorderHasChanged())
3111     fade_mask = REDRAW_ALL;
3112
3113   FadeOut(fade_mask);
3114
3115   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3116
3117   ClearField();
3118
3119   DrawCompleteVideoDisplay();
3120
3121   InitGameEngine();
3122   InitGameControlValues();
3123
3124   /* don't play tapes over network */
3125   network_playing = (options.network && !tape.playing);
3126
3127   for (i = 0; i < MAX_PLAYERS; i++)
3128   {
3129     struct PlayerInfo *player = &stored_player[i];
3130
3131     player->index_nr = i;
3132     player->index_bit = (1 << i);
3133     player->element_nr = EL_PLAYER_1 + i;
3134
3135     player->present = FALSE;
3136     player->active = FALSE;
3137     player->mapped = FALSE;
3138
3139     player->killed = FALSE;
3140     player->reanimated = FALSE;
3141
3142     player->action = 0;
3143     player->effective_action = 0;
3144     player->programmed_action = 0;
3145
3146     player->score = 0;
3147     player->score_final = 0;
3148
3149     player->gems_still_needed = level.gems_needed;
3150     player->sokobanfields_still_needed = 0;
3151     player->lights_still_needed = 0;
3152     player->friends_still_needed = 0;
3153
3154     for (j = 0; j < MAX_NUM_KEYS; j++)
3155       player->key[j] = FALSE;
3156
3157     player->num_white_keys = 0;
3158
3159     player->dynabomb_count = 0;
3160     player->dynabomb_size = 1;
3161     player->dynabombs_left = 0;
3162     player->dynabomb_xl = FALSE;
3163
3164     player->MovDir = initial_move_dir;
3165     player->MovPos = 0;
3166     player->GfxPos = 0;
3167     player->GfxDir = initial_move_dir;
3168     player->GfxAction = ACTION_DEFAULT;
3169     player->Frame = 0;
3170     player->StepFrame = 0;
3171
3172     player->initial_element = player->element_nr;
3173     player->artwork_element =
3174       (level.use_artwork_element[i] ? level.artwork_element[i] :
3175        player->element_nr);
3176     player->use_murphy = FALSE;
3177
3178     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3179     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3180
3181     player->gravity = level.initial_player_gravity[i];
3182
3183     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3184
3185     player->actual_frame_counter = 0;
3186
3187     player->step_counter = 0;
3188
3189     player->last_move_dir = initial_move_dir;
3190
3191     player->is_active = FALSE;
3192
3193     player->is_waiting = FALSE;
3194     player->is_moving = FALSE;
3195     player->is_auto_moving = FALSE;
3196     player->is_digging = FALSE;
3197     player->is_snapping = FALSE;
3198     player->is_collecting = FALSE;
3199     player->is_pushing = FALSE;
3200     player->is_switching = FALSE;
3201     player->is_dropping = FALSE;
3202     player->is_dropping_pressed = FALSE;
3203
3204     player->is_bored = FALSE;
3205     player->is_sleeping = FALSE;
3206
3207     player->frame_counter_bored = -1;
3208     player->frame_counter_sleeping = -1;
3209
3210     player->anim_delay_counter = 0;
3211     player->post_delay_counter = 0;
3212
3213     player->dir_waiting = initial_move_dir;
3214     player->action_waiting = ACTION_DEFAULT;
3215     player->last_action_waiting = ACTION_DEFAULT;
3216     player->special_action_bored = ACTION_DEFAULT;
3217     player->special_action_sleeping = ACTION_DEFAULT;
3218
3219     player->switch_x = -1;
3220     player->switch_y = -1;
3221
3222     player->drop_x = -1;
3223     player->drop_y = -1;
3224
3225     player->show_envelope = 0;
3226
3227     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3228
3229     player->push_delay       = -1;      /* initialized when pushing starts */
3230     player->push_delay_value = game.initial_push_delay_value;
3231
3232     player->drop_delay = 0;
3233     player->drop_pressed_delay = 0;
3234
3235     player->last_jx = -1;
3236     player->last_jy = -1;
3237     player->jx = -1;
3238     player->jy = -1;
3239
3240     player->shield_normal_time_left = 0;
3241     player->shield_deadly_time_left = 0;
3242
3243     player->inventory_infinite_element = EL_UNDEFINED;
3244     player->inventory_size = 0;
3245
3246     if (level.use_initial_inventory[i])
3247     {
3248       for (j = 0; j < level.initial_inventory_size[i]; j++)
3249       {
3250         int element = level.initial_inventory_content[i][j];
3251         int collect_count = element_info[element].collect_count_initial;
3252         int k;
3253
3254         if (!IS_CUSTOM_ELEMENT(element))
3255           collect_count = 1;
3256
3257         if (collect_count == 0)
3258           player->inventory_infinite_element = element;
3259         else
3260           for (k = 0; k < collect_count; k++)
3261             if (player->inventory_size < MAX_INVENTORY_SIZE)
3262               player->inventory_element[player->inventory_size++] = element;
3263       }
3264     }
3265
3266     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3267     SnapField(player, 0, 0);
3268
3269     player->LevelSolved = FALSE;
3270     player->GameOver = FALSE;
3271
3272     player->LevelSolved_GameWon = FALSE;
3273     player->LevelSolved_GameEnd = FALSE;
3274     player->LevelSolved_PanelOff = FALSE;
3275     player->LevelSolved_SaveTape = FALSE;
3276     player->LevelSolved_SaveScore = FALSE;
3277     player->LevelSolved_CountingTime = 0;
3278     player->LevelSolved_CountingScore = 0;
3279
3280     map_player_action[i] = i;
3281   }
3282
3283   network_player_action_received = FALSE;
3284
3285 #if defined(NETWORK_AVALIABLE)
3286   /* initial null action */
3287   if (network_playing)
3288     SendToServer_MovePlayer(MV_NONE);
3289 #endif
3290
3291   ZX = ZY = -1;
3292   ExitX = ExitY = -1;
3293
3294   FrameCounter = 0;
3295   TimeFrames = 0;
3296   TimePlayed = 0;
3297   TimeLeft = level.time;
3298   TapeTime = 0;
3299
3300   ScreenMovDir = MV_NONE;
3301   ScreenMovPos = 0;
3302   ScreenGfxPos = 0;
3303
3304   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3305
3306   AllPlayersGone = FALSE;
3307
3308   game.no_time_limit = (level.time == 0);
3309
3310   game.yamyam_content_nr = 0;
3311   game.robot_wheel_active = FALSE;
3312   game.magic_wall_active = FALSE;
3313   game.magic_wall_time_left = 0;
3314   game.light_time_left = 0;
3315   game.timegate_time_left = 0;
3316   game.switchgate_pos = 0;
3317   game.wind_direction = level.wind_direction_initial;
3318
3319   game.lenses_time_left = 0;
3320   game.magnify_time_left = 0;
3321
3322   game.ball_state = level.ball_state_initial;
3323   game.ball_content_nr = 0;
3324
3325   game.envelope_active = FALSE;
3326
3327   /* set focus to local player for network games, else to all players */
3328   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3329   game.centered_player_nr_next = game.centered_player_nr;
3330   game.set_centered_player = FALSE;
3331
3332   if (network_playing && tape.recording)
3333   {
3334     /* store client dependent player focus when recording network games */
3335     tape.centered_player_nr_next = game.centered_player_nr_next;
3336     tape.set_centered_player = TRUE;
3337   }
3338
3339   for (i = 0; i < NUM_BELTS; i++)
3340   {
3341     game.belt_dir[i] = MV_NONE;
3342     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3343   }
3344
3345   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3346     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3347
3348 #if DEBUG_INIT_PLAYER
3349   if (options.debug)
3350   {
3351     printf("Player status at level initialization:\n");
3352   }
3353 #endif
3354
3355   SCAN_PLAYFIELD(x, y)
3356   {
3357     Feld[x][y] = level.field[x][y];
3358     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3359     ChangeDelay[x][y] = 0;
3360     ChangePage[x][y] = -1;
3361     CustomValue[x][y] = 0;              /* initialized in InitField() */
3362     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3363     AmoebaNr[x][y] = 0;
3364     WasJustMoving[x][y] = 0;
3365     WasJustFalling[x][y] = 0;
3366     CheckCollision[x][y] = 0;
3367     CheckImpact[x][y] = 0;
3368     Stop[x][y] = FALSE;
3369     Pushed[x][y] = FALSE;
3370
3371     ChangeCount[x][y] = 0;
3372     ChangeEvent[x][y] = -1;
3373
3374     ExplodePhase[x][y] = 0;
3375     ExplodeDelay[x][y] = 0;
3376     ExplodeField[x][y] = EX_TYPE_NONE;
3377
3378     RunnerVisit[x][y] = 0;
3379     PlayerVisit[x][y] = 0;
3380
3381     GfxFrame[x][y] = 0;
3382     GfxRandom[x][y] = INIT_GFX_RANDOM();
3383     GfxElement[x][y] = EL_UNDEFINED;
3384     GfxAction[x][y] = ACTION_DEFAULT;
3385     GfxDir[x][y] = MV_NONE;
3386     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3387   }
3388
3389   SCAN_PLAYFIELD(x, y)
3390   {
3391     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3392       emulate_bd = FALSE;
3393     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3394       emulate_sb = FALSE;
3395     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3396       emulate_sp = FALSE;
3397
3398     InitField(x, y, TRUE);
3399
3400     ResetGfxAnimation(x, y);
3401   }
3402
3403   InitBeltMovement();
3404
3405   for (i = 0; i < MAX_PLAYERS; i++)
3406   {
3407     struct PlayerInfo *player = &stored_player[i];
3408
3409     /* set number of special actions for bored and sleeping animation */
3410     player->num_special_action_bored =
3411       get_num_special_action(player->artwork_element,
3412                              ACTION_BORING_1, ACTION_BORING_LAST);
3413     player->num_special_action_sleeping =
3414       get_num_special_action(player->artwork_element,
3415                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3416   }
3417
3418   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3419                     emulate_sb ? EMU_SOKOBAN :
3420                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3421
3422   /* initialize type of slippery elements */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (!IS_CUSTOM_ELEMENT(i))
3426     {
3427       /* default: elements slip down either to the left or right randomly */
3428       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3429
3430       /* SP style elements prefer to slip down on the left side */
3431       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3432         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3433
3434       /* BD style elements prefer to slip down on the left side */
3435       if (game.emulation == EMU_BOULDERDASH)
3436         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3437     }
3438   }
3439
3440   /* initialize explosion and ignition delay */
3441   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442   {
3443     if (!IS_CUSTOM_ELEMENT(i))
3444     {
3445       int num_phase = 8;
3446       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3447                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3448                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3449       int last_phase = (num_phase + 1) * delay;
3450       int half_phase = (num_phase / 2) * delay;
3451
3452       element_info[i].explosion_delay = last_phase - 1;
3453       element_info[i].ignition_delay = half_phase;
3454
3455       if (i == EL_BLACK_ORB)
3456         element_info[i].ignition_delay = 1;
3457     }
3458   }
3459
3460   /* correct non-moving belts to start moving left */
3461   for (i = 0; i < NUM_BELTS; i++)
3462     if (game.belt_dir[i] == MV_NONE)
3463       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3464
3465 #if USE_NEW_PLAYER_ASSIGNMENTS
3466   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3467   /* choose default local player */
3468   local_player = &stored_player[0];
3469
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     stored_player[i].connected = FALSE;
3472
3473   local_player->connected = TRUE;
3474   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3475
3476   if (tape.playing)
3477   {
3478     for (i = 0; i < MAX_PLAYERS; i++)
3479       stored_player[i].connected = tape.player_participates[i];
3480   }
3481   else if (game.team_mode && !options.network)
3482   {
3483     /* try to guess locally connected team mode players (needed for correct
3484        assignment of player figures from level to locally playing players) */
3485
3486     for (i = 0; i < MAX_PLAYERS; i++)
3487       if (setup.input[i].use_joystick ||
3488           setup.input[i].key.left != KSYM_UNDEFINED)
3489         stored_player[i].connected = TRUE;
3490   }
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494   {
3495     printf("Player status after level initialization:\n");
3496
3497     for (i = 0; i < MAX_PLAYERS; i++)
3498     {
3499       struct PlayerInfo *player = &stored_player[i];
3500
3501       printf("- player %d: present == %d, connected == %d, active == %d",
3502              i + 1,
3503              player->present,
3504              player->connected,
3505              player->active);
3506
3507       if (local_player == player)
3508         printf(" (local player)");
3509
3510       printf("\n");
3511     }
3512   }
3513 #endif
3514
3515 #if DEBUG_INIT_PLAYER
3516   if (options.debug)
3517     printf("Reassigning players ...\n");
3518 #endif
3519
3520   /* check if any connected player was not found in playfield */
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     if (player->connected && !player->present)
3526     {
3527       struct PlayerInfo *field_player = NULL;
3528
3529 #if DEBUG_INIT_PLAYER
3530       if (options.debug)
3531         printf("- looking for field player for player %d ...\n", i + 1);
3532 #endif
3533
3534       /* assign first free player found that is present in the playfield */
3535
3536       /* first try: look for unmapped playfield player that is not connected */
3537       for (j = 0; j < MAX_PLAYERS; j++)
3538         if (field_player == NULL &&
3539             stored_player[j].present &&
3540             !stored_player[j].mapped &&
3541             !stored_player[j].connected)
3542           field_player = &stored_player[j];
3543
3544       /* second try: look for *any* unmapped playfield player */
3545       for (j = 0; j < MAX_PLAYERS; j++)
3546         if (field_player == NULL &&
3547             stored_player[j].present &&
3548             !stored_player[j].mapped)
3549           field_player = &stored_player[j];
3550
3551       if (field_player != NULL)
3552       {
3553         int jx = field_player->jx, jy = field_player->jy;
3554
3555 #if DEBUG_INIT_PLAYER
3556         if (options.debug)
3557           printf("- found player %d\n", field_player->index_nr + 1);
3558 #endif
3559
3560         player->present = FALSE;
3561         player->active = FALSE;
3562
3563         field_player->present = TRUE;
3564         field_player->active = TRUE;
3565
3566         /*
3567         player->initial_element = field_player->initial_element;
3568         player->artwork_element = field_player->artwork_element;
3569
3570         player->block_last_field       = field_player->block_last_field;
3571         player->block_delay_adjustment = field_player->block_delay_adjustment;
3572         */
3573
3574         StorePlayer[jx][jy] = field_player->element_nr;
3575
3576         field_player->jx = field_player->last_jx = jx;
3577         field_player->jy = field_player->last_jy = jy;
3578
3579         if (local_player == player)
3580           local_player = field_player;
3581
3582         map_player_action[field_player->index_nr] = i;
3583
3584         field_player->mapped = TRUE;
3585
3586 #if DEBUG_INIT_PLAYER
3587         if (options.debug)
3588           printf("- map_player_action[%d] == %d\n",
3589                  field_player->index_nr + 1, i + 1);
3590 #endif
3591       }
3592     }
3593
3594     if (player->connected && player->present)
3595       player->mapped = TRUE;
3596   }
3597
3598 #if DEBUG_INIT_PLAYER
3599   if (options.debug)
3600   {
3601     printf("Player status after player assignment (first stage):\n");
3602
3603     for (i = 0; i < MAX_PLAYERS; i++)
3604     {
3605       struct PlayerInfo *player = &stored_player[i];
3606
3607       printf("- player %d: present == %d, connected == %d, active == %d",
3608              i + 1,
3609              player->present,
3610              player->connected,
3611              player->active);
3612
3613       if (local_player == player)
3614         printf(" (local player)");
3615
3616       printf("\n");
3617     }
3618   }
3619 #endif
3620
3621 #else
3622
3623   /* check if any connected player was not found in playfield */
3624   for (i = 0; i < MAX_PLAYERS; i++)
3625   {
3626     struct PlayerInfo *player = &stored_player[i];
3627
3628     if (player->connected && !player->present)
3629     {
3630       for (j = 0; j < MAX_PLAYERS; j++)
3631       {
3632         struct PlayerInfo *field_player = &stored_player[j];
3633         int jx = field_player->jx, jy = field_player->jy;
3634
3635         /* assign first free player found that is present in the playfield */
3636         if (field_player->present && !field_player->connected)
3637         {
3638           player->present = TRUE;
3639           player->active = TRUE;
3640
3641           field_player->present = FALSE;
3642           field_player->active = FALSE;
3643
3644           player->initial_element = field_player->initial_element;
3645           player->artwork_element = field_player->artwork_element;
3646
3647           player->block_last_field       = field_player->block_last_field;
3648           player->block_delay_adjustment = field_player->block_delay_adjustment;
3649
3650           StorePlayer[jx][jy] = player->element_nr;
3651
3652           player->jx = player->last_jx = jx;
3653           player->jy = player->last_jy = jy;
3654
3655           break;
3656         }
3657       }
3658     }
3659   }
3660 #endif
3661
3662 #if 0
3663   printf("::: local_player->present == %d\n", local_player->present);
3664 #endif
3665
3666   if (tape.playing)
3667   {
3668     /* when playing a tape, eliminate all players who do not participate */
3669
3670 #if USE_NEW_PLAYER_ASSIGNMENTS
3671
3672     if (!game.team_mode)
3673     {
3674       for (i = 0; i < MAX_PLAYERS; i++)
3675       {
3676         if (stored_player[i].active &&
3677             !tape.player_participates[map_player_action[i]])
3678         {
3679           struct PlayerInfo *player = &stored_player[i];
3680           int jx = player->jx, jy = player->jy;
3681
3682 #if DEBUG_INIT_PLAYER
3683           if (options.debug)
3684             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3685 #endif
3686
3687           player->active = FALSE;
3688           StorePlayer[jx][jy] = 0;
3689           Feld[jx][jy] = EL_EMPTY;
3690         }
3691       }
3692     }
3693
3694 #else
3695
3696     for (i = 0; i < MAX_PLAYERS; i++)
3697     {
3698       if (stored_player[i].active &&
3699           !tape.player_participates[i])
3700       {
3701         struct PlayerInfo *player = &stored_player[i];
3702         int jx = player->jx, jy = player->jy;
3703
3704         player->active = FALSE;
3705         StorePlayer[jx][jy] = 0;
3706         Feld[jx][jy] = EL_EMPTY;
3707       }
3708     }
3709 #endif
3710   }
3711   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3712   {
3713     /* when in single player mode, eliminate all but the first active player */
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716     {
3717       if (stored_player[i].active)
3718       {
3719         for (j = i + 1; j < MAX_PLAYERS; j++)
3720         {
3721           if (stored_player[j].active)
3722           {
3723             struct PlayerInfo *player = &stored_player[j];
3724             int jx = player->jx, jy = player->jy;
3725
3726             player->active = FALSE;
3727             player->present = FALSE;
3728
3729             StorePlayer[jx][jy] = 0;
3730             Feld[jx][jy] = EL_EMPTY;
3731           }
3732         }
3733       }
3734     }
3735   }
3736
3737   /* when recording the game, store which players take part in the game */
3738   if (tape.recording)
3739   {
3740 #if USE_NEW_PLAYER_ASSIGNMENTS
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742       if (stored_player[i].connected)
3743         tape.player_participates[i] = TRUE;
3744 #else
3745     for (i = 0; i < MAX_PLAYERS; i++)
3746       if (stored_player[i].active)
3747         tape.player_participates[i] = TRUE;
3748 #endif
3749   }
3750
3751 #if DEBUG_INIT_PLAYER
3752   if (options.debug)
3753   {
3754     printf("Player status after player assignment (final stage):\n");
3755
3756     for (i = 0; i < MAX_PLAYERS; i++)
3757     {
3758       struct PlayerInfo *player = &stored_player[i];
3759
3760       printf("- player %d: present == %d, connected == %d, active == %d",
3761              i + 1,
3762              player->present,
3763              player->connected,
3764              player->active);
3765
3766       if (local_player == player)
3767         printf(" (local player)");
3768
3769       printf("\n");
3770     }
3771   }
3772 #endif
3773
3774   if (BorderElement == EL_EMPTY)
3775   {
3776     SBX_Left = 0;
3777     SBX_Right = lev_fieldx - SCR_FIELDX;
3778     SBY_Upper = 0;
3779     SBY_Lower = lev_fieldy - SCR_FIELDY;
3780   }
3781   else
3782   {
3783     SBX_Left = -1;
3784     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3785     SBY_Upper = -1;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3787   }
3788
3789   if (full_lev_fieldx <= SCR_FIELDX)
3790     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3791   if (full_lev_fieldy <= SCR_FIELDY)
3792     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3793
3794   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3795     SBX_Left--;
3796   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3797     SBY_Upper--;
3798
3799   /* if local player not found, look for custom element that might create
3800      the player (make some assumptions about the right custom element) */
3801   if (!local_player->present)
3802   {
3803     int start_x = 0, start_y = 0;
3804     int found_rating = 0;
3805     int found_element = EL_UNDEFINED;
3806     int player_nr = local_player->index_nr;
3807
3808     SCAN_PLAYFIELD(x, y)
3809     {
3810       int element = Feld[x][y];
3811       int content;
3812       int xx, yy;
3813       boolean is_player;
3814
3815       if (level.use_start_element[player_nr] &&
3816           level.start_element[player_nr] == element &&
3817           found_rating < 4)
3818       {
3819         start_x = x;
3820         start_y = y;
3821
3822         found_rating = 4;
3823         found_element = element;
3824       }
3825
3826       if (!IS_CUSTOM_ELEMENT(element))
3827         continue;
3828
3829       if (CAN_CHANGE(element))
3830       {
3831         for (i = 0; i < element_info[element].num_change_pages; i++)
3832         {
3833           /* check for player created from custom element as single target */
3834           content = element_info[element].change_page[i].target_element;
3835           is_player = ELEM_IS_PLAYER(content);
3836
3837           if (is_player && (found_rating < 3 ||
3838                             (found_rating == 3 && element < found_element)))
3839           {
3840             start_x = x;
3841             start_y = y;
3842
3843             found_rating = 3;
3844             found_element = element;
3845           }
3846         }
3847       }
3848
3849       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3850       {
3851         /* check for player created from custom element as explosion content */
3852         content = element_info[element].content.e[xx][yy];
3853         is_player = ELEM_IS_PLAYER(content);
3854
3855         if (is_player && (found_rating < 2 ||
3856                           (found_rating == 2 && element < found_element)))
3857         {
3858           start_x = x + xx - 1;
3859           start_y = y + yy - 1;
3860
3861           found_rating = 2;
3862           found_element = element;
3863         }
3864
3865         if (!CAN_CHANGE(element))
3866           continue;
3867
3868         for (i = 0; i < element_info[element].num_change_pages; i++)
3869         {
3870           /* check for player created from custom element as extended target */
3871           content =
3872             element_info[element].change_page[i].target_content.e[xx][yy];
3873
3874           is_player = ELEM_IS_PLAYER(content);
3875
3876           if (is_player && (found_rating < 1 ||
3877                             (found_rating == 1 && element < found_element)))
3878           {
3879             start_x = x + xx - 1;
3880             start_y = y + yy - 1;
3881
3882             found_rating = 1;
3883             found_element = element;
3884           }
3885         }
3886       }
3887     }
3888
3889     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3891                 start_x - MIDPOSX);
3892
3893     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 start_y - MIDPOSY);
3896   }
3897   else
3898   {
3899     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3901                 local_player->jx - MIDPOSX);
3902
3903     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 local_player->jy - MIDPOSY);
3906   }
3907
3908   /* !!! FIX THIS (START) !!! */
3909   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3910   {
3911     InitGameEngine_EM();
3912   }
3913   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3914   {
3915     InitGameEngine_SP();
3916   }
3917   else
3918   {
3919     DrawLevel(REDRAW_FIELD);
3920     DrawAllPlayers();
3921
3922     /* after drawing the level, correct some elements */
3923     if (game.timegate_time_left == 0)
3924       CloseAllOpenTimegates();
3925   }
3926
3927   /* blit playfield from scroll buffer to normal back buffer for fading in */
3928   BlitScreenToBitmap(backbuffer);
3929   /* !!! FIX THIS (END) !!! */
3930
3931   DrawMaskedBorder(fade_mask);
3932
3933   FadeIn(fade_mask);
3934
3935 #if 1
3936   // full screen redraw is required at this point in the following cases:
3937   // - special editor door undrawn when game was started from level editor
3938   // - drawing area (playfield) was changed and has to be removed completely
3939   redraw_mask = REDRAW_ALL;
3940   BackToFront();
3941 #endif
3942
3943   if (!game.restart_level)
3944   {
3945     /* copy default game door content to main double buffer */
3946
3947     /* !!! CHECK AGAIN !!! */
3948     SetPanelBackground();
3949     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3950     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3951   }
3952
3953   SetPanelBackground();
3954   SetDrawBackgroundMask(REDRAW_DOOR_1);
3955
3956   UpdateAndDisplayGameControlValues();
3957
3958   if (!game.restart_level)
3959   {
3960     UnmapGameButtons();
3961     UnmapTapeButtons();
3962
3963     FreeGameButtons();
3964     CreateGameButtons();
3965
3966     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3967     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3968     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3969
3970     MapGameButtons();
3971     MapTapeButtons();
3972
3973     /* copy actual game door content to door double buffer for OpenDoor() */
3974     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3975
3976     OpenDoor(DOOR_OPEN_ALL);
3977
3978     PlaySound(SND_GAME_STARTING);
3979
3980     if (setup.sound_music)
3981       PlayLevelMusic();
3982
3983     KeyboardAutoRepeatOffUnlessAutoplay();
3984
3985 #if DEBUG_INIT_PLAYER
3986     if (options.debug)
3987     {
3988       printf("Player status (final):\n");
3989
3990       for (i = 0; i < MAX_PLAYERS; i++)
3991       {
3992         struct PlayerInfo *player = &stored_player[i];
3993
3994         printf("- player %d: present == %d, connected == %d, active == %d",
3995                i + 1,
3996                player->present,
3997                player->connected,
3998                player->active);
3999
4000         if (local_player == player)
4001           printf(" (local player)");
4002
4003         printf("\n");
4004       }
4005     }
4006 #endif
4007   }
4008
4009   UnmapAllGadgets();
4010
4011   MapGameButtons();
4012   MapTapeButtons();
4013
4014   if (!game.restart_level && !tape.playing)
4015   {
4016     LevelStats_incPlayed(level_nr);
4017
4018     SaveLevelSetup_SeriesInfo();
4019   }
4020
4021   game.restart_level = FALSE;
4022
4023   SaveEngineSnapshotToListInitial();
4024 }
4025
4026 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4027 {
4028   /* this is used for non-R'n'D game engines to update certain engine values */
4029
4030   /* needed to determine if sounds are played within the visible screen area */
4031   scroll_x = actual_scroll_x;
4032   scroll_y = actual_scroll_y;
4033 }
4034
4035 void InitMovDir(int x, int y)
4036 {
4037   int i, element = Feld[x][y];
4038   static int xy[4][2] =
4039   {
4040     {  0, +1 },
4041     { +1,  0 },
4042     {  0, -1 },
4043     { -1,  0 }
4044   };
4045   static int direction[3][4] =
4046   {
4047     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4048     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4049     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4050   };
4051
4052   switch (element)
4053   {
4054     case EL_BUG_RIGHT:
4055     case EL_BUG_UP:
4056     case EL_BUG_LEFT:
4057     case EL_BUG_DOWN:
4058       Feld[x][y] = EL_BUG;
4059       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4060       break;
4061
4062     case EL_SPACESHIP_RIGHT:
4063     case EL_SPACESHIP_UP:
4064     case EL_SPACESHIP_LEFT:
4065     case EL_SPACESHIP_DOWN:
4066       Feld[x][y] = EL_SPACESHIP;
4067       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4068       break;
4069
4070     case EL_BD_BUTTERFLY_RIGHT:
4071     case EL_BD_BUTTERFLY_UP:
4072     case EL_BD_BUTTERFLY_LEFT:
4073     case EL_BD_BUTTERFLY_DOWN:
4074       Feld[x][y] = EL_BD_BUTTERFLY;
4075       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4076       break;
4077
4078     case EL_BD_FIREFLY_RIGHT:
4079     case EL_BD_FIREFLY_UP:
4080     case EL_BD_FIREFLY_LEFT:
4081     case EL_BD_FIREFLY_DOWN:
4082       Feld[x][y] = EL_BD_FIREFLY;
4083       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4084       break;
4085
4086     case EL_PACMAN_RIGHT:
4087     case EL_PACMAN_UP:
4088     case EL_PACMAN_LEFT:
4089     case EL_PACMAN_DOWN:
4090       Feld[x][y] = EL_PACMAN;
4091       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4092       break;
4093
4094     case EL_YAMYAM_LEFT:
4095     case EL_YAMYAM_RIGHT:
4096     case EL_YAMYAM_UP:
4097     case EL_YAMYAM_DOWN:
4098       Feld[x][y] = EL_YAMYAM;
4099       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4100       break;
4101
4102     case EL_SP_SNIKSNAK:
4103       MovDir[x][y] = MV_UP;
4104       break;
4105
4106     case EL_SP_ELECTRON:
4107       MovDir[x][y] = MV_LEFT;
4108       break;
4109
4110     case EL_MOLE_LEFT:
4111     case EL_MOLE_RIGHT:
4112     case EL_MOLE_UP:
4113     case EL_MOLE_DOWN:
4114       Feld[x][y] = EL_MOLE;
4115       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4116       break;
4117
4118     default:
4119       if (IS_CUSTOM_ELEMENT(element))
4120       {
4121         struct ElementInfo *ei = &element_info[element];
4122         int move_direction_initial = ei->move_direction_initial;
4123         int move_pattern = ei->move_pattern;
4124
4125         if (move_direction_initial == MV_START_PREVIOUS)
4126         {
4127           if (MovDir[x][y] != MV_NONE)
4128             return;
4129
4130           move_direction_initial = MV_START_AUTOMATIC;
4131         }
4132
4133         if (move_direction_initial == MV_START_RANDOM)
4134           MovDir[x][y] = 1 << RND(4);
4135         else if (move_direction_initial & MV_ANY_DIRECTION)
4136           MovDir[x][y] = move_direction_initial;
4137         else if (move_pattern == MV_ALL_DIRECTIONS ||
4138                  move_pattern == MV_TURNING_LEFT ||
4139                  move_pattern == MV_TURNING_RIGHT ||
4140                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4141                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4142                  move_pattern == MV_TURNING_RANDOM)
4143           MovDir[x][y] = 1 << RND(4);
4144         else if (move_pattern == MV_HORIZONTAL)
4145           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4146         else if (move_pattern == MV_VERTICAL)
4147           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4148         else if (move_pattern & MV_ANY_DIRECTION)
4149           MovDir[x][y] = element_info[element].move_pattern;
4150         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4151                  move_pattern == MV_ALONG_RIGHT_SIDE)
4152         {
4153           /* use random direction as default start direction */
4154           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4155             MovDir[x][y] = 1 << RND(4);
4156
4157           for (i = 0; i < NUM_DIRECTIONS; i++)
4158           {
4159             int x1 = x + xy[i][0];
4160             int y1 = y + xy[i][1];
4161
4162             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4163             {
4164               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4165                 MovDir[x][y] = direction[0][i];
4166               else
4167                 MovDir[x][y] = direction[1][i];
4168
4169               break;
4170             }
4171           }
4172         }                
4173       }
4174       else
4175       {
4176         MovDir[x][y] = 1 << RND(4);
4177
4178         if (element != EL_BUG &&
4179             element != EL_SPACESHIP &&
4180             element != EL_BD_BUTTERFLY &&
4181             element != EL_BD_FIREFLY)
4182           break;
4183
4184         for (i = 0; i < NUM_DIRECTIONS; i++)
4185         {
4186           int x1 = x + xy[i][0];
4187           int y1 = y + xy[i][1];
4188
4189           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4190           {
4191             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4192             {
4193               MovDir[x][y] = direction[0][i];
4194               break;
4195             }
4196             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4197                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4198             {
4199               MovDir[x][y] = direction[1][i];
4200               break;
4201             }
4202           }
4203         }
4204       }
4205       break;
4206   }
4207
4208   GfxDir[x][y] = MovDir[x][y];
4209 }
4210
4211 void InitAmoebaNr(int x, int y)
4212 {
4213   int i;
4214   int group_nr = AmoebeNachbarNr(x, y);
4215
4216   if (group_nr == 0)
4217   {
4218     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4219     {
4220       if (AmoebaCnt[i] == 0)
4221       {
4222         group_nr = i;
4223         break;
4224       }
4225     }
4226   }
4227
4228   AmoebaNr[x][y] = group_nr;
4229   AmoebaCnt[group_nr]++;
4230   AmoebaCnt2[group_nr]++;
4231 }
4232
4233 static void PlayerWins(struct PlayerInfo *player)
4234 {
4235   player->LevelSolved = TRUE;
4236   player->GameOver = TRUE;
4237
4238   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4239                          level.native_em_level->lev->score : player->score);
4240
4241   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4242                                       TimeLeft);
4243   player->LevelSolved_CountingScore = player->score_final;
4244 }
4245
4246 void GameWon()
4247 {
4248   static int time, time_final;
4249   static int score, score_final;
4250   static int game_over_delay_1 = 0;
4251   static int game_over_delay_2 = 0;
4252   int game_over_delay_value_1 = 50;
4253   int game_over_delay_value_2 = 50;
4254
4255   if (!local_player->LevelSolved_GameWon)
4256   {
4257     int i;
4258
4259     /* do not start end game actions before the player stops moving (to exit) */
4260     if (local_player->MovPos)
4261       return;
4262
4263     local_player->LevelSolved_GameWon = TRUE;
4264     local_player->LevelSolved_SaveTape = tape.recording;
4265     local_player->LevelSolved_SaveScore = !tape.playing;
4266
4267     if (!tape.playing)
4268     {
4269       LevelStats_incSolved(level_nr);
4270
4271       SaveLevelSetup_SeriesInfo();
4272     }
4273
4274     if (tape.auto_play)         /* tape might already be stopped here */
4275       tape.auto_play_level_solved = TRUE;
4276
4277     TapeStop();
4278
4279     game_over_delay_1 = game_over_delay_value_1;
4280     game_over_delay_2 = game_over_delay_value_2;
4281
4282     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4283     score = score_final = local_player->score_final;
4284
4285     if (TimeLeft > 0)
4286     {
4287       time_final = 0;
4288       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4289     }
4290     else if (game.no_time_limit && TimePlayed < 999)
4291     {
4292       time_final = 999;
4293       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4294     }
4295
4296     local_player->score_final = score_final;
4297
4298     if (level_editor_test_game)
4299     {
4300       time = time_final;
4301       score = score_final;
4302
4303       local_player->LevelSolved_CountingTime = time;
4304       local_player->LevelSolved_CountingScore = score;
4305
4306       game_panel_controls[GAME_PANEL_TIME].value = time;
4307       game_panel_controls[GAME_PANEL_SCORE].value = score;
4308
4309       DisplayGameControlValues();
4310     }
4311
4312     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4313     {
4314       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4315       {
4316         /* close exit door after last player */
4317         if ((AllPlayersGone &&
4318              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4319               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4320               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4321             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4322             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4323         {
4324           int element = Feld[ExitX][ExitY];
4325
4326           Feld[ExitX][ExitY] =
4327             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4328              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4329              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4330              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4331              EL_EM_STEEL_EXIT_CLOSING);
4332
4333           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4334         }
4335
4336         /* player disappears */
4337         DrawLevelField(ExitX, ExitY);
4338       }
4339
4340       for (i = 0; i < MAX_PLAYERS; i++)
4341       {
4342         struct PlayerInfo *player = &stored_player[i];
4343
4344         if (player->present)
4345         {
4346           RemovePlayer(player);
4347
4348           /* player disappears */
4349           DrawLevelField(player->jx, player->jy);
4350         }
4351       }
4352     }
4353
4354     PlaySound(SND_GAME_WINNING);
4355   }
4356
4357   if (game_over_delay_1 > 0)
4358   {
4359     game_over_delay_1--;
4360
4361     return;
4362   }
4363
4364   if (time != time_final)
4365   {
4366     int time_to_go = ABS(time_final - time);
4367     int time_count_dir = (time < time_final ? +1 : -1);
4368     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4369
4370     time  += time_count_steps * time_count_dir;
4371     score += time_count_steps * level.score[SC_TIME_BONUS];
4372
4373     local_player->LevelSolved_CountingTime = time;
4374     local_player->LevelSolved_CountingScore = score;
4375
4376     game_panel_controls[GAME_PANEL_TIME].value = time;
4377     game_panel_controls[GAME_PANEL_SCORE].value = score;
4378
4379     DisplayGameControlValues();
4380
4381     if (time == time_final)
4382       StopSound(SND_GAME_LEVELTIME_BONUS);
4383     else if (setup.sound_loops)
4384       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4385     else
4386       PlaySound(SND_GAME_LEVELTIME_BONUS);
4387
4388     return;
4389   }
4390
4391   local_player->LevelSolved_PanelOff = TRUE;
4392
4393   if (game_over_delay_2 > 0)
4394   {
4395     game_over_delay_2--;
4396
4397     return;
4398   }
4399
4400   GameEnd();
4401 }
4402
4403 void GameEnd()
4404 {
4405   int hi_pos;
4406   boolean raise_level = FALSE;
4407
4408   local_player->LevelSolved_GameEnd = TRUE;
4409
4410   if (!global.use_envelope_request)
4411     CloseDoor(DOOR_CLOSE_1);
4412
4413   if (local_player->LevelSolved_SaveTape)
4414   {
4415     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4416   }
4417
4418   CloseDoor(DOOR_CLOSE_ALL);
4419
4420   if (level_editor_test_game)
4421   {
4422     game_status = GAME_MODE_MAIN;
4423
4424     DrawMainMenu();
4425
4426     return;
4427   }
4428
4429   if (!local_player->LevelSolved_SaveScore)
4430   {
4431     game_status = GAME_MODE_MAIN;
4432
4433     DrawMainMenu();
4434
4435     return;
4436   }
4437
4438   if (level_nr == leveldir_current->handicap_level)
4439   {
4440     leveldir_current->handicap_level++;
4441
4442     SaveLevelSetup_SeriesInfo();
4443   }
4444
4445   if (level_nr < leveldir_current->last_level)
4446     raise_level = TRUE;                 /* advance to next level */
4447
4448   if ((hi_pos = NewHiScore()) >= 0) 
4449   {
4450     game_status = GAME_MODE_SCORES;
4451
4452     DrawHallOfFame(hi_pos);
4453
4454     if (raise_level)
4455     {
4456       level_nr++;
4457       TapeErase();
4458     }
4459   }
4460   else
4461   {
4462     game_status = GAME_MODE_MAIN;
4463
4464     if (raise_level)
4465     {
4466       level_nr++;
4467       TapeErase();
4468     }
4469
4470     DrawMainMenu();
4471   }
4472 }
4473
4474 int NewHiScore()
4475 {
4476   int k, l;
4477   int position = -1;
4478
4479   LoadScore(level_nr);
4480
4481   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4482       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4483     return -1;
4484
4485   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4486   {
4487     if (local_player->score_final > highscore[k].Score)
4488     {
4489       /* player has made it to the hall of fame */
4490
4491       if (k < MAX_SCORE_ENTRIES - 1)
4492       {
4493         int m = MAX_SCORE_ENTRIES - 1;
4494
4495 #ifdef ONE_PER_NAME
4496         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4497           if (strEqual(setup.player_name, highscore[l].Name))
4498             m = l;
4499         if (m == k)     /* player's new highscore overwrites his old one */
4500           goto put_into_list;
4501 #endif
4502
4503         for (l = m; l > k; l--)
4504         {
4505           strcpy(highscore[l].Name, highscore[l - 1].Name);
4506           highscore[l].Score = highscore[l - 1].Score;
4507         }
4508       }
4509
4510 #ifdef ONE_PER_NAME
4511       put_into_list:
4512 #endif
4513       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4514       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4515       highscore[k].Score = local_player->score_final; 
4516       position = k;
4517       break;
4518     }
4519
4520 #ifdef ONE_PER_NAME
4521     else if (!strncmp(setup.player_name, highscore[k].Name,
4522                       MAX_PLAYER_NAME_LEN))
4523       break;    /* player already there with a higher score */
4524 #endif
4525
4526   }
4527
4528   if (position >= 0) 
4529     SaveScore(level_nr);
4530
4531   return position;
4532 }
4533
4534 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4535 {
4536   int element = Feld[x][y];
4537   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4538   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4539   int horiz_move = (dx != 0);
4540   int sign = (horiz_move ? dx : dy);
4541   int step = sign * element_info[element].move_stepsize;
4542
4543   /* special values for move stepsize for spring and things on conveyor belt */
4544   if (horiz_move)
4545   {
4546     if (CAN_FALL(element) &&
4547         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4548       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4549     else if (element == EL_SPRING)
4550       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4551   }
4552
4553   return step;
4554 }
4555
4556 inline static int getElementMoveStepsize(int x, int y)
4557 {
4558   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4559 }
4560
4561 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4562 {
4563   if (player->GfxAction != action || player->GfxDir != dir)
4564   {
4565     player->GfxAction = action;
4566     player->GfxDir = dir;
4567     player->Frame = 0;
4568     player->StepFrame = 0;
4569   }
4570 }
4571
4572 static void ResetGfxFrame(int x, int y, boolean redraw)
4573 {
4574   int element = Feld[x][y];
4575   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4576   int last_gfx_frame = GfxFrame[x][y];
4577
4578   if (graphic_info[graphic].anim_global_sync)
4579     GfxFrame[x][y] = FrameCounter;
4580   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4581     GfxFrame[x][y] = CustomValue[x][y];
4582   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4583     GfxFrame[x][y] = element_info[element].collect_score;
4584   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4585     GfxFrame[x][y] = ChangeDelay[x][y];
4586
4587   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4588     DrawLevelGraphicAnimation(x, y, graphic);
4589 }
4590
4591 static void ResetGfxAnimation(int x, int y)
4592 {
4593   GfxAction[x][y] = ACTION_DEFAULT;
4594   GfxDir[x][y] = MovDir[x][y];
4595   GfxFrame[x][y] = 0;
4596
4597   ResetGfxFrame(x, y, FALSE);
4598 }
4599
4600 static void ResetRandomAnimationValue(int x, int y)
4601 {
4602   GfxRandom[x][y] = INIT_GFX_RANDOM();
4603 }
4604
4605 void InitMovingField(int x, int y, int direction)
4606 {
4607   int element = Feld[x][y];
4608   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4609   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4610   int newx = x + dx;
4611   int newy = y + dy;
4612   boolean is_moving_before, is_moving_after;
4613
4614   /* check if element was/is moving or being moved before/after mode change */
4615   is_moving_before = (WasJustMoving[x][y] != 0);
4616   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4617
4618   /* reset animation only for moving elements which change direction of moving
4619      or which just started or stopped moving
4620      (else CEs with property "can move" / "not moving" are reset each frame) */
4621   if (is_moving_before != is_moving_after ||
4622       direction != MovDir[x][y])
4623     ResetGfxAnimation(x, y);
4624
4625   MovDir[x][y] = direction;
4626   GfxDir[x][y] = direction;
4627
4628   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4629                      direction == MV_DOWN && CAN_FALL(element) ?
4630                      ACTION_FALLING : ACTION_MOVING);
4631
4632   /* this is needed for CEs with property "can move" / "not moving" */
4633
4634   if (is_moving_after)
4635   {
4636     if (Feld[newx][newy] == EL_EMPTY)
4637       Feld[newx][newy] = EL_BLOCKED;
4638
4639     MovDir[newx][newy] = MovDir[x][y];
4640
4641     CustomValue[newx][newy] = CustomValue[x][y];
4642
4643     GfxFrame[newx][newy] = GfxFrame[x][y];
4644     GfxRandom[newx][newy] = GfxRandom[x][y];
4645     GfxAction[newx][newy] = GfxAction[x][y];
4646     GfxDir[newx][newy] = GfxDir[x][y];
4647   }
4648 }
4649
4650 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4651 {
4652   int direction = MovDir[x][y];
4653   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4654   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4655
4656   *goes_to_x = newx;
4657   *goes_to_y = newy;
4658 }
4659
4660 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4661 {
4662   int oldx = x, oldy = y;
4663   int direction = MovDir[x][y];
4664
4665   if (direction == MV_LEFT)
4666     oldx++;
4667   else if (direction == MV_RIGHT)
4668     oldx--;
4669   else if (direction == MV_UP)
4670     oldy++;
4671   else if (direction == MV_DOWN)
4672     oldy--;
4673
4674   *comes_from_x = oldx;
4675   *comes_from_y = oldy;
4676 }
4677
4678 int MovingOrBlocked2Element(int x, int y)
4679 {
4680   int element = Feld[x][y];
4681
4682   if (element == EL_BLOCKED)
4683   {
4684     int oldx, oldy;
4685
4686     Blocked2Moving(x, y, &oldx, &oldy);
4687     return Feld[oldx][oldy];
4688   }
4689   else
4690     return element;
4691 }
4692
4693 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4694 {
4695   /* like MovingOrBlocked2Element(), but if element is moving
4696      and (x,y) is the field the moving element is just leaving,
4697      return EL_BLOCKED instead of the element value */
4698   int element = Feld[x][y];
4699
4700   if (IS_MOVING(x, y))
4701   {
4702     if (element == EL_BLOCKED)
4703     {
4704       int oldx, oldy;
4705
4706       Blocked2Moving(x, y, &oldx, &oldy);
4707       return Feld[oldx][oldy];
4708     }
4709     else
4710       return EL_BLOCKED;
4711   }
4712   else
4713     return element;
4714 }
4715
4716 static void RemoveField(int x, int y)
4717 {
4718   Feld[x][y] = EL_EMPTY;
4719
4720   MovPos[x][y] = 0;
4721   MovDir[x][y] = 0;
4722   MovDelay[x][y] = 0;
4723
4724   CustomValue[x][y] = 0;
4725
4726   AmoebaNr[x][y] = 0;
4727   ChangeDelay[x][y] = 0;
4728   ChangePage[x][y] = -1;
4729   Pushed[x][y] = FALSE;
4730
4731   GfxElement[x][y] = EL_UNDEFINED;
4732   GfxAction[x][y] = ACTION_DEFAULT;
4733   GfxDir[x][y] = MV_NONE;
4734 }
4735
4736 void RemoveMovingField(int x, int y)
4737 {
4738   int oldx = x, oldy = y, newx = x, newy = y;
4739   int element = Feld[x][y];
4740   int next_element = EL_UNDEFINED;
4741
4742   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4743     return;
4744
4745   if (IS_MOVING(x, y))
4746   {
4747     Moving2Blocked(x, y, &newx, &newy);
4748
4749     if (Feld[newx][newy] != EL_BLOCKED)
4750     {
4751       /* element is moving, but target field is not free (blocked), but
4752          already occupied by something different (example: acid pool);
4753          in this case, only remove the moving field, but not the target */
4754
4755       RemoveField(oldx, oldy);
4756
4757       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4758
4759       TEST_DrawLevelField(oldx, oldy);
4760
4761       return;
4762     }
4763   }
4764   else if (element == EL_BLOCKED)
4765   {
4766     Blocked2Moving(x, y, &oldx, &oldy);
4767     if (!IS_MOVING(oldx, oldy))
4768       return;
4769   }
4770
4771   if (element == EL_BLOCKED &&
4772       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4773        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4774        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4775        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4776        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4777        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4778     next_element = get_next_element(Feld[oldx][oldy]);
4779
4780   RemoveField(oldx, oldy);
4781   RemoveField(newx, newy);
4782
4783   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4784
4785   if (next_element != EL_UNDEFINED)
4786     Feld[oldx][oldy] = next_element;
4787
4788   TEST_DrawLevelField(oldx, oldy);
4789   TEST_DrawLevelField(newx, newy);
4790 }
4791
4792 void DrawDynamite(int x, int y)
4793 {
4794   int sx = SCREENX(x), sy = SCREENY(y);
4795   int graphic = el2img(Feld[x][y]);
4796   int frame;
4797
4798   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4799     return;
4800
4801   if (IS_WALKABLE_INSIDE(Back[x][y]))
4802     return;
4803
4804   if (Back[x][y])
4805     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4806   else if (Store[x][y])
4807     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4808
4809   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4810
4811   if (Back[x][y] || Store[x][y])
4812     DrawGraphicThruMask(sx, sy, graphic, frame);
4813   else
4814     DrawGraphic(sx, sy, graphic, frame);
4815 }
4816
4817 void CheckDynamite(int x, int y)
4818 {
4819   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4820   {
4821     MovDelay[x][y]--;
4822
4823     if (MovDelay[x][y] != 0)
4824     {
4825       DrawDynamite(x, y);
4826       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4827
4828       return;
4829     }
4830   }
4831
4832   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4833
4834   Bang(x, y);
4835 }
4836
4837 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4838 {
4839   boolean num_checked_players = 0;
4840   int i;
4841
4842   for (i = 0; i < MAX_PLAYERS; i++)
4843   {
4844     if (stored_player[i].active)
4845     {
4846       int sx = stored_player[i].jx;
4847       int sy = stored_player[i].jy;
4848
4849       if (num_checked_players == 0)
4850       {
4851         *sx1 = *sx2 = sx;
4852         *sy1 = *sy2 = sy;
4853       }
4854       else
4855       {
4856         *sx1 = MIN(*sx1, sx);
4857         *sy1 = MIN(*sy1, sy);
4858         *sx2 = MAX(*sx2, sx);
4859         *sy2 = MAX(*sy2, sy);
4860       }
4861
4862       num_checked_players++;
4863     }
4864   }
4865 }
4866
4867 static boolean checkIfAllPlayersFitToScreen_RND()
4868 {
4869   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4870
4871   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4872
4873   return (sx2 - sx1 < SCR_FIELDX &&
4874           sy2 - sy1 < SCR_FIELDY);
4875 }
4876
4877 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4878 {
4879   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4880
4881   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4882
4883   *sx = (sx1 + sx2) / 2;
4884   *sy = (sy1 + sy2) / 2;
4885 }
4886
4887 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4888                         boolean center_screen, boolean quick_relocation)
4889 {
4890   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4891   boolean no_delay = (tape.warp_forward);
4892   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4893   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4894   int new_scroll_x, new_scroll_y;
4895
4896   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4897   {
4898     /* case 1: quick relocation inside visible screen (without scrolling) */
4899
4900     RedrawPlayfield();
4901
4902     return;
4903   }
4904
4905   if (!level.shifted_relocation || center_screen)
4906   {
4907     /* relocation _with_ centering of screen */
4908
4909     new_scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4910                     x > SBX_Right + MIDPOSX ? SBX_Right :
4911                     x - MIDPOSX);
4912
4913     new_scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4914                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4915                     y - MIDPOSY);
4916   }
4917   else
4918   {
4919     /* relocation _without_ centering of screen */
4920
4921     int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4922                            old_x > SBX_Right + MIDPOSX ? SBX_Right :
4923                            old_x - MIDPOSX);
4924
4925     int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4926                            old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4927                            old_y - MIDPOSY);
4928
4929     int offset_x = x + (scroll_x - center_scroll_x);
4930     int offset_y = y + (scroll_y - center_scroll_y);
4931
4932     new_scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4933                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4934                     offset_x - MIDPOSX);
4935
4936     new_scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4937                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4938                     offset_y - MIDPOSY);
4939   }
4940
4941   if (quick_relocation)
4942   {
4943     /* case 2: quick relocation (redraw without visible scrolling) */
4944
4945     scroll_x = new_scroll_x;
4946     scroll_y = new_scroll_y;
4947
4948     RedrawPlayfield();
4949
4950     return;
4951   }
4952
4953   /* case 3: visible relocation (with scrolling to new position) */
4954
4955   ScrollScreen(NULL, SCROLL_GO_ON);     /* scroll last frame to full tile */
4956
4957   while (scroll_x != new_scroll_x || scroll_y != new_scroll_y)
4958   {
4959     int dx = 0, dy = 0;
4960     int fx = FX, fy = FY;
4961
4962     dx = (new_scroll_x < scroll_x ? +1 : new_scroll_x > scroll_x ? -1 : 0);
4963     dy = (new_scroll_y < scroll_y ? +1 : new_scroll_y > scroll_y ? -1 : 0);
4964
4965     if (dx == 0 && dy == 0)             /* no scrolling needed at all */
4966       break;
4967
4968     scroll_x -= dx;
4969     scroll_y -= dy;
4970
4971     fx += dx * TILEX / 2;
4972     fy += dy * TILEY / 2;
4973
4974     ScrollLevel(dx, dy);
4975     DrawAllPlayers();
4976
4977     /* scroll in two steps of half tile size to make things smoother */
4978     BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
4979     Delay(wait_delay_value);
4980
4981     /* scroll second step to align at full tile size */
4982     BackToFront();
4983     Delay(wait_delay_value);
4984   }
4985
4986   DrawAllPlayers();
4987   BackToFront();
4988   Delay(wait_delay_value);
4989 }
4990
4991 void RelocatePlayer(int jx, int jy, int el_player_raw)
4992 {
4993   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
4994   int player_nr = GET_PLAYER_NR(el_player);
4995   struct PlayerInfo *player = &stored_player[player_nr];
4996   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4997   boolean no_delay = (tape.warp_forward);
4998   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4999   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5000   int old_jx = player->jx;
5001   int old_jy = player->jy;
5002   int old_element = Feld[old_jx][old_jy];
5003   int element = Feld[jx][jy];
5004   boolean player_relocated = (old_jx != jx || old_jy != jy);
5005
5006   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5007   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5008   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5009   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5010   int leave_side_horiz = move_dir_horiz;
5011   int leave_side_vert  = move_dir_vert;
5012   int enter_side = enter_side_horiz | enter_side_vert;
5013   int leave_side = leave_side_horiz | leave_side_vert;
5014
5015   if (player->GameOver)         /* do not reanimate dead player */
5016     return;
5017
5018   if (!player_relocated)        /* no need to relocate the player */
5019     return;
5020
5021   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5022   {
5023     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5024     DrawLevelField(jx, jy);
5025   }
5026
5027   if (player->present)
5028   {
5029     while (player->MovPos)
5030     {
5031       ScrollPlayer(player, SCROLL_GO_ON);
5032       ScrollScreen(NULL, SCROLL_GO_ON);
5033
5034       AdvanceFrameAndPlayerCounters(player->index_nr);
5035
5036       DrawPlayer(player);
5037
5038       BackToFront();
5039       Delay(wait_delay_value);
5040     }
5041
5042     DrawPlayer(player);         /* needed here only to cleanup last field */
5043     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5044
5045     player->is_moving = FALSE;
5046   }
5047
5048   if (IS_CUSTOM_ELEMENT(old_element))
5049     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5050                                CE_LEFT_BY_PLAYER,
5051                                player->index_bit, leave_side);
5052
5053   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5054                                       CE_PLAYER_LEAVES_X,
5055                                       player->index_bit, leave_side);
5056
5057   Feld[jx][jy] = el_player;
5058   InitPlayerField(jx, jy, el_player, TRUE);
5059
5060   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5061      possible that the relocation target field did not contain a player element,
5062      but a walkable element, to which the new player was relocated -- in this
5063      case, restore that (already initialized!) element on the player field */
5064   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5065   {
5066     Feld[jx][jy] = element;     /* restore previously existing element */
5067   }
5068
5069   /* only visually relocate centered player */
5070   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5071                      FALSE, level.instant_relocation);
5072
5073   TestIfPlayerTouchesBadThing(jx, jy);
5074   TestIfPlayerTouchesCustomElement(jx, jy);
5075
5076   if (IS_CUSTOM_ELEMENT(element))
5077     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5078                                player->index_bit, enter_side);
5079
5080   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5081                                       player->index_bit, enter_side);
5082
5083   if (player->is_switching)
5084   {
5085     /* ensure that relocation while still switching an element does not cause
5086        a new element to be treated as also switched directly after relocation
5087        (this is important for teleporter switches that teleport the player to
5088        a place where another teleporter switch is in the same direction, which
5089        would then incorrectly be treated as immediately switched before the
5090        direction key that caused the switch was released) */
5091
5092     player->switch_x += jx - old_jx;
5093     player->switch_y += jy - old_jy;
5094   }
5095 }
5096
5097 void Explode(int ex, int ey, int phase, int mode)
5098 {
5099   int x, y;
5100   int last_phase;
5101   int border_element;
5102
5103   /* !!! eliminate this variable !!! */
5104   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5105
5106   if (game.explosions_delayed)
5107   {
5108     ExplodeField[ex][ey] = mode;
5109     return;
5110   }
5111
5112   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5113   {
5114     int center_element = Feld[ex][ey];
5115     int artwork_element, explosion_element;     /* set these values later */
5116
5117     /* remove things displayed in background while burning dynamite */
5118     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5119       Back[ex][ey] = 0;
5120
5121     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5122     {
5123       /* put moving element to center field (and let it explode there) */
5124       center_element = MovingOrBlocked2Element(ex, ey);
5125       RemoveMovingField(ex, ey);
5126       Feld[ex][ey] = center_element;
5127     }
5128
5129     /* now "center_element" is finally determined -- set related values now */
5130     artwork_element = center_element;           /* for custom player artwork */
5131     explosion_element = center_element;         /* for custom player artwork */
5132
5133     if (IS_PLAYER(ex, ey))
5134     {
5135       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5136
5137       artwork_element = stored_player[player_nr].artwork_element;
5138
5139       if (level.use_explosion_element[player_nr])
5140       {
5141         explosion_element = level.explosion_element[player_nr];
5142         artwork_element = explosion_element;
5143       }
5144     }
5145
5146     if (mode == EX_TYPE_NORMAL ||
5147         mode == EX_TYPE_CENTER ||
5148         mode == EX_TYPE_CROSS)
5149       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5150
5151     last_phase = element_info[explosion_element].explosion_delay + 1;
5152
5153     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5154     {
5155       int xx = x - ex + 1;
5156       int yy = y - ey + 1;
5157       int element;
5158
5159       if (!IN_LEV_FIELD(x, y) ||
5160           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5161           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5162         continue;
5163
5164       element = Feld[x][y];
5165
5166       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5167       {
5168         element = MovingOrBlocked2Element(x, y);
5169
5170         if (!IS_EXPLOSION_PROOF(element))
5171           RemoveMovingField(x, y);
5172       }
5173
5174       /* indestructible elements can only explode in center (but not flames) */
5175       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5176                                            mode == EX_TYPE_BORDER)) ||
5177           element == EL_FLAMES)
5178         continue;
5179
5180       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5181          behaviour, for example when touching a yamyam that explodes to rocks
5182          with active deadly shield, a rock is created under the player !!! */
5183       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5184 #if 0
5185       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5186           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5187            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5188 #else
5189       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5190 #endif
5191       {
5192         if (IS_ACTIVE_BOMB(element))
5193         {
5194           /* re-activate things under the bomb like gate or penguin */
5195           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5196           Back[x][y] = 0;
5197         }
5198
5199         continue;
5200       }
5201
5202       /* save walkable background elements while explosion on same tile */
5203       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5204           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5205         Back[x][y] = element;
5206
5207       /* ignite explodable elements reached by other explosion */
5208       if (element == EL_EXPLOSION)
5209         element = Store2[x][y];
5210
5211       if (AmoebaNr[x][y] &&
5212           (element == EL_AMOEBA_FULL ||
5213            element == EL_BD_AMOEBA ||
5214            element == EL_AMOEBA_GROWING))
5215       {
5216         AmoebaCnt[AmoebaNr[x][y]]--;
5217         AmoebaCnt2[AmoebaNr[x][y]]--;
5218       }
5219
5220       RemoveField(x, y);
5221
5222       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5223       {
5224         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5225
5226         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5227
5228         if (PLAYERINFO(ex, ey)->use_murphy)
5229           Store[x][y] = EL_EMPTY;
5230       }
5231
5232       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5233          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5234       else if (ELEM_IS_PLAYER(center_element))
5235         Store[x][y] = EL_EMPTY;
5236       else if (center_element == EL_YAMYAM)
5237         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5238       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5239         Store[x][y] = element_info[center_element].content.e[xx][yy];
5240 #if 1
5241       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5242          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5243          otherwise) -- FIX THIS !!! */
5244       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5245         Store[x][y] = element_info[element].content.e[1][1];
5246 #else
5247       else if (!CAN_EXPLODE(element))
5248         Store[x][y] = element_info[element].content.e[1][1];
5249 #endif
5250       else
5251         Store[x][y] = EL_EMPTY;
5252
5253       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5254           center_element == EL_AMOEBA_TO_DIAMOND)
5255         Store2[x][y] = element;
5256
5257       Feld[x][y] = EL_EXPLOSION;
5258       GfxElement[x][y] = artwork_element;
5259
5260       ExplodePhase[x][y] = 1;
5261       ExplodeDelay[x][y] = last_phase;
5262
5263       Stop[x][y] = TRUE;
5264     }
5265
5266     if (center_element == EL_YAMYAM)
5267       game.yamyam_content_nr =
5268         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5269
5270     return;
5271   }
5272
5273   if (Stop[ex][ey])
5274     return;
5275
5276   x = ex;
5277   y = ey;
5278
5279   if (phase == 1)
5280     GfxFrame[x][y] = 0;         /* restart explosion animation */
5281
5282   last_phase = ExplodeDelay[x][y];
5283
5284   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5285
5286   /* this can happen if the player leaves an explosion just in time */
5287   if (GfxElement[x][y] == EL_UNDEFINED)
5288     GfxElement[x][y] = EL_EMPTY;
5289
5290   border_element = Store2[x][y];
5291   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5292     border_element = StorePlayer[x][y];
5293
5294   if (phase == element_info[border_element].ignition_delay ||
5295       phase == last_phase)
5296   {
5297     boolean border_explosion = FALSE;
5298
5299     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5300         !PLAYER_EXPLOSION_PROTECTED(x, y))
5301     {
5302       KillPlayerUnlessExplosionProtected(x, y);
5303       border_explosion = TRUE;
5304     }
5305     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5306     {
5307       Feld[x][y] = Store2[x][y];
5308       Store2[x][y] = 0;
5309       Bang(x, y);
5310       border_explosion = TRUE;
5311     }
5312     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5313     {
5314       AmoebeUmwandeln(x, y);
5315       Store2[x][y] = 0;
5316       border_explosion = TRUE;
5317     }
5318
5319     /* if an element just explodes due to another explosion (chain-reaction),
5320        do not immediately end the new explosion when it was the last frame of
5321        the explosion (as it would be done in the following "if"-statement!) */
5322     if (border_explosion && phase == last_phase)
5323       return;
5324   }
5325
5326   if (phase == last_phase)
5327   {
5328     int element;
5329
5330     element = Feld[x][y] = Store[x][y];
5331     Store[x][y] = Store2[x][y] = 0;
5332     GfxElement[x][y] = EL_UNDEFINED;
5333
5334     /* player can escape from explosions and might therefore be still alive */
5335     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5336         element <= EL_PLAYER_IS_EXPLODING_4)
5337     {
5338       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5339       int explosion_element = EL_PLAYER_1 + player_nr;
5340       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5341       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5342
5343       if (level.use_explosion_element[player_nr])
5344         explosion_element = level.explosion_element[player_nr];
5345
5346       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5347                     element_info[explosion_element].content.e[xx][yy]);
5348     }
5349
5350     /* restore probably existing indestructible background element */
5351     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5352       element = Feld[x][y] = Back[x][y];
5353     Back[x][y] = 0;
5354
5355     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5356     GfxDir[x][y] = MV_NONE;
5357     ChangeDelay[x][y] = 0;
5358     ChangePage[x][y] = -1;
5359
5360     CustomValue[x][y] = 0;
5361
5362     InitField_WithBug2(x, y, FALSE);
5363
5364     TEST_DrawLevelField(x, y);
5365
5366     TestIfElementTouchesCustomElement(x, y);
5367
5368     if (GFX_CRUMBLED(element))
5369       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5370
5371     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5372       StorePlayer[x][y] = 0;
5373
5374     if (ELEM_IS_PLAYER(element))
5375       RelocatePlayer(x, y, element);
5376   }
5377   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5378   {
5379     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5380     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5381
5382     if (phase == delay)
5383       TEST_DrawLevelFieldCrumbled(x, y);
5384
5385     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5386     {
5387       DrawLevelElement(x, y, Back[x][y]);
5388       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5389     }
5390     else if (IS_WALKABLE_UNDER(Back[x][y]))
5391     {
5392       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5393       DrawLevelElementThruMask(x, y, Back[x][y]);
5394     }
5395     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5396       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5397   }
5398 }
5399
5400 void DynaExplode(int ex, int ey)
5401 {
5402   int i, j;
5403   int dynabomb_element = Feld[ex][ey];
5404   int dynabomb_size = 1;
5405   boolean dynabomb_xl = FALSE;
5406   struct PlayerInfo *player;
5407   static int xy[4][2] =
5408   {
5409     { 0, -1 },
5410     { -1, 0 },
5411     { +1, 0 },
5412     { 0, +1 }
5413   };
5414
5415   if (IS_ACTIVE_BOMB(dynabomb_element))
5416   {
5417     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5418     dynabomb_size = player->dynabomb_size;
5419     dynabomb_xl = player->dynabomb_xl;
5420     player->dynabombs_left++;
5421   }
5422
5423   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5424
5425   for (i = 0; i < NUM_DIRECTIONS; i++)
5426   {
5427     for (j = 1; j <= dynabomb_size; j++)
5428     {
5429       int x = ex + j * xy[i][0];
5430       int y = ey + j * xy[i][1];
5431       int element;
5432
5433       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5434         break;
5435
5436       element = Feld[x][y];
5437
5438       /* do not restart explosions of fields with active bombs */
5439       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5440         continue;
5441
5442       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5443
5444       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5445           !IS_DIGGABLE(element) && !dynabomb_xl)
5446         break;
5447     }
5448   }
5449 }
5450
5451 void Bang(int x, int y)
5452 {
5453   int element = MovingOrBlocked2Element(x, y);
5454   int explosion_type = EX_TYPE_NORMAL;
5455
5456   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5457   {
5458     struct PlayerInfo *player = PLAYERINFO(x, y);
5459
5460     element = Feld[x][y] = player->initial_element;
5461
5462     if (level.use_explosion_element[player->index_nr])
5463     {
5464       int explosion_element = level.explosion_element[player->index_nr];
5465
5466       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5467         explosion_type = EX_TYPE_CROSS;
5468       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5469         explosion_type = EX_TYPE_CENTER;
5470     }
5471   }
5472
5473   switch (element)
5474   {
5475     case EL_BUG:
5476     case EL_SPACESHIP:
5477     case EL_BD_BUTTERFLY:
5478     case EL_BD_FIREFLY:
5479     case EL_YAMYAM:
5480     case EL_DARK_YAMYAM:
5481     case EL_ROBOT:
5482     case EL_PACMAN:
5483     case EL_MOLE:
5484       RaiseScoreElement(element);
5485       break;
5486
5487     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5488     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5489     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5490     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5491     case EL_DYNABOMB_INCREASE_NUMBER:
5492     case EL_DYNABOMB_INCREASE_SIZE:
5493     case EL_DYNABOMB_INCREASE_POWER:
5494       explosion_type = EX_TYPE_DYNA;
5495       break;
5496
5497     case EL_DC_LANDMINE:
5498       explosion_type = EX_TYPE_CENTER;
5499       break;
5500
5501     case EL_PENGUIN:
5502     case EL_LAMP:
5503     case EL_LAMP_ACTIVE:
5504     case EL_AMOEBA_TO_DIAMOND:
5505       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5506         explosion_type = EX_TYPE_CENTER;
5507       break;
5508
5509     default:
5510       if (element_info[element].explosion_type == EXPLODES_CROSS)
5511         explosion_type = EX_TYPE_CROSS;
5512       else if (element_info[element].explosion_type == EXPLODES_1X1)
5513         explosion_type = EX_TYPE_CENTER;
5514       break;
5515   }
5516
5517   if (explosion_type == EX_TYPE_DYNA)
5518     DynaExplode(x, y);
5519   else
5520     Explode(x, y, EX_PHASE_START, explosion_type);
5521
5522   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5523 }
5524
5525 void SplashAcid(int x, int y)
5526 {
5527   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5528       (!IN_LEV_FIELD(x - 1, y - 2) ||
5529        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5530     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5531
5532   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5533       (!IN_LEV_FIELD(x + 1, y - 2) ||
5534        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5535     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5536
5537   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5538 }
5539
5540 static void InitBeltMovement()
5541 {
5542   static int belt_base_element[4] =
5543   {
5544     EL_CONVEYOR_BELT_1_LEFT,
5545     EL_CONVEYOR_BELT_2_LEFT,
5546     EL_CONVEYOR_BELT_3_LEFT,
5547     EL_CONVEYOR_BELT_4_LEFT
5548   };
5549   static int belt_base_active_element[4] =
5550   {
5551     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5552     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5553     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5554     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5555   };
5556
5557   int x, y, i, j;
5558
5559   /* set frame order for belt animation graphic according to belt direction */
5560   for (i = 0; i < NUM_BELTS; i++)
5561   {
5562     int belt_nr = i;
5563
5564     for (j = 0; j < NUM_BELT_PARTS; j++)
5565     {
5566       int element = belt_base_active_element[belt_nr] + j;
5567       int graphic_1 = el2img(element);
5568       int graphic_2 = el2panelimg(element);
5569
5570       if (game.belt_dir[i] == MV_LEFT)
5571       {
5572         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5573         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5574       }
5575       else
5576       {
5577         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5578         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5579       }
5580     }
5581   }
5582
5583   SCAN_PLAYFIELD(x, y)
5584   {
5585     int element = Feld[x][y];
5586
5587     for (i = 0; i < NUM_BELTS; i++)
5588     {
5589       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5590       {
5591         int e_belt_nr = getBeltNrFromBeltElement(element);
5592         int belt_nr = i;
5593
5594         if (e_belt_nr == belt_nr)
5595         {
5596           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5597
5598           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5599         }
5600       }
5601     }
5602   }
5603 }
5604
5605 static void ToggleBeltSwitch(int x, int y)
5606 {
5607   static int belt_base_element[4] =
5608   {
5609     EL_CONVEYOR_BELT_1_LEFT,
5610     EL_CONVEYOR_BELT_2_LEFT,
5611     EL_CONVEYOR_BELT_3_LEFT,
5612     EL_CONVEYOR_BELT_4_LEFT
5613   };
5614   static int belt_base_active_element[4] =
5615   {
5616     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5617     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5618     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5619     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5620   };
5621   static int belt_base_switch_element[4] =
5622   {
5623     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5624     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5625     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5626     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5627   };
5628   static int belt_move_dir[4] =
5629   {
5630     MV_LEFT,
5631     MV_NONE,
5632     MV_RIGHT,
5633     MV_NONE,
5634   };
5635
5636   int element = Feld[x][y];
5637   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5638   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5639   int belt_dir = belt_move_dir[belt_dir_nr];
5640   int xx, yy, i;
5641
5642   if (!IS_BELT_SWITCH(element))
5643     return;
5644
5645   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5646   game.belt_dir[belt_nr] = belt_dir;
5647
5648   if (belt_dir_nr == 3)
5649     belt_dir_nr = 1;
5650
5651   /* set frame order for belt animation graphic according to belt direction */
5652   for (i = 0; i < NUM_BELT_PARTS; i++)
5653   {
5654     int element = belt_base_active_element[belt_nr] + i;
5655     int graphic_1 = el2img(element);
5656     int graphic_2 = el2panelimg(element);
5657
5658     if (belt_dir == MV_LEFT)
5659     {
5660       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5661       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5662     }
5663     else
5664     {
5665       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5666       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5667     }
5668   }
5669
5670   SCAN_PLAYFIELD(xx, yy)
5671   {
5672     int element = Feld[xx][yy];
5673
5674     if (IS_BELT_SWITCH(element))
5675     {
5676       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5677
5678       if (e_belt_nr == belt_nr)
5679       {
5680         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5681         TEST_DrawLevelField(xx, yy);
5682       }
5683     }
5684     else if (IS_BELT(element) && belt_dir != MV_NONE)
5685     {
5686       int e_belt_nr = getBeltNrFromBeltElement(element);
5687
5688       if (e_belt_nr == belt_nr)
5689       {
5690         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5691
5692         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5693         TEST_DrawLevelField(xx, yy);
5694       }
5695     }
5696     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5697     {
5698       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5699
5700       if (e_belt_nr == belt_nr)
5701       {
5702         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5703
5704         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5705         TEST_DrawLevelField(xx, yy);
5706       }
5707     }
5708   }
5709 }
5710
5711 static void ToggleSwitchgateSwitch(int x, int y)
5712 {
5713   int xx, yy;
5714
5715   game.switchgate_pos = !game.switchgate_pos;
5716
5717   SCAN_PLAYFIELD(xx, yy)
5718   {
5719     int element = Feld[xx][yy];
5720
5721     if (element == EL_SWITCHGATE_SWITCH_UP)
5722     {
5723       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5724       TEST_DrawLevelField(xx, yy);
5725     }
5726     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5727     {
5728       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5729       TEST_DrawLevelField(xx, yy);
5730     }
5731     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5732     {
5733       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5734       TEST_DrawLevelField(xx, yy);
5735     }
5736     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5737     {
5738       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5739       TEST_DrawLevelField(xx, yy);
5740     }
5741     else if (element == EL_SWITCHGATE_OPEN ||
5742              element == EL_SWITCHGATE_OPENING)
5743     {
5744       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5745
5746       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5747     }
5748     else if (element == EL_SWITCHGATE_CLOSED ||
5749              element == EL_SWITCHGATE_CLOSING)
5750     {
5751       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5752
5753       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5754     }
5755   }
5756 }
5757
5758 static int getInvisibleActiveFromInvisibleElement(int element)
5759 {
5760   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5761           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5762           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5763           element);
5764 }
5765
5766 static int getInvisibleFromInvisibleActiveElement(int element)
5767 {
5768   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5769           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5770           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5771           element);
5772 }
5773
5774 static void RedrawAllLightSwitchesAndInvisibleElements()
5775 {
5776   int x, y;
5777
5778   SCAN_PLAYFIELD(x, y)
5779   {
5780     int element = Feld[x][y];
5781
5782     if (element == EL_LIGHT_SWITCH &&
5783         game.light_time_left > 0)
5784     {
5785       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5786       TEST_DrawLevelField(x, y);
5787     }
5788     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5789              game.light_time_left == 0)
5790     {
5791       Feld[x][y] = EL_LIGHT_SWITCH;
5792       TEST_DrawLevelField(x, y);
5793     }
5794     else if (element == EL_EMC_DRIPPER &&
5795              game.light_time_left > 0)
5796     {
5797       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5798       TEST_DrawLevelField(x, y);
5799     }
5800     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5801              game.light_time_left == 0)
5802     {
5803       Feld[x][y] = EL_EMC_DRIPPER;
5804       TEST_DrawLevelField(x, y);
5805     }
5806     else if (element == EL_INVISIBLE_STEELWALL ||
5807              element == EL_INVISIBLE_WALL ||
5808              element == EL_INVISIBLE_SAND)
5809     {
5810       if (game.light_time_left > 0)
5811         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5812
5813       TEST_DrawLevelField(x, y);
5814
5815       /* uncrumble neighbour fields, if needed */
5816       if (element == EL_INVISIBLE_SAND)
5817         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5818     }
5819     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5820              element == EL_INVISIBLE_WALL_ACTIVE ||
5821              element == EL_INVISIBLE_SAND_ACTIVE)
5822     {
5823       if (game.light_time_left == 0)
5824         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5825
5826       TEST_DrawLevelField(x, y);
5827
5828       /* re-crumble neighbour fields, if needed */
5829       if (element == EL_INVISIBLE_SAND)
5830         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5831     }
5832   }
5833 }
5834
5835 static void RedrawAllInvisibleElementsForLenses()
5836 {
5837   int x, y;
5838
5839   SCAN_PLAYFIELD(x, y)
5840   {
5841     int element = Feld[x][y];
5842
5843     if (element == EL_EMC_DRIPPER &&
5844         game.lenses_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.lenses_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.lenses_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.lenses_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 RedrawAllInvisibleElementsForMagnifier()
5885 {
5886   int x, y;
5887
5888   SCAN_PLAYFIELD(x, y)
5889   {
5890     int element = Feld[x][y];
5891
5892     if (element == EL_EMC_FAKE_GRASS &&
5893         game.magnify_time_left > 0)
5894     {
5895       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5896       TEST_DrawLevelField(x, y);
5897     }
5898     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5899              game.magnify_time_left == 0)
5900     {
5901       Feld[x][y] = EL_EMC_FAKE_GRASS;
5902       TEST_DrawLevelField(x, y);
5903     }
5904     else if (IS_GATE_GRAY(element) &&
5905              game.magnify_time_left > 0)
5906     {
5907       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5908                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5909                     IS_EM_GATE_GRAY(element) ?
5910                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5911                     IS_EMC_GATE_GRAY(element) ?
5912                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5913                     IS_DC_GATE_GRAY(element) ?
5914                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5915                     element);
5916       TEST_DrawLevelField(x, y);
5917     }
5918     else if (IS_GATE_GRAY_ACTIVE(element) &&
5919              game.magnify_time_left == 0)
5920     {
5921       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5922                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5923                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5924                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5925                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5926                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5927                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5928                     EL_DC_GATE_WHITE_GRAY :
5929                     element);
5930       TEST_DrawLevelField(x, y);
5931     }
5932   }
5933 }
5934
5935 static void ToggleLightSwitch(int x, int y)
5936 {
5937   int element = Feld[x][y];
5938
5939   game.light_time_left =
5940     (element == EL_LIGHT_SWITCH ?
5941      level.time_light * FRAMES_PER_SECOND : 0);
5942
5943   RedrawAllLightSwitchesAndInvisibleElements();
5944 }
5945
5946 static void ActivateTimegateSwitch(int x, int y)
5947 {
5948   int xx, yy;
5949
5950   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5951
5952   SCAN_PLAYFIELD(xx, yy)
5953   {
5954     int element = Feld[xx][yy];
5955
5956     if (element == EL_TIMEGATE_CLOSED ||
5957         element == EL_TIMEGATE_CLOSING)
5958     {
5959       Feld[xx][yy] = EL_TIMEGATE_OPENING;
5960       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
5961     }
5962
5963     /*
5964     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
5965     {
5966       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
5967       TEST_DrawLevelField(xx, yy);
5968     }
5969     */
5970
5971   }
5972
5973   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
5974                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
5975 }
5976
5977 void Impact(int x, int y)
5978 {
5979   boolean last_line = (y == lev_fieldy - 1);
5980   boolean object_hit = FALSE;
5981   boolean impact = (last_line || object_hit);
5982   int element = Feld[x][y];
5983   int smashed = EL_STEELWALL;
5984
5985   if (!last_line)       /* check if element below was hit */
5986   {
5987     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
5988       return;
5989
5990     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
5991                                          MovDir[x][y + 1] != MV_DOWN ||
5992                                          MovPos[x][y + 1] <= TILEY / 2));
5993
5994     /* do not smash moving elements that left the smashed field in time */
5995     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
5996         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
5997       object_hit = FALSE;
5998
5999 #if USE_QUICKSAND_IMPACT_BUGFIX
6000     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6001     {
6002       RemoveMovingField(x, y + 1);
6003       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6004       Feld[x][y + 2] = EL_ROCK;
6005       TEST_DrawLevelField(x, y + 2);
6006
6007       object_hit = TRUE;
6008     }
6009
6010     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6011     {
6012       RemoveMovingField(x, y + 1);
6013       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6014       Feld[x][y + 2] = EL_ROCK;
6015       TEST_DrawLevelField(x, y + 2);
6016
6017       object_hit = TRUE;
6018     }
6019 #endif
6020
6021     if (object_hit)
6022       smashed = MovingOrBlocked2Element(x, y + 1);
6023
6024     impact = (last_line || object_hit);
6025   }
6026
6027   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6028   {
6029     SplashAcid(x, y + 1);
6030     return;
6031   }
6032
6033   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6034   /* only reset graphic animation if graphic really changes after impact */
6035   if (impact &&
6036       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6037   {
6038     ResetGfxAnimation(x, y);
6039     TEST_DrawLevelField(x, y);
6040   }
6041
6042   if (impact && CAN_EXPLODE_IMPACT(element))
6043   {
6044     Bang(x, y);
6045     return;
6046   }
6047   else if (impact && element == EL_PEARL &&
6048            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6049   {
6050     ResetGfxAnimation(x, y);
6051
6052     Feld[x][y] = EL_PEARL_BREAKING;
6053     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6054     return;
6055   }
6056   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6057   {
6058     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6059
6060     return;
6061   }
6062
6063   if (impact && element == EL_AMOEBA_DROP)
6064   {
6065     if (object_hit && IS_PLAYER(x, y + 1))
6066       KillPlayerUnlessEnemyProtected(x, y + 1);
6067     else if (object_hit && smashed == EL_PENGUIN)
6068       Bang(x, y + 1);
6069     else
6070     {
6071       Feld[x][y] = EL_AMOEBA_GROWING;
6072       Store[x][y] = EL_AMOEBA_WET;
6073
6074       ResetRandomAnimationValue(x, y);
6075     }
6076     return;
6077   }
6078
6079   if (object_hit)               /* check which object was hit */
6080   {
6081     if ((CAN_PASS_MAGIC_WALL(element) && 
6082          (smashed == EL_MAGIC_WALL ||
6083           smashed == EL_BD_MAGIC_WALL)) ||
6084         (CAN_PASS_DC_MAGIC_WALL(element) &&
6085          smashed == EL_DC_MAGIC_WALL))
6086     {
6087       int xx, yy;
6088       int activated_magic_wall =
6089         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6090          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6091          EL_DC_MAGIC_WALL_ACTIVE);
6092
6093       /* activate magic wall / mill */
6094       SCAN_PLAYFIELD(xx, yy)
6095       {
6096         if (Feld[xx][yy] == smashed)
6097           Feld[xx][yy] = activated_magic_wall;
6098       }
6099
6100       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6101       game.magic_wall_active = TRUE;
6102
6103       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6104                             SND_MAGIC_WALL_ACTIVATING :
6105                             smashed == EL_BD_MAGIC_WALL ?
6106                             SND_BD_MAGIC_WALL_ACTIVATING :
6107                             SND_DC_MAGIC_WALL_ACTIVATING));
6108     }
6109
6110     if (IS_PLAYER(x, y + 1))
6111     {
6112       if (CAN_SMASH_PLAYER(element))
6113       {
6114         KillPlayerUnlessEnemyProtected(x, y + 1);
6115         return;
6116       }
6117     }
6118     else if (smashed == EL_PENGUIN)
6119     {
6120       if (CAN_SMASH_PLAYER(element))
6121       {
6122         Bang(x, y + 1);
6123         return;
6124       }
6125     }
6126     else if (element == EL_BD_DIAMOND)
6127     {
6128       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6129       {
6130         Bang(x, y + 1);
6131         return;
6132       }
6133     }
6134     else if (((element == EL_SP_INFOTRON ||
6135                element == EL_SP_ZONK) &&
6136               (smashed == EL_SP_SNIKSNAK ||
6137                smashed == EL_SP_ELECTRON ||
6138                smashed == EL_SP_DISK_ORANGE)) ||
6139              (element == EL_SP_INFOTRON &&
6140               smashed == EL_SP_DISK_YELLOW))
6141     {
6142       Bang(x, y + 1);
6143       return;
6144     }
6145     else if (CAN_SMASH_EVERYTHING(element))
6146     {
6147       if (IS_CLASSIC_ENEMY(smashed) ||
6148           CAN_EXPLODE_SMASHED(smashed))
6149       {
6150         Bang(x, y + 1);
6151         return;
6152       }
6153       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6154       {
6155         if (smashed == EL_LAMP ||
6156             smashed == EL_LAMP_ACTIVE)
6157         {
6158           Bang(x, y + 1);
6159           return;
6160         }
6161         else if (smashed == EL_NUT)
6162         {
6163           Feld[x][y + 1] = EL_NUT_BREAKING;
6164           PlayLevelSound(x, y, SND_NUT_BREAKING);
6165           RaiseScoreElement(EL_NUT);
6166           return;
6167         }
6168         else if (smashed == EL_PEARL)
6169         {
6170           ResetGfxAnimation(x, y);
6171
6172           Feld[x][y + 1] = EL_PEARL_BREAKING;
6173           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6174           return;
6175         }
6176         else if (smashed == EL_DIAMOND)
6177         {
6178           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6179           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6180           return;
6181         }
6182         else if (IS_BELT_SWITCH(smashed))
6183         {
6184           ToggleBeltSwitch(x, y + 1);
6185         }
6186         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6187                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6188                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6189                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6190         {
6191           ToggleSwitchgateSwitch(x, y + 1);
6192         }
6193         else if (smashed == EL_LIGHT_SWITCH ||
6194                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6195         {
6196           ToggleLightSwitch(x, y + 1);
6197         }
6198         else
6199         {
6200           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6201
6202           CheckElementChangeBySide(x, y + 1, smashed, element,
6203                                    CE_SWITCHED, CH_SIDE_TOP);
6204           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6205                                             CH_SIDE_TOP);
6206         }
6207       }
6208       else
6209       {
6210         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6211       }
6212     }
6213   }
6214
6215   /* play sound of magic wall / mill */
6216   if (!last_line &&
6217       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6218        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6219        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6220   {
6221     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6222       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6223     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6224       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6225     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6226       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6227
6228     return;
6229   }
6230
6231   /* play sound of object that hits the ground */
6232   if (last_line || object_hit)
6233     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6234 }
6235
6236 inline static void TurnRoundExt(int x, int y)
6237 {
6238   static struct
6239   {
6240     int dx, dy;
6241   } move_xy[] =
6242   {
6243     {  0,  0 },
6244     { -1,  0 },
6245     { +1,  0 },
6246     {  0,  0 },
6247     {  0, -1 },
6248     {  0,  0 }, { 0, 0 }, { 0, 0 },
6249     {  0, +1 }
6250   };
6251   static struct
6252   {
6253     int left, right, back;
6254   } turn[] =
6255   {
6256     { 0,        0,              0        },
6257     { MV_DOWN,  MV_UP,          MV_RIGHT },
6258     { MV_UP,    MV_DOWN,        MV_LEFT  },
6259     { 0,        0,              0        },
6260     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6261     { 0,        0,              0        },
6262     { 0,        0,              0        },
6263     { 0,        0,              0        },
6264     { MV_RIGHT, MV_LEFT,        MV_UP    }
6265   };
6266
6267   int element = Feld[x][y];
6268   int move_pattern = element_info[element].move_pattern;
6269
6270   int old_move_dir = MovDir[x][y];
6271   int left_dir  = turn[old_move_dir].left;
6272   int right_dir = turn[old_move_dir].right;
6273   int back_dir  = turn[old_move_dir].back;
6274
6275   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6276   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6277   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6278   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6279
6280   int left_x  = x + left_dx,  left_y  = y + left_dy;
6281   int right_x = x + right_dx, right_y = y + right_dy;
6282   int move_x  = x + move_dx,  move_y  = y + move_dy;
6283
6284   int xx, yy;
6285
6286   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6287   {
6288     TestIfBadThingTouchesOtherBadThing(x, y);
6289
6290     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6291       MovDir[x][y] = right_dir;
6292     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6293       MovDir[x][y] = left_dir;
6294
6295     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6296       MovDelay[x][y] = 9;
6297     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6298       MovDelay[x][y] = 1;
6299   }
6300   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6301   {
6302     TestIfBadThingTouchesOtherBadThing(x, y);
6303
6304     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6305       MovDir[x][y] = left_dir;
6306     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6307       MovDir[x][y] = right_dir;
6308
6309     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6310       MovDelay[x][y] = 9;
6311     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6312       MovDelay[x][y] = 1;
6313   }
6314   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6315   {
6316     TestIfBadThingTouchesOtherBadThing(x, y);
6317
6318     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6319       MovDir[x][y] = left_dir;
6320     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6321       MovDir[x][y] = right_dir;
6322
6323     if (MovDir[x][y] != old_move_dir)
6324       MovDelay[x][y] = 9;
6325   }
6326   else if (element == EL_YAMYAM)
6327   {
6328     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6329     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6330
6331     if (can_turn_left && can_turn_right)
6332       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6333     else if (can_turn_left)
6334       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6335     else if (can_turn_right)
6336       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6337     else
6338       MovDir[x][y] = back_dir;
6339
6340     MovDelay[x][y] = 16 + 16 * RND(3);
6341   }
6342   else if (element == EL_DARK_YAMYAM)
6343   {
6344     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6345                                                          left_x, left_y);
6346     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6347                                                          right_x, right_y);
6348
6349     if (can_turn_left && can_turn_right)
6350       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6351     else if (can_turn_left)
6352       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6353     else if (can_turn_right)
6354       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6355     else
6356       MovDir[x][y] = back_dir;
6357
6358     MovDelay[x][y] = 16 + 16 * RND(3);
6359   }
6360   else if (element == EL_PACMAN)
6361   {
6362     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6363     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6364
6365     if (can_turn_left && can_turn_right)
6366       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6367     else if (can_turn_left)
6368       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6369     else if (can_turn_right)
6370       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6371     else
6372       MovDir[x][y] = back_dir;
6373
6374     MovDelay[x][y] = 6 + RND(40);
6375   }
6376   else if (element == EL_PIG)
6377   {
6378     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6379     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6380     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6381     boolean should_turn_left, should_turn_right, should_move_on;
6382     int rnd_value = 24;
6383     int rnd = RND(rnd_value);
6384
6385     should_turn_left = (can_turn_left &&
6386                         (!can_move_on ||
6387                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6388                                                    y + back_dy + left_dy)));
6389     should_turn_right = (can_turn_right &&
6390                          (!can_move_on ||
6391                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6392                                                     y + back_dy + right_dy)));
6393     should_move_on = (can_move_on &&
6394                       (!can_turn_left ||
6395                        !can_turn_right ||
6396                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6397                                                  y + move_dy + left_dy) ||
6398                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6399                                                  y + move_dy + right_dy)));
6400
6401     if (should_turn_left || should_turn_right || should_move_on)
6402     {
6403       if (should_turn_left && should_turn_right && should_move_on)
6404         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6405                         rnd < 2 * rnd_value / 3 ? right_dir :
6406                         old_move_dir);
6407       else if (should_turn_left && should_turn_right)
6408         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6409       else if (should_turn_left && should_move_on)
6410         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6411       else if (should_turn_right && should_move_on)
6412         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6413       else if (should_turn_left)
6414         MovDir[x][y] = left_dir;
6415       else if (should_turn_right)
6416         MovDir[x][y] = right_dir;
6417       else if (should_move_on)
6418         MovDir[x][y] = old_move_dir;
6419     }
6420     else if (can_move_on && rnd > rnd_value / 8)
6421       MovDir[x][y] = old_move_dir;
6422     else if (can_turn_left && can_turn_right)
6423       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6424     else if (can_turn_left && rnd > rnd_value / 8)
6425       MovDir[x][y] = left_dir;
6426     else if (can_turn_right && rnd > rnd_value/8)
6427       MovDir[x][y] = right_dir;
6428     else
6429       MovDir[x][y] = back_dir;
6430
6431     xx = x + move_xy[MovDir[x][y]].dx;
6432     yy = y + move_xy[MovDir[x][y]].dy;
6433
6434     if (!IN_LEV_FIELD(xx, yy) ||
6435         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6436       MovDir[x][y] = old_move_dir;
6437
6438     MovDelay[x][y] = 0;
6439   }
6440   else if (element == EL_DRAGON)
6441   {
6442     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6443     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6444     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6445     int rnd_value = 24;
6446     int rnd = RND(rnd_value);
6447
6448     if (can_move_on && rnd > rnd_value / 8)
6449       MovDir[x][y] = old_move_dir;
6450     else if (can_turn_left && can_turn_right)
6451       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6452     else if (can_turn_left && rnd > rnd_value / 8)
6453       MovDir[x][y] = left_dir;
6454     else if (can_turn_right && rnd > rnd_value / 8)
6455       MovDir[x][y] = right_dir;
6456     else
6457       MovDir[x][y] = back_dir;
6458
6459     xx = x + move_xy[MovDir[x][y]].dx;
6460     yy = y + move_xy[MovDir[x][y]].dy;
6461
6462     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6463       MovDir[x][y] = old_move_dir;
6464
6465     MovDelay[x][y] = 0;
6466   }
6467   else if (element == EL_MOLE)
6468   {
6469     boolean can_move_on =
6470       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6471                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6472                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6473     if (!can_move_on)
6474     {
6475       boolean can_turn_left =
6476         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6477                               IS_AMOEBOID(Feld[left_x][left_y])));
6478
6479       boolean can_turn_right =
6480         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6481                               IS_AMOEBOID(Feld[right_x][right_y])));
6482
6483       if (can_turn_left && can_turn_right)
6484         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6485       else if (can_turn_left)
6486         MovDir[x][y] = left_dir;
6487       else
6488         MovDir[x][y] = right_dir;
6489     }
6490
6491     if (MovDir[x][y] != old_move_dir)
6492       MovDelay[x][y] = 9;
6493   }
6494   else if (element == EL_BALLOON)
6495   {
6496     MovDir[x][y] = game.wind_direction;
6497     MovDelay[x][y] = 0;
6498   }
6499   else if (element == EL_SPRING)
6500   {
6501     if (MovDir[x][y] & MV_HORIZONTAL)
6502     {
6503       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6504           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6505       {
6506         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6507         ResetGfxAnimation(move_x, move_y);
6508         TEST_DrawLevelField(move_x, move_y);
6509
6510         MovDir[x][y] = back_dir;
6511       }
6512       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6513                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6514         MovDir[x][y] = MV_NONE;
6515     }
6516
6517     MovDelay[x][y] = 0;
6518   }
6519   else if (element == EL_ROBOT ||
6520            element == EL_SATELLITE ||
6521            element == EL_PENGUIN ||
6522            element == EL_EMC_ANDROID)
6523   {
6524     int attr_x = -1, attr_y = -1;
6525
6526     if (AllPlayersGone)
6527     {
6528       attr_x = ExitX;
6529       attr_y = ExitY;
6530     }
6531     else
6532     {
6533       int i;
6534
6535       for (i = 0; i < MAX_PLAYERS; i++)
6536       {
6537         struct PlayerInfo *player = &stored_player[i];
6538         int jx = player->jx, jy = player->jy;
6539
6540         if (!player->active)
6541           continue;
6542
6543         if (attr_x == -1 ||
6544             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6545         {
6546           attr_x = jx;
6547           attr_y = jy;
6548         }
6549       }
6550     }
6551
6552     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6553         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6554          game.engine_version < VERSION_IDENT(3,1,0,0)))
6555     {
6556       attr_x = ZX;
6557       attr_y = ZY;
6558     }
6559
6560     if (element == EL_PENGUIN)
6561     {
6562       int i;
6563       static int xy[4][2] =
6564       {
6565         { 0, -1 },
6566         { -1, 0 },
6567         { +1, 0 },
6568         { 0, +1 }
6569       };
6570
6571       for (i = 0; i < NUM_DIRECTIONS; i++)
6572       {
6573         int ex = x + xy[i][0];
6574         int ey = y + xy[i][1];
6575
6576         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6577                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6578                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6579                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6580         {
6581           attr_x = ex;
6582           attr_y = ey;
6583           break;
6584         }
6585       }
6586     }
6587
6588     MovDir[x][y] = MV_NONE;
6589     if (attr_x < x)
6590       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6591     else if (attr_x > x)
6592       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6593     if (attr_y < y)
6594       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6595     else if (attr_y > y)
6596       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6597
6598     if (element == EL_ROBOT)
6599     {
6600       int newx, newy;
6601
6602       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6603         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6604       Moving2Blocked(x, y, &newx, &newy);
6605
6606       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6607         MovDelay[x][y] = 8 + 8 * !RND(3);
6608       else
6609         MovDelay[x][y] = 16;
6610     }
6611     else if (element == EL_PENGUIN)
6612     {
6613       int newx, newy;
6614
6615       MovDelay[x][y] = 1;
6616
6617       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6618       {
6619         boolean first_horiz = RND(2);
6620         int new_move_dir = MovDir[x][y];
6621
6622         MovDir[x][y] =
6623           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6624         Moving2Blocked(x, y, &newx, &newy);
6625
6626         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6627           return;
6628
6629         MovDir[x][y] =
6630           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6631         Moving2Blocked(x, y, &newx, &newy);
6632
6633         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6634           return;
6635
6636         MovDir[x][y] = old_move_dir;
6637         return;
6638       }
6639     }
6640     else if (element == EL_SATELLITE)
6641     {
6642       int newx, newy;
6643
6644       MovDelay[x][y] = 1;
6645
6646       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6647       {
6648         boolean first_horiz = RND(2);
6649         int new_move_dir = MovDir[x][y];
6650
6651         MovDir[x][y] =
6652           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6653         Moving2Blocked(x, y, &newx, &newy);
6654
6655         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6656           return;
6657
6658         MovDir[x][y] =
6659           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6660         Moving2Blocked(x, y, &newx, &newy);
6661
6662         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6663           return;
6664
6665         MovDir[x][y] = old_move_dir;
6666         return;
6667       }
6668     }
6669     else if (element == EL_EMC_ANDROID)
6670     {
6671       static int check_pos[16] =
6672       {
6673         -1,             /*  0 => (invalid)          */
6674         7,              /*  1 => MV_LEFT            */
6675         3,              /*  2 => MV_RIGHT           */
6676         -1,             /*  3 => (invalid)          */
6677         1,              /*  4 =>            MV_UP   */
6678         0,              /*  5 => MV_LEFT  | MV_UP   */
6679         2,              /*  6 => MV_RIGHT | MV_UP   */
6680         -1,             /*  7 => (invalid)          */
6681         5,              /*  8 =>            MV_DOWN */
6682         6,              /*  9 => MV_LEFT  | MV_DOWN */
6683         4,              /* 10 => MV_RIGHT | MV_DOWN */
6684         -1,             /* 11 => (invalid)          */
6685         -1,             /* 12 => (invalid)          */
6686         -1,             /* 13 => (invalid)          */
6687         -1,             /* 14 => (invalid)          */
6688         -1,             /* 15 => (invalid)          */
6689       };
6690       static struct
6691       {
6692         int dx, dy;
6693         int dir;
6694       } check_xy[8] =
6695       {
6696         { -1, -1,       MV_LEFT  | MV_UP   },
6697         {  0, -1,                  MV_UP   },
6698         { +1, -1,       MV_RIGHT | MV_UP   },
6699         { +1,  0,       MV_RIGHT           },
6700         { +1, +1,       MV_RIGHT | MV_DOWN },
6701         {  0, +1,                  MV_DOWN },
6702         { -1, +1,       MV_LEFT  | MV_DOWN },
6703         { -1,  0,       MV_LEFT            },
6704       };
6705       int start_pos, check_order;
6706       boolean can_clone = FALSE;
6707       int i;
6708
6709       /* check if there is any free field around current position */
6710       for (i = 0; i < 8; i++)
6711       {
6712         int newx = x + check_xy[i].dx;
6713         int newy = y + check_xy[i].dy;
6714
6715         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6716         {
6717           can_clone = TRUE;
6718
6719           break;
6720         }
6721       }
6722
6723       if (can_clone)            /* randomly find an element to clone */
6724       {
6725         can_clone = FALSE;
6726
6727         start_pos = check_pos[RND(8)];
6728         check_order = (RND(2) ? -1 : +1);
6729
6730         for (i = 0; i < 8; i++)
6731         {
6732           int pos_raw = start_pos + i * check_order;
6733           int pos = (pos_raw + 8) % 8;
6734           int newx = x + check_xy[pos].dx;
6735           int newy = y + check_xy[pos].dy;
6736
6737           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6738           {
6739             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6740             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6741
6742             Store[x][y] = Feld[newx][newy];
6743
6744             can_clone = TRUE;
6745
6746             break;
6747           }
6748         }
6749       }
6750
6751       if (can_clone)            /* randomly find a direction to move */
6752       {
6753         can_clone = FALSE;
6754
6755         start_pos = check_pos[RND(8)];
6756         check_order = (RND(2) ? -1 : +1);
6757
6758         for (i = 0; i < 8; i++)
6759         {
6760           int pos_raw = start_pos + i * check_order;
6761           int pos = (pos_raw + 8) % 8;
6762           int newx = x + check_xy[pos].dx;
6763           int newy = y + check_xy[pos].dy;
6764           int new_move_dir = check_xy[pos].dir;
6765
6766           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6767           {
6768             MovDir[x][y] = new_move_dir;
6769             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6770
6771             can_clone = TRUE;
6772
6773             break;
6774           }
6775         }
6776       }
6777
6778       if (can_clone)            /* cloning and moving successful */
6779         return;
6780
6781       /* cannot clone -- try to move towards player */
6782
6783       start_pos = check_pos[MovDir[x][y] & 0x0f];
6784       check_order = (RND(2) ? -1 : +1);
6785
6786       for (i = 0; i < 3; i++)
6787       {
6788         /* first check start_pos, then previous/next or (next/previous) pos */
6789         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6790         int pos = (pos_raw + 8) % 8;
6791         int newx = x + check_xy[pos].dx;
6792         int newy = y + check_xy[pos].dy;
6793         int new_move_dir = check_xy[pos].dir;
6794
6795         if (IS_PLAYER(newx, newy))
6796           break;
6797
6798         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6799         {
6800           MovDir[x][y] = new_move_dir;
6801           MovDelay[x][y] = level.android_move_time * 8 + 1;
6802
6803           break;
6804         }
6805       }
6806     }
6807   }
6808   else if (move_pattern == MV_TURNING_LEFT ||
6809            move_pattern == MV_TURNING_RIGHT ||
6810            move_pattern == MV_TURNING_LEFT_RIGHT ||
6811            move_pattern == MV_TURNING_RIGHT_LEFT ||
6812            move_pattern == MV_TURNING_RANDOM ||
6813            move_pattern == MV_ALL_DIRECTIONS)
6814   {
6815     boolean can_turn_left =
6816       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6817     boolean can_turn_right =
6818       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6819
6820     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6821       return;
6822
6823     if (move_pattern == MV_TURNING_LEFT)
6824       MovDir[x][y] = left_dir;
6825     else if (move_pattern == MV_TURNING_RIGHT)
6826       MovDir[x][y] = right_dir;
6827     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6828       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6829     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6830       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6831     else if (move_pattern == MV_TURNING_RANDOM)
6832       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6833                       can_turn_right && !can_turn_left ? right_dir :
6834                       RND(2) ? left_dir : right_dir);
6835     else if (can_turn_left && can_turn_right)
6836       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6837     else if (can_turn_left)
6838       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6839     else if (can_turn_right)
6840       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6841     else
6842       MovDir[x][y] = back_dir;
6843
6844     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6845   }
6846   else if (move_pattern == MV_HORIZONTAL ||
6847            move_pattern == MV_VERTICAL)
6848   {
6849     if (move_pattern & old_move_dir)
6850       MovDir[x][y] = back_dir;
6851     else if (move_pattern == MV_HORIZONTAL)
6852       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6853     else if (move_pattern == MV_VERTICAL)
6854       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6855
6856     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6857   }
6858   else if (move_pattern & MV_ANY_DIRECTION)
6859   {
6860     MovDir[x][y] = move_pattern;
6861     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6862   }
6863   else if (move_pattern & MV_WIND_DIRECTION)
6864   {
6865     MovDir[x][y] = game.wind_direction;
6866     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6867   }
6868   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6869   {
6870     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6871       MovDir[x][y] = left_dir;
6872     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6873       MovDir[x][y] = right_dir;
6874
6875     if (MovDir[x][y] != old_move_dir)
6876       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6877   }
6878   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6879   {
6880     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6881       MovDir[x][y] = right_dir;
6882     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6883       MovDir[x][y] = left_dir;
6884
6885     if (MovDir[x][y] != old_move_dir)
6886       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6887   }
6888   else if (move_pattern == MV_TOWARDS_PLAYER ||
6889            move_pattern == MV_AWAY_FROM_PLAYER)
6890   {
6891     int attr_x = -1, attr_y = -1;
6892     int newx, newy;
6893     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6894
6895     if (AllPlayersGone)
6896     {
6897       attr_x = ExitX;
6898       attr_y = ExitY;
6899     }
6900     else
6901     {
6902       int i;
6903
6904       for (i = 0; i < MAX_PLAYERS; i++)
6905       {
6906         struct PlayerInfo *player = &stored_player[i];
6907         int jx = player->jx, jy = player->jy;
6908
6909         if (!player->active)
6910           continue;
6911
6912         if (attr_x == -1 ||
6913             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6914         {
6915           attr_x = jx;
6916           attr_y = jy;
6917         }
6918       }
6919     }
6920
6921     MovDir[x][y] = MV_NONE;
6922     if (attr_x < x)
6923       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6924     else if (attr_x > x)
6925       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6926     if (attr_y < y)
6927       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6928     else if (attr_y > y)
6929       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6930
6931     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6932
6933     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6934     {
6935       boolean first_horiz = RND(2);
6936       int new_move_dir = MovDir[x][y];
6937
6938       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6939       {
6940         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6941         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6942
6943         return;
6944       }
6945
6946       MovDir[x][y] =
6947         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6948       Moving2Blocked(x, y, &newx, &newy);
6949
6950       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6951         return;
6952
6953       MovDir[x][y] =
6954         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6955       Moving2Blocked(x, y, &newx, &newy);
6956
6957       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6958         return;
6959
6960       MovDir[x][y] = old_move_dir;
6961     }
6962   }
6963   else if (move_pattern == MV_WHEN_PUSHED ||
6964            move_pattern == MV_WHEN_DROPPED)
6965   {
6966     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6967       MovDir[x][y] = MV_NONE;
6968
6969     MovDelay[x][y] = 0;
6970   }
6971   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
6972   {
6973     static int test_xy[7][2] =
6974     {
6975       { 0, -1 },
6976       { -1, 0 },
6977       { +1, 0 },
6978       { 0, +1 },
6979       { 0, -1 },
6980       { -1, 0 },
6981       { +1, 0 },
6982     };
6983     static int test_dir[7] =
6984     {
6985       MV_UP,
6986       MV_LEFT,
6987       MV_RIGHT,
6988       MV_DOWN,
6989       MV_UP,
6990       MV_LEFT,
6991       MV_RIGHT,
6992     };
6993     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
6994     int move_preference = -1000000;     /* start with very low preference */
6995     int new_move_dir = MV_NONE;
6996     int start_test = RND(4);
6997     int i;
6998
6999     for (i = 0; i < NUM_DIRECTIONS; i++)
7000     {
7001       int move_dir = test_dir[start_test + i];
7002       int move_dir_preference;
7003
7004       xx = x + test_xy[start_test + i][0];
7005       yy = y + test_xy[start_test + i][1];
7006
7007       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7008           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7009       {
7010         new_move_dir = move_dir;
7011
7012         break;
7013       }
7014
7015       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7016         continue;
7017
7018       move_dir_preference = -1 * RunnerVisit[xx][yy];
7019       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7020         move_dir_preference = PlayerVisit[xx][yy];
7021
7022       if (move_dir_preference > move_preference)
7023       {
7024         /* prefer field that has not been visited for the longest time */
7025         move_preference = move_dir_preference;
7026         new_move_dir = move_dir;
7027       }
7028       else if (move_dir_preference == move_preference &&
7029                move_dir == old_move_dir)
7030       {
7031         /* prefer last direction when all directions are preferred equally */
7032         move_preference = move_dir_preference;
7033         new_move_dir = move_dir;
7034       }
7035     }
7036
7037     MovDir[x][y] = new_move_dir;
7038     if (old_move_dir != new_move_dir)
7039       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7040   }
7041 }
7042
7043 static void TurnRound(int x, int y)
7044 {
7045   int direction = MovDir[x][y];
7046
7047   TurnRoundExt(x, y);
7048
7049   GfxDir[x][y] = MovDir[x][y];
7050
7051   if (direction != MovDir[x][y])
7052     GfxFrame[x][y] = 0;
7053
7054   if (MovDelay[x][y])
7055     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7056
7057   ResetGfxFrame(x, y, FALSE);
7058 }
7059
7060 static boolean JustBeingPushed(int x, int y)
7061 {
7062   int i;
7063
7064   for (i = 0; i < MAX_PLAYERS; i++)
7065   {
7066     struct PlayerInfo *player = &stored_player[i];
7067
7068     if (player->active && player->is_pushing && player->MovPos)
7069     {
7070       int next_jx = player->jx + (player->jx - player->last_jx);
7071       int next_jy = player->jy + (player->jy - player->last_jy);
7072
7073       if (x == next_jx && y == next_jy)
7074         return TRUE;
7075     }
7076   }
7077
7078   return FALSE;
7079 }
7080
7081 void StartMoving(int x, int y)
7082 {
7083   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7084   int element = Feld[x][y];
7085
7086   if (Stop[x][y])
7087     return;
7088
7089   if (MovDelay[x][y] == 0)
7090     GfxAction[x][y] = ACTION_DEFAULT;
7091
7092   if (CAN_FALL(element) && y < lev_fieldy - 1)
7093   {
7094     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7095         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7096       if (JustBeingPushed(x, y))
7097         return;
7098
7099     if (element == EL_QUICKSAND_FULL)
7100     {
7101       if (IS_FREE(x, y + 1))
7102       {
7103         InitMovingField(x, y, MV_DOWN);
7104         started_moving = TRUE;
7105
7106         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7107 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7108         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7109           Store[x][y] = EL_ROCK;
7110 #else
7111         Store[x][y] = EL_ROCK;
7112 #endif
7113
7114         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7115       }
7116       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7117       {
7118         if (!MovDelay[x][y])
7119         {
7120           MovDelay[x][y] = TILEY + 1;
7121
7122           ResetGfxAnimation(x, y);
7123           ResetGfxAnimation(x, y + 1);
7124         }
7125
7126         if (MovDelay[x][y])
7127         {
7128           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7129           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7130
7131           MovDelay[x][y]--;
7132           if (MovDelay[x][y])
7133             return;
7134         }
7135
7136         Feld[x][y] = EL_QUICKSAND_EMPTY;
7137         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7138         Store[x][y + 1] = Store[x][y];
7139         Store[x][y] = 0;
7140
7141         PlayLevelSoundAction(x, y, ACTION_FILLING);
7142       }
7143       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7144       {
7145         if (!MovDelay[x][y])
7146         {
7147           MovDelay[x][y] = TILEY + 1;
7148
7149           ResetGfxAnimation(x, y);
7150           ResetGfxAnimation(x, y + 1);
7151         }
7152
7153         if (MovDelay[x][y])
7154         {
7155           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7156           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7157
7158           MovDelay[x][y]--;
7159           if (MovDelay[x][y])
7160             return;
7161         }
7162
7163         Feld[x][y] = EL_QUICKSAND_EMPTY;
7164         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7165         Store[x][y + 1] = Store[x][y];
7166         Store[x][y] = 0;
7167
7168         PlayLevelSoundAction(x, y, ACTION_FILLING);
7169       }
7170     }
7171     else if (element == EL_QUICKSAND_FAST_FULL)
7172     {
7173       if (IS_FREE(x, y + 1))
7174       {
7175         InitMovingField(x, y, MV_DOWN);
7176         started_moving = TRUE;
7177
7178         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7179 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7180         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7181           Store[x][y] = EL_ROCK;
7182 #else
7183         Store[x][y] = EL_ROCK;
7184 #endif
7185
7186         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7187       }
7188       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7189       {
7190         if (!MovDelay[x][y])
7191         {
7192           MovDelay[x][y] = TILEY + 1;
7193
7194           ResetGfxAnimation(x, y);
7195           ResetGfxAnimation(x, y + 1);
7196         }
7197
7198         if (MovDelay[x][y])
7199         {
7200           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7201           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7202
7203           MovDelay[x][y]--;
7204           if (MovDelay[x][y])
7205             return;
7206         }
7207
7208         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7209         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7210         Store[x][y + 1] = Store[x][y];
7211         Store[x][y] = 0;
7212
7213         PlayLevelSoundAction(x, y, ACTION_FILLING);
7214       }
7215       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7216       {
7217         if (!MovDelay[x][y])
7218         {
7219           MovDelay[x][y] = TILEY + 1;
7220
7221           ResetGfxAnimation(x, y);
7222           ResetGfxAnimation(x, y + 1);
7223         }
7224
7225         if (MovDelay[x][y])
7226         {
7227           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7228           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7229
7230           MovDelay[x][y]--;
7231           if (MovDelay[x][y])
7232             return;
7233         }
7234
7235         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7236         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7237         Store[x][y + 1] = Store[x][y];
7238         Store[x][y] = 0;
7239
7240         PlayLevelSoundAction(x, y, ACTION_FILLING);
7241       }
7242     }
7243     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7244              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7245     {
7246       InitMovingField(x, y, MV_DOWN);
7247       started_moving = TRUE;
7248
7249       Feld[x][y] = EL_QUICKSAND_FILLING;
7250       Store[x][y] = element;
7251
7252       PlayLevelSoundAction(x, y, ACTION_FILLING);
7253     }
7254     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7255              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7256     {
7257       InitMovingField(x, y, MV_DOWN);
7258       started_moving = TRUE;
7259
7260       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7261       Store[x][y] = element;
7262
7263       PlayLevelSoundAction(x, y, ACTION_FILLING);
7264     }
7265     else if (element == EL_MAGIC_WALL_FULL)
7266     {
7267       if (IS_FREE(x, y + 1))
7268       {
7269         InitMovingField(x, y, MV_DOWN);
7270         started_moving = TRUE;
7271
7272         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7273         Store[x][y] = EL_CHANGED(Store[x][y]);
7274       }
7275       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7276       {
7277         if (!MovDelay[x][y])
7278           MovDelay[x][y] = TILEY / 4 + 1;
7279
7280         if (MovDelay[x][y])
7281         {
7282           MovDelay[x][y]--;
7283           if (MovDelay[x][y])
7284             return;
7285         }
7286
7287         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7288         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7289         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7290         Store[x][y] = 0;
7291       }
7292     }
7293     else if (element == EL_BD_MAGIC_WALL_FULL)
7294     {
7295       if (IS_FREE(x, y + 1))
7296       {
7297         InitMovingField(x, y, MV_DOWN);
7298         started_moving = TRUE;
7299
7300         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7301         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7302       }
7303       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7304       {
7305         if (!MovDelay[x][y])
7306           MovDelay[x][y] = TILEY / 4 + 1;
7307
7308         if (MovDelay[x][y])
7309         {
7310           MovDelay[x][y]--;
7311           if (MovDelay[x][y])
7312             return;
7313         }
7314
7315         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7316         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7317         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7318         Store[x][y] = 0;
7319       }
7320     }
7321     else if (element == EL_DC_MAGIC_WALL_FULL)
7322     {
7323       if (IS_FREE(x, y + 1))
7324       {
7325         InitMovingField(x, y, MV_DOWN);
7326         started_moving = TRUE;
7327
7328         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7329         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7330       }
7331       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7332       {
7333         if (!MovDelay[x][y])
7334           MovDelay[x][y] = TILEY / 4 + 1;
7335
7336         if (MovDelay[x][y])
7337         {
7338           MovDelay[x][y]--;
7339           if (MovDelay[x][y])
7340             return;
7341         }
7342
7343         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7344         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7345         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7346         Store[x][y] = 0;
7347       }
7348     }
7349     else if ((CAN_PASS_MAGIC_WALL(element) &&
7350               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7351                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7352              (CAN_PASS_DC_MAGIC_WALL(element) &&
7353               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7354
7355     {
7356       InitMovingField(x, y, MV_DOWN);
7357       started_moving = TRUE;
7358
7359       Feld[x][y] =
7360         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7361          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7362          EL_DC_MAGIC_WALL_FILLING);
7363       Store[x][y] = element;
7364     }
7365     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7366     {
7367       SplashAcid(x, y + 1);
7368
7369       InitMovingField(x, y, MV_DOWN);
7370       started_moving = TRUE;
7371
7372       Store[x][y] = EL_ACID;
7373     }
7374     else if (
7375              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7376               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7377              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7378               CAN_FALL(element) && WasJustFalling[x][y] &&
7379               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7380
7381              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7382               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7383               (Feld[x][y + 1] == EL_BLOCKED)))
7384     {
7385       /* this is needed for a special case not covered by calling "Impact()"
7386          from "ContinueMoving()": if an element moves to a tile directly below
7387          another element which was just falling on that tile (which was empty
7388          in the previous frame), the falling element above would just stop
7389          instead of smashing the element below (in previous version, the above
7390          element was just checked for "moving" instead of "falling", resulting
7391          in incorrect smashes caused by horizontal movement of the above
7392          element; also, the case of the player being the element to smash was
7393          simply not covered here... :-/ ) */
7394
7395       CheckCollision[x][y] = 0;
7396       CheckImpact[x][y] = 0;
7397
7398       Impact(x, y);
7399     }
7400     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7401     {
7402       if (MovDir[x][y] == MV_NONE)
7403       {
7404         InitMovingField(x, y, MV_DOWN);
7405         started_moving = TRUE;
7406       }
7407     }
7408     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7409     {
7410       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7411         MovDir[x][y] = MV_DOWN;
7412
7413       InitMovingField(x, y, MV_DOWN);
7414       started_moving = TRUE;
7415     }
7416     else if (element == EL_AMOEBA_DROP)
7417     {
7418       Feld[x][y] = EL_AMOEBA_GROWING;
7419       Store[x][y] = EL_AMOEBA_WET;
7420     }
7421     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7422               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7423              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7424              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7425     {
7426       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7427                                 (IS_FREE(x - 1, y + 1) ||
7428                                  Feld[x - 1][y + 1] == EL_ACID));
7429       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7430                                 (IS_FREE(x + 1, y + 1) ||
7431                                  Feld[x + 1][y + 1] == EL_ACID));
7432       boolean can_fall_any  = (can_fall_left || can_fall_right);
7433       boolean can_fall_both = (can_fall_left && can_fall_right);
7434       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7435
7436       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7437       {
7438         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7439           can_fall_right = FALSE;
7440         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7441           can_fall_left = FALSE;
7442         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7443           can_fall_right = FALSE;
7444         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7445           can_fall_left = FALSE;
7446
7447         can_fall_any  = (can_fall_left || can_fall_right);
7448         can_fall_both = FALSE;
7449       }
7450
7451       if (can_fall_both)
7452       {
7453         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7454           can_fall_right = FALSE;       /* slip down on left side */
7455         else
7456           can_fall_left = !(can_fall_right = RND(2));
7457
7458         can_fall_both = FALSE;
7459       }
7460
7461       if (can_fall_any)
7462       {
7463         /* if not determined otherwise, prefer left side for slipping down */
7464         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7465         started_moving = TRUE;
7466       }
7467     }
7468     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7469     {
7470       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7471       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7472       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7473       int belt_dir = game.belt_dir[belt_nr];
7474
7475       if ((belt_dir == MV_LEFT  && left_is_free) ||
7476           (belt_dir == MV_RIGHT && right_is_free))
7477       {
7478         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7479
7480         InitMovingField(x, y, belt_dir);
7481         started_moving = TRUE;
7482
7483         Pushed[x][y] = TRUE;
7484         Pushed[nextx][y] = TRUE;
7485
7486         GfxAction[x][y] = ACTION_DEFAULT;
7487       }
7488       else
7489       {
7490         MovDir[x][y] = 0;       /* if element was moving, stop it */
7491       }
7492     }
7493   }
7494
7495   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7496   if (CAN_MOVE(element) && !started_moving)
7497   {
7498     int move_pattern = element_info[element].move_pattern;
7499     int newx, newy;
7500
7501     Moving2Blocked(x, y, &newx, &newy);
7502
7503     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7504       return;
7505
7506     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7507         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7508     {
7509       WasJustMoving[x][y] = 0;
7510       CheckCollision[x][y] = 0;
7511
7512       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7513
7514       if (Feld[x][y] != element)        /* element has changed */
7515         return;
7516     }
7517
7518     if (!MovDelay[x][y])        /* start new movement phase */
7519     {
7520       /* all objects that can change their move direction after each step
7521          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7522
7523       if (element != EL_YAMYAM &&
7524           element != EL_DARK_YAMYAM &&
7525           element != EL_PACMAN &&
7526           !(move_pattern & MV_ANY_DIRECTION) &&
7527           move_pattern != MV_TURNING_LEFT &&
7528           move_pattern != MV_TURNING_RIGHT &&
7529           move_pattern != MV_TURNING_LEFT_RIGHT &&
7530           move_pattern != MV_TURNING_RIGHT_LEFT &&
7531           move_pattern != MV_TURNING_RANDOM)
7532       {
7533         TurnRound(x, y);
7534
7535         if (MovDelay[x][y] && (element == EL_BUG ||
7536                                element == EL_SPACESHIP ||
7537                                element == EL_SP_SNIKSNAK ||
7538                                element == EL_SP_ELECTRON ||
7539                                element == EL_MOLE))
7540           TEST_DrawLevelField(x, y);
7541       }
7542     }
7543
7544     if (MovDelay[x][y])         /* wait some time before next movement */
7545     {
7546       MovDelay[x][y]--;
7547
7548       if (element == EL_ROBOT ||
7549           element == EL_YAMYAM ||
7550           element == EL_DARK_YAMYAM)
7551       {
7552         DrawLevelElementAnimationIfNeeded(x, y, element);
7553         PlayLevelSoundAction(x, y, ACTION_WAITING);
7554       }
7555       else if (element == EL_SP_ELECTRON)
7556         DrawLevelElementAnimationIfNeeded(x, y, element);
7557       else if (element == EL_DRAGON)
7558       {
7559         int i;
7560         int dir = MovDir[x][y];
7561         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7562         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7563         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7564                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7565                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7566                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7567         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7568
7569         GfxAction[x][y] = ACTION_ATTACKING;
7570
7571         if (IS_PLAYER(x, y))
7572           DrawPlayerField(x, y);
7573         else
7574           TEST_DrawLevelField(x, y);
7575
7576         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7577
7578         for (i = 1; i <= 3; i++)
7579         {
7580           int xx = x + i * dx;
7581           int yy = y + i * dy;
7582           int sx = SCREENX(xx);
7583           int sy = SCREENY(yy);
7584           int flame_graphic = graphic + (i - 1);
7585
7586           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7587             break;
7588
7589           if (MovDelay[x][y])
7590           {
7591             int flamed = MovingOrBlocked2Element(xx, yy);
7592
7593             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7594               Bang(xx, yy);
7595             else
7596               RemoveMovingField(xx, yy);
7597
7598             ChangeDelay[xx][yy] = 0;
7599
7600             Feld[xx][yy] = EL_FLAMES;
7601
7602             if (IN_SCR_FIELD(sx, sy))
7603             {
7604               TEST_DrawLevelFieldCrumbled(xx, yy);
7605               DrawGraphic(sx, sy, flame_graphic, frame);
7606             }
7607           }
7608           else
7609           {
7610             if (Feld[xx][yy] == EL_FLAMES)
7611               Feld[xx][yy] = EL_EMPTY;
7612             TEST_DrawLevelField(xx, yy);
7613           }
7614         }
7615       }
7616
7617       if (MovDelay[x][y])       /* element still has to wait some time */
7618       {
7619         PlayLevelSoundAction(x, y, ACTION_WAITING);
7620
7621         return;
7622       }
7623     }
7624
7625     /* now make next step */
7626
7627     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7628
7629     if (DONT_COLLIDE_WITH(element) &&
7630         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7631         !PLAYER_ENEMY_PROTECTED(newx, newy))
7632     {
7633       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7634
7635       return;
7636     }
7637
7638     else if (CAN_MOVE_INTO_ACID(element) &&
7639              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7640              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7641              (MovDir[x][y] == MV_DOWN ||
7642               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7643     {
7644       SplashAcid(newx, newy);
7645       Store[x][y] = EL_ACID;
7646     }
7647     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7648     {
7649       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7650           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7651           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7652           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7653       {
7654         RemoveField(x, y);
7655         TEST_DrawLevelField(x, y);
7656
7657         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7658         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7659           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7660
7661         local_player->friends_still_needed--;
7662         if (!local_player->friends_still_needed &&
7663             !local_player->GameOver && AllPlayersGone)
7664           PlayerWins(local_player);
7665
7666         return;
7667       }
7668       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7669       {
7670         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7671           TEST_DrawLevelField(newx, newy);
7672         else
7673           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7674       }
7675       else if (!IS_FREE(newx, newy))
7676       {
7677         GfxAction[x][y] = ACTION_WAITING;
7678
7679         if (IS_PLAYER(x, y))
7680           DrawPlayerField(x, y);
7681         else
7682           TEST_DrawLevelField(x, y);
7683
7684         return;
7685       }
7686     }
7687     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7688     {
7689       if (IS_FOOD_PIG(Feld[newx][newy]))
7690       {
7691         if (IS_MOVING(newx, newy))
7692           RemoveMovingField(newx, newy);
7693         else
7694         {
7695           Feld[newx][newy] = EL_EMPTY;
7696           TEST_DrawLevelField(newx, newy);
7697         }
7698
7699         PlayLevelSound(x, y, SND_PIG_DIGGING);
7700       }
7701       else if (!IS_FREE(newx, newy))
7702       {
7703         if (IS_PLAYER(x, y))
7704           DrawPlayerField(x, y);
7705         else
7706           TEST_DrawLevelField(x, y);
7707
7708         return;
7709       }
7710     }
7711     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7712     {
7713       if (Store[x][y] != EL_EMPTY)
7714       {
7715         boolean can_clone = FALSE;
7716         int xx, yy;
7717
7718         /* check if element to clone is still there */
7719         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7720         {
7721           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7722           {
7723             can_clone = TRUE;
7724
7725             break;
7726           }
7727         }
7728
7729         /* cannot clone or target field not free anymore -- do not clone */
7730         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7731           Store[x][y] = EL_EMPTY;
7732       }
7733
7734       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7735       {
7736         if (IS_MV_DIAGONAL(MovDir[x][y]))
7737         {
7738           int diagonal_move_dir = MovDir[x][y];
7739           int stored = Store[x][y];
7740           int change_delay = 8;
7741           int graphic;
7742
7743           /* android is moving diagonally */
7744
7745           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7746
7747           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7748           GfxElement[x][y] = EL_EMC_ANDROID;
7749           GfxAction[x][y] = ACTION_SHRINKING;
7750           GfxDir[x][y] = diagonal_move_dir;
7751           ChangeDelay[x][y] = change_delay;
7752
7753           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7754                                    GfxDir[x][y]);
7755
7756           DrawLevelGraphicAnimation(x, y, graphic);
7757           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7758
7759           if (Feld[newx][newy] == EL_ACID)
7760           {
7761             SplashAcid(newx, newy);
7762
7763             return;
7764           }
7765
7766           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7767
7768           Store[newx][newy] = EL_EMC_ANDROID;
7769           GfxElement[newx][newy] = EL_EMC_ANDROID;
7770           GfxAction[newx][newy] = ACTION_GROWING;
7771           GfxDir[newx][newy] = diagonal_move_dir;
7772           ChangeDelay[newx][newy] = change_delay;
7773
7774           graphic = el_act_dir2img(GfxElement[newx][newy],
7775                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7776
7777           DrawLevelGraphicAnimation(newx, newy, graphic);
7778           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7779
7780           return;
7781         }
7782         else
7783         {
7784           Feld[newx][newy] = EL_EMPTY;
7785           TEST_DrawLevelField(newx, newy);
7786
7787           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7788         }
7789       }
7790       else if (!IS_FREE(newx, newy))
7791       {
7792         return;
7793       }
7794     }
7795     else if (IS_CUSTOM_ELEMENT(element) &&
7796              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7797     {
7798       if (!DigFieldByCE(newx, newy, element))
7799         return;
7800
7801       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7802       {
7803         RunnerVisit[x][y] = FrameCounter;
7804         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7805       }
7806     }
7807     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7808     {
7809       if (!IS_FREE(newx, newy))
7810       {
7811         if (IS_PLAYER(x, y))
7812           DrawPlayerField(x, y);
7813         else
7814           TEST_DrawLevelField(x, y);
7815
7816         return;
7817       }
7818       else
7819       {
7820         boolean wanna_flame = !RND(10);
7821         int dx = newx - x, dy = newy - y;
7822         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7823         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7824         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7825                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7826         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7827                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7828
7829         if ((wanna_flame ||
7830              IS_CLASSIC_ENEMY(element1) ||
7831              IS_CLASSIC_ENEMY(element2)) &&
7832             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7833             element1 != EL_FLAMES && element2 != EL_FLAMES)
7834         {
7835           ResetGfxAnimation(x, y);
7836           GfxAction[x][y] = ACTION_ATTACKING;
7837
7838           if (IS_PLAYER(x, y))
7839             DrawPlayerField(x, y);
7840           else
7841             TEST_DrawLevelField(x, y);
7842
7843           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7844
7845           MovDelay[x][y] = 50;
7846
7847           Feld[newx][newy] = EL_FLAMES;
7848           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7849             Feld[newx1][newy1] = EL_FLAMES;
7850           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7851             Feld[newx2][newy2] = EL_FLAMES;
7852
7853           return;
7854         }
7855       }
7856     }
7857     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7858              Feld[newx][newy] == EL_DIAMOND)
7859     {
7860       if (IS_MOVING(newx, newy))
7861         RemoveMovingField(newx, newy);
7862       else
7863       {
7864         Feld[newx][newy] = EL_EMPTY;
7865         TEST_DrawLevelField(newx, newy);
7866       }
7867
7868       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7869     }
7870     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7871              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7872     {
7873       if (AmoebaNr[newx][newy])
7874       {
7875         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7876         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7877             Feld[newx][newy] == EL_BD_AMOEBA)
7878           AmoebaCnt[AmoebaNr[newx][newy]]--;
7879       }
7880
7881       if (IS_MOVING(newx, newy))
7882       {
7883         RemoveMovingField(newx, newy);
7884       }
7885       else
7886       {
7887         Feld[newx][newy] = EL_EMPTY;
7888         TEST_DrawLevelField(newx, newy);
7889       }
7890
7891       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7892     }
7893     else if ((element == EL_PACMAN || element == EL_MOLE)
7894              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7895     {
7896       if (AmoebaNr[newx][newy])
7897       {
7898         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7899         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7900             Feld[newx][newy] == EL_BD_AMOEBA)
7901           AmoebaCnt[AmoebaNr[newx][newy]]--;
7902       }
7903
7904       if (element == EL_MOLE)
7905       {
7906         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7907         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7908
7909         ResetGfxAnimation(x, y);
7910         GfxAction[x][y] = ACTION_DIGGING;
7911         TEST_DrawLevelField(x, y);
7912
7913         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7914
7915         return;                         /* wait for shrinking amoeba */
7916       }
7917       else      /* element == EL_PACMAN */
7918       {
7919         Feld[newx][newy] = EL_EMPTY;
7920         TEST_DrawLevelField(newx, newy);
7921         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7922       }
7923     }
7924     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7925              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7926               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7927     {
7928       /* wait for shrinking amoeba to completely disappear */
7929       return;
7930     }
7931     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7932     {
7933       /* object was running against a wall */
7934
7935       TurnRound(x, y);
7936
7937       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7938         DrawLevelElementAnimation(x, y, element);
7939
7940       if (DONT_TOUCH(element))
7941         TestIfBadThingTouchesPlayer(x, y);
7942
7943       return;
7944     }
7945
7946     InitMovingField(x, y, MovDir[x][y]);
7947
7948     PlayLevelSoundAction(x, y, ACTION_MOVING);
7949   }
7950
7951   if (MovDir[x][y])
7952     ContinueMoving(x, y);
7953 }
7954
7955 void ContinueMoving(int x, int y)
7956 {
7957   int element = Feld[x][y];
7958   struct ElementInfo *ei = &element_info[element];
7959   int direction = MovDir[x][y];
7960   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
7961   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
7962   int newx = x + dx, newy = y + dy;
7963   int stored = Store[x][y];
7964   int stored_new = Store[newx][newy];
7965   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
7966   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
7967   boolean last_line = (newy == lev_fieldy - 1);
7968
7969   MovPos[x][y] += getElementMoveStepsize(x, y);
7970
7971   if (pushed_by_player) /* special case: moving object pushed by player */
7972     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
7973
7974   if (ABS(MovPos[x][y]) < TILEX)
7975   {
7976     TEST_DrawLevelField(x, y);
7977
7978     return;     /* element is still moving */
7979   }
7980
7981   /* element reached destination field */
7982
7983   Feld[x][y] = EL_EMPTY;
7984   Feld[newx][newy] = element;
7985   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
7986
7987   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
7988   {
7989     element = Feld[newx][newy] = EL_ACID;
7990   }
7991   else if (element == EL_MOLE)
7992   {
7993     Feld[x][y] = EL_SAND;
7994
7995     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
7996   }
7997   else if (element == EL_QUICKSAND_FILLING)
7998   {
7999     element = Feld[newx][newy] = get_next_element(element);
8000     Store[newx][newy] = Store[x][y];
8001   }
8002   else if (element == EL_QUICKSAND_EMPTYING)
8003   {
8004     Feld[x][y] = get_next_element(element);
8005     element = Feld[newx][newy] = Store[x][y];
8006   }
8007   else if (element == EL_QUICKSAND_FAST_FILLING)
8008   {
8009     element = Feld[newx][newy] = get_next_element(element);
8010     Store[newx][newy] = Store[x][y];
8011   }
8012   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8013   {
8014     Feld[x][y] = get_next_element(element);
8015     element = Feld[newx][newy] = Store[x][y];
8016   }
8017   else if (element == EL_MAGIC_WALL_FILLING)
8018   {
8019     element = Feld[newx][newy] = get_next_element(element);
8020     if (!game.magic_wall_active)
8021       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8022     Store[newx][newy] = Store[x][y];
8023   }
8024   else if (element == EL_MAGIC_WALL_EMPTYING)
8025   {
8026     Feld[x][y] = get_next_element(element);
8027     if (!game.magic_wall_active)
8028       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8029     element = Feld[newx][newy] = Store[x][y];
8030
8031     InitField(newx, newy, FALSE);
8032   }
8033   else if (element == EL_BD_MAGIC_WALL_FILLING)
8034   {
8035     element = Feld[newx][newy] = get_next_element(element);
8036     if (!game.magic_wall_active)
8037       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8038     Store[newx][newy] = Store[x][y];
8039   }
8040   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8041   {
8042     Feld[x][y] = get_next_element(element);
8043     if (!game.magic_wall_active)
8044       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8045     element = Feld[newx][newy] = Store[x][y];
8046
8047     InitField(newx, newy, FALSE);
8048   }
8049   else if (element == EL_DC_MAGIC_WALL_FILLING)
8050   {
8051     element = Feld[newx][newy] = get_next_element(element);
8052     if (!game.magic_wall_active)
8053       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8054     Store[newx][newy] = Store[x][y];
8055   }
8056   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8057   {
8058     Feld[x][y] = get_next_element(element);
8059     if (!game.magic_wall_active)
8060       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8061     element = Feld[newx][newy] = Store[x][y];
8062
8063     InitField(newx, newy, FALSE);
8064   }
8065   else if (element == EL_AMOEBA_DROPPING)
8066   {
8067     Feld[x][y] = get_next_element(element);
8068     element = Feld[newx][newy] = Store[x][y];
8069   }
8070   else if (element == EL_SOKOBAN_OBJECT)
8071   {
8072     if (Back[x][y])
8073       Feld[x][y] = Back[x][y];
8074
8075     if (Back[newx][newy])
8076       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8077
8078     Back[x][y] = Back[newx][newy] = 0;
8079   }
8080
8081   Store[x][y] = EL_EMPTY;
8082   MovPos[x][y] = 0;
8083   MovDir[x][y] = 0;
8084   MovDelay[x][y] = 0;
8085
8086   MovDelay[newx][newy] = 0;
8087
8088   if (CAN_CHANGE_OR_HAS_ACTION(element))
8089   {
8090     /* copy element change control values to new field */
8091     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8092     ChangePage[newx][newy]  = ChangePage[x][y];
8093     ChangeCount[newx][newy] = ChangeCount[x][y];
8094     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8095   }
8096
8097   CustomValue[newx][newy] = CustomValue[x][y];
8098
8099   ChangeDelay[x][y] = 0;
8100   ChangePage[x][y] = -1;
8101   ChangeCount[x][y] = 0;
8102   ChangeEvent[x][y] = -1;
8103
8104   CustomValue[x][y] = 0;
8105
8106   /* copy animation control values to new field */
8107   GfxFrame[newx][newy]  = GfxFrame[x][y];
8108   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8109   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8110   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8111
8112   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8113
8114   /* some elements can leave other elements behind after moving */
8115   if (ei->move_leave_element != EL_EMPTY &&
8116       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8117       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8118   {
8119     int move_leave_element = ei->move_leave_element;
8120
8121     /* this makes it possible to leave the removed element again */
8122     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8123       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8124
8125     Feld[x][y] = move_leave_element;
8126
8127     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8128       MovDir[x][y] = direction;
8129
8130     InitField(x, y, FALSE);
8131
8132     if (GFX_CRUMBLED(Feld[x][y]))
8133       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8134
8135     if (ELEM_IS_PLAYER(move_leave_element))
8136       RelocatePlayer(x, y, move_leave_element);
8137   }
8138
8139   /* do this after checking for left-behind element */
8140   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8141
8142   if (!CAN_MOVE(element) ||
8143       (CAN_FALL(element) && direction == MV_DOWN &&
8144        (element == EL_SPRING ||
8145         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8146         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8147     GfxDir[x][y] = MovDir[newx][newy] = 0;
8148
8149   TEST_DrawLevelField(x, y);
8150   TEST_DrawLevelField(newx, newy);
8151
8152   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8153
8154   /* prevent pushed element from moving on in pushed direction */
8155   if (pushed_by_player && CAN_MOVE(element) &&
8156       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8157       !(element_info[element].move_pattern & direction))
8158     TurnRound(newx, newy);
8159
8160   /* prevent elements on conveyor belt from moving on in last direction */
8161   if (pushed_by_conveyor && CAN_FALL(element) &&
8162       direction & MV_HORIZONTAL)
8163     MovDir[newx][newy] = 0;
8164
8165   if (!pushed_by_player)
8166   {
8167     int nextx = newx + dx, nexty = newy + dy;
8168     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8169
8170     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8171
8172     if (CAN_FALL(element) && direction == MV_DOWN)
8173       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8174
8175     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8176       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8177
8178     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8179       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8180   }
8181
8182   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8183   {
8184     TestIfBadThingTouchesPlayer(newx, newy);
8185     TestIfBadThingTouchesFriend(newx, newy);
8186
8187     if (!IS_CUSTOM_ELEMENT(element))
8188       TestIfBadThingTouchesOtherBadThing(newx, newy);
8189   }
8190   else if (element == EL_PENGUIN)
8191     TestIfFriendTouchesBadThing(newx, newy);
8192
8193   if (DONT_GET_HIT_BY(element))
8194   {
8195     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8196   }
8197
8198   /* give the player one last chance (one more frame) to move away */
8199   if (CAN_FALL(element) && direction == MV_DOWN &&
8200       (last_line || (!IS_FREE(x, newy + 1) &&
8201                      (!IS_PLAYER(x, newy + 1) ||
8202                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8203     Impact(x, newy);
8204
8205   if (pushed_by_player && !game.use_change_when_pushing_bug)
8206   {
8207     int push_side = MV_DIR_OPPOSITE(direction);
8208     struct PlayerInfo *player = PLAYERINFO(x, y);
8209
8210     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8211                                player->index_bit, push_side);
8212     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8213                                         player->index_bit, push_side);
8214   }
8215
8216   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8217     MovDelay[newx][newy] = 1;
8218
8219   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8220
8221   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8222   TestIfElementHitsCustomElement(newx, newy, direction);
8223   TestIfPlayerTouchesCustomElement(newx, newy);
8224   TestIfElementTouchesCustomElement(newx, newy);
8225
8226   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8227       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8228     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8229                              MV_DIR_OPPOSITE(direction));
8230 }
8231
8232 int AmoebeNachbarNr(int ax, int ay)
8233 {
8234   int i;
8235   int element = Feld[ax][ay];
8236   int group_nr = 0;
8237   static int xy[4][2] =
8238   {
8239     { 0, -1 },
8240     { -1, 0 },
8241     { +1, 0 },
8242     { 0, +1 }
8243   };
8244
8245   for (i = 0; i < NUM_DIRECTIONS; i++)
8246   {
8247     int x = ax + xy[i][0];
8248     int y = ay + xy[i][1];
8249
8250     if (!IN_LEV_FIELD(x, y))
8251       continue;
8252
8253     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8254       group_nr = AmoebaNr[x][y];
8255   }
8256
8257   return group_nr;
8258 }
8259
8260 void AmoebenVereinigen(int ax, int ay)
8261 {
8262   int i, x, y, xx, yy;
8263   int new_group_nr = AmoebaNr[ax][ay];
8264   static int xy[4][2] =
8265   {
8266     { 0, -1 },
8267     { -1, 0 },
8268     { +1, 0 },
8269     { 0, +1 }
8270   };
8271
8272   if (new_group_nr == 0)
8273     return;
8274
8275   for (i = 0; i < NUM_DIRECTIONS; i++)
8276   {
8277     x = ax + xy[i][0];
8278     y = ay + xy[i][1];
8279
8280     if (!IN_LEV_FIELD(x, y))
8281       continue;
8282
8283     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8284          Feld[x][y] == EL_BD_AMOEBA ||
8285          Feld[x][y] == EL_AMOEBA_DEAD) &&
8286         AmoebaNr[x][y] != new_group_nr)
8287     {
8288       int old_group_nr = AmoebaNr[x][y];
8289
8290       if (old_group_nr == 0)
8291         return;
8292
8293       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8294       AmoebaCnt[old_group_nr] = 0;
8295       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8296       AmoebaCnt2[old_group_nr] = 0;
8297
8298       SCAN_PLAYFIELD(xx, yy)
8299       {
8300         if (AmoebaNr[xx][yy] == old_group_nr)
8301           AmoebaNr[xx][yy] = new_group_nr;
8302       }
8303     }
8304   }
8305 }
8306
8307 void AmoebeUmwandeln(int ax, int ay)
8308 {
8309   int i, x, y;
8310
8311   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8312   {
8313     int group_nr = AmoebaNr[ax][ay];
8314
8315 #ifdef DEBUG
8316     if (group_nr == 0)
8317     {
8318       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8319       printf("AmoebeUmwandeln(): This should never happen!\n");
8320       return;
8321     }
8322 #endif
8323
8324     SCAN_PLAYFIELD(x, y)
8325     {
8326       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8327       {
8328         AmoebaNr[x][y] = 0;
8329         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8330       }
8331     }
8332
8333     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8334                             SND_AMOEBA_TURNING_TO_GEM :
8335                             SND_AMOEBA_TURNING_TO_ROCK));
8336     Bang(ax, ay);
8337   }
8338   else
8339   {
8340     static int xy[4][2] =
8341     {
8342       { 0, -1 },
8343       { -1, 0 },
8344       { +1, 0 },
8345       { 0, +1 }
8346     };
8347
8348     for (i = 0; i < NUM_DIRECTIONS; i++)
8349     {
8350       x = ax + xy[i][0];
8351       y = ay + xy[i][1];
8352
8353       if (!IN_LEV_FIELD(x, y))
8354         continue;
8355
8356       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8357       {
8358         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8359                               SND_AMOEBA_TURNING_TO_GEM :
8360                               SND_AMOEBA_TURNING_TO_ROCK));
8361         Bang(x, y);
8362       }
8363     }
8364   }
8365 }
8366
8367 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8368 {
8369   int x, y;
8370   int group_nr = AmoebaNr[ax][ay];
8371   boolean done = FALSE;
8372
8373 #ifdef DEBUG
8374   if (group_nr == 0)
8375   {
8376     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8377     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8378     return;
8379   }
8380 #endif
8381
8382   SCAN_PLAYFIELD(x, y)
8383   {
8384     if (AmoebaNr[x][y] == group_nr &&
8385         (Feld[x][y] == EL_AMOEBA_DEAD ||
8386          Feld[x][y] == EL_BD_AMOEBA ||
8387          Feld[x][y] == EL_AMOEBA_GROWING))
8388     {
8389       AmoebaNr[x][y] = 0;
8390       Feld[x][y] = new_element;
8391       InitField(x, y, FALSE);
8392       TEST_DrawLevelField(x, y);
8393       done = TRUE;
8394     }
8395   }
8396
8397   if (done)
8398     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8399                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8400                             SND_BD_AMOEBA_TURNING_TO_GEM));
8401 }
8402
8403 void AmoebeWaechst(int x, int y)
8404 {
8405   static unsigned int sound_delay = 0;
8406   static unsigned int sound_delay_value = 0;
8407
8408   if (!MovDelay[x][y])          /* start new growing cycle */
8409   {
8410     MovDelay[x][y] = 7;
8411
8412     if (DelayReached(&sound_delay, sound_delay_value))
8413     {
8414       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8415       sound_delay_value = 30;
8416     }
8417   }
8418
8419   if (MovDelay[x][y])           /* wait some time before growing bigger */
8420   {
8421     MovDelay[x][y]--;
8422     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8423     {
8424       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8425                                            6 - MovDelay[x][y]);
8426
8427       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8428     }
8429
8430     if (!MovDelay[x][y])
8431     {
8432       Feld[x][y] = Store[x][y];
8433       Store[x][y] = 0;
8434       TEST_DrawLevelField(x, y);
8435     }
8436   }
8437 }
8438
8439 void AmoebaDisappearing(int x, int y)
8440 {
8441   static unsigned int sound_delay = 0;
8442   static unsigned int sound_delay_value = 0;
8443
8444   if (!MovDelay[x][y])          /* start new shrinking cycle */
8445   {
8446     MovDelay[x][y] = 7;
8447
8448     if (DelayReached(&sound_delay, sound_delay_value))
8449       sound_delay_value = 30;
8450   }
8451
8452   if (MovDelay[x][y])           /* wait some time before shrinking */
8453   {
8454     MovDelay[x][y]--;
8455     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8456     {
8457       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8458                                            6 - MovDelay[x][y]);
8459
8460       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8461     }
8462
8463     if (!MovDelay[x][y])
8464     {
8465       Feld[x][y] = EL_EMPTY;
8466       TEST_DrawLevelField(x, y);
8467
8468       /* don't let mole enter this field in this cycle;
8469          (give priority to objects falling to this field from above) */
8470       Stop[x][y] = TRUE;
8471     }
8472   }
8473 }
8474
8475 void AmoebeAbleger(int ax, int ay)
8476 {
8477   int i;
8478   int element = Feld[ax][ay];
8479   int graphic = el2img(element);
8480   int newax = ax, neway = ay;
8481   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8482   static int xy[4][2] =
8483   {
8484     { 0, -1 },
8485     { -1, 0 },
8486     { +1, 0 },
8487     { 0, +1 }
8488   };
8489
8490   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8491   {
8492     Feld[ax][ay] = EL_AMOEBA_DEAD;
8493     TEST_DrawLevelField(ax, ay);
8494     return;
8495   }
8496
8497   if (IS_ANIMATED(graphic))
8498     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8499
8500   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8501     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8502
8503   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8504   {
8505     MovDelay[ax][ay]--;
8506     if (MovDelay[ax][ay])
8507       return;
8508   }
8509
8510   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8511   {
8512     int start = RND(4);
8513     int x = ax + xy[start][0];
8514     int y = ay + xy[start][1];
8515
8516     if (!IN_LEV_FIELD(x, y))
8517       return;
8518
8519     if (IS_FREE(x, y) ||
8520         CAN_GROW_INTO(Feld[x][y]) ||
8521         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8522         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8523     {
8524       newax = x;
8525       neway = y;
8526     }
8527
8528     if (newax == ax && neway == ay)
8529       return;
8530   }
8531   else                          /* normal or "filled" (BD style) amoeba */
8532   {
8533     int start = RND(4);
8534     boolean waiting_for_player = FALSE;
8535
8536     for (i = 0; i < NUM_DIRECTIONS; i++)
8537     {
8538       int j = (start + i) % 4;
8539       int x = ax + xy[j][0];
8540       int y = ay + xy[j][1];
8541
8542       if (!IN_LEV_FIELD(x, y))
8543         continue;
8544
8545       if (IS_FREE(x, y) ||
8546           CAN_GROW_INTO(Feld[x][y]) ||
8547           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8548           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8549       {
8550         newax = x;
8551         neway = y;
8552         break;
8553       }
8554       else if (IS_PLAYER(x, y))
8555         waiting_for_player = TRUE;
8556     }
8557
8558     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8559     {
8560       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8561       {
8562         Feld[ax][ay] = EL_AMOEBA_DEAD;
8563         TEST_DrawLevelField(ax, ay);
8564         AmoebaCnt[AmoebaNr[ax][ay]]--;
8565
8566         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8567         {
8568           if (element == EL_AMOEBA_FULL)
8569             AmoebeUmwandeln(ax, ay);
8570           else if (element == EL_BD_AMOEBA)
8571             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8572         }
8573       }
8574       return;
8575     }
8576     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8577     {
8578       /* amoeba gets larger by growing in some direction */
8579
8580       int new_group_nr = AmoebaNr[ax][ay];
8581
8582 #ifdef DEBUG
8583   if (new_group_nr == 0)
8584   {
8585     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8586     printf("AmoebeAbleger(): This should never happen!\n");
8587     return;
8588   }
8589 #endif
8590
8591       AmoebaNr[newax][neway] = new_group_nr;
8592       AmoebaCnt[new_group_nr]++;
8593       AmoebaCnt2[new_group_nr]++;
8594
8595       /* if amoeba touches other amoeba(s) after growing, unify them */
8596       AmoebenVereinigen(newax, neway);
8597
8598       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8599       {
8600         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8601         return;
8602       }
8603     }
8604   }
8605
8606   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8607       (neway == lev_fieldy - 1 && newax != ax))
8608   {
8609     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8610     Store[newax][neway] = element;
8611   }
8612   else if (neway == ay || element == EL_EMC_DRIPPER)
8613   {
8614     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8615
8616     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8617   }
8618   else
8619   {
8620     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8621     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8622     Store[ax][ay] = EL_AMOEBA_DROP;
8623     ContinueMoving(ax, ay);
8624     return;
8625   }
8626
8627   TEST_DrawLevelField(newax, neway);
8628 }
8629
8630 void Life(int ax, int ay)
8631 {
8632   int x1, y1, x2, y2;
8633   int life_time = 40;
8634   int element = Feld[ax][ay];
8635   int graphic = el2img(element);
8636   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8637                          level.biomaze);
8638   boolean changed = FALSE;
8639
8640   if (IS_ANIMATED(graphic))
8641     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8642
8643   if (Stop[ax][ay])
8644     return;
8645
8646   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8647     MovDelay[ax][ay] = life_time;
8648
8649   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8650   {
8651     MovDelay[ax][ay]--;
8652     if (MovDelay[ax][ay])
8653       return;
8654   }
8655
8656   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8657   {
8658     int xx = ax+x1, yy = ay+y1;
8659     int nachbarn = 0;
8660
8661     if (!IN_LEV_FIELD(xx, yy))
8662       continue;
8663
8664     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8665     {
8666       int x = xx+x2, y = yy+y2;
8667
8668       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8669         continue;
8670
8671       if (((Feld[x][y] == element ||
8672             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8673            !Stop[x][y]) ||
8674           (IS_FREE(x, y) && Stop[x][y]))
8675         nachbarn++;
8676     }
8677
8678     if (xx == ax && yy == ay)           /* field in the middle */
8679     {
8680       if (nachbarn < life_parameter[0] ||
8681           nachbarn > life_parameter[1])
8682       {
8683         Feld[xx][yy] = EL_EMPTY;
8684         if (!Stop[xx][yy])
8685           TEST_DrawLevelField(xx, yy);
8686         Stop[xx][yy] = TRUE;
8687         changed = TRUE;
8688       }
8689     }
8690     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8691     {                                   /* free border field */
8692       if (nachbarn >= life_parameter[2] &&
8693           nachbarn <= life_parameter[3])
8694       {
8695         Feld[xx][yy] = element;
8696         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8697         if (!Stop[xx][yy])
8698           TEST_DrawLevelField(xx, yy);
8699         Stop[xx][yy] = TRUE;
8700         changed = TRUE;
8701       }
8702     }
8703   }
8704
8705   if (changed)
8706     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8707                    SND_GAME_OF_LIFE_GROWING);
8708 }
8709
8710 static void InitRobotWheel(int x, int y)
8711 {
8712   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8713 }
8714
8715 static void RunRobotWheel(int x, int y)
8716 {
8717   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8718 }
8719
8720 static void StopRobotWheel(int x, int y)
8721 {
8722   if (ZX == x && ZY == y)
8723   {
8724     ZX = ZY = -1;
8725
8726     game.robot_wheel_active = FALSE;
8727   }
8728 }
8729
8730 static void InitTimegateWheel(int x, int y)
8731 {
8732   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8733 }
8734
8735 static void RunTimegateWheel(int x, int y)
8736 {
8737   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8738 }
8739
8740 static void InitMagicBallDelay(int x, int y)
8741 {
8742   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8743 }
8744
8745 static void ActivateMagicBall(int bx, int by)
8746 {
8747   int x, y;
8748
8749   if (level.ball_random)
8750   {
8751     int pos_border = RND(8);    /* select one of the eight border elements */
8752     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8753     int xx = pos_content % 3;
8754     int yy = pos_content / 3;
8755
8756     x = bx - 1 + xx;
8757     y = by - 1 + yy;
8758
8759     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8760       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8761   }
8762   else
8763   {
8764     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8765     {
8766       int xx = x - bx + 1;
8767       int yy = y - by + 1;
8768
8769       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8770         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8771     }
8772   }
8773
8774   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8775 }
8776
8777 void CheckExit(int x, int y)
8778 {
8779   if (local_player->gems_still_needed > 0 ||
8780       local_player->sokobanfields_still_needed > 0 ||
8781       local_player->lights_still_needed > 0)
8782   {
8783     int element = Feld[x][y];
8784     int graphic = el2img(element);
8785
8786     if (IS_ANIMATED(graphic))
8787       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8788
8789     return;
8790   }
8791
8792   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8793     return;
8794
8795   Feld[x][y] = EL_EXIT_OPENING;
8796
8797   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8798 }
8799
8800 void CheckExitEM(int x, int y)
8801 {
8802   if (local_player->gems_still_needed > 0 ||
8803       local_player->sokobanfields_still_needed > 0 ||
8804       local_player->lights_still_needed > 0)
8805   {
8806     int element = Feld[x][y];
8807     int graphic = el2img(element);
8808
8809     if (IS_ANIMATED(graphic))
8810       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8811
8812     return;
8813   }
8814
8815   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8816     return;
8817
8818   Feld[x][y] = EL_EM_EXIT_OPENING;
8819
8820   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8821 }
8822
8823 void CheckExitSteel(int x, int y)
8824 {
8825   if (local_player->gems_still_needed > 0 ||
8826       local_player->sokobanfields_still_needed > 0 ||
8827       local_player->lights_still_needed > 0)
8828   {
8829     int element = Feld[x][y];
8830     int graphic = el2img(element);
8831
8832     if (IS_ANIMATED(graphic))
8833       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8834
8835     return;
8836   }
8837
8838   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8839     return;
8840
8841   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8842
8843   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8844 }
8845
8846 void CheckExitSteelEM(int x, int y)
8847 {
8848   if (local_player->gems_still_needed > 0 ||
8849       local_player->sokobanfields_still_needed > 0 ||
8850       local_player->lights_still_needed > 0)
8851   {
8852     int element = Feld[x][y];
8853     int graphic = el2img(element);
8854
8855     if (IS_ANIMATED(graphic))
8856       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8857
8858     return;
8859   }
8860
8861   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8862     return;
8863
8864   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8865
8866   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8867 }
8868
8869 void CheckExitSP(int x, int y)
8870 {
8871   if (local_player->gems_still_needed > 0)
8872   {
8873     int element = Feld[x][y];
8874     int graphic = el2img(element);
8875
8876     if (IS_ANIMATED(graphic))
8877       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8878
8879     return;
8880   }
8881
8882   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8883     return;
8884
8885   Feld[x][y] = EL_SP_EXIT_OPENING;
8886
8887   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8888 }
8889
8890 static void CloseAllOpenTimegates()
8891 {
8892   int x, y;
8893
8894   SCAN_PLAYFIELD(x, y)
8895   {
8896     int element = Feld[x][y];
8897
8898     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8899     {
8900       Feld[x][y] = EL_TIMEGATE_CLOSING;
8901
8902       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8903     }
8904   }
8905 }
8906
8907 void DrawTwinkleOnField(int x, int y)
8908 {
8909   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8910     return;
8911
8912   if (Feld[x][y] == EL_BD_DIAMOND)
8913     return;
8914
8915   if (MovDelay[x][y] == 0)      /* next animation frame */
8916     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8917
8918   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8919   {
8920     MovDelay[x][y]--;
8921
8922     DrawLevelElementAnimation(x, y, Feld[x][y]);
8923
8924     if (MovDelay[x][y] != 0)
8925     {
8926       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8927                                            10 - MovDelay[x][y]);
8928
8929       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8930     }
8931   }
8932 }
8933
8934 void MauerWaechst(int x, int y)
8935 {
8936   int delay = 6;
8937
8938   if (!MovDelay[x][y])          /* next animation frame */
8939     MovDelay[x][y] = 3 * delay;
8940
8941   if (MovDelay[x][y])           /* wait some time before next frame */
8942   {
8943     MovDelay[x][y]--;
8944
8945     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8946     {
8947       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8948       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8949
8950       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8951     }
8952
8953     if (!MovDelay[x][y])
8954     {
8955       if (MovDir[x][y] == MV_LEFT)
8956       {
8957         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
8958           TEST_DrawLevelField(x - 1, y);
8959       }
8960       else if (MovDir[x][y] == MV_RIGHT)
8961       {
8962         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
8963           TEST_DrawLevelField(x + 1, y);
8964       }
8965       else if (MovDir[x][y] == MV_UP)
8966       {
8967         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
8968           TEST_DrawLevelField(x, y - 1);
8969       }
8970       else
8971       {
8972         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
8973           TEST_DrawLevelField(x, y + 1);
8974       }
8975
8976       Feld[x][y] = Store[x][y];
8977       Store[x][y] = 0;
8978       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
8979       TEST_DrawLevelField(x, y);
8980     }
8981   }
8982 }
8983
8984 void MauerAbleger(int ax, int ay)
8985 {
8986   int element = Feld[ax][ay];
8987   int graphic = el2img(element);
8988   boolean oben_frei = FALSE, unten_frei = FALSE;
8989   boolean links_frei = FALSE, rechts_frei = FALSE;
8990   boolean oben_massiv = FALSE, unten_massiv = FALSE;
8991   boolean links_massiv = FALSE, rechts_massiv = FALSE;
8992   boolean new_wall = FALSE;
8993
8994   if (IS_ANIMATED(graphic))
8995     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8996
8997   if (!MovDelay[ax][ay])        /* start building new wall */
8998     MovDelay[ax][ay] = 6;
8999
9000   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9001   {
9002     MovDelay[ax][ay]--;
9003     if (MovDelay[ax][ay])
9004       return;
9005   }
9006
9007   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9008     oben_frei = TRUE;
9009   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9010     unten_frei = TRUE;
9011   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9012     links_frei = TRUE;
9013   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9014     rechts_frei = TRUE;
9015
9016   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9017       element == EL_EXPANDABLE_WALL_ANY)
9018   {
9019     if (oben_frei)
9020     {
9021       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9022       Store[ax][ay-1] = element;
9023       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9024       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9025         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9026                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9027       new_wall = TRUE;
9028     }
9029     if (unten_frei)
9030     {
9031       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9032       Store[ax][ay+1] = element;
9033       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9034       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9035         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9036                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9037       new_wall = TRUE;
9038     }
9039   }
9040
9041   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9042       element == EL_EXPANDABLE_WALL_ANY ||
9043       element == EL_EXPANDABLE_WALL ||
9044       element == EL_BD_EXPANDABLE_WALL)
9045   {
9046     if (links_frei)
9047     {
9048       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9049       Store[ax-1][ay] = element;
9050       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9051       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9052         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9053                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9054       new_wall = TRUE;
9055     }
9056
9057     if (rechts_frei)
9058     {
9059       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9060       Store[ax+1][ay] = element;
9061       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9062       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9063         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9064                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9065       new_wall = TRUE;
9066     }
9067   }
9068
9069   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9070     TEST_DrawLevelField(ax, ay);
9071
9072   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9073     oben_massiv = TRUE;
9074   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9075     unten_massiv = TRUE;
9076   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9077     links_massiv = TRUE;
9078   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9079     rechts_massiv = TRUE;
9080
9081   if (((oben_massiv && unten_massiv) ||
9082        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9083        element == EL_EXPANDABLE_WALL) &&
9084       ((links_massiv && rechts_massiv) ||
9085        element == EL_EXPANDABLE_WALL_VERTICAL))
9086     Feld[ax][ay] = EL_WALL;
9087
9088   if (new_wall)
9089     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9090 }
9091
9092 void MauerAblegerStahl(int ax, int ay)
9093 {
9094   int element = Feld[ax][ay];
9095   int graphic = el2img(element);
9096   boolean oben_frei = FALSE, unten_frei = FALSE;
9097   boolean links_frei = FALSE, rechts_frei = FALSE;
9098   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9099   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9100   boolean new_wall = FALSE;
9101
9102   if (IS_ANIMATED(graphic))
9103     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9104
9105   if (!MovDelay[ax][ay])        /* start building new wall */
9106     MovDelay[ax][ay] = 6;
9107
9108   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9109   {
9110     MovDelay[ax][ay]--;
9111     if (MovDelay[ax][ay])
9112       return;
9113   }
9114
9115   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9116     oben_frei = TRUE;
9117   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9118     unten_frei = TRUE;
9119   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9120     links_frei = TRUE;
9121   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9122     rechts_frei = TRUE;
9123
9124   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9125       element == EL_EXPANDABLE_STEELWALL_ANY)
9126   {
9127     if (oben_frei)
9128     {
9129       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9130       Store[ax][ay-1] = element;
9131       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9132       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9133         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9134                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9135       new_wall = TRUE;
9136     }
9137     if (unten_frei)
9138     {
9139       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9140       Store[ax][ay+1] = element;
9141       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9142       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9143         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9144                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9145       new_wall = TRUE;
9146     }
9147   }
9148
9149   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9150       element == EL_EXPANDABLE_STEELWALL_ANY)
9151   {
9152     if (links_frei)
9153     {
9154       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9155       Store[ax-1][ay] = element;
9156       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9157       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9158         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9159                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9160       new_wall = TRUE;
9161     }
9162
9163     if (rechts_frei)
9164     {
9165       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9166       Store[ax+1][ay] = element;
9167       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9168       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9169         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9170                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9171       new_wall = TRUE;
9172     }
9173   }
9174
9175   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9176     oben_massiv = TRUE;
9177   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9178     unten_massiv = TRUE;
9179   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9180     links_massiv = TRUE;
9181   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9182     rechts_massiv = TRUE;
9183
9184   if (((oben_massiv && unten_massiv) ||
9185        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9186       ((links_massiv && rechts_massiv) ||
9187        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9188     Feld[ax][ay] = EL_STEELWALL;
9189
9190   if (new_wall)
9191     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9192 }
9193
9194 void CheckForDragon(int x, int y)
9195 {
9196   int i, j;
9197   boolean dragon_found = FALSE;
9198   static int xy[4][2] =
9199   {
9200     { 0, -1 },
9201     { -1, 0 },
9202     { +1, 0 },
9203     { 0, +1 }
9204   };
9205
9206   for (i = 0; i < NUM_DIRECTIONS; i++)
9207   {
9208     for (j = 0; j < 4; j++)
9209     {
9210       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9211
9212       if (IN_LEV_FIELD(xx, yy) &&
9213           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9214       {
9215         if (Feld[xx][yy] == EL_DRAGON)
9216           dragon_found = TRUE;
9217       }
9218       else
9219         break;
9220     }
9221   }
9222
9223   if (!dragon_found)
9224   {
9225     for (i = 0; i < NUM_DIRECTIONS; i++)
9226     {
9227       for (j = 0; j < 3; j++)
9228       {
9229         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9230   
9231         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9232         {
9233           Feld[xx][yy] = EL_EMPTY;
9234           TEST_DrawLevelField(xx, yy);
9235         }
9236         else
9237           break;
9238       }
9239     }
9240   }
9241 }
9242
9243 static void InitBuggyBase(int x, int y)
9244 {
9245   int element = Feld[x][y];
9246   int activating_delay = FRAMES_PER_SECOND / 4;
9247
9248   ChangeDelay[x][y] =
9249     (element == EL_SP_BUGGY_BASE ?
9250      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9251      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9252      activating_delay :
9253      element == EL_SP_BUGGY_BASE_ACTIVE ?
9254      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9255 }
9256
9257 static void WarnBuggyBase(int x, int y)
9258 {
9259   int i;
9260   static int xy[4][2] =
9261   {
9262     { 0, -1 },
9263     { -1, 0 },
9264     { +1, 0 },
9265     { 0, +1 }
9266   };
9267
9268   for (i = 0; i < NUM_DIRECTIONS; i++)
9269   {
9270     int xx = x + xy[i][0];
9271     int yy = y + xy[i][1];
9272
9273     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9274     {
9275       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9276
9277       break;
9278     }
9279   }
9280 }
9281
9282 static void InitTrap(int x, int y)
9283 {
9284   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9285 }
9286
9287 static void ActivateTrap(int x, int y)
9288 {
9289   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9290 }
9291
9292 static void ChangeActiveTrap(int x, int y)
9293 {
9294   int graphic = IMG_TRAP_ACTIVE;
9295
9296   /* if new animation frame was drawn, correct crumbled sand border */
9297   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9298     TEST_DrawLevelFieldCrumbled(x, y);
9299 }
9300
9301 static int getSpecialActionElement(int element, int number, int base_element)
9302 {
9303   return (element != EL_EMPTY ? element :
9304           number != -1 ? base_element + number - 1 :
9305           EL_EMPTY);
9306 }
9307
9308 static int getModifiedActionNumber(int value_old, int operator, int operand,
9309                                    int value_min, int value_max)
9310 {
9311   int value_new = (operator == CA_MODE_SET      ? operand :
9312                    operator == CA_MODE_ADD      ? value_old + operand :
9313                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9314                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9315                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9316                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9317                    value_old);
9318
9319   return (value_new < value_min ? value_min :
9320           value_new > value_max ? value_max :
9321           value_new);
9322 }
9323
9324 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9325 {
9326   struct ElementInfo *ei = &element_info[element];
9327   struct ElementChangeInfo *change = &ei->change_page[page];
9328   int target_element = change->target_element;
9329   int action_type = change->action_type;
9330   int action_mode = change->action_mode;
9331   int action_arg = change->action_arg;
9332   int action_element = change->action_element;
9333   int i;
9334
9335   if (!change->has_action)
9336     return;
9337
9338   /* ---------- determine action paramater values -------------------------- */
9339
9340   int level_time_value =
9341     (level.time > 0 ? TimeLeft :
9342      TimePlayed);
9343
9344   int action_arg_element_raw =
9345     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9346      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9347      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9348      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9349      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9350      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9351      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9352      EL_EMPTY);
9353   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9354
9355   int action_arg_direction =
9356     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9357      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9358      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9359      change->actual_trigger_side :
9360      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9361      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9362      MV_NONE);
9363
9364   int action_arg_number_min =
9365     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9366      CA_ARG_MIN);
9367
9368   int action_arg_number_max =
9369     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9370      action_type == CA_SET_LEVEL_GEMS ? 999 :
9371      action_type == CA_SET_LEVEL_TIME ? 9999 :
9372      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9373      action_type == CA_SET_CE_VALUE ? 9999 :
9374      action_type == CA_SET_CE_SCORE ? 9999 :
9375      CA_ARG_MAX);
9376
9377   int action_arg_number_reset =
9378     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9379      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9380      action_type == CA_SET_LEVEL_TIME ? level.time :
9381      action_type == CA_SET_LEVEL_SCORE ? 0 :
9382      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9383      action_type == CA_SET_CE_SCORE ? 0 :
9384      0);
9385
9386   int action_arg_number =
9387     (action_arg <= CA_ARG_MAX ? action_arg :
9388      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9389      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9390      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9391      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9392      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9393      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9394      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9395      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9396      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9397      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9398      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9399      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9400      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9401      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9402      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9403      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9404      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9405      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9406      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9407      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9408      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9409      -1);
9410
9411   int action_arg_number_old =
9412     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9413      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9414      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9415      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9416      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9417      0);
9418
9419   int action_arg_number_new =
9420     getModifiedActionNumber(action_arg_number_old,
9421                             action_mode, action_arg_number,
9422                             action_arg_number_min, action_arg_number_max);
9423
9424   int trigger_player_bits =
9425     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9426      change->actual_trigger_player_bits : change->trigger_player);
9427
9428   int action_arg_player_bits =
9429     (action_arg >= CA_ARG_PLAYER_1 &&
9430      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9431      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9432      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9433      PLAYER_BITS_ANY);
9434
9435   /* ---------- execute action  -------------------------------------------- */
9436
9437   switch (action_type)
9438   {
9439     case CA_NO_ACTION:
9440     {
9441       return;
9442     }
9443
9444     /* ---------- level actions  ------------------------------------------- */
9445
9446     case CA_RESTART_LEVEL:
9447     {
9448       game.restart_level = TRUE;
9449
9450       break;
9451     }
9452
9453     case CA_SHOW_ENVELOPE:
9454     {
9455       int element = getSpecialActionElement(action_arg_element,
9456                                             action_arg_number, EL_ENVELOPE_1);
9457
9458       if (IS_ENVELOPE(element))
9459         local_player->show_envelope = element;
9460
9461       break;
9462     }
9463
9464     case CA_SET_LEVEL_TIME:
9465     {
9466       if (level.time > 0)       /* only modify limited time value */
9467       {
9468         TimeLeft = action_arg_number_new;
9469
9470         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9471
9472         DisplayGameControlValues();
9473
9474         if (!TimeLeft && setup.time_limit)
9475           for (i = 0; i < MAX_PLAYERS; i++)
9476             KillPlayer(&stored_player[i]);
9477       }
9478
9479       break;
9480     }
9481
9482     case CA_SET_LEVEL_SCORE:
9483     {
9484       local_player->score = action_arg_number_new;
9485
9486       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9487
9488       DisplayGameControlValues();
9489
9490       break;
9491     }
9492
9493     case CA_SET_LEVEL_GEMS:
9494     {
9495       local_player->gems_still_needed = action_arg_number_new;
9496
9497       game_panel_controls[GAME_PANEL_GEMS].value =
9498         local_player->gems_still_needed;
9499
9500       DisplayGameControlValues();
9501
9502       break;
9503     }
9504
9505     case CA_SET_LEVEL_WIND:
9506     {
9507       game.wind_direction = action_arg_direction;
9508
9509       break;
9510     }
9511
9512     case CA_SET_LEVEL_RANDOM_SEED:
9513     {
9514       /* ensure that setting a new random seed while playing is predictable */
9515       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9516
9517       break;
9518     }
9519
9520     /* ---------- player actions  ------------------------------------------ */
9521
9522     case CA_MOVE_PLAYER:
9523     {
9524       /* automatically move to the next field in specified direction */
9525       for (i = 0; i < MAX_PLAYERS; i++)
9526         if (trigger_player_bits & (1 << i))
9527           stored_player[i].programmed_action = action_arg_direction;
9528
9529       break;
9530     }
9531
9532     case CA_EXIT_PLAYER:
9533     {
9534       for (i = 0; i < MAX_PLAYERS; i++)
9535         if (action_arg_player_bits & (1 << i))
9536           PlayerWins(&stored_player[i]);
9537
9538       break;
9539     }
9540
9541     case CA_KILL_PLAYER:
9542     {
9543       for (i = 0; i < MAX_PLAYERS; i++)
9544         if (action_arg_player_bits & (1 << i))
9545           KillPlayer(&stored_player[i]);
9546
9547       break;
9548     }
9549
9550     case CA_SET_PLAYER_KEYS:
9551     {
9552       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9553       int element = getSpecialActionElement(action_arg_element,
9554                                             action_arg_number, EL_KEY_1);
9555
9556       if (IS_KEY(element))
9557       {
9558         for (i = 0; i < MAX_PLAYERS; i++)
9559         {
9560           if (trigger_player_bits & (1 << i))
9561           {
9562             stored_player[i].key[KEY_NR(element)] = key_state;
9563
9564             DrawGameDoorValues();
9565           }
9566         }
9567       }
9568
9569       break;
9570     }
9571
9572     case CA_SET_PLAYER_SPEED:
9573     {
9574       for (i = 0; i < MAX_PLAYERS; i++)
9575       {
9576         if (trigger_player_bits & (1 << i))
9577         {
9578           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9579
9580           if (action_arg == CA_ARG_SPEED_FASTER &&
9581               stored_player[i].cannot_move)
9582           {
9583             action_arg_number = STEPSIZE_VERY_SLOW;
9584           }
9585           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9586                    action_arg == CA_ARG_SPEED_FASTER)
9587           {
9588             action_arg_number = 2;
9589             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9590                            CA_MODE_MULTIPLY);
9591           }
9592           else if (action_arg == CA_ARG_NUMBER_RESET)
9593           {
9594             action_arg_number = level.initial_player_stepsize[i];
9595           }
9596
9597           move_stepsize =
9598             getModifiedActionNumber(move_stepsize,
9599                                     action_mode,
9600                                     action_arg_number,
9601                                     action_arg_number_min,
9602                                     action_arg_number_max);
9603
9604           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9605         }
9606       }
9607
9608       break;
9609     }
9610
9611     case CA_SET_PLAYER_SHIELD:
9612     {
9613       for (i = 0; i < MAX_PLAYERS; i++)
9614       {
9615         if (trigger_player_bits & (1 << i))
9616         {
9617           if (action_arg == CA_ARG_SHIELD_OFF)
9618           {
9619             stored_player[i].shield_normal_time_left = 0;
9620             stored_player[i].shield_deadly_time_left = 0;
9621           }
9622           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9623           {
9624             stored_player[i].shield_normal_time_left = 999999;
9625           }
9626           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9627           {
9628             stored_player[i].shield_normal_time_left = 999999;
9629             stored_player[i].shield_deadly_time_left = 999999;
9630           }
9631         }
9632       }
9633
9634       break;
9635     }
9636
9637     case CA_SET_PLAYER_GRAVITY:
9638     {
9639       for (i = 0; i < MAX_PLAYERS; i++)
9640       {
9641         if (trigger_player_bits & (1 << i))
9642         {
9643           stored_player[i].gravity =
9644             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9645              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9646              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9647              stored_player[i].gravity);
9648         }
9649       }
9650
9651       break;
9652     }
9653
9654     case CA_SET_PLAYER_ARTWORK:
9655     {
9656       for (i = 0; i < MAX_PLAYERS; i++)
9657       {
9658         if (trigger_player_bits & (1 << i))
9659         {
9660           int artwork_element = action_arg_element;
9661
9662           if (action_arg == CA_ARG_ELEMENT_RESET)
9663             artwork_element =
9664               (level.use_artwork_element[i] ? level.artwork_element[i] :
9665                stored_player[i].element_nr);
9666
9667           if (stored_player[i].artwork_element != artwork_element)
9668             stored_player[i].Frame = 0;
9669
9670           stored_player[i].artwork_element = artwork_element;
9671
9672           SetPlayerWaiting(&stored_player[i], FALSE);
9673
9674           /* set number of special actions for bored and sleeping animation */
9675           stored_player[i].num_special_action_bored =
9676             get_num_special_action(artwork_element,
9677                                    ACTION_BORING_1, ACTION_BORING_LAST);
9678           stored_player[i].num_special_action_sleeping =
9679             get_num_special_action(artwork_element,
9680                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9681         }
9682       }
9683
9684       break;
9685     }
9686
9687     case CA_SET_PLAYER_INVENTORY:
9688     {
9689       for (i = 0; i < MAX_PLAYERS; i++)
9690       {
9691         struct PlayerInfo *player = &stored_player[i];
9692         int j, k;
9693
9694         if (trigger_player_bits & (1 << i))
9695         {
9696           int inventory_element = action_arg_element;
9697
9698           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9699               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9700               action_arg == CA_ARG_ELEMENT_ACTION)
9701           {
9702             int element = inventory_element;
9703             int collect_count = element_info[element].collect_count_initial;
9704
9705             if (!IS_CUSTOM_ELEMENT(element))
9706               collect_count = 1;
9707
9708             if (collect_count == 0)
9709               player->inventory_infinite_element = element;
9710             else
9711               for (k = 0; k < collect_count; k++)
9712                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9713                   player->inventory_element[player->inventory_size++] =
9714                     element;
9715           }
9716           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9717                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9718                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9719           {
9720             if (player->inventory_infinite_element != EL_UNDEFINED &&
9721                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9722                                      action_arg_element_raw))
9723               player->inventory_infinite_element = EL_UNDEFINED;
9724
9725             for (k = 0, j = 0; j < player->inventory_size; j++)
9726             {
9727               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9728                                         action_arg_element_raw))
9729                 player->inventory_element[k++] = player->inventory_element[j];
9730             }
9731
9732             player->inventory_size = k;
9733           }
9734           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9735           {
9736             if (player->inventory_size > 0)
9737             {
9738               for (j = 0; j < player->inventory_size - 1; j++)
9739                 player->inventory_element[j] = player->inventory_element[j + 1];
9740
9741               player->inventory_size--;
9742             }
9743           }
9744           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9745           {
9746             if (player->inventory_size > 0)
9747               player->inventory_size--;
9748           }
9749           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9750           {
9751             player->inventory_infinite_element = EL_UNDEFINED;
9752             player->inventory_size = 0;
9753           }
9754           else if (action_arg == CA_ARG_INVENTORY_RESET)
9755           {
9756             player->inventory_infinite_element = EL_UNDEFINED;
9757             player->inventory_size = 0;
9758
9759             if (level.use_initial_inventory[i])
9760             {
9761               for (j = 0; j < level.initial_inventory_size[i]; j++)
9762               {
9763                 int element = level.initial_inventory_content[i][j];
9764                 int collect_count = element_info[element].collect_count_initial;
9765
9766                 if (!IS_CUSTOM_ELEMENT(element))
9767                   collect_count = 1;
9768
9769                 if (collect_count == 0)
9770                   player->inventory_infinite_element = element;
9771                 else
9772                   for (k = 0; k < collect_count; k++)
9773                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9774                       player->inventory_element[player->inventory_size++] =
9775                         element;
9776               }
9777             }
9778           }
9779         }
9780       }
9781
9782       break;
9783     }
9784
9785     /* ---------- CE actions  ---------------------------------------------- */
9786
9787     case CA_SET_CE_VALUE:
9788     {
9789       int last_ce_value = CustomValue[x][y];
9790
9791       CustomValue[x][y] = action_arg_number_new;
9792
9793       if (CustomValue[x][y] != last_ce_value)
9794       {
9795         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9796         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9797
9798         if (CustomValue[x][y] == 0)
9799         {
9800           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9801           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9802         }
9803       }
9804
9805       break;
9806     }
9807
9808     case CA_SET_CE_SCORE:
9809     {
9810       int last_ce_score = ei->collect_score;
9811
9812       ei->collect_score = action_arg_number_new;
9813
9814       if (ei->collect_score != last_ce_score)
9815       {
9816         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9817         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9818
9819         if (ei->collect_score == 0)
9820         {
9821           int xx, yy;
9822
9823           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9824           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9825
9826           /*
9827             This is a very special case that seems to be a mixture between
9828             CheckElementChange() and CheckTriggeredElementChange(): while
9829             the first one only affects single elements that are triggered
9830             directly, the second one affects multiple elements in the playfield
9831             that are triggered indirectly by another element. This is a third
9832             case: Changing the CE score always affects multiple identical CEs,
9833             so every affected CE must be checked, not only the single CE for
9834             which the CE score was changed in the first place (as every instance
9835             of that CE shares the same CE score, and therefore also can change)!
9836           */
9837           SCAN_PLAYFIELD(xx, yy)
9838           {
9839             if (Feld[xx][yy] == element)
9840               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9841                                  CE_SCORE_GETS_ZERO);
9842           }
9843         }
9844       }
9845
9846       break;
9847     }
9848
9849     case CA_SET_CE_ARTWORK:
9850     {
9851       int artwork_element = action_arg_element;
9852       boolean reset_frame = FALSE;
9853       int xx, yy;
9854
9855       if (action_arg == CA_ARG_ELEMENT_RESET)
9856         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9857                            element);
9858
9859       if (ei->gfx_element != artwork_element)
9860         reset_frame = TRUE;
9861
9862       ei->gfx_element = artwork_element;
9863
9864       SCAN_PLAYFIELD(xx, yy)
9865       {
9866         if (Feld[xx][yy] == element)
9867         {
9868           if (reset_frame)
9869           {
9870             ResetGfxAnimation(xx, yy);
9871             ResetRandomAnimationValue(xx, yy);
9872           }
9873
9874           TEST_DrawLevelField(xx, yy);
9875         }
9876       }
9877
9878       break;
9879     }
9880
9881     /* ---------- engine actions  ------------------------------------------ */
9882
9883     case CA_SET_ENGINE_SCAN_MODE:
9884     {
9885       InitPlayfieldScanMode(action_arg);
9886
9887       break;
9888     }
9889
9890     default:
9891       break;
9892   }
9893 }
9894
9895 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9896 {
9897   int old_element = Feld[x][y];
9898   int new_element = GetElementFromGroupElement(element);
9899   int previous_move_direction = MovDir[x][y];
9900   int last_ce_value = CustomValue[x][y];
9901   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9902   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9903   boolean add_player_onto_element = (new_element_is_player &&
9904                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9905                                      IS_WALKABLE(old_element));
9906
9907   if (!add_player_onto_element)
9908   {
9909     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9910       RemoveMovingField(x, y);
9911     else
9912       RemoveField(x, y);
9913
9914     Feld[x][y] = new_element;
9915
9916     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9917       MovDir[x][y] = previous_move_direction;
9918
9919     if (element_info[new_element].use_last_ce_value)
9920       CustomValue[x][y] = last_ce_value;
9921
9922     InitField_WithBug1(x, y, FALSE);
9923
9924     new_element = Feld[x][y];   /* element may have changed */
9925
9926     ResetGfxAnimation(x, y);
9927     ResetRandomAnimationValue(x, y);
9928
9929     TEST_DrawLevelField(x, y);
9930
9931     if (GFX_CRUMBLED(new_element))
9932       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9933   }
9934
9935   /* check if element under the player changes from accessible to unaccessible
9936      (needed for special case of dropping element which then changes) */
9937   /* (must be checked after creating new element for walkable group elements) */
9938   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9939       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9940   {
9941     Bang(x, y);
9942
9943     return;
9944   }
9945
9946   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9947   if (new_element_is_player)
9948     RelocatePlayer(x, y, new_element);
9949
9950   if (is_change)
9951     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9952
9953   TestIfBadThingTouchesPlayer(x, y);
9954   TestIfPlayerTouchesCustomElement(x, y);
9955   TestIfElementTouchesCustomElement(x, y);
9956 }
9957
9958 static void CreateField(int x, int y, int element)
9959 {
9960   CreateFieldExt(x, y, element, FALSE);
9961 }
9962
9963 static void CreateElementFromChange(int x, int y, int element)
9964 {
9965   element = GET_VALID_RUNTIME_ELEMENT(element);
9966
9967   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
9968   {
9969     int old_element = Feld[x][y];
9970
9971     /* prevent changed element from moving in same engine frame
9972        unless both old and new element can either fall or move */
9973     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
9974         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
9975       Stop[x][y] = TRUE;
9976   }
9977
9978   CreateFieldExt(x, y, element, TRUE);
9979 }
9980
9981 static boolean ChangeElement(int x, int y, int element, int page)
9982 {
9983   struct ElementInfo *ei = &element_info[element];
9984   struct ElementChangeInfo *change = &ei->change_page[page];
9985   int ce_value = CustomValue[x][y];
9986   int ce_score = ei->collect_score;
9987   int target_element;
9988   int old_element = Feld[x][y];
9989
9990   /* always use default change event to prevent running into a loop */
9991   if (ChangeEvent[x][y] == -1)
9992     ChangeEvent[x][y] = CE_DELAY;
9993
9994   if (ChangeEvent[x][y] == CE_DELAY)
9995   {
9996     /* reset actual trigger element, trigger player and action element */
9997     change->actual_trigger_element = EL_EMPTY;
9998     change->actual_trigger_player = EL_EMPTY;
9999     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10000     change->actual_trigger_side = CH_SIDE_NONE;
10001     change->actual_trigger_ce_value = 0;
10002     change->actual_trigger_ce_score = 0;
10003   }
10004
10005   /* do not change elements more than a specified maximum number of changes */
10006   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10007     return FALSE;
10008
10009   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10010
10011   if (change->explode)
10012   {
10013     Bang(x, y);
10014
10015     return TRUE;
10016   }
10017
10018   if (change->use_target_content)
10019   {
10020     boolean complete_replace = TRUE;
10021     boolean can_replace[3][3];
10022     int xx, yy;
10023
10024     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10025     {
10026       boolean is_empty;
10027       boolean is_walkable;
10028       boolean is_diggable;
10029       boolean is_collectible;
10030       boolean is_removable;
10031       boolean is_destructible;
10032       int ex = x + xx - 1;
10033       int ey = y + yy - 1;
10034       int content_element = change->target_content.e[xx][yy];
10035       int e;
10036
10037       can_replace[xx][yy] = TRUE;
10038
10039       if (ex == x && ey == y)   /* do not check changing element itself */
10040         continue;
10041
10042       if (content_element == EL_EMPTY_SPACE)
10043       {
10044         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10045
10046         continue;
10047       }
10048
10049       if (!IN_LEV_FIELD(ex, ey))
10050       {
10051         can_replace[xx][yy] = FALSE;
10052         complete_replace = FALSE;
10053
10054         continue;
10055       }
10056
10057       e = Feld[ex][ey];
10058
10059       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10060         e = MovingOrBlocked2Element(ex, ey);
10061
10062       is_empty = (IS_FREE(ex, ey) ||
10063                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10064
10065       is_walkable     = (is_empty || IS_WALKABLE(e));
10066       is_diggable     = (is_empty || IS_DIGGABLE(e));
10067       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10068       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10069       is_removable    = (is_diggable || is_collectible);
10070
10071       can_replace[xx][yy] =
10072         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10073           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10074           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10075           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10076           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10077           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10078          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10079
10080       if (!can_replace[xx][yy])
10081         complete_replace = FALSE;
10082     }
10083
10084     if (!change->only_if_complete || complete_replace)
10085     {
10086       boolean something_has_changed = FALSE;
10087
10088       if (change->only_if_complete && change->use_random_replace &&
10089           RND(100) < change->random_percentage)
10090         return FALSE;
10091
10092       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10093       {
10094         int ex = x + xx - 1;
10095         int ey = y + yy - 1;
10096         int content_element;
10097
10098         if (can_replace[xx][yy] && (!change->use_random_replace ||
10099                                     RND(100) < change->random_percentage))
10100         {
10101           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10102             RemoveMovingField(ex, ey);
10103
10104           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10105
10106           content_element = change->target_content.e[xx][yy];
10107           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10108                                               ce_value, ce_score);
10109
10110           CreateElementFromChange(ex, ey, target_element);
10111
10112           something_has_changed = TRUE;
10113
10114           /* for symmetry reasons, freeze newly created border elements */
10115           if (ex != x || ey != y)
10116             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10117         }
10118       }
10119
10120       if (something_has_changed)
10121       {
10122         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10123         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10124       }
10125     }
10126   }
10127   else
10128   {
10129     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10130                                         ce_value, ce_score);
10131
10132     if (element == EL_DIAGONAL_GROWING ||
10133         element == EL_DIAGONAL_SHRINKING)
10134     {
10135       target_element = Store[x][y];
10136
10137       Store[x][y] = EL_EMPTY;
10138     }
10139
10140     CreateElementFromChange(x, y, target_element);
10141
10142     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10143     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10144   }
10145
10146   /* this uses direct change before indirect change */
10147   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10148
10149   return TRUE;
10150 }
10151
10152 static void HandleElementChange(int x, int y, int page)
10153 {
10154   int element = MovingOrBlocked2Element(x, y);
10155   struct ElementInfo *ei = &element_info[element];
10156   struct ElementChangeInfo *change = &ei->change_page[page];
10157   boolean handle_action_before_change = FALSE;
10158
10159 #ifdef DEBUG
10160   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10161       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10162   {
10163     printf("\n\n");
10164     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10165            x, y, element, element_info[element].token_name);
10166     printf("HandleElementChange(): This should never happen!\n");
10167     printf("\n\n");
10168   }
10169 #endif
10170
10171   /* this can happen with classic bombs on walkable, changing elements */
10172   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10173   {
10174     return;
10175   }
10176
10177   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10178   {
10179     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10180
10181     if (change->can_change)
10182     {
10183       /* !!! not clear why graphic animation should be reset at all here !!! */
10184       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10185       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10186
10187       /*
10188         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10189
10190         When using an animation frame delay of 1 (this only happens with
10191         "sp_zonk.moving.left/right" in the classic graphics), the default
10192         (non-moving) animation shows wrong animation frames (while the
10193         moving animation, like "sp_zonk.moving.left/right", is correct,
10194         so this graphical bug never shows up with the classic graphics).
10195         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10196         be drawn instead of the correct frames 0,1,2,3. This is caused by
10197         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10198         an element change: First when the change delay ("ChangeDelay[][]")
10199         counter has reached zero after decrementing, then a second time in
10200         the next frame (after "GfxFrame[][]" was already incremented) when
10201         "ChangeDelay[][]" is reset to the initial delay value again.
10202
10203         This causes frame 0 to be drawn twice, while the last frame won't
10204         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10205
10206         As some animations may already be cleverly designed around this bug
10207         (at least the "Snake Bite" snake tail animation does this), it cannot
10208         simply be fixed here without breaking such existing animations.
10209         Unfortunately, it cannot easily be detected if a graphics set was
10210         designed "before" or "after" the bug was fixed. As a workaround,
10211         a new graphics set option "game.graphics_engine_version" was added
10212         to be able to specify the game's major release version for which the
10213         graphics set was designed, which can then be used to decide if the
10214         bugfix should be used (version 4 and above) or not (version 3 or
10215         below, or if no version was specified at all, as with old sets).
10216
10217         (The wrong/fixed animation frames can be tested with the test level set
10218         "test_gfxframe" and level "000", which contains a specially prepared
10219         custom element at level position (x/y) == (11/9) which uses the zonk
10220         animation mentioned above. Using "game.graphics_engine_version: 4"
10221         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10222         This can also be seen from the debug output for this test element.)
10223       */
10224
10225       /* when a custom element is about to change (for example by change delay),
10226          do not reset graphic animation when the custom element is moving */
10227       if (game.graphics_engine_version < 4 &&
10228           !IS_MOVING(x, y))
10229       {
10230         ResetGfxAnimation(x, y);
10231         ResetRandomAnimationValue(x, y);
10232       }
10233
10234       if (change->pre_change_function)
10235         change->pre_change_function(x, y);
10236     }
10237   }
10238
10239   ChangeDelay[x][y]--;
10240
10241   if (ChangeDelay[x][y] != 0)           /* continue element change */
10242   {
10243     if (change->can_change)
10244     {
10245       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10246
10247       if (IS_ANIMATED(graphic))
10248         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10249
10250       if (change->change_function)
10251         change->change_function(x, y);
10252     }
10253   }
10254   else                                  /* finish element change */
10255   {
10256     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10257     {
10258       page = ChangePage[x][y];
10259       ChangePage[x][y] = -1;
10260
10261       change = &ei->change_page[page];
10262     }
10263
10264     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10265     {
10266       ChangeDelay[x][y] = 1;            /* try change after next move step */
10267       ChangePage[x][y] = page;          /* remember page to use for change */
10268
10269       return;
10270     }
10271
10272     /* special case: set new level random seed before changing element */
10273     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10274       handle_action_before_change = TRUE;
10275
10276     if (change->has_action && handle_action_before_change)
10277       ExecuteCustomElementAction(x, y, element, page);
10278
10279     if (change->can_change)
10280     {
10281       if (ChangeElement(x, y, element, page))
10282       {
10283         if (change->post_change_function)
10284           change->post_change_function(x, y);
10285       }
10286     }
10287
10288     if (change->has_action && !handle_action_before_change)
10289       ExecuteCustomElementAction(x, y, element, page);
10290   }
10291 }
10292
10293 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10294                                               int trigger_element,
10295                                               int trigger_event,
10296                                               int trigger_player,
10297                                               int trigger_side,
10298                                               int trigger_page)
10299 {
10300   boolean change_done_any = FALSE;
10301   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10302   int i;
10303
10304   if (!(trigger_events[trigger_element][trigger_event]))
10305     return FALSE;
10306
10307   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10308
10309   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10310   {
10311     int element = EL_CUSTOM_START + i;
10312     boolean change_done = FALSE;
10313     int p;
10314
10315     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10316         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10317       continue;
10318
10319     for (p = 0; p < element_info[element].num_change_pages; p++)
10320     {
10321       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10322
10323       if (change->can_change_or_has_action &&
10324           change->has_event[trigger_event] &&
10325           change->trigger_side & trigger_side &&
10326           change->trigger_player & trigger_player &&
10327           change->trigger_page & trigger_page_bits &&
10328           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10329       {
10330         change->actual_trigger_element = trigger_element;
10331         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10332         change->actual_trigger_player_bits = trigger_player;
10333         change->actual_trigger_side = trigger_side;
10334         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10335         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10336
10337         if ((change->can_change && !change_done) || change->has_action)
10338         {
10339           int x, y;
10340
10341           SCAN_PLAYFIELD(x, y)
10342           {
10343             if (Feld[x][y] == element)
10344             {
10345               if (change->can_change && !change_done)
10346               {
10347                 /* if element already changed in this frame, not only prevent
10348                    another element change (checked in ChangeElement()), but
10349                    also prevent additional element actions for this element */
10350
10351                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10352                     !level.use_action_after_change_bug)
10353                   continue;
10354
10355                 ChangeDelay[x][y] = 1;
10356                 ChangeEvent[x][y] = trigger_event;
10357
10358                 HandleElementChange(x, y, p);
10359               }
10360               else if (change->has_action)
10361               {
10362                 /* if element already changed in this frame, not only prevent
10363                    another element change (checked in ChangeElement()), but
10364                    also prevent additional element actions for this element */
10365
10366                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10367                     !level.use_action_after_change_bug)
10368                   continue;
10369
10370                 ExecuteCustomElementAction(x, y, element, p);
10371                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10372               }
10373             }
10374           }
10375
10376           if (change->can_change)
10377           {
10378             change_done = TRUE;
10379             change_done_any = TRUE;
10380           }
10381         }
10382       }
10383     }
10384   }
10385
10386   RECURSION_LOOP_DETECTION_END();
10387
10388   return change_done_any;
10389 }
10390
10391 static boolean CheckElementChangeExt(int x, int y,
10392                                      int element,
10393                                      int trigger_element,
10394                                      int trigger_event,
10395                                      int trigger_player,
10396                                      int trigger_side)
10397 {
10398   boolean change_done = FALSE;
10399   int p;
10400
10401   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10402       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10403     return FALSE;
10404
10405   if (Feld[x][y] == EL_BLOCKED)
10406   {
10407     Blocked2Moving(x, y, &x, &y);
10408     element = Feld[x][y];
10409   }
10410
10411   /* check if element has already changed or is about to change after moving */
10412   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10413        Feld[x][y] != element) ||
10414
10415       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10416        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10417         ChangePage[x][y] != -1)))
10418     return FALSE;
10419
10420   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10421
10422   for (p = 0; p < element_info[element].num_change_pages; p++)
10423   {
10424     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10425
10426     /* check trigger element for all events where the element that is checked
10427        for changing interacts with a directly adjacent element -- this is
10428        different to element changes that affect other elements to change on the
10429        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10430     boolean check_trigger_element =
10431       (trigger_event == CE_TOUCHING_X ||
10432        trigger_event == CE_HITTING_X ||
10433        trigger_event == CE_HIT_BY_X ||
10434        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10435
10436     if (change->can_change_or_has_action &&
10437         change->has_event[trigger_event] &&
10438         change->trigger_side & trigger_side &&
10439         change->trigger_player & trigger_player &&
10440         (!check_trigger_element ||
10441          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10442     {
10443       change->actual_trigger_element = trigger_element;
10444       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10445       change->actual_trigger_player_bits = trigger_player;
10446       change->actual_trigger_side = trigger_side;
10447       change->actual_trigger_ce_value = CustomValue[x][y];
10448       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10449
10450       /* special case: trigger element not at (x,y) position for some events */
10451       if (check_trigger_element)
10452       {
10453         static struct
10454         {
10455           int dx, dy;
10456         } move_xy[] =
10457           {
10458             {  0,  0 },
10459             { -1,  0 },
10460             { +1,  0 },
10461             {  0,  0 },
10462             {  0, -1 },
10463             {  0,  0 }, { 0, 0 }, { 0, 0 },
10464             {  0, +1 }
10465           };
10466
10467         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10468         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10469
10470         change->actual_trigger_ce_value = CustomValue[xx][yy];
10471         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10472       }
10473
10474       if (change->can_change && !change_done)
10475       {
10476         ChangeDelay[x][y] = 1;
10477         ChangeEvent[x][y] = trigger_event;
10478
10479         HandleElementChange(x, y, p);
10480
10481         change_done = TRUE;
10482       }
10483       else if (change->has_action)
10484       {
10485         ExecuteCustomElementAction(x, y, element, p);
10486         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10487       }
10488     }
10489   }
10490
10491   RECURSION_LOOP_DETECTION_END();
10492
10493   return change_done;
10494 }
10495
10496 static void PlayPlayerSound(struct PlayerInfo *player)
10497 {
10498   int jx = player->jx, jy = player->jy;
10499   int sound_element = player->artwork_element;
10500   int last_action = player->last_action_waiting;
10501   int action = player->action_waiting;
10502
10503   if (player->is_waiting)
10504   {
10505     if (action != last_action)
10506       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10507     else
10508       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10509   }
10510   else
10511   {
10512     if (action != last_action)
10513       StopSound(element_info[sound_element].sound[last_action]);
10514
10515     if (last_action == ACTION_SLEEPING)
10516       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10517   }
10518 }
10519
10520 static void PlayAllPlayersSound()
10521 {
10522   int i;
10523
10524   for (i = 0; i < MAX_PLAYERS; i++)
10525     if (stored_player[i].active)
10526       PlayPlayerSound(&stored_player[i]);
10527 }
10528
10529 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10530 {
10531   boolean last_waiting = player->is_waiting;
10532   int move_dir = player->MovDir;
10533
10534   player->dir_waiting = move_dir;
10535   player->last_action_waiting = player->action_waiting;
10536
10537   if (is_waiting)
10538   {
10539     if (!last_waiting)          /* not waiting -> waiting */
10540     {
10541       player->is_waiting = TRUE;
10542
10543       player->frame_counter_bored =
10544         FrameCounter +
10545         game.player_boring_delay_fixed +
10546         GetSimpleRandom(game.player_boring_delay_random);
10547       player->frame_counter_sleeping =
10548         FrameCounter +
10549         game.player_sleeping_delay_fixed +
10550         GetSimpleRandom(game.player_sleeping_delay_random);
10551
10552       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10553     }
10554
10555     if (game.player_sleeping_delay_fixed +
10556         game.player_sleeping_delay_random > 0 &&
10557         player->anim_delay_counter == 0 &&
10558         player->post_delay_counter == 0 &&
10559         FrameCounter >= player->frame_counter_sleeping)
10560       player->is_sleeping = TRUE;
10561     else if (game.player_boring_delay_fixed +
10562              game.player_boring_delay_random > 0 &&
10563              FrameCounter >= player->frame_counter_bored)
10564       player->is_bored = TRUE;
10565
10566     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10567                               player->is_bored ? ACTION_BORING :
10568                               ACTION_WAITING);
10569
10570     if (player->is_sleeping && player->use_murphy)
10571     {
10572       /* special case for sleeping Murphy when leaning against non-free tile */
10573
10574       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10575           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10576            !IS_MOVING(player->jx - 1, player->jy)))
10577         move_dir = MV_LEFT;
10578       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10579                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10580                 !IS_MOVING(player->jx + 1, player->jy)))
10581         move_dir = MV_RIGHT;
10582       else
10583         player->is_sleeping = FALSE;
10584
10585       player->dir_waiting = move_dir;
10586     }
10587
10588     if (player->is_sleeping)
10589     {
10590       if (player->num_special_action_sleeping > 0)
10591       {
10592         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10593         {
10594           int last_special_action = player->special_action_sleeping;
10595           int num_special_action = player->num_special_action_sleeping;
10596           int special_action =
10597             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10598              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10599              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10600              last_special_action + 1 : ACTION_SLEEPING);
10601           int special_graphic =
10602             el_act_dir2img(player->artwork_element, special_action, move_dir);
10603
10604           player->anim_delay_counter =
10605             graphic_info[special_graphic].anim_delay_fixed +
10606             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10607           player->post_delay_counter =
10608             graphic_info[special_graphic].post_delay_fixed +
10609             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10610
10611           player->special_action_sleeping = special_action;
10612         }
10613
10614         if (player->anim_delay_counter > 0)
10615         {
10616           player->action_waiting = player->special_action_sleeping;
10617           player->anim_delay_counter--;
10618         }
10619         else if (player->post_delay_counter > 0)
10620         {
10621           player->post_delay_counter--;
10622         }
10623       }
10624     }
10625     else if (player->is_bored)
10626     {
10627       if (player->num_special_action_bored > 0)
10628       {
10629         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10630         {
10631           int special_action =
10632             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10633           int special_graphic =
10634             el_act_dir2img(player->artwork_element, special_action, move_dir);
10635
10636           player->anim_delay_counter =
10637             graphic_info[special_graphic].anim_delay_fixed +
10638             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10639           player->post_delay_counter =
10640             graphic_info[special_graphic].post_delay_fixed +
10641             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10642
10643           player->special_action_bored = special_action;
10644         }
10645
10646         if (player->anim_delay_counter > 0)
10647         {
10648           player->action_waiting = player->special_action_bored;
10649           player->anim_delay_counter--;
10650         }
10651         else if (player->post_delay_counter > 0)
10652         {
10653           player->post_delay_counter--;
10654         }
10655       }
10656     }
10657   }
10658   else if (last_waiting)        /* waiting -> not waiting */
10659   {
10660     player->is_waiting = FALSE;
10661     player->is_bored = FALSE;
10662     player->is_sleeping = FALSE;
10663
10664     player->frame_counter_bored = -1;
10665     player->frame_counter_sleeping = -1;
10666
10667     player->anim_delay_counter = 0;
10668     player->post_delay_counter = 0;
10669
10670     player->dir_waiting = player->MovDir;
10671     player->action_waiting = ACTION_DEFAULT;
10672
10673     player->special_action_bored = ACTION_DEFAULT;
10674     player->special_action_sleeping = ACTION_DEFAULT;
10675   }
10676 }
10677
10678 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10679 {
10680   static boolean player_was_moving = FALSE;
10681   static boolean player_was_snapping = FALSE;
10682   static boolean player_was_dropping = FALSE;
10683
10684   if ((!player->is_moving  && player_was_moving) ||
10685       (player->MovPos == 0 && player_was_moving) ||
10686       (player->is_snapping && !player_was_snapping) ||
10687       (player->is_dropping && !player_was_dropping))
10688   {
10689     if (!SaveEngineSnapshotToList())
10690       return;
10691
10692     player_was_moving = FALSE;
10693     player_was_snapping = TRUE;
10694     player_was_dropping = TRUE;
10695   }
10696   else
10697   {
10698     if (player->is_moving)
10699       player_was_moving = TRUE;
10700
10701     if (!player->is_snapping)
10702       player_was_snapping = FALSE;
10703
10704     if (!player->is_dropping)
10705       player_was_dropping = FALSE;
10706   }
10707 }
10708
10709 static void CheckSingleStepMode(struct PlayerInfo *player)
10710 {
10711   if (tape.single_step && tape.recording && !tape.pausing)
10712   {
10713     /* as it is called "single step mode", just return to pause mode when the
10714        player stopped moving after one tile (or never starts moving at all) */
10715     if (!player->is_moving && !player->is_pushing)
10716     {
10717       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10718       SnapField(player, 0, 0);                  /* stop snapping */
10719     }
10720   }
10721
10722   CheckSaveEngineSnapshot(player);
10723 }
10724
10725 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10726 {
10727   int left      = player_action & JOY_LEFT;
10728   int right     = player_action & JOY_RIGHT;
10729   int up        = player_action & JOY_UP;
10730   int down      = player_action & JOY_DOWN;
10731   int button1   = player_action & JOY_BUTTON_1;
10732   int button2   = player_action & JOY_BUTTON_2;
10733   int dx        = (left ? -1 : right ? 1 : 0);
10734   int dy        = (up   ? -1 : down  ? 1 : 0);
10735
10736   if (!player->active || tape.pausing)
10737     return 0;
10738
10739   if (player_action)
10740   {
10741     if (button1)
10742       SnapField(player, dx, dy);
10743     else
10744     {
10745       if (button2)
10746         DropElement(player);
10747
10748       MovePlayer(player, dx, dy);
10749     }
10750
10751     CheckSingleStepMode(player);
10752
10753     SetPlayerWaiting(player, FALSE);
10754
10755     return player_action;
10756   }
10757   else
10758   {
10759     /* no actions for this player (no input at player's configured device) */
10760
10761     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10762     SnapField(player, 0, 0);
10763     CheckGravityMovementWhenNotMoving(player);
10764
10765     if (player->MovPos == 0)
10766       SetPlayerWaiting(player, TRUE);
10767
10768     if (player->MovPos == 0)    /* needed for tape.playing */
10769       player->is_moving = FALSE;
10770
10771     player->is_dropping = FALSE;
10772     player->is_dropping_pressed = FALSE;
10773     player->drop_pressed_delay = 0;
10774
10775     CheckSingleStepMode(player);
10776
10777     return 0;
10778   }
10779 }
10780
10781 static void CheckLevelTime()
10782 {
10783   int i;
10784
10785   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10786   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10787   {
10788     if (level.native_em_level->lev->home == 0)  /* all players at home */
10789     {
10790       PlayerWins(local_player);
10791
10792       AllPlayersGone = TRUE;
10793
10794       level.native_em_level->lev->home = -1;
10795     }
10796
10797     if (level.native_em_level->ply[0]->alive == 0 &&
10798         level.native_em_level->ply[1]->alive == 0 &&
10799         level.native_em_level->ply[2]->alive == 0 &&
10800         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10801       AllPlayersGone = TRUE;
10802   }
10803   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10804   {
10805     if (game_sp.LevelSolved &&
10806         !game_sp.GameOver)                              /* game won */
10807     {
10808       PlayerWins(local_player);
10809
10810       game_sp.GameOver = TRUE;
10811
10812       AllPlayersGone = TRUE;
10813     }
10814
10815     if (game_sp.GameOver)                               /* game lost */
10816       AllPlayersGone = TRUE;
10817   }
10818
10819   if (TimeFrames >= FRAMES_PER_SECOND)
10820   {
10821     TimeFrames = 0;
10822     TapeTime++;
10823
10824     for (i = 0; i < MAX_PLAYERS; i++)
10825     {
10826       struct PlayerInfo *player = &stored_player[i];
10827
10828       if (SHIELD_ON(player))
10829       {
10830         player->shield_normal_time_left--;
10831
10832         if (player->shield_deadly_time_left > 0)
10833           player->shield_deadly_time_left--;
10834       }
10835     }
10836
10837     if (!local_player->LevelSolved && !level.use_step_counter)
10838     {
10839       TimePlayed++;
10840
10841       if (TimeLeft > 0)
10842       {
10843         TimeLeft--;
10844
10845         if (TimeLeft <= 10 && setup.time_limit)
10846           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10847
10848         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10849            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10850
10851         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10852
10853         if (!TimeLeft && setup.time_limit)
10854         {
10855           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10856             level.native_em_level->lev->killed_out_of_time = TRUE;
10857           else
10858             for (i = 0; i < MAX_PLAYERS; i++)
10859               KillPlayer(&stored_player[i]);
10860         }
10861       }
10862       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10863       {
10864         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10865       }
10866
10867       level.native_em_level->lev->time =
10868         (game.no_time_limit ? TimePlayed : TimeLeft);
10869     }
10870
10871     if (tape.recording || tape.playing)
10872       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10873   }
10874
10875   if (tape.recording || tape.playing)
10876     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10877
10878   UpdateAndDisplayGameControlValues();
10879 }
10880
10881 void AdvanceFrameAndPlayerCounters(int player_nr)
10882 {
10883   int i;
10884
10885   /* advance frame counters (global frame counter and time frame counter) */
10886   FrameCounter++;
10887   TimeFrames++;
10888
10889   /* advance player counters (counters for move delay, move animation etc.) */
10890   for (i = 0; i < MAX_PLAYERS; i++)
10891   {
10892     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10893     int move_delay_value = stored_player[i].move_delay_value;
10894     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10895
10896     if (!advance_player_counters)       /* not all players may be affected */
10897       continue;
10898
10899     if (move_frames == 0)       /* less than one move per game frame */
10900     {
10901       int stepsize = TILEX / move_delay_value;
10902       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10903       int count = (stored_player[i].is_moving ?
10904                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10905
10906       if (count % delay == 0)
10907         move_frames = 1;
10908     }
10909
10910     stored_player[i].Frame += move_frames;
10911
10912     if (stored_player[i].MovPos != 0)
10913       stored_player[i].StepFrame += move_frames;
10914
10915     if (stored_player[i].move_delay > 0)
10916       stored_player[i].move_delay--;
10917
10918     /* due to bugs in previous versions, counter must count up, not down */
10919     if (stored_player[i].push_delay != -1)
10920       stored_player[i].push_delay++;
10921
10922     if (stored_player[i].drop_delay > 0)
10923       stored_player[i].drop_delay--;
10924
10925     if (stored_player[i].is_dropping_pressed)
10926       stored_player[i].drop_pressed_delay++;
10927   }
10928 }
10929
10930 void StartGameActions(boolean init_network_game, boolean record_tape,
10931                       int random_seed)
10932 {
10933   unsigned int new_random_seed = InitRND(random_seed);
10934
10935   if (record_tape)
10936     TapeStartRecording(new_random_seed);
10937
10938 #if defined(NETWORK_AVALIABLE)
10939   if (init_network_game)
10940   {
10941     SendToServer_StartPlaying();
10942
10943     return;
10944   }
10945 #endif
10946
10947   InitGame();
10948 }
10949
10950 void GameActions()
10951 {
10952   static unsigned int game_frame_delay = 0;
10953   unsigned int game_frame_delay_value;
10954   byte *recorded_player_action;
10955   byte summarized_player_action = 0;
10956   byte tape_action[MAX_PLAYERS];
10957   int i;
10958
10959   /* detect endless loops, caused by custom element programming */
10960   if (recursion_loop_detected && recursion_loop_depth == 0)
10961   {
10962     char *message = getStringCat3("Internal Error! Element ",
10963                                   EL_NAME(recursion_loop_element),
10964                                   " caused endless loop! Quit the game?");
10965
10966     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10967           EL_NAME(recursion_loop_element));
10968
10969     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10970
10971     recursion_loop_detected = FALSE;    /* if game should be continued */
10972
10973     free(message);
10974
10975     return;
10976   }
10977
10978   if (game.restart_level)
10979     StartGameActions(options.network, setup.autorecord, level.random_seed);
10980
10981   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
10982   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10983   {
10984     if (level.native_em_level->lev->home == 0)  /* all players at home */
10985     {
10986       PlayerWins(local_player);
10987
10988       AllPlayersGone = TRUE;
10989
10990       level.native_em_level->lev->home = -1;
10991     }
10992
10993     if (level.native_em_level->ply[0]->alive == 0 &&
10994         level.native_em_level->ply[1]->alive == 0 &&
10995         level.native_em_level->ply[2]->alive == 0 &&
10996         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10997       AllPlayersGone = TRUE;
10998   }
10999   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11000   {
11001     if (game_sp.LevelSolved &&
11002         !game_sp.GameOver)                              /* game won */
11003     {
11004       PlayerWins(local_player);
11005
11006       game_sp.GameOver = TRUE;
11007
11008       AllPlayersGone = TRUE;
11009     }
11010
11011     if (game_sp.GameOver)                               /* game lost */
11012       AllPlayersGone = TRUE;
11013   }
11014
11015   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11016     GameWon();
11017
11018   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11019     TapeStop();
11020
11021   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11022     return;
11023
11024   game_frame_delay_value =
11025     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11026
11027   if (tape.playing && tape.warp_forward && !tape.pausing)
11028     game_frame_delay_value = 0;
11029
11030 #if 0
11031   /* ---------- main game synchronization point ---------- */
11032
11033   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11034
11035   printf("::: skip == %d\n", skip);
11036
11037 #else
11038   /* ---------- main game synchronization point ---------- */
11039
11040   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11041 #endif
11042
11043   if (network_playing && !network_player_action_received)
11044   {
11045     /* try to get network player actions in time */
11046
11047 #if defined(NETWORK_AVALIABLE)
11048     /* last chance to get network player actions without main loop delay */
11049     HandleNetworking();
11050 #endif
11051
11052     /* game was quit by network peer */
11053     if (game_status != GAME_MODE_PLAYING)
11054       return;
11055
11056     if (!network_player_action_received)
11057       return;           /* failed to get network player actions in time */
11058
11059     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11060   }
11061
11062   if (tape.pausing)
11063     return;
11064
11065   /* at this point we know that we really continue executing the game */
11066
11067   network_player_action_received = FALSE;
11068
11069   /* when playing tape, read previously recorded player input from tape data */
11070   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11071
11072   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11073   if (tape.pausing)
11074     return;
11075
11076   if (tape.set_centered_player)
11077   {
11078     game.centered_player_nr_next = tape.centered_player_nr_next;
11079     game.set_centered_player = TRUE;
11080   }
11081
11082   for (i = 0; i < MAX_PLAYERS; i++)
11083   {
11084     summarized_player_action |= stored_player[i].action;
11085
11086     if (!network_playing && (game.team_mode || tape.playing))
11087       stored_player[i].effective_action = stored_player[i].action;
11088   }
11089
11090 #if defined(NETWORK_AVALIABLE)
11091   if (network_playing)
11092     SendToServer_MovePlayer(summarized_player_action);
11093 #endif
11094
11095   if (!options.network && !game.team_mode)
11096     local_player->effective_action = summarized_player_action;
11097
11098   if (tape.recording &&
11099       setup.team_mode &&
11100       setup.input_on_focus &&
11101       game.centered_player_nr != -1)
11102   {
11103     for (i = 0; i < MAX_PLAYERS; i++)
11104       stored_player[i].effective_action =
11105         (i == game.centered_player_nr ? summarized_player_action : 0);
11106   }
11107
11108   if (recorded_player_action != NULL)
11109     for (i = 0; i < MAX_PLAYERS; i++)
11110       stored_player[i].effective_action = recorded_player_action[i];
11111
11112   for (i = 0; i < MAX_PLAYERS; i++)
11113   {
11114     tape_action[i] = stored_player[i].effective_action;
11115
11116     /* (this may happen in the RND game engine if a player was not present on
11117        the playfield on level start, but appeared later from a custom element */
11118     if (setup.team_mode &&
11119         tape.recording &&
11120         tape_action[i] &&
11121         !tape.player_participates[i])
11122       tape.player_participates[i] = TRUE;
11123   }
11124
11125   /* only record actions from input devices, but not programmed actions */
11126   if (tape.recording)
11127     TapeRecordAction(tape_action);
11128
11129 #if USE_NEW_PLAYER_ASSIGNMENTS
11130   // !!! also map player actions in single player mode !!!
11131   // if (game.team_mode)
11132   {
11133     byte mapped_action[MAX_PLAYERS];
11134
11135 #if DEBUG_PLAYER_ACTIONS
11136     printf(":::");
11137     for (i = 0; i < MAX_PLAYERS; i++)
11138       printf(" %d, ", stored_player[i].effective_action);
11139 #endif
11140
11141     for (i = 0; i < MAX_PLAYERS; i++)
11142       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11143
11144     for (i = 0; i < MAX_PLAYERS; i++)
11145       stored_player[i].effective_action = mapped_action[i];
11146
11147 #if DEBUG_PLAYER_ACTIONS
11148     printf(" =>");
11149     for (i = 0; i < MAX_PLAYERS; i++)
11150       printf(" %d, ", stored_player[i].effective_action);
11151     printf("\n");
11152 #endif
11153   }
11154 #if DEBUG_PLAYER_ACTIONS
11155   else
11156   {
11157     printf(":::");
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       printf(" %d, ", stored_player[i].effective_action);
11160     printf("\n");
11161   }
11162 #endif
11163 #endif
11164
11165   for (i = 0; i < MAX_PLAYERS; i++)
11166   {
11167     // allow engine snapshot in case of changed movement attempt
11168     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11169         (stored_player[i].effective_action & KEY_MOTION))
11170       game.snapshot.changed_action = TRUE;
11171
11172     // allow engine snapshot in case of snapping/dropping attempt
11173     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11174         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11175       game.snapshot.changed_action = TRUE;
11176
11177     game.snapshot.last_action[i] = stored_player[i].effective_action;
11178   }
11179
11180   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11181   {
11182     GameActions_EM_Main();
11183   }
11184   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11185   {
11186     GameActions_SP_Main();
11187   }
11188   else
11189   {
11190     GameActions_RND_Main();
11191   }
11192
11193   BlitScreenToBitmap(backbuffer);
11194
11195   CheckLevelTime();
11196
11197   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11198
11199   if (options.debug)                    /* calculate frames per second */
11200   {
11201     static unsigned int fps_counter = 0;
11202     static int fps_frames = 0;
11203     unsigned int fps_delay_ms = Counter() - fps_counter;
11204
11205     fps_frames++;
11206
11207     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11208     {
11209       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11210
11211       fps_frames = 0;
11212       fps_counter = Counter();
11213     }
11214
11215     redraw_mask |= REDRAW_FPS;
11216   }
11217 }
11218
11219 void GameActions_EM_Main()
11220 {
11221   byte effective_action[MAX_PLAYERS];
11222   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11223   int i;
11224
11225   for (i = 0; i < MAX_PLAYERS; i++)
11226     effective_action[i] = stored_player[i].effective_action;
11227
11228   GameActions_EM(effective_action, warp_mode);
11229 }
11230
11231 void GameActions_SP_Main()
11232 {
11233   byte effective_action[MAX_PLAYERS];
11234   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11235   int i;
11236
11237   for (i = 0; i < MAX_PLAYERS; i++)
11238     effective_action[i] = stored_player[i].effective_action;
11239
11240   GameActions_SP(effective_action, warp_mode);
11241 }
11242
11243 void GameActions_RND_Main()
11244 {
11245   GameActions_RND();
11246 }
11247
11248 void GameActions_RND()
11249 {
11250   int magic_wall_x = 0, magic_wall_y = 0;
11251   int i, x, y, element, graphic;
11252
11253   InitPlayfieldScanModeVars();
11254
11255   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11256   {
11257     SCAN_PLAYFIELD(x, y)
11258     {
11259       ChangeCount[x][y] = 0;
11260       ChangeEvent[x][y] = -1;
11261     }
11262   }
11263
11264   if (game.set_centered_player)
11265   {
11266     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11267
11268     /* switching to "all players" only possible if all players fit to screen */
11269     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11270     {
11271       game.centered_player_nr_next = game.centered_player_nr;
11272       game.set_centered_player = FALSE;
11273     }
11274
11275     /* do not switch focus to non-existing (or non-active) player */
11276     if (game.centered_player_nr_next >= 0 &&
11277         !stored_player[game.centered_player_nr_next].active)
11278     {
11279       game.centered_player_nr_next = game.centered_player_nr;
11280       game.set_centered_player = FALSE;
11281     }
11282   }
11283
11284   if (game.set_centered_player &&
11285       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11286   {
11287     int sx, sy;
11288
11289     if (game.centered_player_nr_next == -1)
11290     {
11291       setScreenCenteredToAllPlayers(&sx, &sy);
11292     }
11293     else
11294     {
11295       sx = stored_player[game.centered_player_nr_next].jx;
11296       sy = stored_player[game.centered_player_nr_next].jy;
11297     }
11298
11299     game.centered_player_nr = game.centered_player_nr_next;
11300     game.set_centered_player = FALSE;
11301
11302     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11303     DrawGameDoorValues();
11304   }
11305
11306   for (i = 0; i < MAX_PLAYERS; i++)
11307   {
11308     int actual_player_action = stored_player[i].effective_action;
11309
11310 #if 1
11311     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11312        - rnd_equinox_tetrachloride 048
11313        - rnd_equinox_tetrachloride_ii 096
11314        - rnd_emanuel_schmieg 002
11315        - doctor_sloan_ww 001, 020
11316     */
11317     if (stored_player[i].MovPos == 0)
11318       CheckGravityMovement(&stored_player[i]);
11319 #endif
11320
11321     /* overwrite programmed action with tape action */
11322     if (stored_player[i].programmed_action)
11323       actual_player_action = stored_player[i].programmed_action;
11324
11325     PlayerActions(&stored_player[i], actual_player_action);
11326
11327     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11328   }
11329
11330   ScrollScreen(NULL, SCROLL_GO_ON);
11331
11332   /* for backwards compatibility, the following code emulates a fixed bug that
11333      occured when pushing elements (causing elements that just made their last
11334      pushing step to already (if possible) make their first falling step in the
11335      same game frame, which is bad); this code is also needed to use the famous
11336      "spring push bug" which is used in older levels and might be wanted to be
11337      used also in newer levels, but in this case the buggy pushing code is only
11338      affecting the "spring" element and no other elements */
11339
11340   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11341   {
11342     for (i = 0; i < MAX_PLAYERS; i++)
11343     {
11344       struct PlayerInfo *player = &stored_player[i];
11345       int x = player->jx;
11346       int y = player->jy;
11347
11348       if (player->active && player->is_pushing && player->is_moving &&
11349           IS_MOVING(x, y) &&
11350           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11351            Feld[x][y] == EL_SPRING))
11352       {
11353         ContinueMoving(x, y);
11354
11355         /* continue moving after pushing (this is actually a bug) */
11356         if (!IS_MOVING(x, y))
11357           Stop[x][y] = FALSE;
11358       }
11359     }
11360   }
11361
11362   SCAN_PLAYFIELD(x, y)
11363   {
11364     ChangeCount[x][y] = 0;
11365     ChangeEvent[x][y] = -1;
11366
11367     /* this must be handled before main playfield loop */
11368     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11369     {
11370       MovDelay[x][y]--;
11371       if (MovDelay[x][y] <= 0)
11372         RemoveField(x, y);
11373     }
11374
11375     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11376     {
11377       MovDelay[x][y]--;
11378       if (MovDelay[x][y] <= 0)
11379       {
11380         RemoveField(x, y);
11381         TEST_DrawLevelField(x, y);
11382
11383         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11384       }
11385     }
11386
11387 #if DEBUG
11388     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11389     {
11390       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11391       printf("GameActions(): This should never happen!\n");
11392
11393       ChangePage[x][y] = -1;
11394     }
11395 #endif
11396
11397     Stop[x][y] = FALSE;
11398     if (WasJustMoving[x][y] > 0)
11399       WasJustMoving[x][y]--;
11400     if (WasJustFalling[x][y] > 0)
11401       WasJustFalling[x][y]--;
11402     if (CheckCollision[x][y] > 0)
11403       CheckCollision[x][y]--;
11404     if (CheckImpact[x][y] > 0)
11405       CheckImpact[x][y]--;
11406
11407     GfxFrame[x][y]++;
11408
11409     /* reset finished pushing action (not done in ContinueMoving() to allow
11410        continuous pushing animation for elements with zero push delay) */
11411     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11412     {
11413       ResetGfxAnimation(x, y);
11414       TEST_DrawLevelField(x, y);
11415     }
11416
11417 #if DEBUG
11418     if (IS_BLOCKED(x, y))
11419     {
11420       int oldx, oldy;
11421
11422       Blocked2Moving(x, y, &oldx, &oldy);
11423       if (!IS_MOVING(oldx, oldy))
11424       {
11425         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11426         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11427         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11428         printf("GameActions(): This should never happen!\n");
11429       }
11430     }
11431 #endif
11432   }
11433
11434   SCAN_PLAYFIELD(x, y)
11435   {
11436     element = Feld[x][y];
11437     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11438
11439     ResetGfxFrame(x, y, TRUE);
11440
11441     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11442         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11443       ResetRandomAnimationValue(x, y);
11444
11445     SetRandomAnimationValue(x, y);
11446
11447     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11448
11449     if (IS_INACTIVE(element))
11450     {
11451       if (IS_ANIMATED(graphic))
11452         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11453
11454       continue;
11455     }
11456
11457     /* this may take place after moving, so 'element' may have changed */
11458     if (IS_CHANGING(x, y) &&
11459         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11460     {
11461       int page = element_info[element].event_page_nr[CE_DELAY];
11462
11463       HandleElementChange(x, y, page);
11464
11465       element = Feld[x][y];
11466       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11467     }
11468
11469     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11470     {
11471       StartMoving(x, y);
11472
11473       element = Feld[x][y];
11474       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11475
11476       if (IS_ANIMATED(graphic) &&
11477           !IS_MOVING(x, y) &&
11478           !Stop[x][y])
11479         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11480
11481       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11482         TEST_DrawTwinkleOnField(x, y);
11483     }
11484     else if ((element == EL_ACID ||
11485               element == EL_EXIT_OPEN ||
11486               element == EL_EM_EXIT_OPEN ||
11487               element == EL_SP_EXIT_OPEN ||
11488               element == EL_STEEL_EXIT_OPEN ||
11489               element == EL_EM_STEEL_EXIT_OPEN ||
11490               element == EL_SP_TERMINAL ||
11491               element == EL_SP_TERMINAL_ACTIVE ||
11492               element == EL_EXTRA_TIME ||
11493               element == EL_SHIELD_NORMAL ||
11494               element == EL_SHIELD_DEADLY) &&
11495              IS_ANIMATED(graphic))
11496       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11497     else if (IS_MOVING(x, y))
11498       ContinueMoving(x, y);
11499     else if (IS_ACTIVE_BOMB(element))
11500       CheckDynamite(x, y);
11501     else if (element == EL_AMOEBA_GROWING)
11502       AmoebeWaechst(x, y);
11503     else if (element == EL_AMOEBA_SHRINKING)
11504       AmoebaDisappearing(x, y);
11505
11506 #if !USE_NEW_AMOEBA_CODE
11507     else if (IS_AMOEBALIVE(element))
11508       AmoebeAbleger(x, y);
11509 #endif
11510
11511     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11512       Life(x, y);
11513     else if (element == EL_EXIT_CLOSED)
11514       CheckExit(x, y);
11515     else if (element == EL_EM_EXIT_CLOSED)
11516       CheckExitEM(x, y);
11517     else if (element == EL_STEEL_EXIT_CLOSED)
11518       CheckExitSteel(x, y);
11519     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11520       CheckExitSteelEM(x, y);
11521     else if (element == EL_SP_EXIT_CLOSED)
11522       CheckExitSP(x, y);
11523     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11524              element == EL_EXPANDABLE_STEELWALL_GROWING)
11525       MauerWaechst(x, y);
11526     else if (element == EL_EXPANDABLE_WALL ||
11527              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11528              element == EL_EXPANDABLE_WALL_VERTICAL ||
11529              element == EL_EXPANDABLE_WALL_ANY ||
11530              element == EL_BD_EXPANDABLE_WALL)
11531       MauerAbleger(x, y);
11532     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11533              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11534              element == EL_EXPANDABLE_STEELWALL_ANY)
11535       MauerAblegerStahl(x, y);
11536     else if (element == EL_FLAMES)
11537       CheckForDragon(x, y);
11538     else if (element == EL_EXPLOSION)
11539       ; /* drawing of correct explosion animation is handled separately */
11540     else if (element == EL_ELEMENT_SNAPPING ||
11541              element == EL_DIAGONAL_SHRINKING ||
11542              element == EL_DIAGONAL_GROWING)
11543     {
11544       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11545
11546       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11547     }
11548     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11549       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11550
11551     if (IS_BELT_ACTIVE(element))
11552       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11553
11554     if (game.magic_wall_active)
11555     {
11556       int jx = local_player->jx, jy = local_player->jy;
11557
11558       /* play the element sound at the position nearest to the player */
11559       if ((element == EL_MAGIC_WALL_FULL ||
11560            element == EL_MAGIC_WALL_ACTIVE ||
11561            element == EL_MAGIC_WALL_EMPTYING ||
11562            element == EL_BD_MAGIC_WALL_FULL ||
11563            element == EL_BD_MAGIC_WALL_ACTIVE ||
11564            element == EL_BD_MAGIC_WALL_EMPTYING ||
11565            element == EL_DC_MAGIC_WALL_FULL ||
11566            element == EL_DC_MAGIC_WALL_ACTIVE ||
11567            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11568           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11569       {
11570         magic_wall_x = x;
11571         magic_wall_y = y;
11572       }
11573     }
11574   }
11575
11576 #if USE_NEW_AMOEBA_CODE
11577   /* new experimental amoeba growth stuff */
11578   if (!(FrameCounter % 8))
11579   {
11580     static unsigned int random = 1684108901;
11581
11582     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11583     {
11584       x = RND(lev_fieldx);
11585       y = RND(lev_fieldy);
11586       element = Feld[x][y];
11587
11588       if (!IS_PLAYER(x,y) &&
11589           (element == EL_EMPTY ||
11590            CAN_GROW_INTO(element) ||
11591            element == EL_QUICKSAND_EMPTY ||
11592            element == EL_QUICKSAND_FAST_EMPTY ||
11593            element == EL_ACID_SPLASH_LEFT ||
11594            element == EL_ACID_SPLASH_RIGHT))
11595       {
11596         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11597             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11598             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11599             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11600           Feld[x][y] = EL_AMOEBA_DROP;
11601       }
11602
11603       random = random * 129 + 1;
11604     }
11605   }
11606 #endif
11607
11608   game.explosions_delayed = FALSE;
11609
11610   SCAN_PLAYFIELD(x, y)
11611   {
11612     element = Feld[x][y];
11613
11614     if (ExplodeField[x][y])
11615       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11616     else if (element == EL_EXPLOSION)
11617       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11618
11619     ExplodeField[x][y] = EX_TYPE_NONE;
11620   }
11621
11622   game.explosions_delayed = TRUE;
11623
11624   if (game.magic_wall_active)
11625   {
11626     if (!(game.magic_wall_time_left % 4))
11627     {
11628       int element = Feld[magic_wall_x][magic_wall_y];
11629
11630       if (element == EL_BD_MAGIC_WALL_FULL ||
11631           element == EL_BD_MAGIC_WALL_ACTIVE ||
11632           element == EL_BD_MAGIC_WALL_EMPTYING)
11633         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11634       else if (element == EL_DC_MAGIC_WALL_FULL ||
11635                element == EL_DC_MAGIC_WALL_ACTIVE ||
11636                element == EL_DC_MAGIC_WALL_EMPTYING)
11637         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11638       else
11639         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11640     }
11641
11642     if (game.magic_wall_time_left > 0)
11643     {
11644       game.magic_wall_time_left--;
11645
11646       if (!game.magic_wall_time_left)
11647       {
11648         SCAN_PLAYFIELD(x, y)
11649         {
11650           element = Feld[x][y];
11651
11652           if (element == EL_MAGIC_WALL_ACTIVE ||
11653               element == EL_MAGIC_WALL_FULL)
11654           {
11655             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11656             TEST_DrawLevelField(x, y);
11657           }
11658           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11659                    element == EL_BD_MAGIC_WALL_FULL)
11660           {
11661             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11662             TEST_DrawLevelField(x, y);
11663           }
11664           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11665                    element == EL_DC_MAGIC_WALL_FULL)
11666           {
11667             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11668             TEST_DrawLevelField(x, y);
11669           }
11670         }
11671
11672         game.magic_wall_active = FALSE;
11673       }
11674     }
11675   }
11676
11677   if (game.light_time_left > 0)
11678   {
11679     game.light_time_left--;
11680
11681     if (game.light_time_left == 0)
11682       RedrawAllLightSwitchesAndInvisibleElements();
11683   }
11684
11685   if (game.timegate_time_left > 0)
11686   {
11687     game.timegate_time_left--;
11688
11689     if (game.timegate_time_left == 0)
11690       CloseAllOpenTimegates();
11691   }
11692
11693   if (game.lenses_time_left > 0)
11694   {
11695     game.lenses_time_left--;
11696
11697     if (game.lenses_time_left == 0)
11698       RedrawAllInvisibleElementsForLenses();
11699   }
11700
11701   if (game.magnify_time_left > 0)
11702   {
11703     game.magnify_time_left--;
11704
11705     if (game.magnify_time_left == 0)
11706       RedrawAllInvisibleElementsForMagnifier();
11707   }
11708
11709   for (i = 0; i < MAX_PLAYERS; i++)
11710   {
11711     struct PlayerInfo *player = &stored_player[i];
11712
11713     if (SHIELD_ON(player))
11714     {
11715       if (player->shield_deadly_time_left)
11716         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11717       else if (player->shield_normal_time_left)
11718         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11719     }
11720   }
11721
11722 #if USE_DELAYED_GFX_REDRAW
11723   SCAN_PLAYFIELD(x, y)
11724   {
11725     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11726     {
11727       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11728          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11729
11730       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11731         DrawLevelField(x, y);
11732
11733       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11734         DrawLevelFieldCrumbled(x, y);
11735
11736       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11737         DrawLevelFieldCrumbledNeighbours(x, y);
11738
11739       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11740         DrawTwinkleOnField(x, y);
11741     }
11742
11743     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11744   }
11745 #endif
11746
11747   DrawAllPlayers();
11748   PlayAllPlayersSound();
11749
11750   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11751   {
11752     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11753
11754     local_player->show_envelope = 0;
11755   }
11756
11757   /* use random number generator in every frame to make it less predictable */
11758   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11759     RND(1);
11760 }
11761
11762 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11763 {
11764   int min_x = x, min_y = y, max_x = x, max_y = y;
11765   int i;
11766
11767   for (i = 0; i < MAX_PLAYERS; i++)
11768   {
11769     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11770
11771     if (!stored_player[i].active || &stored_player[i] == player)
11772       continue;
11773
11774     min_x = MIN(min_x, jx);
11775     min_y = MIN(min_y, jy);
11776     max_x = MAX(max_x, jx);
11777     max_y = MAX(max_y, jy);
11778   }
11779
11780   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11781 }
11782
11783 static boolean AllPlayersInVisibleScreen()
11784 {
11785   int i;
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11790
11791     if (!stored_player[i].active)
11792       continue;
11793
11794     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11795       return FALSE;
11796   }
11797
11798   return TRUE;
11799 }
11800
11801 void ScrollLevel(int dx, int dy)
11802 {
11803   int scroll_offset = 2 * TILEX_VAR;
11804   int x, y;
11805
11806   BlitBitmap(drawto_field, drawto_field,
11807              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11808              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11809              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11810              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11811              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11812              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11813
11814   if (dx != 0)
11815   {
11816     x = (dx == 1 ? BX1 : BX2);
11817     for (y = BY1; y <= BY2; y++)
11818       DrawScreenField(x, y);
11819   }
11820
11821   if (dy != 0)
11822   {
11823     y = (dy == 1 ? BY1 : BY2);
11824     for (x = BX1; x <= BX2; x++)
11825       DrawScreenField(x, y);
11826   }
11827
11828   redraw_mask |= REDRAW_FIELD;
11829 }
11830
11831 static boolean canFallDown(struct PlayerInfo *player)
11832 {
11833   int jx = player->jx, jy = player->jy;
11834
11835   return (IN_LEV_FIELD(jx, jy + 1) &&
11836           (IS_FREE(jx, jy + 1) ||
11837            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11838           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11839           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11840 }
11841
11842 static boolean canPassField(int x, int y, int move_dir)
11843 {
11844   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11845   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11846   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11847   int nextx = x + dx;
11848   int nexty = y + dy;
11849   int element = Feld[x][y];
11850
11851   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11852           !CAN_MOVE(element) &&
11853           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11854           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11855           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11856 }
11857
11858 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11859 {
11860   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11861   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11862   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11863   int newx = x + dx;
11864   int newy = y + dy;
11865
11866   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11867           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11868           (IS_DIGGABLE(Feld[newx][newy]) ||
11869            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11870            canPassField(newx, newy, move_dir)));
11871 }
11872
11873 static void CheckGravityMovement(struct PlayerInfo *player)
11874 {
11875   if (player->gravity && !player->programmed_action)
11876   {
11877     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11878     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11879     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11880     int jx = player->jx, jy = player->jy;
11881     boolean player_is_moving_to_valid_field =
11882       (!player_is_snapping &&
11883        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11884         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11885     boolean player_can_fall_down = canFallDown(player);
11886
11887     if (player_can_fall_down &&
11888         !player_is_moving_to_valid_field)
11889       player->programmed_action = MV_DOWN;
11890   }
11891 }
11892
11893 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11894 {
11895   return CheckGravityMovement(player);
11896
11897   if (player->gravity && !player->programmed_action)
11898   {
11899     int jx = player->jx, jy = player->jy;
11900     boolean field_under_player_is_free =
11901       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11902     boolean player_is_standing_on_valid_field =
11903       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11904        (IS_WALKABLE(Feld[jx][jy]) &&
11905         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11906
11907     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11908       player->programmed_action = MV_DOWN;
11909   }
11910 }
11911
11912 /*
11913   MovePlayerOneStep()
11914   -----------------------------------------------------------------------------
11915   dx, dy:               direction (non-diagonal) to try to move the player to
11916   real_dx, real_dy:     direction as read from input device (can be diagonal)
11917 */
11918
11919 boolean MovePlayerOneStep(struct PlayerInfo *player,
11920                           int dx, int dy, int real_dx, int real_dy)
11921 {
11922   int jx = player->jx, jy = player->jy;
11923   int new_jx = jx + dx, new_jy = jy + dy;
11924   int can_move;
11925   boolean player_can_move = !player->cannot_move;
11926
11927   if (!player->active || (!dx && !dy))
11928     return MP_NO_ACTION;
11929
11930   player->MovDir = (dx < 0 ? MV_LEFT :
11931                     dx > 0 ? MV_RIGHT :
11932                     dy < 0 ? MV_UP :
11933                     dy > 0 ? MV_DOWN :  MV_NONE);
11934
11935   if (!IN_LEV_FIELD(new_jx, new_jy))
11936     return MP_NO_ACTION;
11937
11938   if (!player_can_move)
11939   {
11940     if (player->MovPos == 0)
11941     {
11942       player->is_moving = FALSE;
11943       player->is_digging = FALSE;
11944       player->is_collecting = FALSE;
11945       player->is_snapping = FALSE;
11946       player->is_pushing = FALSE;
11947     }
11948   }
11949
11950   if (!options.network && game.centered_player_nr == -1 &&
11951       !AllPlayersInSight(player, new_jx, new_jy))
11952     return MP_NO_ACTION;
11953
11954   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11955   if (can_move != MP_MOVING)
11956     return can_move;
11957
11958   /* check if DigField() has caused relocation of the player */
11959   if (player->jx != jx || player->jy != jy)
11960     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11961
11962   StorePlayer[jx][jy] = 0;
11963   player->last_jx = jx;
11964   player->last_jy = jy;
11965   player->jx = new_jx;
11966   player->jy = new_jy;
11967   StorePlayer[new_jx][new_jy] = player->element_nr;
11968
11969   if (player->move_delay_value_next != -1)
11970   {
11971     player->move_delay_value = player->move_delay_value_next;
11972     player->move_delay_value_next = -1;
11973   }
11974
11975   player->MovPos =
11976     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11977
11978   player->step_counter++;
11979
11980   PlayerVisit[jx][jy] = FrameCounter;
11981
11982   player->is_moving = TRUE;
11983
11984 #if 1
11985   /* should better be called in MovePlayer(), but this breaks some tapes */
11986   ScrollPlayer(player, SCROLL_INIT);
11987 #endif
11988
11989   return MP_MOVING;
11990 }
11991
11992 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11993 {
11994   int jx = player->jx, jy = player->jy;
11995   int old_jx = jx, old_jy = jy;
11996   int moved = MP_NO_ACTION;
11997
11998   if (!player->active)
11999     return FALSE;
12000
12001   if (!dx && !dy)
12002   {
12003     if (player->MovPos == 0)
12004     {
12005       player->is_moving = FALSE;
12006       player->is_digging = FALSE;
12007       player->is_collecting = FALSE;
12008       player->is_snapping = FALSE;
12009       player->is_pushing = FALSE;
12010     }
12011
12012     return FALSE;
12013   }
12014
12015   if (player->move_delay > 0)
12016     return FALSE;
12017
12018   player->move_delay = -1;              /* set to "uninitialized" value */
12019
12020   /* store if player is automatically moved to next field */
12021   player->is_auto_moving = (player->programmed_action != MV_NONE);
12022
12023   /* remove the last programmed player action */
12024   player->programmed_action = 0;
12025
12026   if (player->MovPos)
12027   {
12028     /* should only happen if pre-1.2 tape recordings are played */
12029     /* this is only for backward compatibility */
12030
12031     int original_move_delay_value = player->move_delay_value;
12032
12033 #if DEBUG
12034     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12035            tape.counter);
12036 #endif
12037
12038     /* scroll remaining steps with finest movement resolution */
12039     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12040
12041     while (player->MovPos)
12042     {
12043       ScrollPlayer(player, SCROLL_GO_ON);
12044       ScrollScreen(NULL, SCROLL_GO_ON);
12045
12046       AdvanceFrameAndPlayerCounters(player->index_nr);
12047
12048       DrawAllPlayers();
12049       BackToFront();
12050     }
12051
12052     player->move_delay_value = original_move_delay_value;
12053   }
12054
12055   player->is_active = FALSE;
12056
12057   if (player->last_move_dir & MV_HORIZONTAL)
12058   {
12059     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12060       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12061   }
12062   else
12063   {
12064     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12065       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12066   }
12067
12068   if (!moved && !player->is_active)
12069   {
12070     player->is_moving = FALSE;
12071     player->is_digging = FALSE;
12072     player->is_collecting = FALSE;
12073     player->is_snapping = FALSE;
12074     player->is_pushing = FALSE;
12075   }
12076
12077   jx = player->jx;
12078   jy = player->jy;
12079
12080   if (moved & MP_MOVING && !ScreenMovPos &&
12081       (player->index_nr == game.centered_player_nr ||
12082        game.centered_player_nr == -1))
12083   {
12084     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12085     int offset = game.scroll_delay_value;
12086
12087     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12088     {
12089       /* actual player has left the screen -- scroll in that direction */
12090       if (jx != old_jx)         /* player has moved horizontally */
12091         scroll_x += (jx - old_jx);
12092       else                      /* player has moved vertically */
12093         scroll_y += (jy - old_jy);
12094     }
12095     else
12096     {
12097       if (jx != old_jx)         /* player has moved horizontally */
12098       {
12099         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12100             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12101           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12102
12103         /* don't scroll over playfield boundaries */
12104         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12105           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12106
12107         /* don't scroll more than one field at a time */
12108         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12109
12110         /* don't scroll against the player's moving direction */
12111         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12112             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12113           scroll_x = old_scroll_x;
12114       }
12115       else                      /* player has moved vertically */
12116       {
12117         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12118             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12119           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12120
12121         /* don't scroll over playfield boundaries */
12122         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12123           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12124
12125         /* don't scroll more than one field at a time */
12126         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12127
12128         /* don't scroll against the player's moving direction */
12129         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12130             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12131           scroll_y = old_scroll_y;
12132       }
12133     }
12134
12135     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12136     {
12137       if (!options.network && game.centered_player_nr == -1 &&
12138           !AllPlayersInVisibleScreen())
12139       {
12140         scroll_x = old_scroll_x;
12141         scroll_y = old_scroll_y;
12142       }
12143       else
12144       {
12145         ScrollScreen(player, SCROLL_INIT);
12146         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12147       }
12148     }
12149   }
12150
12151   player->StepFrame = 0;
12152
12153   if (moved & MP_MOVING)
12154   {
12155     if (old_jx != jx && old_jy == jy)
12156       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12157     else if (old_jx == jx && old_jy != jy)
12158       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12159
12160     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12161
12162     player->last_move_dir = player->MovDir;
12163     player->is_moving = TRUE;
12164     player->is_snapping = FALSE;
12165     player->is_switching = FALSE;
12166     player->is_dropping = FALSE;
12167     player->is_dropping_pressed = FALSE;
12168     player->drop_pressed_delay = 0;
12169
12170 #if 0
12171     /* should better be called here than above, but this breaks some tapes */
12172     ScrollPlayer(player, SCROLL_INIT);
12173 #endif
12174   }
12175   else
12176   {
12177     CheckGravityMovementWhenNotMoving(player);
12178
12179     player->is_moving = FALSE;
12180
12181     /* at this point, the player is allowed to move, but cannot move right now
12182        (e.g. because of something blocking the way) -- ensure that the player
12183        is also allowed to move in the next frame (in old versions before 3.1.1,
12184        the player was forced to wait again for eight frames before next try) */
12185
12186     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12187       player->move_delay = 0;   /* allow direct movement in the next frame */
12188   }
12189
12190   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12191     player->move_delay = player->move_delay_value;
12192
12193   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12194   {
12195     TestIfPlayerTouchesBadThing(jx, jy);
12196     TestIfPlayerTouchesCustomElement(jx, jy);
12197   }
12198
12199   if (!player->active)
12200     RemovePlayer(player);
12201
12202   return moved;
12203 }
12204
12205 void ScrollPlayer(struct PlayerInfo *player, int mode)
12206 {
12207   int jx = player->jx, jy = player->jy;
12208   int last_jx = player->last_jx, last_jy = player->last_jy;
12209   int move_stepsize = TILEX / player->move_delay_value;
12210
12211   if (!player->active)
12212     return;
12213
12214   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12215     return;
12216
12217   if (mode == SCROLL_INIT)
12218   {
12219     player->actual_frame_counter = FrameCounter;
12220     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12221
12222     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12223         Feld[last_jx][last_jy] == EL_EMPTY)
12224     {
12225       int last_field_block_delay = 0;   /* start with no blocking at all */
12226       int block_delay_adjustment = player->block_delay_adjustment;
12227
12228       /* if player blocks last field, add delay for exactly one move */
12229       if (player->block_last_field)
12230       {
12231         last_field_block_delay += player->move_delay_value;
12232
12233         /* when blocking enabled, prevent moving up despite gravity */
12234         if (player->gravity && player->MovDir == MV_UP)
12235           block_delay_adjustment = -1;
12236       }
12237
12238       /* add block delay adjustment (also possible when not blocking) */
12239       last_field_block_delay += block_delay_adjustment;
12240
12241       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12242       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12243     }
12244
12245     if (player->MovPos != 0)    /* player has not yet reached destination */
12246       return;
12247   }
12248   else if (!FrameReached(&player->actual_frame_counter, 1))
12249     return;
12250
12251   if (player->MovPos != 0)
12252   {
12253     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12254     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12255
12256     /* before DrawPlayer() to draw correct player graphic for this case */
12257     if (player->MovPos == 0)
12258       CheckGravityMovement(player);
12259   }
12260
12261   if (player->MovPos == 0)      /* player reached destination field */
12262   {
12263     if (player->move_delay_reset_counter > 0)
12264     {
12265       player->move_delay_reset_counter--;
12266
12267       if (player->move_delay_reset_counter == 0)
12268       {
12269         /* continue with normal speed after quickly moving through gate */
12270         HALVE_PLAYER_SPEED(player);
12271
12272         /* be able to make the next move without delay */
12273         player->move_delay = 0;
12274       }
12275     }
12276
12277     player->last_jx = jx;
12278     player->last_jy = jy;
12279
12280     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12281         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12282         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12283         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12284         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12285         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12286         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12287         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12288     {
12289       DrawPlayer(player);       /* needed here only to cleanup last field */
12290       RemovePlayer(player);
12291
12292       if (local_player->friends_still_needed == 0 ||
12293           IS_SP_ELEMENT(Feld[jx][jy]))
12294         PlayerWins(player);
12295     }
12296
12297     /* this breaks one level: "machine", level 000 */
12298     {
12299       int move_direction = player->MovDir;
12300       int enter_side = MV_DIR_OPPOSITE(move_direction);
12301       int leave_side = move_direction;
12302       int old_jx = last_jx;
12303       int old_jy = last_jy;
12304       int old_element = Feld[old_jx][old_jy];
12305       int new_element = Feld[jx][jy];
12306
12307       if (IS_CUSTOM_ELEMENT(old_element))
12308         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12309                                    CE_LEFT_BY_PLAYER,
12310                                    player->index_bit, leave_side);
12311
12312       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12313                                           CE_PLAYER_LEAVES_X,
12314                                           player->index_bit, leave_side);
12315
12316       if (IS_CUSTOM_ELEMENT(new_element))
12317         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12318                                    player->index_bit, enter_side);
12319
12320       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12321                                           CE_PLAYER_ENTERS_X,
12322                                           player->index_bit, enter_side);
12323
12324       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12325                                         CE_MOVE_OF_X, move_direction);
12326     }
12327
12328     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12329     {
12330       TestIfPlayerTouchesBadThing(jx, jy);
12331       TestIfPlayerTouchesCustomElement(jx, jy);
12332
12333       /* needed because pushed element has not yet reached its destination,
12334          so it would trigger a change event at its previous field location */
12335       if (!player->is_pushing)
12336         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12337
12338       if (!player->active)
12339         RemovePlayer(player);
12340     }
12341
12342     if (!local_player->LevelSolved && level.use_step_counter)
12343     {
12344       int i;
12345
12346       TimePlayed++;
12347
12348       if (TimeLeft > 0)
12349       {
12350         TimeLeft--;
12351
12352         if (TimeLeft <= 10 && setup.time_limit)
12353           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12354
12355         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12356
12357         DisplayGameControlValues();
12358
12359         if (!TimeLeft && setup.time_limit)
12360           for (i = 0; i < MAX_PLAYERS; i++)
12361             KillPlayer(&stored_player[i]);
12362       }
12363       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12364       {
12365         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12366
12367         DisplayGameControlValues();
12368       }
12369     }
12370
12371     if (tape.single_step && tape.recording && !tape.pausing &&
12372         !player->programmed_action)
12373       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12374
12375     if (!player->programmed_action)
12376       CheckSaveEngineSnapshot(player);
12377   }
12378 }
12379
12380 void ScrollScreen(struct PlayerInfo *player, int mode)
12381 {
12382   static unsigned int screen_frame_counter = 0;
12383
12384   if (mode == SCROLL_INIT)
12385   {
12386     /* set scrolling step size according to actual player's moving speed */
12387     ScrollStepSize = TILEX / player->move_delay_value;
12388
12389     screen_frame_counter = FrameCounter;
12390     ScreenMovDir = player->MovDir;
12391     ScreenMovPos = player->MovPos;
12392     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12393     return;
12394   }
12395   else if (!FrameReached(&screen_frame_counter, 1))
12396     return;
12397
12398   if (ScreenMovPos)
12399   {
12400     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12401     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12402     redraw_mask |= REDRAW_FIELD;
12403   }
12404   else
12405     ScreenMovDir = MV_NONE;
12406 }
12407
12408 void TestIfPlayerTouchesCustomElement(int x, int y)
12409 {
12410   static int xy[4][2] =
12411   {
12412     { 0, -1 },
12413     { -1, 0 },
12414     { +1, 0 },
12415     { 0, +1 }
12416   };
12417   static int trigger_sides[4][2] =
12418   {
12419     /* center side       border side */
12420     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12421     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12422     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12423     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12424   };
12425   static int touch_dir[4] =
12426   {
12427     MV_LEFT | MV_RIGHT,
12428     MV_UP   | MV_DOWN,
12429     MV_UP   | MV_DOWN,
12430     MV_LEFT | MV_RIGHT
12431   };
12432   int center_element = Feld[x][y];      /* should always be non-moving! */
12433   int i;
12434
12435   for (i = 0; i < NUM_DIRECTIONS; i++)
12436   {
12437     int xx = x + xy[i][0];
12438     int yy = y + xy[i][1];
12439     int center_side = trigger_sides[i][0];
12440     int border_side = trigger_sides[i][1];
12441     int border_element;
12442
12443     if (!IN_LEV_FIELD(xx, yy))
12444       continue;
12445
12446     if (IS_PLAYER(x, y))                /* player found at center element */
12447     {
12448       struct PlayerInfo *player = PLAYERINFO(x, y);
12449
12450       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12451         border_element = Feld[xx][yy];          /* may be moving! */
12452       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12453         border_element = Feld[xx][yy];
12454       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12455         border_element = MovingOrBlocked2Element(xx, yy);
12456       else
12457         continue;               /* center and border element do not touch */
12458
12459       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12460                                  player->index_bit, border_side);
12461       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12462                                           CE_PLAYER_TOUCHES_X,
12463                                           player->index_bit, border_side);
12464
12465       {
12466         /* use player element that is initially defined in the level playfield,
12467            not the player element that corresponds to the runtime player number
12468            (example: a level that contains EL_PLAYER_3 as the only player would
12469            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12470         int player_element = PLAYERINFO(x, y)->initial_element;
12471
12472         CheckElementChangeBySide(xx, yy, border_element, player_element,
12473                                  CE_TOUCHING_X, border_side);
12474       }
12475     }
12476     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12477     {
12478       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12479
12480       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12481       {
12482         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12483           continue;             /* center and border element do not touch */
12484       }
12485
12486       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12487                                  player->index_bit, center_side);
12488       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12489                                           CE_PLAYER_TOUCHES_X,
12490                                           player->index_bit, center_side);
12491
12492       {
12493         /* use player element that is initially defined in the level playfield,
12494            not the player element that corresponds to the runtime player number
12495            (example: a level that contains EL_PLAYER_3 as the only player would
12496            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12497         int player_element = PLAYERINFO(xx, yy)->initial_element;
12498
12499         CheckElementChangeBySide(x, y, center_element, player_element,
12500                                  CE_TOUCHING_X, center_side);
12501       }
12502
12503       break;
12504     }
12505   }
12506 }
12507
12508 void TestIfElementTouchesCustomElement(int x, int y)
12509 {
12510   static int xy[4][2] =
12511   {
12512     { 0, -1 },
12513     { -1, 0 },
12514     { +1, 0 },
12515     { 0, +1 }
12516   };
12517   static int trigger_sides[4][2] =
12518   {
12519     /* center side      border side */
12520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12524   };
12525   static int touch_dir[4] =
12526   {
12527     MV_LEFT | MV_RIGHT,
12528     MV_UP   | MV_DOWN,
12529     MV_UP   | MV_DOWN,
12530     MV_LEFT | MV_RIGHT
12531   };
12532   boolean change_center_element = FALSE;
12533   int center_element = Feld[x][y];      /* should always be non-moving! */
12534   int border_element_old[NUM_DIRECTIONS];
12535   int i;
12536
12537   for (i = 0; i < NUM_DIRECTIONS; i++)
12538   {
12539     int xx = x + xy[i][0];
12540     int yy = y + xy[i][1];
12541     int border_element;
12542
12543     border_element_old[i] = -1;
12544
12545     if (!IN_LEV_FIELD(xx, yy))
12546       continue;
12547
12548     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12549       border_element = Feld[xx][yy];    /* may be moving! */
12550     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12551       border_element = Feld[xx][yy];
12552     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12553       border_element = MovingOrBlocked2Element(xx, yy);
12554     else
12555       continue;                 /* center and border element do not touch */
12556
12557     border_element_old[i] = border_element;
12558   }
12559
12560   for (i = 0; i < NUM_DIRECTIONS; i++)
12561   {
12562     int xx = x + xy[i][0];
12563     int yy = y + xy[i][1];
12564     int center_side = trigger_sides[i][0];
12565     int border_element = border_element_old[i];
12566
12567     if (border_element == -1)
12568       continue;
12569
12570     /* check for change of border element */
12571     CheckElementChangeBySide(xx, yy, border_element, center_element,
12572                              CE_TOUCHING_X, center_side);
12573
12574     /* (center element cannot be player, so we dont have to check this here) */
12575   }
12576
12577   for (i = 0; i < NUM_DIRECTIONS; i++)
12578   {
12579     int xx = x + xy[i][0];
12580     int yy = y + xy[i][1];
12581     int border_side = trigger_sides[i][1];
12582     int border_element = border_element_old[i];
12583
12584     if (border_element == -1)
12585       continue;
12586
12587     /* check for change of center element (but change it only once) */
12588     if (!change_center_element)
12589       change_center_element =
12590         CheckElementChangeBySide(x, y, center_element, border_element,
12591                                  CE_TOUCHING_X, border_side);
12592
12593     if (IS_PLAYER(xx, yy))
12594     {
12595       /* use player element that is initially defined in the level playfield,
12596          not the player element that corresponds to the runtime player number
12597          (example: a level that contains EL_PLAYER_3 as the only player would
12598          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12599       int player_element = PLAYERINFO(xx, yy)->initial_element;
12600
12601       CheckElementChangeBySide(x, y, center_element, player_element,
12602                                CE_TOUCHING_X, border_side);
12603     }
12604   }
12605 }
12606
12607 void TestIfElementHitsCustomElement(int x, int y, int direction)
12608 {
12609   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12610   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12611   int hitx = x + dx, hity = y + dy;
12612   int hitting_element = Feld[x][y];
12613   int touched_element;
12614
12615   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12616     return;
12617
12618   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12619                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12620
12621   if (IN_LEV_FIELD(hitx, hity))
12622   {
12623     int opposite_direction = MV_DIR_OPPOSITE(direction);
12624     int hitting_side = direction;
12625     int touched_side = opposite_direction;
12626     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12627                           MovDir[hitx][hity] != direction ||
12628                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12629
12630     object_hit = TRUE;
12631
12632     if (object_hit)
12633     {
12634       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12635                                CE_HITTING_X, touched_side);
12636
12637       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12638                                CE_HIT_BY_X, hitting_side);
12639
12640       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12641                                CE_HIT_BY_SOMETHING, opposite_direction);
12642
12643       if (IS_PLAYER(hitx, hity))
12644       {
12645         /* use player element that is initially defined in the level playfield,
12646            not the player element that corresponds to the runtime player number
12647            (example: a level that contains EL_PLAYER_3 as the only player would
12648            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12649         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12650
12651         CheckElementChangeBySide(x, y, hitting_element, player_element,
12652                                  CE_HITTING_X, touched_side);
12653       }
12654     }
12655   }
12656
12657   /* "hitting something" is also true when hitting the playfield border */
12658   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12659                            CE_HITTING_SOMETHING, direction);
12660 }
12661
12662 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12663 {
12664   int i, kill_x = -1, kill_y = -1;
12665
12666   int bad_element = -1;
12667   static int test_xy[4][2] =
12668   {
12669     { 0, -1 },
12670     { -1, 0 },
12671     { +1, 0 },
12672     { 0, +1 }
12673   };
12674   static int test_dir[4] =
12675   {
12676     MV_UP,
12677     MV_LEFT,
12678     MV_RIGHT,
12679     MV_DOWN
12680   };
12681
12682   for (i = 0; i < NUM_DIRECTIONS; i++)
12683   {
12684     int test_x, test_y, test_move_dir, test_element;
12685
12686     test_x = good_x + test_xy[i][0];
12687     test_y = good_y + test_xy[i][1];
12688
12689     if (!IN_LEV_FIELD(test_x, test_y))
12690       continue;
12691
12692     test_move_dir =
12693       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12694
12695     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12696
12697     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12698        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12699     */
12700     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12701         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12702     {
12703       kill_x = test_x;
12704       kill_y = test_y;
12705       bad_element = test_element;
12706
12707       break;
12708     }
12709   }
12710
12711   if (kill_x != -1 || kill_y != -1)
12712   {
12713     if (IS_PLAYER(good_x, good_y))
12714     {
12715       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12716
12717       if (player->shield_deadly_time_left > 0 &&
12718           !IS_INDESTRUCTIBLE(bad_element))
12719         Bang(kill_x, kill_y);
12720       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12721         KillPlayer(player);
12722     }
12723     else
12724       Bang(good_x, good_y);
12725   }
12726 }
12727
12728 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12729 {
12730   int i, kill_x = -1, kill_y = -1;
12731   int bad_element = Feld[bad_x][bad_y];
12732   static int test_xy[4][2] =
12733   {
12734     { 0, -1 },
12735     { -1, 0 },
12736     { +1, 0 },
12737     { 0, +1 }
12738   };
12739   static int touch_dir[4] =
12740   {
12741     MV_LEFT | MV_RIGHT,
12742     MV_UP   | MV_DOWN,
12743     MV_UP   | MV_DOWN,
12744     MV_LEFT | MV_RIGHT
12745   };
12746   static int test_dir[4] =
12747   {
12748     MV_UP,
12749     MV_LEFT,
12750     MV_RIGHT,
12751     MV_DOWN
12752   };
12753
12754   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12755     return;
12756
12757   for (i = 0; i < NUM_DIRECTIONS; i++)
12758   {
12759     int test_x, test_y, test_move_dir, test_element;
12760
12761     test_x = bad_x + test_xy[i][0];
12762     test_y = bad_y + test_xy[i][1];
12763
12764     if (!IN_LEV_FIELD(test_x, test_y))
12765       continue;
12766
12767     test_move_dir =
12768       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12769
12770     test_element = Feld[test_x][test_y];
12771
12772     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12773        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12774     */
12775     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12776         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12777     {
12778       /* good thing is player or penguin that does not move away */
12779       if (IS_PLAYER(test_x, test_y))
12780       {
12781         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12782
12783         if (bad_element == EL_ROBOT && player->is_moving)
12784           continue;     /* robot does not kill player if he is moving */
12785
12786         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12787         {
12788           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12789             continue;           /* center and border element do not touch */
12790         }
12791
12792         kill_x = test_x;
12793         kill_y = test_y;
12794
12795         break;
12796       }
12797       else if (test_element == EL_PENGUIN)
12798       {
12799         kill_x = test_x;
12800         kill_y = test_y;
12801
12802         break;
12803       }
12804     }
12805   }
12806
12807   if (kill_x != -1 || kill_y != -1)
12808   {
12809     if (IS_PLAYER(kill_x, kill_y))
12810     {
12811       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12812
12813       if (player->shield_deadly_time_left > 0 &&
12814           !IS_INDESTRUCTIBLE(bad_element))
12815         Bang(bad_x, bad_y);
12816       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12817         KillPlayer(player);
12818     }
12819     else
12820       Bang(kill_x, kill_y);
12821   }
12822 }
12823
12824 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12825 {
12826   int bad_element = Feld[bad_x][bad_y];
12827   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12828   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12829   int test_x = bad_x + dx, test_y = bad_y + dy;
12830   int test_move_dir, test_element;
12831   int kill_x = -1, kill_y = -1;
12832
12833   if (!IN_LEV_FIELD(test_x, test_y))
12834     return;
12835
12836   test_move_dir =
12837     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12838
12839   test_element = Feld[test_x][test_y];
12840
12841   if (test_move_dir != bad_move_dir)
12842   {
12843     /* good thing can be player or penguin that does not move away */
12844     if (IS_PLAYER(test_x, test_y))
12845     {
12846       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12847
12848       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12849          player as being hit when he is moving towards the bad thing, because
12850          the "get hit by" condition would be lost after the player stops) */
12851       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12852         return;         /* player moves away from bad thing */
12853
12854       kill_x = test_x;
12855       kill_y = test_y;
12856     }
12857     else if (test_element == EL_PENGUIN)
12858     {
12859       kill_x = test_x;
12860       kill_y = test_y;
12861     }
12862   }
12863
12864   if (kill_x != -1 || kill_y != -1)
12865   {
12866     if (IS_PLAYER(kill_x, kill_y))
12867     {
12868       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12869
12870       if (player->shield_deadly_time_left > 0 &&
12871           !IS_INDESTRUCTIBLE(bad_element))
12872         Bang(bad_x, bad_y);
12873       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12874         KillPlayer(player);
12875     }
12876     else
12877       Bang(kill_x, kill_y);
12878   }
12879 }
12880
12881 void TestIfPlayerTouchesBadThing(int x, int y)
12882 {
12883   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12884 }
12885
12886 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12887 {
12888   TestIfGoodThingHitsBadThing(x, y, move_dir);
12889 }
12890
12891 void TestIfBadThingTouchesPlayer(int x, int y)
12892 {
12893   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12894 }
12895
12896 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12897 {
12898   TestIfBadThingHitsGoodThing(x, y, move_dir);
12899 }
12900
12901 void TestIfFriendTouchesBadThing(int x, int y)
12902 {
12903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12904 }
12905
12906 void TestIfBadThingTouchesFriend(int x, int y)
12907 {
12908   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12909 }
12910
12911 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12912 {
12913   int i, kill_x = bad_x, kill_y = bad_y;
12914   static int xy[4][2] =
12915   {
12916     { 0, -1 },
12917     { -1, 0 },
12918     { +1, 0 },
12919     { 0, +1 }
12920   };
12921
12922   for (i = 0; i < NUM_DIRECTIONS; i++)
12923   {
12924     int x, y, element;
12925
12926     x = bad_x + xy[i][0];
12927     y = bad_y + xy[i][1];
12928     if (!IN_LEV_FIELD(x, y))
12929       continue;
12930
12931     element = Feld[x][y];
12932     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12933         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12934     {
12935       kill_x = x;
12936       kill_y = y;
12937       break;
12938     }
12939   }
12940
12941   if (kill_x != bad_x || kill_y != bad_y)
12942     Bang(bad_x, bad_y);
12943 }
12944
12945 void KillPlayer(struct PlayerInfo *player)
12946 {
12947   int jx = player->jx, jy = player->jy;
12948
12949   if (!player->active)
12950     return;
12951
12952 #if 0
12953   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12954          player->killed, player->active, player->reanimated);
12955 #endif
12956
12957   /* the following code was introduced to prevent an infinite loop when calling
12958      -> Bang()
12959      -> CheckTriggeredElementChangeExt()
12960      -> ExecuteCustomElementAction()
12961      -> KillPlayer()
12962      -> (infinitely repeating the above sequence of function calls)
12963      which occurs when killing the player while having a CE with the setting
12964      "kill player X when explosion of <player X>"; the solution using a new
12965      field "player->killed" was chosen for backwards compatibility, although
12966      clever use of the fields "player->active" etc. would probably also work */
12967 #if 1
12968   if (player->killed)
12969     return;
12970 #endif
12971
12972   player->killed = TRUE;
12973
12974   /* remove accessible field at the player's position */
12975   Feld[jx][jy] = EL_EMPTY;
12976
12977   /* deactivate shield (else Bang()/Explode() would not work right) */
12978   player->shield_normal_time_left = 0;
12979   player->shield_deadly_time_left = 0;
12980
12981 #if 0
12982   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12983          player->killed, player->active, player->reanimated);
12984 #endif
12985
12986   Bang(jx, jy);
12987
12988 #if 0
12989   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12990          player->killed, player->active, player->reanimated);
12991 #endif
12992
12993   if (player->reanimated)       /* killed player may have been reanimated */
12994     player->killed = player->reanimated = FALSE;
12995   else
12996     BuryPlayer(player);
12997 }
12998
12999 static void KillPlayerUnlessEnemyProtected(int x, int y)
13000 {
13001   if (!PLAYER_ENEMY_PROTECTED(x, y))
13002     KillPlayer(PLAYERINFO(x, y));
13003 }
13004
13005 static void KillPlayerUnlessExplosionProtected(int x, int y)
13006 {
13007   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13008     KillPlayer(PLAYERINFO(x, y));
13009 }
13010
13011 void BuryPlayer(struct PlayerInfo *player)
13012 {
13013   int jx = player->jx, jy = player->jy;
13014
13015   if (!player->active)
13016     return;
13017
13018   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13019   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13020
13021   player->GameOver = TRUE;
13022   RemovePlayer(player);
13023 }
13024
13025 void RemovePlayer(struct PlayerInfo *player)
13026 {
13027   int jx = player->jx, jy = player->jy;
13028   int i, found = FALSE;
13029
13030   player->present = FALSE;
13031   player->active = FALSE;
13032
13033   if (!ExplodeField[jx][jy])
13034     StorePlayer[jx][jy] = 0;
13035
13036   if (player->is_moving)
13037     TEST_DrawLevelField(player->last_jx, player->last_jy);
13038
13039   for (i = 0; i < MAX_PLAYERS; i++)
13040     if (stored_player[i].active)
13041       found = TRUE;
13042
13043   if (!found)
13044     AllPlayersGone = TRUE;
13045
13046   ExitX = ZX = jx;
13047   ExitY = ZY = jy;
13048 }
13049
13050 static void setFieldForSnapping(int x, int y, int element, int direction)
13051 {
13052   struct ElementInfo *ei = &element_info[element];
13053   int direction_bit = MV_DIR_TO_BIT(direction);
13054   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13055   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13056                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13057
13058   Feld[x][y] = EL_ELEMENT_SNAPPING;
13059   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13060
13061   ResetGfxAnimation(x, y);
13062
13063   GfxElement[x][y] = element;
13064   GfxAction[x][y] = action;
13065   GfxDir[x][y] = direction;
13066   GfxFrame[x][y] = -1;
13067 }
13068
13069 /*
13070   =============================================================================
13071   checkDiagonalPushing()
13072   -----------------------------------------------------------------------------
13073   check if diagonal input device direction results in pushing of object
13074   (by checking if the alternative direction is walkable, diggable, ...)
13075   =============================================================================
13076 */
13077
13078 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13079                                     int x, int y, int real_dx, int real_dy)
13080 {
13081   int jx, jy, dx, dy, xx, yy;
13082
13083   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13084     return TRUE;
13085
13086   /* diagonal direction: check alternative direction */
13087   jx = player->jx;
13088   jy = player->jy;
13089   dx = x - jx;
13090   dy = y - jy;
13091   xx = jx + (dx == 0 ? real_dx : 0);
13092   yy = jy + (dy == 0 ? real_dy : 0);
13093
13094   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13095 }
13096
13097 /*
13098   =============================================================================
13099   DigField()
13100   -----------------------------------------------------------------------------
13101   x, y:                 field next to player (non-diagonal) to try to dig to
13102   real_dx, real_dy:     direction as read from input device (can be diagonal)
13103   =============================================================================
13104 */
13105
13106 static int DigField(struct PlayerInfo *player,
13107                     int oldx, int oldy, int x, int y,
13108                     int real_dx, int real_dy, int mode)
13109 {
13110   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13111   boolean player_was_pushing = player->is_pushing;
13112   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13113   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13114   int jx = oldx, jy = oldy;
13115   int dx = x - jx, dy = y - jy;
13116   int nextx = x + dx, nexty = y + dy;
13117   int move_direction = (dx == -1 ? MV_LEFT  :
13118                         dx == +1 ? MV_RIGHT :
13119                         dy == -1 ? MV_UP    :
13120                         dy == +1 ? MV_DOWN  : MV_NONE);
13121   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13122   int dig_side = MV_DIR_OPPOSITE(move_direction);
13123   int old_element = Feld[jx][jy];
13124   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13125   int collect_count;
13126
13127   if (is_player)                /* function can also be called by EL_PENGUIN */
13128   {
13129     if (player->MovPos == 0)
13130     {
13131       player->is_digging = FALSE;
13132       player->is_collecting = FALSE;
13133     }
13134
13135     if (player->MovPos == 0)    /* last pushing move finished */
13136       player->is_pushing = FALSE;
13137
13138     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13139     {
13140       player->is_switching = FALSE;
13141       player->push_delay = -1;
13142
13143       return MP_NO_ACTION;
13144     }
13145   }
13146
13147   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13148     old_element = Back[jx][jy];
13149
13150   /* in case of element dropped at player position, check background */
13151   else if (Back[jx][jy] != EL_EMPTY &&
13152            game.engine_version >= VERSION_IDENT(2,2,0,0))
13153     old_element = Back[jx][jy];
13154
13155   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13156     return MP_NO_ACTION;        /* field has no opening in this direction */
13157
13158   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13159     return MP_NO_ACTION;        /* field has no opening in this direction */
13160
13161   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13162   {
13163     SplashAcid(x, y);
13164
13165     Feld[jx][jy] = player->artwork_element;
13166     InitMovingField(jx, jy, MV_DOWN);
13167     Store[jx][jy] = EL_ACID;
13168     ContinueMoving(jx, jy);
13169     BuryPlayer(player);
13170
13171     return MP_DONT_RUN_INTO;
13172   }
13173
13174   if (player_can_move && DONT_RUN_INTO(element))
13175   {
13176     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13177
13178     return MP_DONT_RUN_INTO;
13179   }
13180
13181   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13182     return MP_NO_ACTION;
13183
13184   collect_count = element_info[element].collect_count_initial;
13185
13186   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13187     return MP_NO_ACTION;
13188
13189   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13190     player_can_move = player_can_move_or_snap;
13191
13192   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13193       game.engine_version >= VERSION_IDENT(2,2,0,0))
13194   {
13195     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13196                                player->index_bit, dig_side);
13197     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13198                                         player->index_bit, dig_side);
13199
13200     if (element == EL_DC_LANDMINE)
13201       Bang(x, y);
13202
13203     if (Feld[x][y] != element)          /* field changed by snapping */
13204       return MP_ACTION;
13205
13206     return MP_NO_ACTION;
13207   }
13208
13209   if (player->gravity && is_player && !player->is_auto_moving &&
13210       canFallDown(player) && move_direction != MV_DOWN &&
13211       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13212     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13213
13214   if (player_can_move &&
13215       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13216   {
13217     int sound_element = SND_ELEMENT(element);
13218     int sound_action = ACTION_WALKING;
13219
13220     if (IS_RND_GATE(element))
13221     {
13222       if (!player->key[RND_GATE_NR(element)])
13223         return MP_NO_ACTION;
13224     }
13225     else if (IS_RND_GATE_GRAY(element))
13226     {
13227       if (!player->key[RND_GATE_GRAY_NR(element)])
13228         return MP_NO_ACTION;
13229     }
13230     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13231     {
13232       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13233         return MP_NO_ACTION;
13234     }
13235     else if (element == EL_EXIT_OPEN ||
13236              element == EL_EM_EXIT_OPEN ||
13237              element == EL_EM_EXIT_OPENING ||
13238              element == EL_STEEL_EXIT_OPEN ||
13239              element == EL_EM_STEEL_EXIT_OPEN ||
13240              element == EL_EM_STEEL_EXIT_OPENING ||
13241              element == EL_SP_EXIT_OPEN ||
13242              element == EL_SP_EXIT_OPENING)
13243     {
13244       sound_action = ACTION_PASSING;    /* player is passing exit */
13245     }
13246     else if (element == EL_EMPTY)
13247     {
13248       sound_action = ACTION_MOVING;             /* nothing to walk on */
13249     }
13250
13251     /* play sound from background or player, whatever is available */
13252     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13253       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13254     else
13255       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13256   }
13257   else if (player_can_move &&
13258            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13259   {
13260     if (!ACCESS_FROM(element, opposite_direction))
13261       return MP_NO_ACTION;      /* field not accessible from this direction */
13262
13263     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13264       return MP_NO_ACTION;
13265
13266     if (IS_EM_GATE(element))
13267     {
13268       if (!player->key[EM_GATE_NR(element)])
13269         return MP_NO_ACTION;
13270     }
13271     else if (IS_EM_GATE_GRAY(element))
13272     {
13273       if (!player->key[EM_GATE_GRAY_NR(element)])
13274         return MP_NO_ACTION;
13275     }
13276     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13277     {
13278       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13279         return MP_NO_ACTION;
13280     }
13281     else if (IS_EMC_GATE(element))
13282     {
13283       if (!player->key[EMC_GATE_NR(element)])
13284         return MP_NO_ACTION;
13285     }
13286     else if (IS_EMC_GATE_GRAY(element))
13287     {
13288       if (!player->key[EMC_GATE_GRAY_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13292     {
13293       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (element == EL_DC_GATE_WHITE ||
13297              element == EL_DC_GATE_WHITE_GRAY ||
13298              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13299     {
13300       if (player->num_white_keys == 0)
13301         return MP_NO_ACTION;
13302
13303       player->num_white_keys--;
13304     }
13305     else if (IS_SP_PORT(element))
13306     {
13307       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13308           element == EL_SP_GRAVITY_PORT_RIGHT ||
13309           element == EL_SP_GRAVITY_PORT_UP ||
13310           element == EL_SP_GRAVITY_PORT_DOWN)
13311         player->gravity = !player->gravity;
13312       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13313                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13314                element == EL_SP_GRAVITY_ON_PORT_UP ||
13315                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13316         player->gravity = TRUE;
13317       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13318                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13319                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13320                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13321         player->gravity = FALSE;
13322     }
13323
13324     /* automatically move to the next field with double speed */
13325     player->programmed_action = move_direction;
13326
13327     if (player->move_delay_reset_counter == 0)
13328     {
13329       player->move_delay_reset_counter = 2;     /* two double speed steps */
13330
13331       DOUBLE_PLAYER_SPEED(player);
13332     }
13333
13334     PlayLevelSoundAction(x, y, ACTION_PASSING);
13335   }
13336   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13337   {
13338     RemoveField(x, y);
13339
13340     if (mode != DF_SNAP)
13341     {
13342       GfxElement[x][y] = GFX_ELEMENT(element);
13343       player->is_digging = TRUE;
13344     }
13345
13346     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13347
13348     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13349                                         player->index_bit, dig_side);
13350
13351     if (mode == DF_SNAP)
13352     {
13353       if (level.block_snap_field)
13354         setFieldForSnapping(x, y, element, move_direction);
13355       else
13356         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13357
13358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13359                                           player->index_bit, dig_side);
13360     }
13361   }
13362   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13363   {
13364     RemoveField(x, y);
13365
13366     if (is_player && mode != DF_SNAP)
13367     {
13368       GfxElement[x][y] = element;
13369       player->is_collecting = TRUE;
13370     }
13371
13372     if (element == EL_SPEED_PILL)
13373     {
13374       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13375     }
13376     else if (element == EL_EXTRA_TIME && level.time > 0)
13377     {
13378       TimeLeft += level.extra_time;
13379
13380       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13381
13382       DisplayGameControlValues();
13383     }
13384     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13385     {
13386       player->shield_normal_time_left += level.shield_normal_time;
13387       if (element == EL_SHIELD_DEADLY)
13388         player->shield_deadly_time_left += level.shield_deadly_time;
13389     }
13390     else if (element == EL_DYNAMITE ||
13391              element == EL_EM_DYNAMITE ||
13392              element == EL_SP_DISK_RED)
13393     {
13394       if (player->inventory_size < MAX_INVENTORY_SIZE)
13395         player->inventory_element[player->inventory_size++] = element;
13396
13397       DrawGameDoorValues();
13398     }
13399     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13400     {
13401       player->dynabomb_count++;
13402       player->dynabombs_left++;
13403     }
13404     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13405     {
13406       player->dynabomb_size++;
13407     }
13408     else if (element == EL_DYNABOMB_INCREASE_POWER)
13409     {
13410       player->dynabomb_xl = TRUE;
13411     }
13412     else if (IS_KEY(element))
13413     {
13414       player->key[KEY_NR(element)] = TRUE;
13415
13416       DrawGameDoorValues();
13417     }
13418     else if (element == EL_DC_KEY_WHITE)
13419     {
13420       player->num_white_keys++;
13421
13422       /* display white keys? */
13423       /* DrawGameDoorValues(); */
13424     }
13425     else if (IS_ENVELOPE(element))
13426     {
13427       player->show_envelope = element;
13428     }
13429     else if (element == EL_EMC_LENSES)
13430     {
13431       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13432
13433       RedrawAllInvisibleElementsForLenses();
13434     }
13435     else if (element == EL_EMC_MAGNIFIER)
13436     {
13437       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13438
13439       RedrawAllInvisibleElementsForMagnifier();
13440     }
13441     else if (IS_DROPPABLE(element) ||
13442              IS_THROWABLE(element))     /* can be collected and dropped */
13443     {
13444       int i;
13445
13446       if (collect_count == 0)
13447         player->inventory_infinite_element = element;
13448       else
13449         for (i = 0; i < collect_count; i++)
13450           if (player->inventory_size < MAX_INVENTORY_SIZE)
13451             player->inventory_element[player->inventory_size++] = element;
13452
13453       DrawGameDoorValues();
13454     }
13455     else if (collect_count > 0)
13456     {
13457       local_player->gems_still_needed -= collect_count;
13458       if (local_player->gems_still_needed < 0)
13459         local_player->gems_still_needed = 0;
13460
13461       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13462
13463       DisplayGameControlValues();
13464     }
13465
13466     RaiseScoreElement(element);
13467     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13468
13469     if (is_player)
13470       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13471                                           player->index_bit, dig_side);
13472
13473     if (mode == DF_SNAP)
13474     {
13475       if (level.block_snap_field)
13476         setFieldForSnapping(x, y, element, move_direction);
13477       else
13478         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13479
13480       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13481                                           player->index_bit, dig_side);
13482     }
13483   }
13484   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13485   {
13486     if (mode == DF_SNAP && element != EL_BD_ROCK)
13487       return MP_NO_ACTION;
13488
13489     if (CAN_FALL(element) && dy)
13490       return MP_NO_ACTION;
13491
13492     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13493         !(element == EL_SPRING && level.use_spring_bug))
13494       return MP_NO_ACTION;
13495
13496     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13497         ((move_direction & MV_VERTICAL &&
13498           ((element_info[element].move_pattern & MV_LEFT &&
13499             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13500            (element_info[element].move_pattern & MV_RIGHT &&
13501             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13502          (move_direction & MV_HORIZONTAL &&
13503           ((element_info[element].move_pattern & MV_UP &&
13504             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13505            (element_info[element].move_pattern & MV_DOWN &&
13506             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13507       return MP_NO_ACTION;
13508
13509     /* do not push elements already moving away faster than player */
13510     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13511         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13512       return MP_NO_ACTION;
13513
13514     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13515     {
13516       if (player->push_delay_value == -1 || !player_was_pushing)
13517         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13518     }
13519     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13520     {
13521       if (player->push_delay_value == -1)
13522         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13523     }
13524     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13525     {
13526       if (!player->is_pushing)
13527         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13528     }
13529
13530     player->is_pushing = TRUE;
13531     player->is_active = TRUE;
13532
13533     if (!(IN_LEV_FIELD(nextx, nexty) &&
13534           (IS_FREE(nextx, nexty) ||
13535            (IS_SB_ELEMENT(element) &&
13536             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13537            (IS_CUSTOM_ELEMENT(element) &&
13538             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13539       return MP_NO_ACTION;
13540
13541     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13542       return MP_NO_ACTION;
13543
13544     if (player->push_delay == -1)       /* new pushing; restart delay */
13545       player->push_delay = 0;
13546
13547     if (player->push_delay < player->push_delay_value &&
13548         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13549         element != EL_SPRING && element != EL_BALLOON)
13550     {
13551       /* make sure that there is no move delay before next try to push */
13552       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13553         player->move_delay = 0;
13554
13555       return MP_NO_ACTION;
13556     }
13557
13558     if (IS_CUSTOM_ELEMENT(element) &&
13559         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13560     {
13561       if (!DigFieldByCE(nextx, nexty, element))
13562         return MP_NO_ACTION;
13563     }
13564
13565     if (IS_SB_ELEMENT(element))
13566     {
13567       if (element == EL_SOKOBAN_FIELD_FULL)
13568       {
13569         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13570         local_player->sokobanfields_still_needed++;
13571       }
13572
13573       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13574       {
13575         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13576         local_player->sokobanfields_still_needed--;
13577       }
13578
13579       Feld[x][y] = EL_SOKOBAN_OBJECT;
13580
13581       if (Back[x][y] == Back[nextx][nexty])
13582         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13583       else if (Back[x][y] != 0)
13584         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13585                                     ACTION_EMPTYING);
13586       else
13587         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13588                                     ACTION_FILLING);
13589
13590       if (local_player->sokobanfields_still_needed == 0 &&
13591           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13592       {
13593         PlayerWins(player);
13594
13595         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13596       }
13597     }
13598     else
13599       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13600
13601     InitMovingField(x, y, move_direction);
13602     GfxAction[x][y] = ACTION_PUSHING;
13603
13604     if (mode == DF_SNAP)
13605       ContinueMoving(x, y);
13606     else
13607       MovPos[x][y] = (dx != 0 ? dx : dy);
13608
13609     Pushed[x][y] = TRUE;
13610     Pushed[nextx][nexty] = TRUE;
13611
13612     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13613       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13614     else
13615       player->push_delay_value = -1;    /* get new value later */
13616
13617     /* check for element change _after_ element has been pushed */
13618     if (game.use_change_when_pushing_bug)
13619     {
13620       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13621                                  player->index_bit, dig_side);
13622       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13623                                           player->index_bit, dig_side);
13624     }
13625   }
13626   else if (IS_SWITCHABLE(element))
13627   {
13628     if (PLAYER_SWITCHING(player, x, y))
13629     {
13630       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13631                                           player->index_bit, dig_side);
13632
13633       return MP_ACTION;
13634     }
13635
13636     player->is_switching = TRUE;
13637     player->switch_x = x;
13638     player->switch_y = y;
13639
13640     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13641
13642     if (element == EL_ROBOT_WHEEL)
13643     {
13644       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13645       ZX = x;
13646       ZY = y;
13647
13648       game.robot_wheel_active = TRUE;
13649
13650       TEST_DrawLevelField(x, y);
13651     }
13652     else if (element == EL_SP_TERMINAL)
13653     {
13654       int xx, yy;
13655
13656       SCAN_PLAYFIELD(xx, yy)
13657       {
13658         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13659           Bang(xx, yy);
13660         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13661           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13662       }
13663     }
13664     else if (IS_BELT_SWITCH(element))
13665     {
13666       ToggleBeltSwitch(x, y);
13667     }
13668     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13669              element == EL_SWITCHGATE_SWITCH_DOWN ||
13670              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13671              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13672     {
13673       ToggleSwitchgateSwitch(x, y);
13674     }
13675     else if (element == EL_LIGHT_SWITCH ||
13676              element == EL_LIGHT_SWITCH_ACTIVE)
13677     {
13678       ToggleLightSwitch(x, y);
13679     }
13680     else if (element == EL_TIMEGATE_SWITCH ||
13681              element == EL_DC_TIMEGATE_SWITCH)
13682     {
13683       ActivateTimegateSwitch(x, y);
13684     }
13685     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13686              element == EL_BALLOON_SWITCH_RIGHT ||
13687              element == EL_BALLOON_SWITCH_UP    ||
13688              element == EL_BALLOON_SWITCH_DOWN  ||
13689              element == EL_BALLOON_SWITCH_NONE  ||
13690              element == EL_BALLOON_SWITCH_ANY)
13691     {
13692       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13693                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13694                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13695                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13696                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13697                              move_direction);
13698     }
13699     else if (element == EL_LAMP)
13700     {
13701       Feld[x][y] = EL_LAMP_ACTIVE;
13702       local_player->lights_still_needed--;
13703
13704       ResetGfxAnimation(x, y);
13705       TEST_DrawLevelField(x, y);
13706     }
13707     else if (element == EL_TIME_ORB_FULL)
13708     {
13709       Feld[x][y] = EL_TIME_ORB_EMPTY;
13710
13711       if (level.time > 0 || level.use_time_orb_bug)
13712       {
13713         TimeLeft += level.time_orb_time;
13714         game.no_time_limit = FALSE;
13715
13716         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13717
13718         DisplayGameControlValues();
13719       }
13720
13721       ResetGfxAnimation(x, y);
13722       TEST_DrawLevelField(x, y);
13723     }
13724     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13725              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13726     {
13727       int xx, yy;
13728
13729       game.ball_state = !game.ball_state;
13730
13731       SCAN_PLAYFIELD(xx, yy)
13732       {
13733         int e = Feld[xx][yy];
13734
13735         if (game.ball_state)
13736         {
13737           if (e == EL_EMC_MAGIC_BALL)
13738             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13739           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13740             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13741         }
13742         else
13743         {
13744           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13745             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13746           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13747             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13748         }
13749       }
13750     }
13751
13752     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13753                                         player->index_bit, dig_side);
13754
13755     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13756                                         player->index_bit, dig_side);
13757
13758     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13759                                         player->index_bit, dig_side);
13760
13761     return MP_ACTION;
13762   }
13763   else
13764   {
13765     if (!PLAYER_SWITCHING(player, x, y))
13766     {
13767       player->is_switching = TRUE;
13768       player->switch_x = x;
13769       player->switch_y = y;
13770
13771       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13772                                  player->index_bit, dig_side);
13773       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13774                                           player->index_bit, dig_side);
13775
13776       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13777                                  player->index_bit, dig_side);
13778       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13779                                           player->index_bit, dig_side);
13780     }
13781
13782     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13783                                player->index_bit, dig_side);
13784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13785                                         player->index_bit, dig_side);
13786
13787     return MP_NO_ACTION;
13788   }
13789
13790   player->push_delay = -1;
13791
13792   if (is_player)                /* function can also be called by EL_PENGUIN */
13793   {
13794     if (Feld[x][y] != element)          /* really digged/collected something */
13795     {
13796       player->is_collecting = !player->is_digging;
13797       player->is_active = TRUE;
13798     }
13799   }
13800
13801   return MP_MOVING;
13802 }
13803
13804 static boolean DigFieldByCE(int x, int y, int digging_element)
13805 {
13806   int element = Feld[x][y];
13807
13808   if (!IS_FREE(x, y))
13809   {
13810     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13811                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13812                   ACTION_BREAKING);
13813
13814     /* no element can dig solid indestructible elements */
13815     if (IS_INDESTRUCTIBLE(element) &&
13816         !IS_DIGGABLE(element) &&
13817         !IS_COLLECTIBLE(element))
13818       return FALSE;
13819
13820     if (AmoebaNr[x][y] &&
13821         (element == EL_AMOEBA_FULL ||
13822          element == EL_BD_AMOEBA ||
13823          element == EL_AMOEBA_GROWING))
13824     {
13825       AmoebaCnt[AmoebaNr[x][y]]--;
13826       AmoebaCnt2[AmoebaNr[x][y]]--;
13827     }
13828
13829     if (IS_MOVING(x, y))
13830       RemoveMovingField(x, y);
13831     else
13832     {
13833       RemoveField(x, y);
13834       TEST_DrawLevelField(x, y);
13835     }
13836
13837     /* if digged element was about to explode, prevent the explosion */
13838     ExplodeField[x][y] = EX_TYPE_NONE;
13839
13840     PlayLevelSoundAction(x, y, action);
13841   }
13842
13843   Store[x][y] = EL_EMPTY;
13844
13845   /* this makes it possible to leave the removed element again */
13846   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13847     Store[x][y] = element;
13848
13849   return TRUE;
13850 }
13851
13852 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13853 {
13854   int jx = player->jx, jy = player->jy;
13855   int x = jx + dx, y = jy + dy;
13856   int snap_direction = (dx == -1 ? MV_LEFT  :
13857                         dx == +1 ? MV_RIGHT :
13858                         dy == -1 ? MV_UP    :
13859                         dy == +1 ? MV_DOWN  : MV_NONE);
13860   boolean can_continue_snapping = (level.continuous_snapping &&
13861                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13862
13863   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13864     return FALSE;
13865
13866   if (!player->active || !IN_LEV_FIELD(x, y))
13867     return FALSE;
13868
13869   if (dx && dy)
13870     return FALSE;
13871
13872   if (!dx && !dy)
13873   {
13874     if (player->MovPos == 0)
13875       player->is_pushing = FALSE;
13876
13877     player->is_snapping = FALSE;
13878
13879     if (player->MovPos == 0)
13880     {
13881       player->is_moving = FALSE;
13882       player->is_digging = FALSE;
13883       player->is_collecting = FALSE;
13884     }
13885
13886     return FALSE;
13887   }
13888
13889   /* prevent snapping with already pressed snap key when not allowed */
13890   if (player->is_snapping && !can_continue_snapping)
13891     return FALSE;
13892
13893   player->MovDir = snap_direction;
13894
13895   if (player->MovPos == 0)
13896   {
13897     player->is_moving = FALSE;
13898     player->is_digging = FALSE;
13899     player->is_collecting = FALSE;
13900   }
13901
13902   player->is_dropping = FALSE;
13903   player->is_dropping_pressed = FALSE;
13904   player->drop_pressed_delay = 0;
13905
13906   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13907     return FALSE;
13908
13909   player->is_snapping = TRUE;
13910   player->is_active = TRUE;
13911
13912   if (player->MovPos == 0)
13913   {
13914     player->is_moving = FALSE;
13915     player->is_digging = FALSE;
13916     player->is_collecting = FALSE;
13917   }
13918
13919   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13920     TEST_DrawLevelField(player->last_jx, player->last_jy);
13921
13922   TEST_DrawLevelField(x, y);
13923
13924   return TRUE;
13925 }
13926
13927 static boolean DropElement(struct PlayerInfo *player)
13928 {
13929   int old_element, new_element;
13930   int dropx = player->jx, dropy = player->jy;
13931   int drop_direction = player->MovDir;
13932   int drop_side = drop_direction;
13933   int drop_element = get_next_dropped_element(player);
13934
13935   player->is_dropping_pressed = TRUE;
13936
13937   /* do not drop an element on top of another element; when holding drop key
13938      pressed without moving, dropped element must move away before the next
13939      element can be dropped (this is especially important if the next element
13940      is dynamite, which can be placed on background for historical reasons) */
13941   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13942     return MP_ACTION;
13943
13944   if (IS_THROWABLE(drop_element))
13945   {
13946     dropx += GET_DX_FROM_DIR(drop_direction);
13947     dropy += GET_DY_FROM_DIR(drop_direction);
13948
13949     if (!IN_LEV_FIELD(dropx, dropy))
13950       return FALSE;
13951   }
13952
13953   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13954   new_element = drop_element;           /* default: no change when dropping */
13955
13956   /* check if player is active, not moving and ready to drop */
13957   if (!player->active || player->MovPos || player->drop_delay > 0)
13958     return FALSE;
13959
13960   /* check if player has anything that can be dropped */
13961   if (new_element == EL_UNDEFINED)
13962     return FALSE;
13963
13964   /* check if drop key was pressed long enough for EM style dynamite */
13965   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13966     return FALSE;
13967
13968   /* check if anything can be dropped at the current position */
13969   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13970     return FALSE;
13971
13972   /* collected custom elements can only be dropped on empty fields */
13973   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13974     return FALSE;
13975
13976   if (old_element != EL_EMPTY)
13977     Back[dropx][dropy] = old_element;   /* store old element on this field */
13978
13979   ResetGfxAnimation(dropx, dropy);
13980   ResetRandomAnimationValue(dropx, dropy);
13981
13982   if (player->inventory_size > 0 ||
13983       player->inventory_infinite_element != EL_UNDEFINED)
13984   {
13985     if (player->inventory_size > 0)
13986     {
13987       player->inventory_size--;
13988
13989       DrawGameDoorValues();
13990
13991       if (new_element == EL_DYNAMITE)
13992         new_element = EL_DYNAMITE_ACTIVE;
13993       else if (new_element == EL_EM_DYNAMITE)
13994         new_element = EL_EM_DYNAMITE_ACTIVE;
13995       else if (new_element == EL_SP_DISK_RED)
13996         new_element = EL_SP_DISK_RED_ACTIVE;
13997     }
13998
13999     Feld[dropx][dropy] = new_element;
14000
14001     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14002       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14003                           el2img(Feld[dropx][dropy]), 0);
14004
14005     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14006
14007     /* needed if previous element just changed to "empty" in the last frame */
14008     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14009
14010     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14011                                player->index_bit, drop_side);
14012     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14013                                         CE_PLAYER_DROPS_X,
14014                                         player->index_bit, drop_side);
14015
14016     TestIfElementTouchesCustomElement(dropx, dropy);
14017   }
14018   else          /* player is dropping a dyna bomb */
14019   {
14020     player->dynabombs_left--;
14021
14022     Feld[dropx][dropy] = new_element;
14023
14024     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14025       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14026                           el2img(Feld[dropx][dropy]), 0);
14027
14028     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14029   }
14030
14031   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14032     InitField_WithBug1(dropx, dropy, FALSE);
14033
14034   new_element = Feld[dropx][dropy];     /* element might have changed */
14035
14036   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14037       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14038   {
14039     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14040       MovDir[dropx][dropy] = drop_direction;
14041
14042     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14043
14044     /* do not cause impact style collision by dropping elements that can fall */
14045     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14046   }
14047
14048   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14049   player->is_dropping = TRUE;
14050
14051   player->drop_pressed_delay = 0;
14052   player->is_dropping_pressed = FALSE;
14053
14054   player->drop_x = dropx;
14055   player->drop_y = dropy;
14056
14057   return TRUE;
14058 }
14059
14060 /* ------------------------------------------------------------------------- */
14061 /* game sound playing functions                                              */
14062 /* ------------------------------------------------------------------------- */
14063
14064 static int *loop_sound_frame = NULL;
14065 static int *loop_sound_volume = NULL;
14066
14067 void InitPlayLevelSound()
14068 {
14069   int num_sounds = getSoundListSize();
14070
14071   checked_free(loop_sound_frame);
14072   checked_free(loop_sound_volume);
14073
14074   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14075   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14076 }
14077
14078 static void PlayLevelSound(int x, int y, int nr)
14079 {
14080   int sx = SCREENX(x), sy = SCREENY(y);
14081   int volume, stereo_position;
14082   int max_distance = 8;
14083   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14084
14085   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14086       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14087     return;
14088
14089   if (!IN_LEV_FIELD(x, y) ||
14090       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14091       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14092     return;
14093
14094   volume = SOUND_MAX_VOLUME;
14095
14096   if (!IN_SCR_FIELD(sx, sy))
14097   {
14098     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14099     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14100
14101     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14102   }
14103
14104   stereo_position = (SOUND_MAX_LEFT +
14105                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14106                      (SCR_FIELDX + 2 * max_distance));
14107
14108   if (IS_LOOP_SOUND(nr))
14109   {
14110     /* This assures that quieter loop sounds do not overwrite louder ones,
14111        while restarting sound volume comparison with each new game frame. */
14112
14113     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14114       return;
14115
14116     loop_sound_volume[nr] = volume;
14117     loop_sound_frame[nr] = FrameCounter;
14118   }
14119
14120   PlaySoundExt(nr, volume, stereo_position, type);
14121 }
14122
14123 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14124 {
14125   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14126                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14127                  y < LEVELY(BY1) ? LEVELY(BY1) :
14128                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14129                  sound_action);
14130 }
14131
14132 static void PlayLevelSoundAction(int x, int y, int action)
14133 {
14134   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14135 }
14136
14137 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14138 {
14139   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14140
14141   if (sound_effect != SND_UNDEFINED)
14142     PlayLevelSound(x, y, sound_effect);
14143 }
14144
14145 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14146                                               int action)
14147 {
14148   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14149
14150   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14151     PlayLevelSound(x, y, sound_effect);
14152 }
14153
14154 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14155 {
14156   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14157
14158   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14159     PlayLevelSound(x, y, sound_effect);
14160 }
14161
14162 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14163 {
14164   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14165
14166   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14167     StopSound(sound_effect);
14168 }
14169
14170 static void PlayLevelMusic()
14171 {
14172   if (levelset.music[level_nr] != MUS_UNDEFINED)
14173     PlayMusic(levelset.music[level_nr]);        /* from config file */
14174   else
14175     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14176 }
14177
14178 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14179 {
14180   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14181   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14182   int x = xx - 1 - offset;
14183   int y = yy - 1 - offset;
14184
14185   switch (sample)
14186   {
14187     case SAMPLE_blank:
14188       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14189       break;
14190
14191     case SAMPLE_roll:
14192       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14193       break;
14194
14195     case SAMPLE_stone:
14196       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14197       break;
14198
14199     case SAMPLE_nut:
14200       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14201       break;
14202
14203     case SAMPLE_crack:
14204       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14205       break;
14206
14207     case SAMPLE_bug:
14208       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14209       break;
14210
14211     case SAMPLE_tank:
14212       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14213       break;
14214
14215     case SAMPLE_android_clone:
14216       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14217       break;
14218
14219     case SAMPLE_android_move:
14220       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14221       break;
14222
14223     case SAMPLE_spring:
14224       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14225       break;
14226
14227     case SAMPLE_slurp:
14228       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14229       break;
14230
14231     case SAMPLE_eater:
14232       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14233       break;
14234
14235     case SAMPLE_eater_eat:
14236       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14237       break;
14238
14239     case SAMPLE_alien:
14240       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14241       break;
14242
14243     case SAMPLE_collect:
14244       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14245       break;
14246
14247     case SAMPLE_diamond:
14248       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14249       break;
14250
14251     case SAMPLE_squash:
14252       /* !!! CHECK THIS !!! */
14253 #if 1
14254       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14255 #else
14256       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14257 #endif
14258       break;
14259
14260     case SAMPLE_wonderfall:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14262       break;
14263
14264     case SAMPLE_drip:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14266       break;
14267
14268     case SAMPLE_push:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14270       break;
14271
14272     case SAMPLE_dirt:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14274       break;
14275
14276     case SAMPLE_acid:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14278       break;
14279
14280     case SAMPLE_ball:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14282       break;
14283
14284     case SAMPLE_grow:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14286       break;
14287
14288     case SAMPLE_wonder:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14290       break;
14291
14292     case SAMPLE_door:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14294       break;
14295
14296     case SAMPLE_exit_open:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14298       break;
14299
14300     case SAMPLE_exit_leave:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14302       break;
14303
14304     case SAMPLE_dynamite:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14306       break;
14307
14308     case SAMPLE_tick:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14310       break;
14311
14312     case SAMPLE_press:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14314       break;
14315
14316     case SAMPLE_wheel:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14318       break;
14319
14320     case SAMPLE_boom:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14322       break;
14323
14324     case SAMPLE_die:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14326       break;
14327
14328     case SAMPLE_time:
14329       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14330       break;
14331
14332     default:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14334       break;
14335   }
14336 }
14337
14338 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14339 {
14340   int element = map_element_SP_to_RND(element_sp);
14341   int action = map_action_SP_to_RND(action_sp);
14342   int offset = (setup.sp_show_border_elements ? 0 : 1);
14343   int x = xx - offset;
14344   int y = yy - offset;
14345
14346   PlayLevelSoundElementAction(x, y, element, action);
14347 }
14348
14349 void RaiseScore(int value)
14350 {
14351   local_player->score += value;
14352
14353   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14354
14355   DisplayGameControlValues();
14356 }
14357
14358 void RaiseScoreElement(int element)
14359 {
14360   switch (element)
14361   {
14362     case EL_EMERALD:
14363     case EL_BD_DIAMOND:
14364     case EL_EMERALD_YELLOW:
14365     case EL_EMERALD_RED:
14366     case EL_EMERALD_PURPLE:
14367     case EL_SP_INFOTRON:
14368       RaiseScore(level.score[SC_EMERALD]);
14369       break;
14370     case EL_DIAMOND:
14371       RaiseScore(level.score[SC_DIAMOND]);
14372       break;
14373     case EL_CRYSTAL:
14374       RaiseScore(level.score[SC_CRYSTAL]);
14375       break;
14376     case EL_PEARL:
14377       RaiseScore(level.score[SC_PEARL]);
14378       break;
14379     case EL_BUG:
14380     case EL_BD_BUTTERFLY:
14381     case EL_SP_ELECTRON:
14382       RaiseScore(level.score[SC_BUG]);
14383       break;
14384     case EL_SPACESHIP:
14385     case EL_BD_FIREFLY:
14386     case EL_SP_SNIKSNAK:
14387       RaiseScore(level.score[SC_SPACESHIP]);
14388       break;
14389     case EL_YAMYAM:
14390     case EL_DARK_YAMYAM:
14391       RaiseScore(level.score[SC_YAMYAM]);
14392       break;
14393     case EL_ROBOT:
14394       RaiseScore(level.score[SC_ROBOT]);
14395       break;
14396     case EL_PACMAN:
14397       RaiseScore(level.score[SC_PACMAN]);
14398       break;
14399     case EL_NUT:
14400       RaiseScore(level.score[SC_NUT]);
14401       break;
14402     case EL_DYNAMITE:
14403     case EL_EM_DYNAMITE:
14404     case EL_SP_DISK_RED:
14405     case EL_DYNABOMB_INCREASE_NUMBER:
14406     case EL_DYNABOMB_INCREASE_SIZE:
14407     case EL_DYNABOMB_INCREASE_POWER:
14408       RaiseScore(level.score[SC_DYNAMITE]);
14409       break;
14410     case EL_SHIELD_NORMAL:
14411     case EL_SHIELD_DEADLY:
14412       RaiseScore(level.score[SC_SHIELD]);
14413       break;
14414     case EL_EXTRA_TIME:
14415       RaiseScore(level.extra_time_score);
14416       break;
14417     case EL_KEY_1:
14418     case EL_KEY_2:
14419     case EL_KEY_3:
14420     case EL_KEY_4:
14421     case EL_EM_KEY_1:
14422     case EL_EM_KEY_2:
14423     case EL_EM_KEY_3:
14424     case EL_EM_KEY_4:
14425     case EL_EMC_KEY_5:
14426     case EL_EMC_KEY_6:
14427     case EL_EMC_KEY_7:
14428     case EL_EMC_KEY_8:
14429     case EL_DC_KEY_WHITE:
14430       RaiseScore(level.score[SC_KEY]);
14431       break;
14432     default:
14433       RaiseScore(element_info[element].collect_score);
14434       break;
14435   }
14436 }
14437
14438 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14439 {
14440   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14441   {
14442     /* closing door required in case of envelope style request dialogs */
14443     if (!skip_request)
14444       CloseDoor(DOOR_CLOSE_1);
14445
14446 #if defined(NETWORK_AVALIABLE)
14447     if (options.network)
14448       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14449     else
14450 #endif
14451     {
14452       if (quick_quit)
14453         FadeSkipNextFadeIn();
14454
14455       game_status = GAME_MODE_MAIN;
14456
14457       DrawMainMenu();
14458     }
14459   }
14460   else          /* continue playing the game */
14461   {
14462     if (tape.playing && tape.deactivate_display)
14463       TapeDeactivateDisplayOff(TRUE);
14464
14465     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14466
14467     if (tape.playing && tape.deactivate_display)
14468       TapeDeactivateDisplayOn();
14469   }
14470 }
14471
14472 void RequestQuitGame(boolean ask_if_really_quit)
14473 {
14474   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14475   boolean skip_request = AllPlayersGone || quick_quit;
14476
14477   RequestQuitGameExt(skip_request, quick_quit,
14478                      "Do you really want to quit the game?");
14479 }
14480
14481
14482 /* ------------------------------------------------------------------------- */
14483 /* random generator functions                                                */
14484 /* ------------------------------------------------------------------------- */
14485
14486 unsigned int InitEngineRandom_RND(int seed)
14487 {
14488   game.num_random_calls = 0;
14489
14490   return InitEngineRandom(seed);
14491 }
14492
14493 unsigned int RND(int max)
14494 {
14495   if (max > 0)
14496   {
14497     game.num_random_calls++;
14498
14499     return GetEngineRandom(max);
14500   }
14501
14502   return 0;
14503 }
14504
14505
14506 /* ------------------------------------------------------------------------- */
14507 /* game engine snapshot handling functions                                   */
14508 /* ------------------------------------------------------------------------- */
14509
14510 struct EngineSnapshotInfo
14511 {
14512   /* runtime values for custom element collect score */
14513   int collect_score[NUM_CUSTOM_ELEMENTS];
14514
14515   /* runtime values for group element choice position */
14516   int choice_pos[NUM_GROUP_ELEMENTS];
14517
14518   /* runtime values for belt position animations */
14519   int belt_graphic[4][NUM_BELT_PARTS];
14520   int belt_anim_mode[4][NUM_BELT_PARTS];
14521 };
14522
14523 static struct EngineSnapshotInfo engine_snapshot_rnd;
14524 static char *snapshot_level_identifier = NULL;
14525 static int snapshot_level_nr = -1;
14526
14527 static void SaveEngineSnapshotValues_RND()
14528 {
14529   static int belt_base_active_element[4] =
14530   {
14531     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14532     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14533     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14534     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14535   };
14536   int i, j;
14537
14538   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14539   {
14540     int element = EL_CUSTOM_START + i;
14541
14542     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14543   }
14544
14545   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14546   {
14547     int element = EL_GROUP_START + i;
14548
14549     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14550   }
14551
14552   for (i = 0; i < 4; i++)
14553   {
14554     for (j = 0; j < NUM_BELT_PARTS; j++)
14555     {
14556       int element = belt_base_active_element[i] + j;
14557       int graphic = el2img(element);
14558       int anim_mode = graphic_info[graphic].anim_mode;
14559
14560       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14561       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14562     }
14563   }
14564 }
14565
14566 static void LoadEngineSnapshotValues_RND()
14567 {
14568   unsigned int num_random_calls = game.num_random_calls;
14569   int i, j;
14570
14571   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14572   {
14573     int element = EL_CUSTOM_START + i;
14574
14575     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14576   }
14577
14578   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14579   {
14580     int element = EL_GROUP_START + i;
14581
14582     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14583   }
14584
14585   for (i = 0; i < 4; i++)
14586   {
14587     for (j = 0; j < NUM_BELT_PARTS; j++)
14588     {
14589       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14590       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14591
14592       graphic_info[graphic].anim_mode = anim_mode;
14593     }
14594   }
14595
14596   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14597   {
14598     InitRND(tape.random_seed);
14599     for (i = 0; i < num_random_calls; i++)
14600       RND(1);
14601   }
14602
14603   if (game.num_random_calls != num_random_calls)
14604   {
14605     Error(ERR_INFO, "number of random calls out of sync");
14606     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14607     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14608     Error(ERR_EXIT, "this should not happen -- please debug");
14609   }
14610 }
14611
14612 void FreeEngineSnapshotSingle()
14613 {
14614   FreeSnapshotSingle();
14615
14616   setString(&snapshot_level_identifier, NULL);
14617   snapshot_level_nr = -1;
14618 }
14619
14620 void FreeEngineSnapshotList()
14621 {
14622   FreeSnapshotList();
14623 }
14624
14625 ListNode *SaveEngineSnapshotBuffers()
14626 {
14627   ListNode *buffers = NULL;
14628
14629   /* copy some special values to a structure better suited for the snapshot */
14630
14631   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14632     SaveEngineSnapshotValues_RND();
14633   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14634     SaveEngineSnapshotValues_EM();
14635   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14636     SaveEngineSnapshotValues_SP(&buffers);
14637
14638   /* save values stored in special snapshot structure */
14639
14640   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14641     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14642   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14643     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14644   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14645     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14646
14647   /* save further RND engine values */
14648
14649   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14650   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14651   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14652
14653   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14654   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14655   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14656   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14657
14658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14660   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14663
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14665   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14667
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14669
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14671
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14674
14675   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14677   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14679   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14682   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14693
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14696
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14700
14701   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14703
14704   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14708   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14709
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14711   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14712
14713 #if 0
14714   ListNode *node = engine_snapshot_list_rnd;
14715   int num_bytes = 0;
14716
14717   while (node != NULL)
14718   {
14719     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14720
14721     node = node->next;
14722   }
14723
14724   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14725 #endif
14726
14727   return buffers;
14728 }
14729
14730 void SaveEngineSnapshotSingle()
14731 {
14732   ListNode *buffers = SaveEngineSnapshotBuffers();
14733
14734   /* finally save all snapshot buffers to single snapshot */
14735   SaveSnapshotSingle(buffers);
14736
14737   /* save level identification information */
14738   setString(&snapshot_level_identifier, leveldir_current->identifier);
14739   snapshot_level_nr = level_nr;
14740 }
14741
14742 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14743 {
14744   boolean save_snapshot =
14745     (initial_snapshot ||
14746      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14747      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14748       game.snapshot.changed_action));
14749
14750   game.snapshot.changed_action = FALSE;
14751
14752   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14753       tape.quick_resume ||
14754       !save_snapshot)
14755     return FALSE;
14756
14757   ListNode *buffers = SaveEngineSnapshotBuffers();
14758
14759   /* finally save all snapshot buffers to snapshot list */
14760   SaveSnapshotToList(buffers);
14761
14762   return TRUE;
14763 }
14764
14765 boolean SaveEngineSnapshotToList()
14766 {
14767   return SaveEngineSnapshotToListExt(FALSE);
14768 }
14769
14770 void SaveEngineSnapshotToListInitial()
14771 {
14772   FreeEngineSnapshotList();
14773
14774   SaveEngineSnapshotToListExt(TRUE);
14775 }
14776
14777 void LoadEngineSnapshotValues()
14778 {
14779   /* restore special values from snapshot structure */
14780
14781   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14782     LoadEngineSnapshotValues_RND();
14783   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14784     LoadEngineSnapshotValues_EM();
14785   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14786     LoadEngineSnapshotValues_SP();
14787 }
14788
14789 void LoadEngineSnapshotSingle()
14790 {
14791   LoadSnapshotSingle();
14792
14793   LoadEngineSnapshotValues();
14794 }
14795
14796 void LoadEngineSnapshot_Undo(int steps)
14797 {
14798   LoadSnapshotFromList_Older(steps);
14799
14800   LoadEngineSnapshotValues();
14801 }
14802
14803 void LoadEngineSnapshot_Redo(int steps)
14804 {
14805   LoadSnapshotFromList_Newer(steps);
14806
14807   LoadEngineSnapshotValues();
14808 }
14809
14810 boolean CheckEngineSnapshotSingle()
14811 {
14812   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14813           snapshot_level_nr == level_nr);
14814 }
14815
14816 boolean CheckEngineSnapshotList()
14817 {
14818   return CheckSnapshotList();
14819 }
14820
14821
14822 /* ---------- new game button stuff ---------------------------------------- */
14823
14824 static struct
14825 {
14826   int graphic;
14827   struct XY *pos;
14828   int gadget_id;
14829   char *infotext;
14830 } gamebutton_info[NUM_GAME_BUTTONS] =
14831 {
14832   {
14833     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14834     GAME_CTRL_ID_STOP,                  "stop game"
14835   },
14836   {
14837     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14838     GAME_CTRL_ID_PAUSE,                 "pause game"
14839   },
14840   {
14841     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14842     GAME_CTRL_ID_PLAY,                  "play game"
14843   },
14844   {
14845     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14846     GAME_CTRL_ID_UNDO,                  "undo step"
14847   },
14848   {
14849     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14850     GAME_CTRL_ID_REDO,                  "redo step"
14851   },
14852   {
14853     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14854     GAME_CTRL_ID_SAVE,                  "save game"
14855   },
14856   {
14857     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14858     GAME_CTRL_ID_PAUSE2,                "pause game"
14859   },
14860   {
14861     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14862     GAME_CTRL_ID_LOAD,                  "load game"
14863   },
14864   {
14865     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14866     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14867   },
14868   {
14869     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14870     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14871   },
14872   {
14873     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14874     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14875   }
14876 };
14877
14878 void CreateGameButtons()
14879 {
14880   int i;
14881
14882   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14883   {
14884     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14885     struct XY *pos = gamebutton_info[i].pos;
14886     struct GadgetInfo *gi;
14887     int button_type;
14888     boolean checked;
14889     unsigned int event_mask;
14890     int base_x = (tape.show_game_buttons ? VX : DX);
14891     int base_y = (tape.show_game_buttons ? VY : DY);
14892     int gd_x   = gfx->src_x;
14893     int gd_y   = gfx->src_y;
14894     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14895     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14896     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14897     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14898     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14899     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14900     int id = i;
14901
14902     if (gfx->bitmap == NULL)
14903     {
14904       game_gadget[id] = NULL;
14905
14906       continue;
14907     }
14908
14909     if (id == GAME_CTRL_ID_STOP ||
14910         id == GAME_CTRL_ID_PLAY ||
14911         id == GAME_CTRL_ID_SAVE ||
14912         id == GAME_CTRL_ID_LOAD)
14913     {
14914       button_type = GD_TYPE_NORMAL_BUTTON;
14915       checked = FALSE;
14916       event_mask = GD_EVENT_RELEASED;
14917     }
14918     else if (id == GAME_CTRL_ID_UNDO ||
14919              id == GAME_CTRL_ID_REDO)
14920     {
14921       button_type = GD_TYPE_NORMAL_BUTTON;
14922       checked = FALSE;
14923       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14924     }
14925     else
14926     {
14927       button_type = GD_TYPE_CHECK_BUTTON;
14928       checked =
14929         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14930          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14931          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14932       event_mask = GD_EVENT_PRESSED;
14933     }
14934
14935     gi = CreateGadget(GDI_CUSTOM_ID, id,
14936                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14937                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14938                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14939                       GDI_WIDTH, gfx->width,
14940                       GDI_HEIGHT, gfx->height,
14941                       GDI_TYPE, button_type,
14942                       GDI_STATE, GD_BUTTON_UNPRESSED,
14943                       GDI_CHECKED, checked,
14944                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14945                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14946                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14947                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14948                       GDI_DIRECT_DRAW, FALSE,
14949                       GDI_EVENT_MASK, event_mask,
14950                       GDI_CALLBACK_ACTION, HandleGameButtons,
14951                       GDI_END);
14952
14953     if (gi == NULL)
14954       Error(ERR_EXIT, "cannot create gadget");
14955
14956     game_gadget[id] = gi;
14957   }
14958 }
14959
14960 void FreeGameButtons()
14961 {
14962   int i;
14963
14964   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14965     FreeGadget(game_gadget[i]);
14966 }
14967
14968 static void MapGameButtonsAtSamePosition(int id)
14969 {
14970   int i;
14971
14972   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14973     if (i != id &&
14974         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14975         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14976       MapGadget(game_gadget[i]);
14977 }
14978
14979 static void UnmapGameButtonsAtSamePosition(int id)
14980 {
14981   int i;
14982
14983   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14984     if (i != id &&
14985         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
14986         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
14987       UnmapGadget(game_gadget[i]);
14988 }
14989
14990 void MapUndoRedoButtons()
14991 {
14992   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
14993   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
14994
14995   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14996   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14997 }
14998
14999 void UnmapUndoRedoButtons()
15000 {
15001   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15002   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15003
15004   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15005   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15006 }
15007
15008 void MapGameButtons()
15009 {
15010   int i;
15011
15012   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15013     if (i != GAME_CTRL_ID_UNDO &&
15014         i != GAME_CTRL_ID_REDO)
15015       MapGadget(game_gadget[i]);
15016
15017   if (setup.show_snapshot_buttons)
15018   {
15019     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15020     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15021     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15022   }
15023   else
15024   {
15025     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15026     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15027     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15028   }
15029
15030   RedrawGameButtons();
15031 }
15032
15033 void UnmapGameButtons()
15034 {
15035   int i;
15036
15037   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15038     UnmapGadget(game_gadget[i]);
15039 }
15040
15041 void RedrawGameButtons()
15042 {
15043   int i;
15044
15045   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15046     RedrawGadget(game_gadget[i]);
15047
15048   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15049   redraw_mask &= ~REDRAW_ALL;
15050 }
15051
15052 void GameUndoRedoExt()
15053 {
15054   ClearPlayerAction();
15055
15056   tape.pausing = TRUE;
15057
15058   RedrawPlayfield();
15059   UpdateAndDisplayGameControlValues();
15060
15061   DrawCompleteVideoDisplay();
15062   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15063   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15064   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15065                     VIDEO_STATE_1STEP_OFF), 0);
15066
15067   BackToFront();
15068 }
15069
15070 void GameUndo(int steps)
15071 {
15072   if (!CheckEngineSnapshotList())
15073     return;
15074
15075   LoadEngineSnapshot_Undo(steps);
15076
15077   GameUndoRedoExt();
15078 }
15079
15080 void GameRedo(int steps)
15081 {
15082   if (!CheckEngineSnapshotList())
15083     return;
15084
15085   LoadEngineSnapshot_Redo(steps);
15086
15087   GameUndoRedoExt();
15088 }
15089
15090 static void HandleGameButtonsExt(int id, int button)
15091 {
15092   int steps = BUTTON_STEPSIZE(button);
15093   boolean handle_game_buttons =
15094     (game_status == GAME_MODE_PLAYING ||
15095      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15096
15097   if (!handle_game_buttons)
15098     return;
15099
15100   switch (id)
15101   {
15102     case GAME_CTRL_ID_STOP:
15103       if (game_status == GAME_MODE_MAIN)
15104         break;
15105
15106       if (tape.playing)
15107         TapeStop();
15108       else
15109         RequestQuitGame(TRUE);
15110
15111       break;
15112
15113     case GAME_CTRL_ID_PAUSE:
15114     case GAME_CTRL_ID_PAUSE2:
15115       if (options.network && game_status == GAME_MODE_PLAYING)
15116       {
15117 #if defined(NETWORK_AVALIABLE)
15118         if (tape.pausing)
15119           SendToServer_ContinuePlaying();
15120         else
15121           SendToServer_PausePlaying();
15122 #endif
15123       }
15124       else
15125         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15126       break;
15127
15128     case GAME_CTRL_ID_PLAY:
15129       if (game_status == GAME_MODE_MAIN)
15130       {
15131         StartGameActions(options.network, setup.autorecord, level.random_seed);
15132       }
15133       else if (tape.pausing)
15134       {
15135 #if defined(NETWORK_AVALIABLE)
15136         if (options.network)
15137           SendToServer_ContinuePlaying();
15138         else
15139 #endif
15140           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15141       }
15142       break;
15143
15144     case GAME_CTRL_ID_UNDO:
15145       GameUndo(steps);
15146       break;
15147
15148     case GAME_CTRL_ID_REDO:
15149       GameRedo(steps);
15150       break;
15151
15152     case GAME_CTRL_ID_SAVE:
15153       TapeQuickSave();
15154       break;
15155
15156     case GAME_CTRL_ID_LOAD:
15157       TapeQuickLoad();
15158       break;
15159
15160     case SOUND_CTRL_ID_MUSIC:
15161       if (setup.sound_music)
15162       { 
15163         setup.sound_music = FALSE;
15164
15165         FadeMusic();
15166       }
15167       else if (audio.music_available)
15168       { 
15169         setup.sound = setup.sound_music = TRUE;
15170
15171         SetAudioMode(setup.sound);
15172
15173         PlayLevelMusic();
15174       }
15175       break;
15176
15177     case SOUND_CTRL_ID_LOOPS:
15178       if (setup.sound_loops)
15179         setup.sound_loops = FALSE;
15180       else if (audio.loops_available)
15181       {
15182         setup.sound = setup.sound_loops = TRUE;
15183
15184         SetAudioMode(setup.sound);
15185       }
15186       break;
15187
15188     case SOUND_CTRL_ID_SIMPLE:
15189       if (setup.sound_simple)
15190         setup.sound_simple = FALSE;
15191       else if (audio.sound_available)
15192       {
15193         setup.sound = setup.sound_simple = TRUE;
15194
15195         SetAudioMode(setup.sound);
15196       }
15197       break;
15198
15199     default:
15200       break;
15201   }
15202 }
15203
15204 static void HandleGameButtons(struct GadgetInfo *gi)
15205 {
15206   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15207 }
15208
15209 void HandleSoundButtonKeys(Key key)
15210 {
15211
15212   if (key == setup.shortcut.sound_simple)
15213     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15214   else if (key == setup.shortcut.sound_loops)
15215     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15216   else if (key == setup.shortcut.sound_music)
15217     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15218 }