removed debug output for 'wrong animation frames' bug
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_PAUSE2             6
978 #define GAME_CTRL_ID_LOAD               7
979 #define SOUND_CTRL_ID_MUSIC             8
980 #define SOUND_CTRL_ID_LOOPS             9
981 #define SOUND_CTRL_ID_SIMPLE            10
982
983 #define NUM_GAME_BUTTONS                11
984
985
986 /* forward declaration for internal use */
987
988 static void CreateField(int, int, int);
989
990 static void ResetGfxAnimation(int, int);
991
992 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
993 static void AdvanceFrameAndPlayerCounters(int);
994
995 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
996 static boolean MovePlayer(struct PlayerInfo *, int, int);
997 static void ScrollPlayer(struct PlayerInfo *, int);
998 static void ScrollScreen(struct PlayerInfo *, int);
999
1000 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1001 static boolean DigFieldByCE(int, int, int);
1002 static boolean SnapField(struct PlayerInfo *, int, int);
1003 static boolean DropElement(struct PlayerInfo *);
1004
1005 static void InitBeltMovement(void);
1006 static void CloseAllOpenTimegates(void);
1007 static void CheckGravityMovement(struct PlayerInfo *);
1008 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1009 static void KillPlayerUnlessEnemyProtected(int, int);
1010 static void KillPlayerUnlessExplosionProtected(int, int);
1011
1012 static void TestIfPlayerTouchesCustomElement(int, int);
1013 static void TestIfElementTouchesCustomElement(int, int);
1014 static void TestIfElementHitsCustomElement(int, int, int);
1015
1016 static void HandleElementChange(int, int, int);
1017 static void ExecuteCustomElementAction(int, int, int, int);
1018 static boolean ChangeElement(int, int, int, int);
1019
1020 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1021 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1022         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1023 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1024         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1025 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1026         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1027 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1028         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1029
1030 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1031 #define CheckElementChange(x, y, e, te, ev)                             \
1032         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1033 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1034         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1035 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1036         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1037
1038 static void PlayLevelSound(int, int, int);
1039 static void PlayLevelSoundNearest(int, int, int);
1040 static void PlayLevelSoundAction(int, int, int);
1041 static void PlayLevelSoundElementAction(int, int, int, int);
1042 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1043 static void PlayLevelSoundActionIfLoop(int, int, int);
1044 static void StopLevelSoundActionIfLoop(int, int, int);
1045 static void PlayLevelMusic();
1046
1047 static void HandleGameButtons(struct GadgetInfo *);
1048
1049 int AmoebeNachbarNr(int, int);
1050 void AmoebeUmwandeln(int, int);
1051 void ContinueMoving(int, int);
1052 void Bang(int, int);
1053 void InitMovDir(int, int);
1054 void InitAmoebaNr(int, int);
1055 int NewHiScore(void);
1056
1057 void TestIfGoodThingHitsBadThing(int, int, int);
1058 void TestIfBadThingHitsGoodThing(int, int, int);
1059 void TestIfPlayerTouchesBadThing(int, int);
1060 void TestIfPlayerRunsIntoBadThing(int, int, int);
1061 void TestIfBadThingTouchesPlayer(int, int);
1062 void TestIfBadThingRunsIntoPlayer(int, int, int);
1063 void TestIfFriendTouchesBadThing(int, int);
1064 void TestIfBadThingTouchesFriend(int, int);
1065 void TestIfBadThingTouchesOtherBadThing(int, int);
1066 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1067
1068 void KillPlayer(struct PlayerInfo *);
1069 void BuryPlayer(struct PlayerInfo *);
1070 void RemovePlayer(struct PlayerInfo *);
1071
1072 static int getInvisibleActiveFromInvisibleElement(int);
1073 static int getInvisibleFromInvisibleActiveElement(int);
1074
1075 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1076
1077 /* for detection of endless loops, caused by custom element programming */
1078 /* (using maximal playfield width x 10 is just a rough approximation) */
1079 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1080
1081 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1082 {                                                                       \
1083   if (recursion_loop_detected)                                          \
1084     return (rc);                                                        \
1085                                                                         \
1086   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1087   {                                                                     \
1088     recursion_loop_detected = TRUE;                                     \
1089     recursion_loop_element = (e);                                       \
1090   }                                                                     \
1091                                                                         \
1092   recursion_loop_depth++;                                               \
1093 }
1094
1095 #define RECURSION_LOOP_DETECTION_END()                                  \
1096 {                                                                       \
1097   recursion_loop_depth--;                                               \
1098 }
1099
1100 static int recursion_loop_depth;
1101 static boolean recursion_loop_detected;
1102 static boolean recursion_loop_element;
1103
1104 static int map_player_action[MAX_PLAYERS];
1105
1106
1107 /* ------------------------------------------------------------------------- */
1108 /* definition of elements that automatically change to other elements after  */
1109 /* a specified time, eventually calling a function when changing             */
1110 /* ------------------------------------------------------------------------- */
1111
1112 /* forward declaration for changer functions */
1113 static void InitBuggyBase(int, int);
1114 static void WarnBuggyBase(int, int);
1115
1116 static void InitTrap(int, int);
1117 static void ActivateTrap(int, int);
1118 static void ChangeActiveTrap(int, int);
1119
1120 static void InitRobotWheel(int, int);
1121 static void RunRobotWheel(int, int);
1122 static void StopRobotWheel(int, int);
1123
1124 static void InitTimegateWheel(int, int);
1125 static void RunTimegateWheel(int, int);
1126
1127 static void InitMagicBallDelay(int, int);
1128 static void ActivateMagicBall(int, int);
1129
1130 struct ChangingElementInfo
1131 {
1132   int element;
1133   int target_element;
1134   int change_delay;
1135   void (*pre_change_function)(int x, int y);
1136   void (*change_function)(int x, int y);
1137   void (*post_change_function)(int x, int y);
1138 };
1139
1140 static struct ChangingElementInfo change_delay_list[] =
1141 {
1142   {
1143     EL_NUT_BREAKING,
1144     EL_EMERALD,
1145     6,
1146     NULL,
1147     NULL,
1148     NULL
1149   },
1150   {
1151     EL_PEARL_BREAKING,
1152     EL_EMPTY,
1153     8,
1154     NULL,
1155     NULL,
1156     NULL
1157   },
1158   {
1159     EL_EXIT_OPENING,
1160     EL_EXIT_OPEN,
1161     29,
1162     NULL,
1163     NULL,
1164     NULL
1165   },
1166   {
1167     EL_EXIT_CLOSING,
1168     EL_EXIT_CLOSED,
1169     29,
1170     NULL,
1171     NULL,
1172     NULL
1173   },
1174   {
1175     EL_STEEL_EXIT_OPENING,
1176     EL_STEEL_EXIT_OPEN,
1177     29,
1178     NULL,
1179     NULL,
1180     NULL
1181   },
1182   {
1183     EL_STEEL_EXIT_CLOSING,
1184     EL_STEEL_EXIT_CLOSED,
1185     29,
1186     NULL,
1187     NULL,
1188     NULL
1189   },
1190   {
1191     EL_EM_EXIT_OPENING,
1192     EL_EM_EXIT_OPEN,
1193     29,
1194     NULL,
1195     NULL,
1196     NULL
1197   },
1198   {
1199     EL_EM_EXIT_CLOSING,
1200     EL_EMPTY,
1201     29,
1202     NULL,
1203     NULL,
1204     NULL
1205   },
1206   {
1207     EL_EM_STEEL_EXIT_OPENING,
1208     EL_EM_STEEL_EXIT_OPEN,
1209     29,
1210     NULL,
1211     NULL,
1212     NULL
1213   },
1214   {
1215     EL_EM_STEEL_EXIT_CLOSING,
1216     EL_STEELWALL,
1217     29,
1218     NULL,
1219     NULL,
1220     NULL
1221   },
1222   {
1223     EL_SP_EXIT_OPENING,
1224     EL_SP_EXIT_OPEN,
1225     29,
1226     NULL,
1227     NULL,
1228     NULL
1229   },
1230   {
1231     EL_SP_EXIT_CLOSING,
1232     EL_SP_EXIT_CLOSED,
1233     29,
1234     NULL,
1235     NULL,
1236     NULL
1237   },
1238   {
1239     EL_SWITCHGATE_OPENING,
1240     EL_SWITCHGATE_OPEN,
1241     29,
1242     NULL,
1243     NULL,
1244     NULL
1245   },
1246   {
1247     EL_SWITCHGATE_CLOSING,
1248     EL_SWITCHGATE_CLOSED,
1249     29,
1250     NULL,
1251     NULL,
1252     NULL
1253   },
1254   {
1255     EL_TIMEGATE_OPENING,
1256     EL_TIMEGATE_OPEN,
1257     29,
1258     NULL,
1259     NULL,
1260     NULL
1261   },
1262   {
1263     EL_TIMEGATE_CLOSING,
1264     EL_TIMEGATE_CLOSED,
1265     29,
1266     NULL,
1267     NULL,
1268     NULL
1269   },
1270
1271   {
1272     EL_ACID_SPLASH_LEFT,
1273     EL_EMPTY,
1274     8,
1275     NULL,
1276     NULL,
1277     NULL
1278   },
1279   {
1280     EL_ACID_SPLASH_RIGHT,
1281     EL_EMPTY,
1282     8,
1283     NULL,
1284     NULL,
1285     NULL
1286   },
1287   {
1288     EL_SP_BUGGY_BASE,
1289     EL_SP_BUGGY_BASE_ACTIVATING,
1290     0,
1291     InitBuggyBase,
1292     NULL,
1293     NULL
1294   },
1295   {
1296     EL_SP_BUGGY_BASE_ACTIVATING,
1297     EL_SP_BUGGY_BASE_ACTIVE,
1298     0,
1299     InitBuggyBase,
1300     NULL,
1301     NULL
1302   },
1303   {
1304     EL_SP_BUGGY_BASE_ACTIVE,
1305     EL_SP_BUGGY_BASE,
1306     0,
1307     InitBuggyBase,
1308     WarnBuggyBase,
1309     NULL
1310   },
1311   {
1312     EL_TRAP,
1313     EL_TRAP_ACTIVE,
1314     0,
1315     InitTrap,
1316     NULL,
1317     ActivateTrap
1318   },
1319   {
1320     EL_TRAP_ACTIVE,
1321     EL_TRAP,
1322     31,
1323     NULL,
1324     ChangeActiveTrap,
1325     NULL
1326   },
1327   {
1328     EL_ROBOT_WHEEL_ACTIVE,
1329     EL_ROBOT_WHEEL,
1330     0,
1331     InitRobotWheel,
1332     RunRobotWheel,
1333     StopRobotWheel
1334   },
1335   {
1336     EL_TIMEGATE_SWITCH_ACTIVE,
1337     EL_TIMEGATE_SWITCH,
1338     0,
1339     InitTimegateWheel,
1340     RunTimegateWheel,
1341     NULL
1342   },
1343   {
1344     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1345     EL_DC_TIMEGATE_SWITCH,
1346     0,
1347     InitTimegateWheel,
1348     RunTimegateWheel,
1349     NULL
1350   },
1351   {
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     EL_EMC_MAGIC_BALL_ACTIVE,
1354     0,
1355     InitMagicBallDelay,
1356     NULL,
1357     ActivateMagicBall
1358   },
1359   {
1360     EL_EMC_SPRING_BUMPER_ACTIVE,
1361     EL_EMC_SPRING_BUMPER,
1362     8,
1363     NULL,
1364     NULL,
1365     NULL
1366   },
1367   {
1368     EL_DIAGONAL_SHRINKING,
1369     EL_UNDEFINED,
1370     0,
1371     NULL,
1372     NULL,
1373     NULL
1374   },
1375   {
1376     EL_DIAGONAL_GROWING,
1377     EL_UNDEFINED,
1378     0,
1379     NULL,
1380     NULL,
1381     NULL,
1382   },
1383
1384   {
1385     EL_UNDEFINED,
1386     EL_UNDEFINED,
1387     -1,
1388     NULL,
1389     NULL,
1390     NULL
1391   }
1392 };
1393
1394 struct
1395 {
1396   int element;
1397   int push_delay_fixed, push_delay_random;
1398 }
1399 push_delay_list[] =
1400 {
1401   { EL_SPRING,                  0, 0 },
1402   { EL_BALLOON,                 0, 0 },
1403
1404   { EL_SOKOBAN_OBJECT,          2, 0 },
1405   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1406   { EL_SATELLITE,               2, 0 },
1407   { EL_SP_DISK_YELLOW,          2, 0 },
1408
1409   { EL_UNDEFINED,               0, 0 },
1410 };
1411
1412 struct
1413 {
1414   int element;
1415   int move_stepsize;
1416 }
1417 move_stepsize_list[] =
1418 {
1419   { EL_AMOEBA_DROP,             2 },
1420   { EL_AMOEBA_DROPPING,         2 },
1421   { EL_QUICKSAND_FILLING,       1 },
1422   { EL_QUICKSAND_EMPTYING,      1 },
1423   { EL_QUICKSAND_FAST_FILLING,  2 },
1424   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1425   { EL_MAGIC_WALL_FILLING,      2 },
1426   { EL_MAGIC_WALL_EMPTYING,     2 },
1427   { EL_BD_MAGIC_WALL_FILLING,   2 },
1428   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1429   { EL_DC_MAGIC_WALL_FILLING,   2 },
1430   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1431
1432   { EL_UNDEFINED,               0 },
1433 };
1434
1435 struct
1436 {
1437   int element;
1438   int count;
1439 }
1440 collect_count_list[] =
1441 {
1442   { EL_EMERALD,                 1 },
1443   { EL_BD_DIAMOND,              1 },
1444   { EL_EMERALD_YELLOW,          1 },
1445   { EL_EMERALD_RED,             1 },
1446   { EL_EMERALD_PURPLE,          1 },
1447   { EL_DIAMOND,                 3 },
1448   { EL_SP_INFOTRON,             1 },
1449   { EL_PEARL,                   5 },
1450   { EL_CRYSTAL,                 8 },
1451
1452   { EL_UNDEFINED,               0 },
1453 };
1454
1455 struct
1456 {
1457   int element;
1458   int direction;
1459 }
1460 access_direction_list[] =
1461 {
1462   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1463   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1464   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1465   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1466   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1467   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1468   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1469   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1470   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1471   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1472   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1473
1474   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1475   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1476   { EL_SP_PORT_UP,                                                   MV_DOWN },
1477   { EL_SP_PORT_DOWN,                                         MV_UP           },
1478   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1479   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1480   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1481   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1482   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1483   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1484   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1485   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1486   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1487   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1488   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1489   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1490   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1491   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1492   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1493
1494   { EL_UNDEFINED,                       MV_NONE                              }
1495 };
1496
1497 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1498
1499 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1500 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1501 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1502                                  IS_JUST_CHANGING(x, y))
1503
1504 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1505
1506 /* static variables for playfield scan mode (scanning forward or backward) */
1507 static int playfield_scan_start_x = 0;
1508 static int playfield_scan_start_y = 0;
1509 static int playfield_scan_delta_x = 1;
1510 static int playfield_scan_delta_y = 1;
1511
1512 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1513                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1514                                      (y) += playfield_scan_delta_y)     \
1515                                 for ((x) = playfield_scan_start_x;      \
1516                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1517                                      (x) += playfield_scan_delta_x)
1518
1519 #ifdef DEBUG
1520 void DEBUG_SetMaximumDynamite()
1521 {
1522   int i;
1523
1524   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1525     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1526       local_player->inventory_element[local_player->inventory_size++] =
1527         EL_DYNAMITE;
1528 }
1529 #endif
1530
1531 static void InitPlayfieldScanModeVars()
1532 {
1533   if (game.use_reverse_scan_direction)
1534   {
1535     playfield_scan_start_x = lev_fieldx - 1;
1536     playfield_scan_start_y = lev_fieldy - 1;
1537
1538     playfield_scan_delta_x = -1;
1539     playfield_scan_delta_y = -1;
1540   }
1541   else
1542   {
1543     playfield_scan_start_x = 0;
1544     playfield_scan_start_y = 0;
1545
1546     playfield_scan_delta_x = 1;
1547     playfield_scan_delta_y = 1;
1548   }
1549 }
1550
1551 static void InitPlayfieldScanMode(int mode)
1552 {
1553   game.use_reverse_scan_direction =
1554     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1555
1556   InitPlayfieldScanModeVars();
1557 }
1558
1559 static int get_move_delay_from_stepsize(int move_stepsize)
1560 {
1561   move_stepsize =
1562     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1563
1564   /* make sure that stepsize value is always a power of 2 */
1565   move_stepsize = (1 << log_2(move_stepsize));
1566
1567   return TILEX / move_stepsize;
1568 }
1569
1570 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1571                                boolean init_game)
1572 {
1573   int player_nr = player->index_nr;
1574   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1575   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1576
1577   /* do no immediately change move delay -- the player might just be moving */
1578   player->move_delay_value_next = move_delay;
1579
1580   /* information if player can move must be set separately */
1581   player->cannot_move = cannot_move;
1582
1583   if (init_game)
1584   {
1585     player->move_delay       = game.initial_move_delay[player_nr];
1586     player->move_delay_value = game.initial_move_delay_value[player_nr];
1587
1588     player->move_delay_value_next = -1;
1589
1590     player->move_delay_reset_counter = 0;
1591   }
1592 }
1593
1594 void GetPlayerConfig()
1595 {
1596   GameFrameDelay = setup.game_frame_delay;
1597
1598   if (!audio.sound_available)
1599     setup.sound_simple = FALSE;
1600
1601   if (!audio.loops_available)
1602     setup.sound_loops = FALSE;
1603
1604   if (!audio.music_available)
1605     setup.sound_music = FALSE;
1606
1607   if (!video.fullscreen_available)
1608     setup.fullscreen = FALSE;
1609
1610   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1611
1612   SetAudioMode(setup.sound);
1613   InitJoysticks();
1614 }
1615
1616 int GetElementFromGroupElement(int element)
1617 {
1618   if (IS_GROUP_ELEMENT(element))
1619   {
1620     struct ElementGroupInfo *group = element_info[element].group;
1621     int last_anim_random_frame = gfx.anim_random_frame;
1622     int element_pos;
1623
1624     if (group->choice_mode == ANIM_RANDOM)
1625       gfx.anim_random_frame = RND(group->num_elements_resolved);
1626
1627     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1628                                     group->choice_mode, 0,
1629                                     group->choice_pos);
1630
1631     if (group->choice_mode == ANIM_RANDOM)
1632       gfx.anim_random_frame = last_anim_random_frame;
1633
1634     group->choice_pos++;
1635
1636     element = group->element_resolved[element_pos];
1637   }
1638
1639   return element;
1640 }
1641
1642 static void InitPlayerField(int x, int y, int element, boolean init_game)
1643 {
1644   if (element == EL_SP_MURPHY)
1645   {
1646     if (init_game)
1647     {
1648       if (stored_player[0].present)
1649       {
1650         Feld[x][y] = EL_SP_MURPHY_CLONE;
1651
1652         return;
1653       }
1654       else
1655       {
1656         stored_player[0].initial_element = element;
1657         stored_player[0].use_murphy = TRUE;
1658
1659         if (!level.use_artwork_element[0])
1660           stored_player[0].artwork_element = EL_SP_MURPHY;
1661       }
1662
1663       Feld[x][y] = EL_PLAYER_1;
1664     }
1665   }
1666
1667   if (init_game)
1668   {
1669     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1670     int jx = player->jx, jy = player->jy;
1671
1672     player->present = TRUE;
1673
1674     player->block_last_field = (element == EL_SP_MURPHY ?
1675                                 level.sp_block_last_field :
1676                                 level.block_last_field);
1677
1678     /* ---------- initialize player's last field block delay --------------- */
1679
1680     /* always start with reliable default value (no adjustment needed) */
1681     player->block_delay_adjustment = 0;
1682
1683     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1684     if (player->block_last_field && element == EL_SP_MURPHY)
1685       player->block_delay_adjustment = 1;
1686
1687     /* special case 2: in game engines before 3.1.1, blocking was different */
1688     if (game.use_block_last_field_bug)
1689       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1690
1691     if (!options.network || player->connected)
1692     {
1693       player->active = TRUE;
1694
1695       /* remove potentially duplicate players */
1696       if (StorePlayer[jx][jy] == Feld[x][y])
1697         StorePlayer[jx][jy] = 0;
1698
1699       StorePlayer[x][y] = Feld[x][y];
1700
1701 #if DEBUG_INIT_PLAYER
1702       if (options.debug)
1703       {
1704         printf("- player element %d activated", player->element_nr);
1705         printf(" (local player is %d and currently %s)\n",
1706                local_player->element_nr,
1707                local_player->active ? "active" : "not active");
1708       }
1709     }
1710 #endif
1711
1712     Feld[x][y] = EL_EMPTY;
1713
1714     player->jx = player->last_jx = x;
1715     player->jy = player->last_jy = y;
1716   }
1717
1718   if (!init_game)
1719   {
1720     int player_nr = GET_PLAYER_NR(element);
1721     struct PlayerInfo *player = &stored_player[player_nr];
1722
1723     if (player->active && player->killed)
1724       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1725   }
1726 }
1727
1728 static void InitField(int x, int y, boolean init_game)
1729 {
1730   int element = Feld[x][y];
1731
1732   switch (element)
1733   {
1734     case EL_SP_MURPHY:
1735     case EL_PLAYER_1:
1736     case EL_PLAYER_2:
1737     case EL_PLAYER_3:
1738     case EL_PLAYER_4:
1739       InitPlayerField(x, y, element, init_game);
1740       break;
1741
1742     case EL_SOKOBAN_FIELD_PLAYER:
1743       element = Feld[x][y] = EL_PLAYER_1;
1744       InitField(x, y, init_game);
1745
1746       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1747       InitField(x, y, init_game);
1748       break;
1749
1750     case EL_SOKOBAN_FIELD_EMPTY:
1751       local_player->sokobanfields_still_needed++;
1752       break;
1753
1754     case EL_STONEBLOCK:
1755       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1756         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1757       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1758         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1759       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1760         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1761       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1762         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1763       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1764         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1765       break;
1766
1767     case EL_BUG:
1768     case EL_BUG_RIGHT:
1769     case EL_BUG_UP:
1770     case EL_BUG_LEFT:
1771     case EL_BUG_DOWN:
1772     case EL_SPACESHIP:
1773     case EL_SPACESHIP_RIGHT:
1774     case EL_SPACESHIP_UP:
1775     case EL_SPACESHIP_LEFT:
1776     case EL_SPACESHIP_DOWN:
1777     case EL_BD_BUTTERFLY:
1778     case EL_BD_BUTTERFLY_RIGHT:
1779     case EL_BD_BUTTERFLY_UP:
1780     case EL_BD_BUTTERFLY_LEFT:
1781     case EL_BD_BUTTERFLY_DOWN:
1782     case EL_BD_FIREFLY:
1783     case EL_BD_FIREFLY_RIGHT:
1784     case EL_BD_FIREFLY_UP:
1785     case EL_BD_FIREFLY_LEFT:
1786     case EL_BD_FIREFLY_DOWN:
1787     case EL_PACMAN_RIGHT:
1788     case EL_PACMAN_UP:
1789     case EL_PACMAN_LEFT:
1790     case EL_PACMAN_DOWN:
1791     case EL_YAMYAM:
1792     case EL_YAMYAM_LEFT:
1793     case EL_YAMYAM_RIGHT:
1794     case EL_YAMYAM_UP:
1795     case EL_YAMYAM_DOWN:
1796     case EL_DARK_YAMYAM:
1797     case EL_ROBOT:
1798     case EL_PACMAN:
1799     case EL_SP_SNIKSNAK:
1800     case EL_SP_ELECTRON:
1801     case EL_MOLE:
1802     case EL_MOLE_LEFT:
1803     case EL_MOLE_RIGHT:
1804     case EL_MOLE_UP:
1805     case EL_MOLE_DOWN:
1806       InitMovDir(x, y);
1807       break;
1808
1809     case EL_AMOEBA_FULL:
1810     case EL_BD_AMOEBA:
1811       InitAmoebaNr(x, y);
1812       break;
1813
1814     case EL_AMOEBA_DROP:
1815       if (y == lev_fieldy - 1)
1816       {
1817         Feld[x][y] = EL_AMOEBA_GROWING;
1818         Store[x][y] = EL_AMOEBA_WET;
1819       }
1820       break;
1821
1822     case EL_DYNAMITE_ACTIVE:
1823     case EL_SP_DISK_RED_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1827     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1828       MovDelay[x][y] = 96;
1829       break;
1830
1831     case EL_EM_DYNAMITE_ACTIVE:
1832       MovDelay[x][y] = 32;
1833       break;
1834
1835     case EL_LAMP:
1836       local_player->lights_still_needed++;
1837       break;
1838
1839     case EL_PENGUIN:
1840       local_player->friends_still_needed++;
1841       break;
1842
1843     case EL_PIG:
1844     case EL_DRAGON:
1845       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1846       break;
1847
1848     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1849     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1850     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1852     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1853     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1855     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1856     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1858     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1859     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1860       if (init_game)
1861       {
1862         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1864         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1865
1866         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1867         {
1868           game.belt_dir[belt_nr] = belt_dir;
1869           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1870         }
1871         else    /* more than one switch -- set it like the first switch */
1872         {
1873           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1874         }
1875       }
1876       break;
1877
1878     case EL_LIGHT_SWITCH_ACTIVE:
1879       if (init_game)
1880         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1881       break;
1882
1883     case EL_INVISIBLE_STEELWALL:
1884     case EL_INVISIBLE_WALL:
1885     case EL_INVISIBLE_SAND:
1886       if (game.light_time_left > 0 ||
1887           game.lenses_time_left > 0)
1888         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1889       break;
1890
1891     case EL_EMC_MAGIC_BALL:
1892       if (game.ball_state)
1893         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1894       break;
1895
1896     case EL_EMC_MAGIC_BALL_SWITCH:
1897       if (game.ball_state)
1898         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1899       break;
1900
1901     case EL_TRIGGER_PLAYER:
1902     case EL_TRIGGER_ELEMENT:
1903     case EL_TRIGGER_CE_VALUE:
1904     case EL_TRIGGER_CE_SCORE:
1905     case EL_SELF:
1906     case EL_ANY_ELEMENT:
1907     case EL_CURRENT_CE_VALUE:
1908     case EL_CURRENT_CE_SCORE:
1909     case EL_PREV_CE_1:
1910     case EL_PREV_CE_2:
1911     case EL_PREV_CE_3:
1912     case EL_PREV_CE_4:
1913     case EL_PREV_CE_5:
1914     case EL_PREV_CE_6:
1915     case EL_PREV_CE_7:
1916     case EL_PREV_CE_8:
1917     case EL_NEXT_CE_1:
1918     case EL_NEXT_CE_2:
1919     case EL_NEXT_CE_3:
1920     case EL_NEXT_CE_4:
1921     case EL_NEXT_CE_5:
1922     case EL_NEXT_CE_6:
1923     case EL_NEXT_CE_7:
1924     case EL_NEXT_CE_8:
1925       /* reference elements should not be used on the playfield */
1926       Feld[x][y] = EL_EMPTY;
1927       break;
1928
1929     default:
1930       if (IS_CUSTOM_ELEMENT(element))
1931       {
1932         if (CAN_MOVE(element))
1933           InitMovDir(x, y);
1934
1935         if (!element_info[element].use_last_ce_value || init_game)
1936           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1937       }
1938       else if (IS_GROUP_ELEMENT(element))
1939       {
1940         Feld[x][y] = GetElementFromGroupElement(element);
1941
1942         InitField(x, y, init_game);
1943       }
1944
1945       break;
1946   }
1947
1948   if (!init_game)
1949     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1950 }
1951
1952 inline static void InitField_WithBug1(int x, int y, boolean init_game)
1953 {
1954   InitField(x, y, init_game);
1955
1956   /* not needed to call InitMovDir() -- already done by InitField()! */
1957   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1958       CAN_MOVE(Feld[x][y]))
1959     InitMovDir(x, y);
1960 }
1961
1962 inline static void InitField_WithBug2(int x, int y, boolean init_game)
1963 {
1964   int old_element = Feld[x][y];
1965
1966   InitField(x, y, init_game);
1967
1968   /* not needed to call InitMovDir() -- already done by InitField()! */
1969   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1970       CAN_MOVE(old_element) &&
1971       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1972     InitMovDir(x, y);
1973
1974   /* this case is in fact a combination of not less than three bugs:
1975      first, it calls InitMovDir() for elements that can move, although this is
1976      already done by InitField(); then, it checks the element that was at this
1977      field _before_ the call to InitField() (which can change it); lastly, it
1978      was not called for "mole with direction" elements, which were treated as
1979      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1980   */
1981 }
1982
1983 static int get_key_element_from_nr(int key_nr)
1984 {
1985   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1986                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1987                           EL_EM_KEY_1 : EL_KEY_1);
1988
1989   return key_base_element + key_nr;
1990 }
1991
1992 static int get_next_dropped_element(struct PlayerInfo *player)
1993 {
1994   return (player->inventory_size > 0 ?
1995           player->inventory_element[player->inventory_size - 1] :
1996           player->inventory_infinite_element != EL_UNDEFINED ?
1997           player->inventory_infinite_element :
1998           player->dynabombs_left > 0 ?
1999           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
2000           EL_UNDEFINED);
2001 }
2002
2003 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2004 {
2005   /* pos >= 0: get element from bottom of the stack;
2006      pos <  0: get element from top of the stack */
2007
2008   if (pos < 0)
2009   {
2010     int min_inventory_size = -pos;
2011     int inventory_pos = player->inventory_size - min_inventory_size;
2012     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2013
2014     return (player->inventory_size >= min_inventory_size ?
2015             player->inventory_element[inventory_pos] :
2016             player->inventory_infinite_element != EL_UNDEFINED ?
2017             player->inventory_infinite_element :
2018             player->dynabombs_left >= min_dynabombs_left ?
2019             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2020             EL_UNDEFINED);
2021   }
2022   else
2023   {
2024     int min_dynabombs_left = pos + 1;
2025     int min_inventory_size = pos + 1 - player->dynabombs_left;
2026     int inventory_pos = pos - player->dynabombs_left;
2027
2028     return (player->inventory_infinite_element != EL_UNDEFINED ?
2029             player->inventory_infinite_element :
2030             player->dynabombs_left >= min_dynabombs_left ?
2031             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2032             player->inventory_size >= min_inventory_size ?
2033             player->inventory_element[inventory_pos] :
2034             EL_UNDEFINED);
2035   }
2036 }
2037
2038 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2039 {
2040   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2041   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2042   int compare_result;
2043
2044   if (gpo1->sort_priority != gpo2->sort_priority)
2045     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2046   else
2047     compare_result = gpo1->nr - gpo2->nr;
2048
2049   return compare_result;
2050 }
2051
2052 void InitGameControlValues()
2053 {
2054   int i;
2055
2056   for (i = 0; game_panel_controls[i].nr != -1; i++)
2057   {
2058     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2059     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2060     struct TextPosInfo *pos = gpc->pos;
2061     int nr = gpc->nr;
2062     int type = gpc->type;
2063
2064     if (nr != i)
2065     {
2066       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2067       Error(ERR_EXIT, "this should not happen -- please debug");
2068     }
2069
2070     /* force update of game controls after initialization */
2071     gpc->value = gpc->last_value = -1;
2072     gpc->frame = gpc->last_frame = -1;
2073     gpc->gfx_frame = -1;
2074
2075     /* determine panel value width for later calculation of alignment */
2076     if (type == TYPE_INTEGER || type == TYPE_STRING)
2077     {
2078       pos->width = pos->size * getFontWidth(pos->font);
2079       pos->height = getFontHeight(pos->font);
2080     }
2081     else if (type == TYPE_ELEMENT)
2082     {
2083       pos->width = pos->size;
2084       pos->height = pos->size;
2085     }
2086
2087     /* fill structure for game panel draw order */
2088     gpo->nr = gpc->nr;
2089     gpo->sort_priority = pos->sort_priority;
2090   }
2091
2092   /* sort game panel controls according to sort_priority and control number */
2093   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2094         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2095 }
2096
2097 void UpdatePlayfieldElementCount()
2098 {
2099   boolean use_element_count = FALSE;
2100   int i, j, x, y;
2101
2102   /* first check if it is needed at all to calculate playfield element count */
2103   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2104     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2105       use_element_count = TRUE;
2106
2107   if (!use_element_count)
2108     return;
2109
2110   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2111     element_info[i].element_count = 0;
2112
2113   SCAN_PLAYFIELD(x, y)
2114   {
2115     element_info[Feld[x][y]].element_count++;
2116   }
2117
2118   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2119     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2120       if (IS_IN_GROUP(j, i))
2121         element_info[EL_GROUP_START + i].element_count +=
2122           element_info[j].element_count;
2123 }
2124
2125 void UpdateGameControlValues()
2126 {
2127   int i, k;
2128   int time = (local_player->LevelSolved ?
2129               local_player->LevelSolved_CountingTime :
2130               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2131               level.native_em_level->lev->time :
2132               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2133               level.native_sp_level->game_sp->time_played :
2134               game.no_time_limit ? TimePlayed : TimeLeft);
2135   int score = (local_player->LevelSolved ?
2136                local_player->LevelSolved_CountingScore :
2137                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2138                level.native_em_level->lev->score :
2139                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2140                level.native_sp_level->game_sp->score :
2141                local_player->score);
2142   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2143               level.native_em_level->lev->required :
2144               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2145               level.native_sp_level->game_sp->infotrons_still_needed :
2146               local_player->gems_still_needed);
2147   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2148                      level.native_em_level->lev->required > 0 :
2149                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2150                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2151                      local_player->gems_still_needed > 0 ||
2152                      local_player->sokobanfields_still_needed > 0 ||
2153                      local_player->lights_still_needed > 0);
2154
2155   UpdatePlayfieldElementCount();
2156
2157   /* update game panel control values */
2158
2159   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2160   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2161
2162   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2163   for (i = 0; i < MAX_NUM_KEYS; i++)
2164     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2166   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2167
2168   if (game.centered_player_nr == -1)
2169   {
2170     for (i = 0; i < MAX_PLAYERS; i++)
2171     {
2172       /* only one player in Supaplex game engine */
2173       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2174         break;
2175
2176       for (k = 0; k < MAX_NUM_KEYS; k++)
2177       {
2178         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2179         {
2180           if (level.native_em_level->ply[i]->keys & (1 << k))
2181             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2182               get_key_element_from_nr(k);
2183         }
2184         else if (stored_player[i].key[k])
2185           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2186             get_key_element_from_nr(k);
2187       }
2188
2189       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2190         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2191           level.native_em_level->ply[i]->dynamite;
2192       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2193         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2194           level.native_sp_level->game_sp->red_disk_count;
2195       else
2196         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2197           stored_player[i].inventory_size;
2198
2199       if (stored_player[i].num_white_keys > 0)
2200         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2201           EL_DC_KEY_WHITE;
2202
2203       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2204         stored_player[i].num_white_keys;
2205     }
2206   }
2207   else
2208   {
2209     int player_nr = game.centered_player_nr;
2210
2211     for (k = 0; k < MAX_NUM_KEYS; k++)
2212     {
2213       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2214       {
2215         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2216           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2217             get_key_element_from_nr(k);
2218       }
2219       else if (stored_player[player_nr].key[k])
2220         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2221           get_key_element_from_nr(k);
2222     }
2223
2224     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2225       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2226         level.native_em_level->ply[player_nr]->dynamite;
2227     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2228       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2229         level.native_sp_level->game_sp->red_disk_count;
2230     else
2231       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2232         stored_player[player_nr].inventory_size;
2233
2234     if (stored_player[player_nr].num_white_keys > 0)
2235       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2236
2237     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2238       stored_player[player_nr].num_white_keys;
2239   }
2240
2241   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2242   {
2243     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2244       get_inventory_element_from_pos(local_player, i);
2245     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2246       get_inventory_element_from_pos(local_player, -i - 1);
2247   }
2248
2249   game_panel_controls[GAME_PANEL_SCORE].value = score;
2250   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2251
2252   game_panel_controls[GAME_PANEL_TIME].value = time;
2253
2254   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2255   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2256   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2257
2258   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2259
2260   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2261     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2262      EL_EMPTY);
2263   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2264     local_player->shield_normal_time_left;
2265   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2266     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2267      EL_EMPTY);
2268   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2269     local_player->shield_deadly_time_left;
2270
2271   game_panel_controls[GAME_PANEL_EXIT].value =
2272     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2273
2274   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2275     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2276   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2277     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2278      EL_EMC_MAGIC_BALL_SWITCH);
2279
2280   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2281     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2282   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2283     game.light_time_left;
2284
2285   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2286     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2287   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2288     game.timegate_time_left;
2289
2290   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2291     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2292
2293   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2294     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2295   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2296     game.lenses_time_left;
2297
2298   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2299     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2300   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2301     game.magnify_time_left;
2302
2303   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2304     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2305      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2306      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2307      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2308      EL_BALLOON_SWITCH_NONE);
2309
2310   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2311     local_player->dynabomb_count;
2312   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2313     local_player->dynabomb_size;
2314   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2315     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2316
2317   game_panel_controls[GAME_PANEL_PENGUINS].value =
2318     local_player->friends_still_needed;
2319
2320   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2321     local_player->sokobanfields_still_needed;
2322   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2323     local_player->sokobanfields_still_needed;
2324
2325   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2326     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2327
2328   for (i = 0; i < NUM_BELTS; i++)
2329   {
2330     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2331       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2332        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2333     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2334       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2335   }
2336
2337   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2338     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2339   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2340     game.magic_wall_time_left;
2341
2342   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2343     local_player->gravity;
2344
2345   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2346     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2347
2348   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2349     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2350       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2351        game.panel.element[i].id : EL_UNDEFINED);
2352
2353   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2354     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2355       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2356        element_info[game.panel.element_count[i].id].element_count : 0);
2357
2358   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2359     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2360       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2361        element_info[game.panel.ce_score[i].id].collect_score : 0);
2362
2363   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2364     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2365       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2366        element_info[game.panel.ce_score_element[i].id].collect_score :
2367        EL_UNDEFINED);
2368
2369   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2371   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2372
2373   /* update game panel control frames */
2374
2375   for (i = 0; game_panel_controls[i].nr != -1; i++)
2376   {
2377     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2378
2379     if (gpc->type == TYPE_ELEMENT)
2380     {
2381       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2382       {
2383         int last_anim_random_frame = gfx.anim_random_frame;
2384         int element = gpc->value;
2385         int graphic = el2panelimg(element);
2386
2387         if (gpc->value != gpc->last_value)
2388         {
2389           gpc->gfx_frame = 0;
2390           gpc->gfx_random = INIT_GFX_RANDOM();
2391         }
2392         else
2393         {
2394           gpc->gfx_frame++;
2395
2396           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2397               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2398             gpc->gfx_random = INIT_GFX_RANDOM();
2399         }
2400
2401         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2402           gfx.anim_random_frame = gpc->gfx_random;
2403
2404         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2405           gpc->gfx_frame = element_info[element].collect_score;
2406
2407         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2408                                               gpc->gfx_frame);
2409
2410         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2411           gfx.anim_random_frame = last_anim_random_frame;
2412       }
2413     }
2414   }
2415 }
2416
2417 void DisplayGameControlValues()
2418 {
2419   boolean redraw_panel = FALSE;
2420   int i;
2421
2422   for (i = 0; game_panel_controls[i].nr != -1; i++)
2423   {
2424     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2425
2426     if (PANEL_DEACTIVATED(gpc->pos))
2427       continue;
2428
2429     if (gpc->value == gpc->last_value &&
2430         gpc->frame == gpc->last_frame)
2431       continue;
2432
2433     redraw_panel = TRUE;
2434   }
2435
2436   if (!redraw_panel)
2437     return;
2438
2439   /* copy default game door content to main double buffer */
2440
2441   /* !!! CHECK AGAIN !!! */
2442   SetPanelBackground();
2443   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2444   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2445
2446   /* redraw game control buttons */
2447   RedrawGameButtons();
2448
2449   game_status = GAME_MODE_PSEUDO_PANEL;
2450
2451   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2452   {
2453     int nr = game_panel_order[i].nr;
2454     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2455     struct TextPosInfo *pos = gpc->pos;
2456     int type = gpc->type;
2457     int value = gpc->value;
2458     int frame = gpc->frame;
2459     int size = pos->size;
2460     int font = pos->font;
2461     boolean draw_masked = pos->draw_masked;
2462     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2463
2464     if (PANEL_DEACTIVATED(pos))
2465       continue;
2466
2467     gpc->last_value = value;
2468     gpc->last_frame = frame;
2469
2470     if (type == TYPE_INTEGER)
2471     {
2472       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2473           nr == GAME_PANEL_TIME)
2474       {
2475         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2476
2477         if (use_dynamic_size)           /* use dynamic number of digits */
2478         {
2479           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2480           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2481           int size2 = size1 + 1;
2482           int font1 = pos->font;
2483           int font2 = pos->font_alt;
2484
2485           size = (value < value_change ? size1 : size2);
2486           font = (value < value_change ? font1 : font2);
2487         }
2488       }
2489
2490       /* correct text size if "digits" is zero or less */
2491       if (size <= 0)
2492         size = strlen(int2str(value, size));
2493
2494       /* dynamically correct text alignment */
2495       pos->width = size * getFontWidth(font);
2496
2497       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2498                   int2str(value, size), font, mask_mode);
2499     }
2500     else if (type == TYPE_ELEMENT)
2501     {
2502       int element, graphic;
2503       Bitmap *src_bitmap;
2504       int src_x, src_y;
2505       int width, height;
2506       int dst_x = PANEL_XPOS(pos);
2507       int dst_y = PANEL_YPOS(pos);
2508
2509       if (value != EL_UNDEFINED && value != EL_EMPTY)
2510       {
2511         element = value;
2512         graphic = el2panelimg(value);
2513
2514         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2515
2516         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2517           size = TILESIZE;
2518
2519         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2520                               &src_x, &src_y);
2521
2522         width  = graphic_info[graphic].width  * size / TILESIZE;
2523         height = graphic_info[graphic].height * size / TILESIZE;
2524
2525         if (draw_masked)
2526           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2527                            dst_x, dst_y);
2528         else
2529           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2530                      dst_x, dst_y);
2531       }
2532     }
2533     else if (type == TYPE_STRING)
2534     {
2535       boolean active = (value != 0);
2536       char *state_normal = "off";
2537       char *state_active = "on";
2538       char *state = (active ? state_active : state_normal);
2539       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2540                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2541                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2542                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2543
2544       if (nr == GAME_PANEL_GRAVITY_STATE)
2545       {
2546         int font1 = pos->font;          /* (used for normal state) */
2547         int font2 = pos->font_alt;      /* (used for active state) */
2548
2549         font = (active ? font2 : font1);
2550       }
2551
2552       if (s != NULL)
2553       {
2554         char *s_cut;
2555
2556         if (size <= 0)
2557         {
2558           /* don't truncate output if "chars" is zero or less */
2559           size = strlen(s);
2560
2561           /* dynamically correct text alignment */
2562           pos->width = size * getFontWidth(font);
2563         }
2564
2565         s_cut = getStringCopyN(s, size);
2566
2567         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2568                     s_cut, font, mask_mode);
2569
2570         free(s_cut);
2571       }
2572     }
2573
2574     redraw_mask |= REDRAW_DOOR_1;
2575   }
2576
2577   game_status = GAME_MODE_PLAYING;
2578 }
2579
2580 void UpdateAndDisplayGameControlValues()
2581 {
2582   if (tape.deactivate_display)
2583     return;
2584
2585   UpdateGameControlValues();
2586   DisplayGameControlValues();
2587 }
2588
2589 void UpdateGameDoorValues()
2590 {
2591   UpdateGameControlValues();
2592 }
2593
2594 void DrawGameDoorValues()
2595 {
2596   DisplayGameControlValues();
2597 }
2598
2599
2600 /*
2601   =============================================================================
2602   InitGameEngine()
2603   -----------------------------------------------------------------------------
2604   initialize game engine due to level / tape version number
2605   =============================================================================
2606 */
2607
2608 static void InitGameEngine()
2609 {
2610   int i, j, k, l, x, y;
2611
2612   /* set game engine from tape file when re-playing, else from level file */
2613   game.engine_version = (tape.playing ? tape.engine_version :
2614                          level.game_version);
2615
2616   /* set single or multi-player game mode (needed for re-playing tapes) */
2617   game.team_mode = setup.team_mode;
2618
2619   if (tape.playing)
2620   {
2621     int num_players = 0;
2622
2623     for (i = 0; i < MAX_PLAYERS; i++)
2624       if (tape.player_participates[i])
2625         num_players++;
2626
2627     /* multi-player tapes contain input data for more than one player */
2628     game.team_mode = (num_players > 1);
2629   }
2630
2631   /* ---------------------------------------------------------------------- */
2632   /* set flags for bugs and changes according to active game engine version */
2633   /* ---------------------------------------------------------------------- */
2634
2635   /*
2636     Summary of bugfix/change:
2637     Fixed handling for custom elements that change when pushed by the player.
2638
2639     Fixed/changed in version:
2640     3.1.0
2641
2642     Description:
2643     Before 3.1.0, custom elements that "change when pushing" changed directly
2644     after the player started pushing them (until then handled in "DigField()").
2645     Since 3.1.0, these custom elements are not changed until the "pushing"
2646     move of the element is finished (now handled in "ContinueMoving()").
2647
2648     Affected levels/tapes:
2649     The first condition is generally needed for all levels/tapes before version
2650     3.1.0, which might use the old behaviour before it was changed; known tapes
2651     that are affected are some tapes from the level set "Walpurgis Gardens" by
2652     Jamie Cullen.
2653     The second condition is an exception from the above case and is needed for
2654     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2655     above (including some development versions of 3.1.0), but before it was
2656     known that this change would break tapes like the above and was fixed in
2657     3.1.1, so that the changed behaviour was active although the engine version
2658     while recording maybe was before 3.1.0. There is at least one tape that is
2659     affected by this exception, which is the tape for the one-level set "Bug
2660     Machine" by Juergen Bonhagen.
2661   */
2662
2663   game.use_change_when_pushing_bug =
2664     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2665      !(tape.playing &&
2666        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2667        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2668
2669   /*
2670     Summary of bugfix/change:
2671     Fixed handling for blocking the field the player leaves when moving.
2672
2673     Fixed/changed in version:
2674     3.1.1
2675
2676     Description:
2677     Before 3.1.1, when "block last field when moving" was enabled, the field
2678     the player is leaving when moving was blocked for the time of the move,
2679     and was directly unblocked afterwards. This resulted in the last field
2680     being blocked for exactly one less than the number of frames of one player
2681     move. Additionally, even when blocking was disabled, the last field was
2682     blocked for exactly one frame.
2683     Since 3.1.1, due to changes in player movement handling, the last field
2684     is not blocked at all when blocking is disabled. When blocking is enabled,
2685     the last field is blocked for exactly the number of frames of one player
2686     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2687     last field is blocked for exactly one more than the number of frames of
2688     one player move.
2689
2690     Affected levels/tapes:
2691     (!!! yet to be determined -- probably many !!!)
2692   */
2693
2694   game.use_block_last_field_bug =
2695     (game.engine_version < VERSION_IDENT(3,1,1,0));
2696
2697   /* ---------------------------------------------------------------------- */
2698
2699   /* set maximal allowed number of custom element changes per game frame */
2700   game.max_num_changes_per_frame = 1;
2701
2702   /* default scan direction: scan playfield from top/left to bottom/right */
2703   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2704
2705   /* dynamically adjust element properties according to game engine version */
2706   InitElementPropertiesEngine(game.engine_version);
2707
2708 #if 0
2709   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2710   printf("          tape version == %06d [%s] [file: %06d]\n",
2711          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2712          tape.file_version);
2713   printf("       => game.engine_version == %06d\n", game.engine_version);
2714 #endif
2715
2716   /* ---------- initialize player's initial move delay --------------------- */
2717
2718   /* dynamically adjust player properties according to level information */
2719   for (i = 0; i < MAX_PLAYERS; i++)
2720     game.initial_move_delay_value[i] =
2721       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2722
2723   /* dynamically adjust player properties according to game engine version */
2724   for (i = 0; i < MAX_PLAYERS; i++)
2725     game.initial_move_delay[i] =
2726       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2727        game.initial_move_delay_value[i] : 0);
2728
2729   /* ---------- initialize player's initial push delay --------------------- */
2730
2731   /* dynamically adjust player properties according to game engine version */
2732   game.initial_push_delay_value =
2733     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2734
2735   /* ---------- initialize changing elements ------------------------------- */
2736
2737   /* initialize changing elements information */
2738   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2739   {
2740     struct ElementInfo *ei = &element_info[i];
2741
2742     /* this pointer might have been changed in the level editor */
2743     ei->change = &ei->change_page[0];
2744
2745     if (!IS_CUSTOM_ELEMENT(i))
2746     {
2747       ei->change->target_element = EL_EMPTY_SPACE;
2748       ei->change->delay_fixed = 0;
2749       ei->change->delay_random = 0;
2750       ei->change->delay_frames = 1;
2751     }
2752
2753     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2754     {
2755       ei->has_change_event[j] = FALSE;
2756
2757       ei->event_page_nr[j] = 0;
2758       ei->event_page[j] = &ei->change_page[0];
2759     }
2760   }
2761
2762   /* add changing elements from pre-defined list */
2763   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2764   {
2765     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2766     struct ElementInfo *ei = &element_info[ch_delay->element];
2767
2768     ei->change->target_element       = ch_delay->target_element;
2769     ei->change->delay_fixed          = ch_delay->change_delay;
2770
2771     ei->change->pre_change_function  = ch_delay->pre_change_function;
2772     ei->change->change_function      = ch_delay->change_function;
2773     ei->change->post_change_function = ch_delay->post_change_function;
2774
2775     ei->change->can_change = TRUE;
2776     ei->change->can_change_or_has_action = TRUE;
2777
2778     ei->has_change_event[CE_DELAY] = TRUE;
2779
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2781     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2782   }
2783
2784   /* ---------- initialize internal run-time variables --------------------- */
2785
2786   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2787   {
2788     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2789
2790     for (j = 0; j < ei->num_change_pages; j++)
2791     {
2792       ei->change_page[j].can_change_or_has_action =
2793         (ei->change_page[j].can_change |
2794          ei->change_page[j].has_action);
2795     }
2796   }
2797
2798   /* add change events from custom element configuration */
2799   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2800   {
2801     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2802
2803     for (j = 0; j < ei->num_change_pages; j++)
2804     {
2805       if (!ei->change_page[j].can_change_or_has_action)
2806         continue;
2807
2808       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2809       {
2810         /* only add event page for the first page found with this event */
2811         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2812         {
2813           ei->has_change_event[k] = TRUE;
2814
2815           ei->event_page_nr[k] = j;
2816           ei->event_page[k] = &ei->change_page[j];
2817         }
2818       }
2819     }
2820   }
2821
2822   /* ---------- initialize reference elements in change conditions --------- */
2823
2824   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2825   {
2826     int element = EL_CUSTOM_START + i;
2827     struct ElementInfo *ei = &element_info[element];
2828
2829     for (j = 0; j < ei->num_change_pages; j++)
2830     {
2831       int trigger_element = ei->change_page[j].initial_trigger_element;
2832
2833       if (trigger_element >= EL_PREV_CE_8 &&
2834           trigger_element <= EL_NEXT_CE_8)
2835         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2836
2837       ei->change_page[j].trigger_element = trigger_element;
2838     }
2839   }
2840
2841   /* ---------- initialize run-time trigger player and element ------------- */
2842
2843   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2844   {
2845     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2846
2847     for (j = 0; j < ei->num_change_pages; j++)
2848     {
2849       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2851       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2852       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2853       ei->change_page[j].actual_trigger_ce_value = 0;
2854       ei->change_page[j].actual_trigger_ce_score = 0;
2855     }
2856   }
2857
2858   /* ---------- initialize trigger events ---------------------------------- */
2859
2860   /* initialize trigger events information */
2861   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2862     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2863       trigger_events[i][j] = FALSE;
2864
2865   /* add trigger events from element change event properties */
2866   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2867   {
2868     struct ElementInfo *ei = &element_info[i];
2869
2870     for (j = 0; j < ei->num_change_pages; j++)
2871     {
2872       if (!ei->change_page[j].can_change_or_has_action)
2873         continue;
2874
2875       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2876       {
2877         int trigger_element = ei->change_page[j].trigger_element;
2878
2879         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2880         {
2881           if (ei->change_page[j].has_event[k])
2882           {
2883             if (IS_GROUP_ELEMENT(trigger_element))
2884             {
2885               struct ElementGroupInfo *group =
2886                 element_info[trigger_element].group;
2887
2888               for (l = 0; l < group->num_elements_resolved; l++)
2889                 trigger_events[group->element_resolved[l]][k] = TRUE;
2890             }
2891             else if (trigger_element == EL_ANY_ELEMENT)
2892               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2893                 trigger_events[l][k] = TRUE;
2894             else
2895               trigger_events[trigger_element][k] = TRUE;
2896           }
2897         }
2898       }
2899     }
2900   }
2901
2902   /* ---------- initialize push delay -------------------------------------- */
2903
2904   /* initialize push delay values to default */
2905   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2906   {
2907     if (!IS_CUSTOM_ELEMENT(i))
2908     {
2909       /* set default push delay values (corrected since version 3.0.7-1) */
2910       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2911       {
2912         element_info[i].push_delay_fixed = 2;
2913         element_info[i].push_delay_random = 8;
2914       }
2915       else
2916       {
2917         element_info[i].push_delay_fixed = 8;
2918         element_info[i].push_delay_random = 8;
2919       }
2920     }
2921   }
2922
2923   /* set push delay value for certain elements from pre-defined list */
2924   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2925   {
2926     int e = push_delay_list[i].element;
2927
2928     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2929     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2930   }
2931
2932   /* set push delay value for Supaplex elements for newer engine versions */
2933   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2934   {
2935     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2936     {
2937       if (IS_SP_ELEMENT(i))
2938       {
2939         /* set SP push delay to just enough to push under a falling zonk */
2940         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2941
2942         element_info[i].push_delay_fixed  = delay;
2943         element_info[i].push_delay_random = 0;
2944       }
2945     }
2946   }
2947
2948   /* ---------- initialize move stepsize ----------------------------------- */
2949
2950   /* initialize move stepsize values to default */
2951   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2952     if (!IS_CUSTOM_ELEMENT(i))
2953       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2954
2955   /* set move stepsize value for certain elements from pre-defined list */
2956   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2957   {
2958     int e = move_stepsize_list[i].element;
2959
2960     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2961   }
2962
2963   /* ---------- initialize collect score ----------------------------------- */
2964
2965   /* initialize collect score values for custom elements from initial value */
2966   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2967     if (IS_CUSTOM_ELEMENT(i))
2968       element_info[i].collect_score = element_info[i].collect_score_initial;
2969
2970   /* ---------- initialize collect count ----------------------------------- */
2971
2972   /* initialize collect count values for non-custom elements */
2973   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2974     if (!IS_CUSTOM_ELEMENT(i))
2975       element_info[i].collect_count_initial = 0;
2976
2977   /* add collect count values for all elements from pre-defined list */
2978   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2979     element_info[collect_count_list[i].element].collect_count_initial =
2980       collect_count_list[i].count;
2981
2982   /* ---------- initialize access direction -------------------------------- */
2983
2984   /* initialize access direction values to default (access from every side) */
2985   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2986     if (!IS_CUSTOM_ELEMENT(i))
2987       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2988
2989   /* set access direction value for certain elements from pre-defined list */
2990   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2991     element_info[access_direction_list[i].element].access_direction =
2992       access_direction_list[i].direction;
2993
2994   /* ---------- initialize explosion content ------------------------------- */
2995   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2996   {
2997     if (IS_CUSTOM_ELEMENT(i))
2998       continue;
2999
3000     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3001     {
3002       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3003
3004       element_info[i].content.e[x][y] =
3005         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3006          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3007          i == EL_PLAYER_3 ? EL_EMERALD :
3008          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3009          i == EL_MOLE ? EL_EMERALD_RED :
3010          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3011          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3012          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3013          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3014          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3015          i == EL_WALL_EMERALD ? EL_EMERALD :
3016          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3017          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3018          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3019          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3020          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3021          i == EL_WALL_PEARL ? EL_PEARL :
3022          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3023          EL_EMPTY);
3024     }
3025   }
3026
3027   /* ---------- initialize recursion detection ------------------------------ */
3028   recursion_loop_depth = 0;
3029   recursion_loop_detected = FALSE;
3030   recursion_loop_element = EL_UNDEFINED;
3031
3032   /* ---------- initialize graphics engine ---------------------------------- */
3033   game.scroll_delay_value =
3034     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3035      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3036   game.scroll_delay_value =
3037     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3038
3039   /* ---------- initialize game engine snapshots ---------------------------- */
3040   for (i = 0; i < MAX_PLAYERS; i++)
3041     game.snapshot.last_action[i] = 0;
3042   game.snapshot.changed_action = FALSE;
3043   game.snapshot.mode =
3044     (strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_STEP) ?
3045      SNAPSHOT_MODE_EVERY_STEP :
3046      strEqual(setup.engine_snapshot_mode, STR_SNAPSHOT_MODE_EVERY_MOVE) ?
3047      SNAPSHOT_MODE_EVERY_MOVE : SNAPSHOT_MODE_OFF);
3048 }
3049
3050 int get_num_special_action(int element, int action_first, int action_last)
3051 {
3052   int num_special_action = 0;
3053   int i, j;
3054
3055   for (i = action_first; i <= action_last; i++)
3056   {
3057     boolean found = FALSE;
3058
3059     for (j = 0; j < NUM_DIRECTIONS; j++)
3060       if (el_act_dir2img(element, i, j) !=
3061           el_act_dir2img(element, ACTION_DEFAULT, j))
3062         found = TRUE;
3063
3064     if (found)
3065       num_special_action++;
3066     else
3067       break;
3068   }
3069
3070   return num_special_action;
3071 }
3072
3073
3074 /*
3075   =============================================================================
3076   InitGame()
3077   -----------------------------------------------------------------------------
3078   initialize and start new game
3079   =============================================================================
3080 */
3081
3082 void InitGame()
3083 {
3084   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3085   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3086   int fade_mask = REDRAW_FIELD;
3087
3088   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3089   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3090   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3091   int initial_move_dir = MV_DOWN;
3092   int i, j, x, y;
3093
3094   // required here to update video display before fading (FIX THIS)
3095   DrawMaskedBorder(REDRAW_DOOR_2);
3096
3097   game_status = GAME_MODE_PLAYING;
3098
3099   if (!game.restart_level)
3100     CloseDoor(DOOR_CLOSE_1);
3101
3102   /* needed if different viewport properties defined for playing */
3103   ChangeViewportPropertiesIfNeeded();
3104
3105   if (level_editor_test_game)
3106     FadeSkipNextFadeIn();
3107   else
3108     FadeSetEnterScreen();
3109
3110   if (CheckIfGlobalBorderHasChanged())
3111     fade_mask = REDRAW_ALL;
3112
3113   FadeOut(fade_mask);
3114
3115   OpenDoor(GetDoorState() | DOOR_NO_DELAY | DOOR_FORCE_REDRAW);
3116
3117   ClearField();
3118
3119   DrawCompleteVideoDisplay();
3120
3121   InitGameEngine();
3122   InitGameControlValues();
3123
3124   /* don't play tapes over network */
3125   network_playing = (options.network && !tape.playing);
3126
3127   for (i = 0; i < MAX_PLAYERS; i++)
3128   {
3129     struct PlayerInfo *player = &stored_player[i];
3130
3131     player->index_nr = i;
3132     player->index_bit = (1 << i);
3133     player->element_nr = EL_PLAYER_1 + i;
3134
3135     player->present = FALSE;
3136     player->active = FALSE;
3137     player->mapped = FALSE;
3138
3139     player->killed = FALSE;
3140     player->reanimated = FALSE;
3141
3142     player->action = 0;
3143     player->effective_action = 0;
3144     player->programmed_action = 0;
3145
3146     player->score = 0;
3147     player->score_final = 0;
3148
3149     player->gems_still_needed = level.gems_needed;
3150     player->sokobanfields_still_needed = 0;
3151     player->lights_still_needed = 0;
3152     player->friends_still_needed = 0;
3153
3154     for (j = 0; j < MAX_NUM_KEYS; j++)
3155       player->key[j] = FALSE;
3156
3157     player->num_white_keys = 0;
3158
3159     player->dynabomb_count = 0;
3160     player->dynabomb_size = 1;
3161     player->dynabombs_left = 0;
3162     player->dynabomb_xl = FALSE;
3163
3164     player->MovDir = initial_move_dir;
3165     player->MovPos = 0;
3166     player->GfxPos = 0;
3167     player->GfxDir = initial_move_dir;
3168     player->GfxAction = ACTION_DEFAULT;
3169     player->Frame = 0;
3170     player->StepFrame = 0;
3171
3172     player->initial_element = player->element_nr;
3173     player->artwork_element =
3174       (level.use_artwork_element[i] ? level.artwork_element[i] :
3175        player->element_nr);
3176     player->use_murphy = FALSE;
3177
3178     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3179     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3180
3181     player->gravity = level.initial_player_gravity[i];
3182
3183     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3184
3185     player->actual_frame_counter = 0;
3186
3187     player->step_counter = 0;
3188
3189     player->last_move_dir = initial_move_dir;
3190
3191     player->is_active = FALSE;
3192
3193     player->is_waiting = FALSE;
3194     player->is_moving = FALSE;
3195     player->is_auto_moving = FALSE;
3196     player->is_digging = FALSE;
3197     player->is_snapping = FALSE;
3198     player->is_collecting = FALSE;
3199     player->is_pushing = FALSE;
3200     player->is_switching = FALSE;
3201     player->is_dropping = FALSE;
3202     player->is_dropping_pressed = FALSE;
3203
3204     player->is_bored = FALSE;
3205     player->is_sleeping = FALSE;
3206
3207     player->frame_counter_bored = -1;
3208     player->frame_counter_sleeping = -1;
3209
3210     player->anim_delay_counter = 0;
3211     player->post_delay_counter = 0;
3212
3213     player->dir_waiting = initial_move_dir;
3214     player->action_waiting = ACTION_DEFAULT;
3215     player->last_action_waiting = ACTION_DEFAULT;
3216     player->special_action_bored = ACTION_DEFAULT;
3217     player->special_action_sleeping = ACTION_DEFAULT;
3218
3219     player->switch_x = -1;
3220     player->switch_y = -1;
3221
3222     player->drop_x = -1;
3223     player->drop_y = -1;
3224
3225     player->show_envelope = 0;
3226
3227     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3228
3229     player->push_delay       = -1;      /* initialized when pushing starts */
3230     player->push_delay_value = game.initial_push_delay_value;
3231
3232     player->drop_delay = 0;
3233     player->drop_pressed_delay = 0;
3234
3235     player->last_jx = -1;
3236     player->last_jy = -1;
3237     player->jx = -1;
3238     player->jy = -1;
3239
3240     player->shield_normal_time_left = 0;
3241     player->shield_deadly_time_left = 0;
3242
3243     player->inventory_infinite_element = EL_UNDEFINED;
3244     player->inventory_size = 0;
3245
3246     if (level.use_initial_inventory[i])
3247     {
3248       for (j = 0; j < level.initial_inventory_size[i]; j++)
3249       {
3250         int element = level.initial_inventory_content[i][j];
3251         int collect_count = element_info[element].collect_count_initial;
3252         int k;
3253
3254         if (!IS_CUSTOM_ELEMENT(element))
3255           collect_count = 1;
3256
3257         if (collect_count == 0)
3258           player->inventory_infinite_element = element;
3259         else
3260           for (k = 0; k < collect_count; k++)
3261             if (player->inventory_size < MAX_INVENTORY_SIZE)
3262               player->inventory_element[player->inventory_size++] = element;
3263       }
3264     }
3265
3266     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3267     SnapField(player, 0, 0);
3268
3269     player->LevelSolved = FALSE;
3270     player->GameOver = FALSE;
3271
3272     player->LevelSolved_GameWon = FALSE;
3273     player->LevelSolved_GameEnd = FALSE;
3274     player->LevelSolved_PanelOff = FALSE;
3275     player->LevelSolved_SaveTape = FALSE;
3276     player->LevelSolved_SaveScore = FALSE;
3277     player->LevelSolved_CountingTime = 0;
3278     player->LevelSolved_CountingScore = 0;
3279
3280     map_player_action[i] = i;
3281   }
3282
3283   network_player_action_received = FALSE;
3284
3285 #if defined(NETWORK_AVALIABLE)
3286   /* initial null action */
3287   if (network_playing)
3288     SendToServer_MovePlayer(MV_NONE);
3289 #endif
3290
3291   ZX = ZY = -1;
3292   ExitX = ExitY = -1;
3293
3294   FrameCounter = 0;
3295   TimeFrames = 0;
3296   TimePlayed = 0;
3297   TimeLeft = level.time;
3298   TapeTime = 0;
3299
3300   ScreenMovDir = MV_NONE;
3301   ScreenMovPos = 0;
3302   ScreenGfxPos = 0;
3303
3304   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3305
3306   AllPlayersGone = FALSE;
3307
3308   game.no_time_limit = (level.time == 0);
3309
3310   game.yamyam_content_nr = 0;
3311   game.robot_wheel_active = FALSE;
3312   game.magic_wall_active = FALSE;
3313   game.magic_wall_time_left = 0;
3314   game.light_time_left = 0;
3315   game.timegate_time_left = 0;
3316   game.switchgate_pos = 0;
3317   game.wind_direction = level.wind_direction_initial;
3318
3319   game.lenses_time_left = 0;
3320   game.magnify_time_left = 0;
3321
3322   game.ball_state = level.ball_state_initial;
3323   game.ball_content_nr = 0;
3324
3325   game.envelope_active = FALSE;
3326
3327   /* set focus to local player for network games, else to all players */
3328   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3329   game.centered_player_nr_next = game.centered_player_nr;
3330   game.set_centered_player = FALSE;
3331
3332   if (network_playing && tape.recording)
3333   {
3334     /* store client dependent player focus when recording network games */
3335     tape.centered_player_nr_next = game.centered_player_nr_next;
3336     tape.set_centered_player = TRUE;
3337   }
3338
3339   for (i = 0; i < NUM_BELTS; i++)
3340   {
3341     game.belt_dir[i] = MV_NONE;
3342     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3343   }
3344
3345   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3346     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3347
3348 #if DEBUG_INIT_PLAYER
3349   if (options.debug)
3350   {
3351     printf("Player status at level initialization:\n");
3352   }
3353 #endif
3354
3355   SCAN_PLAYFIELD(x, y)
3356   {
3357     Feld[x][y] = level.field[x][y];
3358     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3359     ChangeDelay[x][y] = 0;
3360     ChangePage[x][y] = -1;
3361     CustomValue[x][y] = 0;              /* initialized in InitField() */
3362     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3363     AmoebaNr[x][y] = 0;
3364     WasJustMoving[x][y] = 0;
3365     WasJustFalling[x][y] = 0;
3366     CheckCollision[x][y] = 0;
3367     CheckImpact[x][y] = 0;
3368     Stop[x][y] = FALSE;
3369     Pushed[x][y] = FALSE;
3370
3371     ChangeCount[x][y] = 0;
3372     ChangeEvent[x][y] = -1;
3373
3374     ExplodePhase[x][y] = 0;
3375     ExplodeDelay[x][y] = 0;
3376     ExplodeField[x][y] = EX_TYPE_NONE;
3377
3378     RunnerVisit[x][y] = 0;
3379     PlayerVisit[x][y] = 0;
3380
3381     GfxFrame[x][y] = 0;
3382     GfxRandom[x][y] = INIT_GFX_RANDOM();
3383     GfxElement[x][y] = EL_UNDEFINED;
3384     GfxAction[x][y] = ACTION_DEFAULT;
3385     GfxDir[x][y] = MV_NONE;
3386     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3387   }
3388
3389   SCAN_PLAYFIELD(x, y)
3390   {
3391     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3392       emulate_bd = FALSE;
3393     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3394       emulate_sb = FALSE;
3395     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3396       emulate_sp = FALSE;
3397
3398     InitField(x, y, TRUE);
3399
3400     ResetGfxAnimation(x, y);
3401   }
3402
3403   InitBeltMovement();
3404
3405   for (i = 0; i < MAX_PLAYERS; i++)
3406   {
3407     struct PlayerInfo *player = &stored_player[i];
3408
3409     /* set number of special actions for bored and sleeping animation */
3410     player->num_special_action_bored =
3411       get_num_special_action(player->artwork_element,
3412                              ACTION_BORING_1, ACTION_BORING_LAST);
3413     player->num_special_action_sleeping =
3414       get_num_special_action(player->artwork_element,
3415                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3416   }
3417
3418   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3419                     emulate_sb ? EMU_SOKOBAN :
3420                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3421
3422   /* initialize type of slippery elements */
3423   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3424   {
3425     if (!IS_CUSTOM_ELEMENT(i))
3426     {
3427       /* default: elements slip down either to the left or right randomly */
3428       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3429
3430       /* SP style elements prefer to slip down on the left side */
3431       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3432         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3433
3434       /* BD style elements prefer to slip down on the left side */
3435       if (game.emulation == EMU_BOULDERDASH)
3436         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3437     }
3438   }
3439
3440   /* initialize explosion and ignition delay */
3441   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3442   {
3443     if (!IS_CUSTOM_ELEMENT(i))
3444     {
3445       int num_phase = 8;
3446       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3447                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3448                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3449       int last_phase = (num_phase + 1) * delay;
3450       int half_phase = (num_phase / 2) * delay;
3451
3452       element_info[i].explosion_delay = last_phase - 1;
3453       element_info[i].ignition_delay = half_phase;
3454
3455       if (i == EL_BLACK_ORB)
3456         element_info[i].ignition_delay = 1;
3457     }
3458   }
3459
3460   /* correct non-moving belts to start moving left */
3461   for (i = 0; i < NUM_BELTS; i++)
3462     if (game.belt_dir[i] == MV_NONE)
3463       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3464
3465 #if USE_NEW_PLAYER_ASSIGNMENTS
3466   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3467   /* choose default local player */
3468   local_player = &stored_player[0];
3469
3470   for (i = 0; i < MAX_PLAYERS; i++)
3471     stored_player[i].connected = FALSE;
3472
3473   local_player->connected = TRUE;
3474   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3475
3476   if (tape.playing)
3477   {
3478     for (i = 0; i < MAX_PLAYERS; i++)
3479       stored_player[i].connected = tape.player_participates[i];
3480   }
3481   else if (game.team_mode && !options.network)
3482   {
3483     /* try to guess locally connected team mode players (needed for correct
3484        assignment of player figures from level to locally playing players) */
3485
3486     for (i = 0; i < MAX_PLAYERS; i++)
3487       if (setup.input[i].use_joystick ||
3488           setup.input[i].key.left != KSYM_UNDEFINED)
3489         stored_player[i].connected = TRUE;
3490   }
3491
3492 #if DEBUG_INIT_PLAYER
3493   if (options.debug)
3494   {
3495     printf("Player status after level initialization:\n");
3496
3497     for (i = 0; i < MAX_PLAYERS; i++)
3498     {
3499       struct PlayerInfo *player = &stored_player[i];
3500
3501       printf("- player %d: present == %d, connected == %d, active == %d",
3502              i + 1,
3503              player->present,
3504              player->connected,
3505              player->active);
3506
3507       if (local_player == player)
3508         printf(" (local player)");
3509
3510       printf("\n");
3511     }
3512   }
3513 #endif
3514
3515 #if DEBUG_INIT_PLAYER
3516   if (options.debug)
3517     printf("Reassigning players ...\n");
3518 #endif
3519
3520   /* check if any connected player was not found in playfield */
3521   for (i = 0; i < MAX_PLAYERS; i++)
3522   {
3523     struct PlayerInfo *player = &stored_player[i];
3524
3525     if (player->connected && !player->present)
3526     {
3527       struct PlayerInfo *field_player = NULL;
3528
3529 #if DEBUG_INIT_PLAYER
3530       if (options.debug)
3531         printf("- looking for field player for player %d ...\n", i + 1);
3532 #endif
3533
3534       /* assign first free player found that is present in the playfield */
3535
3536       /* first try: look for unmapped playfield player that is not connected */
3537       for (j = 0; j < MAX_PLAYERS; j++)
3538         if (field_player == NULL &&
3539             stored_player[j].present &&
3540             !stored_player[j].mapped &&
3541             !stored_player[j].connected)
3542           field_player = &stored_player[j];
3543
3544       /* second try: look for *any* unmapped playfield player */
3545       for (j = 0; j < MAX_PLAYERS; j++)
3546         if (field_player == NULL &&
3547             stored_player[j].present &&
3548             !stored_player[j].mapped)
3549           field_player = &stored_player[j];
3550
3551       if (field_player != NULL)
3552       {
3553         int jx = field_player->jx, jy = field_player->jy;
3554
3555 #if DEBUG_INIT_PLAYER
3556         if (options.debug)
3557           printf("- found player %d\n", field_player->index_nr + 1);
3558 #endif
3559
3560         player->present = FALSE;
3561         player->active = FALSE;
3562
3563         field_player->present = TRUE;
3564         field_player->active = TRUE;
3565
3566         /*
3567         player->initial_element = field_player->initial_element;
3568         player->artwork_element = field_player->artwork_element;
3569
3570         player->block_last_field       = field_player->block_last_field;
3571         player->block_delay_adjustment = field_player->block_delay_adjustment;
3572         */
3573
3574         StorePlayer[jx][jy] = field_player->element_nr;
3575
3576         field_player->jx = field_player->last_jx = jx;
3577         field_player->jy = field_player->last_jy = jy;
3578
3579         if (local_player == player)
3580           local_player = field_player;
3581
3582         map_player_action[field_player->index_nr] = i;
3583
3584         field_player->mapped = TRUE;
3585
3586 #if DEBUG_INIT_PLAYER
3587         if (options.debug)
3588           printf("- map_player_action[%d] == %d\n",
3589                  field_player->index_nr + 1, i + 1);
3590 #endif
3591       }
3592     }
3593
3594     if (player->connected && player->present)
3595       player->mapped = TRUE;
3596   }
3597
3598 #if DEBUG_INIT_PLAYER
3599   if (options.debug)
3600   {
3601     printf("Player status after player assignment (first stage):\n");
3602
3603     for (i = 0; i < MAX_PLAYERS; i++)
3604     {
3605       struct PlayerInfo *player = &stored_player[i];
3606
3607       printf("- player %d: present == %d, connected == %d, active == %d",
3608              i + 1,
3609              player->present,
3610              player->connected,
3611              player->active);
3612
3613       if (local_player == player)
3614         printf(" (local player)");
3615
3616       printf("\n");
3617     }
3618   }
3619 #endif
3620
3621 #else
3622
3623   /* check if any connected player was not found in playfield */
3624   for (i = 0; i < MAX_PLAYERS; i++)
3625   {
3626     struct PlayerInfo *player = &stored_player[i];
3627
3628     if (player->connected && !player->present)
3629     {
3630       for (j = 0; j < MAX_PLAYERS; j++)
3631       {
3632         struct PlayerInfo *field_player = &stored_player[j];
3633         int jx = field_player->jx, jy = field_player->jy;
3634
3635         /* assign first free player found that is present in the playfield */
3636         if (field_player->present && !field_player->connected)
3637         {
3638           player->present = TRUE;
3639           player->active = TRUE;
3640
3641           field_player->present = FALSE;
3642           field_player->active = FALSE;
3643
3644           player->initial_element = field_player->initial_element;
3645           player->artwork_element = field_player->artwork_element;
3646
3647           player->block_last_field       = field_player->block_last_field;
3648           player->block_delay_adjustment = field_player->block_delay_adjustment;
3649
3650           StorePlayer[jx][jy] = player->element_nr;
3651
3652           player->jx = player->last_jx = jx;
3653           player->jy = player->last_jy = jy;
3654
3655           break;
3656         }
3657       }
3658     }
3659   }
3660 #endif
3661
3662 #if 0
3663   printf("::: local_player->present == %d\n", local_player->present);
3664 #endif
3665
3666   if (tape.playing)
3667   {
3668     /* when playing a tape, eliminate all players who do not participate */
3669
3670 #if USE_NEW_PLAYER_ASSIGNMENTS
3671
3672     if (!game.team_mode)
3673     {
3674       for (i = 0; i < MAX_PLAYERS; i++)
3675       {
3676         if (stored_player[i].active &&
3677             !tape.player_participates[map_player_action[i]])
3678         {
3679           struct PlayerInfo *player = &stored_player[i];
3680           int jx = player->jx, jy = player->jy;
3681
3682 #if DEBUG_INIT_PLAYER
3683           if (options.debug)
3684             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3685 #endif
3686
3687           player->active = FALSE;
3688           StorePlayer[jx][jy] = 0;
3689           Feld[jx][jy] = EL_EMPTY;
3690         }
3691       }
3692     }
3693
3694 #else
3695
3696     for (i = 0; i < MAX_PLAYERS; i++)
3697     {
3698       if (stored_player[i].active &&
3699           !tape.player_participates[i])
3700       {
3701         struct PlayerInfo *player = &stored_player[i];
3702         int jx = player->jx, jy = player->jy;
3703
3704         player->active = FALSE;
3705         StorePlayer[jx][jy] = 0;
3706         Feld[jx][jy] = EL_EMPTY;
3707       }
3708     }
3709 #endif
3710   }
3711   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3712   {
3713     /* when in single player mode, eliminate all but the first active player */
3714
3715     for (i = 0; i < MAX_PLAYERS; i++)
3716     {
3717       if (stored_player[i].active)
3718       {
3719         for (j = i + 1; j < MAX_PLAYERS; j++)
3720         {
3721           if (stored_player[j].active)
3722           {
3723             struct PlayerInfo *player = &stored_player[j];
3724             int jx = player->jx, jy = player->jy;
3725
3726             player->active = FALSE;
3727             player->present = FALSE;
3728
3729             StorePlayer[jx][jy] = 0;
3730             Feld[jx][jy] = EL_EMPTY;
3731           }
3732         }
3733       }
3734     }
3735   }
3736
3737   /* when recording the game, store which players take part in the game */
3738   if (tape.recording)
3739   {
3740 #if USE_NEW_PLAYER_ASSIGNMENTS
3741     for (i = 0; i < MAX_PLAYERS; i++)
3742       if (stored_player[i].connected)
3743         tape.player_participates[i] = TRUE;
3744 #else
3745     for (i = 0; i < MAX_PLAYERS; i++)
3746       if (stored_player[i].active)
3747         tape.player_participates[i] = TRUE;
3748 #endif
3749   }
3750
3751 #if DEBUG_INIT_PLAYER
3752   if (options.debug)
3753   {
3754     printf("Player status after player assignment (final stage):\n");
3755
3756     for (i = 0; i < MAX_PLAYERS; i++)
3757     {
3758       struct PlayerInfo *player = &stored_player[i];
3759
3760       printf("- player %d: present == %d, connected == %d, active == %d",
3761              i + 1,
3762              player->present,
3763              player->connected,
3764              player->active);
3765
3766       if (local_player == player)
3767         printf(" (local player)");
3768
3769       printf("\n");
3770     }
3771   }
3772 #endif
3773
3774   if (BorderElement == EL_EMPTY)
3775   {
3776     SBX_Left = 0;
3777     SBX_Right = lev_fieldx - SCR_FIELDX;
3778     SBY_Upper = 0;
3779     SBY_Lower = lev_fieldy - SCR_FIELDY;
3780   }
3781   else
3782   {
3783     SBX_Left = -1;
3784     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3785     SBY_Upper = -1;
3786     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3787   }
3788
3789   if (full_lev_fieldx <= SCR_FIELDX)
3790     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3791   if (full_lev_fieldy <= SCR_FIELDY)
3792     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3793
3794   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3795     SBX_Left--;
3796   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3797     SBY_Upper--;
3798
3799   /* if local player not found, look for custom element that might create
3800      the player (make some assumptions about the right custom element) */
3801   if (!local_player->present)
3802   {
3803     int start_x = 0, start_y = 0;
3804     int found_rating = 0;
3805     int found_element = EL_UNDEFINED;
3806     int player_nr = local_player->index_nr;
3807
3808     SCAN_PLAYFIELD(x, y)
3809     {
3810       int element = Feld[x][y];
3811       int content;
3812       int xx, yy;
3813       boolean is_player;
3814
3815       if (level.use_start_element[player_nr] &&
3816           level.start_element[player_nr] == element &&
3817           found_rating < 4)
3818       {
3819         start_x = x;
3820         start_y = y;
3821
3822         found_rating = 4;
3823         found_element = element;
3824       }
3825
3826       if (!IS_CUSTOM_ELEMENT(element))
3827         continue;
3828
3829       if (CAN_CHANGE(element))
3830       {
3831         for (i = 0; i < element_info[element].num_change_pages; i++)
3832         {
3833           /* check for player created from custom element as single target */
3834           content = element_info[element].change_page[i].target_element;
3835           is_player = ELEM_IS_PLAYER(content);
3836
3837           if (is_player && (found_rating < 3 ||
3838                             (found_rating == 3 && element < found_element)))
3839           {
3840             start_x = x;
3841             start_y = y;
3842
3843             found_rating = 3;
3844             found_element = element;
3845           }
3846         }
3847       }
3848
3849       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3850       {
3851         /* check for player created from custom element as explosion content */
3852         content = element_info[element].content.e[xx][yy];
3853         is_player = ELEM_IS_PLAYER(content);
3854
3855         if (is_player && (found_rating < 2 ||
3856                           (found_rating == 2 && element < found_element)))
3857         {
3858           start_x = x + xx - 1;
3859           start_y = y + yy - 1;
3860
3861           found_rating = 2;
3862           found_element = element;
3863         }
3864
3865         if (!CAN_CHANGE(element))
3866           continue;
3867
3868         for (i = 0; i < element_info[element].num_change_pages; i++)
3869         {
3870           /* check for player created from custom element as extended target */
3871           content =
3872             element_info[element].change_page[i].target_content.e[xx][yy];
3873
3874           is_player = ELEM_IS_PLAYER(content);
3875
3876           if (is_player && (found_rating < 1 ||
3877                             (found_rating == 1 && element < found_element)))
3878           {
3879             start_x = x + xx - 1;
3880             start_y = y + yy - 1;
3881
3882             found_rating = 1;
3883             found_element = element;
3884           }
3885         }
3886       }
3887     }
3888
3889     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3890                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3891                 start_x - MIDPOSX);
3892
3893     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3894                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3895                 start_y - MIDPOSY);
3896   }
3897   else
3898   {
3899     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3900                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3901                 local_player->jx - MIDPOSX);
3902
3903     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3904                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3905                 local_player->jy - MIDPOSY);
3906   }
3907
3908   /* !!! FIX THIS (START) !!! */
3909   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3910   {
3911     InitGameEngine_EM();
3912   }
3913   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3914   {
3915     InitGameEngine_SP();
3916   }
3917   else
3918   {
3919     DrawLevel(REDRAW_FIELD);
3920     DrawAllPlayers();
3921
3922     /* after drawing the level, correct some elements */
3923     if (game.timegate_time_left == 0)
3924       CloseAllOpenTimegates();
3925   }
3926
3927   /* blit playfield from scroll buffer to normal back buffer for fading in */
3928   BlitScreenToBitmap(backbuffer);
3929   /* !!! FIX THIS (END) !!! */
3930
3931   DrawMaskedBorder(fade_mask);
3932
3933   FadeIn(fade_mask);
3934
3935 #if 1
3936   // full screen redraw is required at this point in the following cases:
3937   // - special editor door undrawn when game was started from level editor
3938   // - drawing area (playfield) was changed and has to be removed completely
3939   redraw_mask = REDRAW_ALL;
3940   BackToFront();
3941 #endif
3942
3943   if (!game.restart_level)
3944   {
3945     /* copy default game door content to main double buffer */
3946
3947     /* !!! CHECK AGAIN !!! */
3948     SetPanelBackground();
3949     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3950     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3951   }
3952
3953   SetPanelBackground();
3954   SetDrawBackgroundMask(REDRAW_DOOR_1);
3955
3956   UpdateAndDisplayGameControlValues();
3957
3958   if (!game.restart_level)
3959   {
3960     UnmapGameButtons();
3961     UnmapTapeButtons();
3962
3963     FreeGameButtons();
3964     CreateGameButtons();
3965
3966     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3967     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3968     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3969
3970     MapGameButtons();
3971     MapTapeButtons();
3972
3973     /* copy actual game door content to door double buffer for OpenDoor() */
3974     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3975
3976     OpenDoor(DOOR_OPEN_ALL);
3977
3978     PlaySound(SND_GAME_STARTING);
3979
3980     if (setup.sound_music)
3981       PlayLevelMusic();
3982
3983     KeyboardAutoRepeatOffUnlessAutoplay();
3984
3985 #if DEBUG_INIT_PLAYER
3986     if (options.debug)
3987     {
3988       printf("Player status (final):\n");
3989
3990       for (i = 0; i < MAX_PLAYERS; i++)
3991       {
3992         struct PlayerInfo *player = &stored_player[i];
3993
3994         printf("- player %d: present == %d, connected == %d, active == %d",
3995                i + 1,
3996                player->present,
3997                player->connected,
3998                player->active);
3999
4000         if (local_player == player)
4001           printf(" (local player)");
4002
4003         printf("\n");
4004       }
4005     }
4006 #endif
4007   }
4008
4009   UnmapAllGadgets();
4010
4011   MapGameButtons();
4012   MapTapeButtons();
4013
4014   if (!game.restart_level && !tape.playing)
4015   {
4016     LevelStats_incPlayed(level_nr);
4017
4018     SaveLevelSetup_SeriesInfo();
4019   }
4020
4021   game.restart_level = FALSE;
4022
4023   SaveEngineSnapshotToListInitial();
4024 }
4025
4026 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4027 {
4028   /* this is used for non-R'n'D game engines to update certain engine values */
4029
4030   /* needed to determine if sounds are played within the visible screen area */
4031   scroll_x = actual_scroll_x;
4032   scroll_y = actual_scroll_y;
4033 }
4034
4035 void InitMovDir(int x, int y)
4036 {
4037   int i, element = Feld[x][y];
4038   static int xy[4][2] =
4039   {
4040     {  0, +1 },
4041     { +1,  0 },
4042     {  0, -1 },
4043     { -1,  0 }
4044   };
4045   static int direction[3][4] =
4046   {
4047     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4048     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4049     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4050   };
4051
4052   switch (element)
4053   {
4054     case EL_BUG_RIGHT:
4055     case EL_BUG_UP:
4056     case EL_BUG_LEFT:
4057     case EL_BUG_DOWN:
4058       Feld[x][y] = EL_BUG;
4059       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4060       break;
4061
4062     case EL_SPACESHIP_RIGHT:
4063     case EL_SPACESHIP_UP:
4064     case EL_SPACESHIP_LEFT:
4065     case EL_SPACESHIP_DOWN:
4066       Feld[x][y] = EL_SPACESHIP;
4067       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4068       break;
4069
4070     case EL_BD_BUTTERFLY_RIGHT:
4071     case EL_BD_BUTTERFLY_UP:
4072     case EL_BD_BUTTERFLY_LEFT:
4073     case EL_BD_BUTTERFLY_DOWN:
4074       Feld[x][y] = EL_BD_BUTTERFLY;
4075       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4076       break;
4077
4078     case EL_BD_FIREFLY_RIGHT:
4079     case EL_BD_FIREFLY_UP:
4080     case EL_BD_FIREFLY_LEFT:
4081     case EL_BD_FIREFLY_DOWN:
4082       Feld[x][y] = EL_BD_FIREFLY;
4083       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4084       break;
4085
4086     case EL_PACMAN_RIGHT:
4087     case EL_PACMAN_UP:
4088     case EL_PACMAN_LEFT:
4089     case EL_PACMAN_DOWN:
4090       Feld[x][y] = EL_PACMAN;
4091       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4092       break;
4093
4094     case EL_YAMYAM_LEFT:
4095     case EL_YAMYAM_RIGHT:
4096     case EL_YAMYAM_UP:
4097     case EL_YAMYAM_DOWN:
4098       Feld[x][y] = EL_YAMYAM;
4099       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4100       break;
4101
4102     case EL_SP_SNIKSNAK:
4103       MovDir[x][y] = MV_UP;
4104       break;
4105
4106     case EL_SP_ELECTRON:
4107       MovDir[x][y] = MV_LEFT;
4108       break;
4109
4110     case EL_MOLE_LEFT:
4111     case EL_MOLE_RIGHT:
4112     case EL_MOLE_UP:
4113     case EL_MOLE_DOWN:
4114       Feld[x][y] = EL_MOLE;
4115       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4116       break;
4117
4118     default:
4119       if (IS_CUSTOM_ELEMENT(element))
4120       {
4121         struct ElementInfo *ei = &element_info[element];
4122         int move_direction_initial = ei->move_direction_initial;
4123         int move_pattern = ei->move_pattern;
4124
4125         if (move_direction_initial == MV_START_PREVIOUS)
4126         {
4127           if (MovDir[x][y] != MV_NONE)
4128             return;
4129
4130           move_direction_initial = MV_START_AUTOMATIC;
4131         }
4132
4133         if (move_direction_initial == MV_START_RANDOM)
4134           MovDir[x][y] = 1 << RND(4);
4135         else if (move_direction_initial & MV_ANY_DIRECTION)
4136           MovDir[x][y] = move_direction_initial;
4137         else if (move_pattern == MV_ALL_DIRECTIONS ||
4138                  move_pattern == MV_TURNING_LEFT ||
4139                  move_pattern == MV_TURNING_RIGHT ||
4140                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4141                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4142                  move_pattern == MV_TURNING_RANDOM)
4143           MovDir[x][y] = 1 << RND(4);
4144         else if (move_pattern == MV_HORIZONTAL)
4145           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4146         else if (move_pattern == MV_VERTICAL)
4147           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4148         else if (move_pattern & MV_ANY_DIRECTION)
4149           MovDir[x][y] = element_info[element].move_pattern;
4150         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4151                  move_pattern == MV_ALONG_RIGHT_SIDE)
4152         {
4153           /* use random direction as default start direction */
4154           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4155             MovDir[x][y] = 1 << RND(4);
4156
4157           for (i = 0; i < NUM_DIRECTIONS; i++)
4158           {
4159             int x1 = x + xy[i][0];
4160             int y1 = y + xy[i][1];
4161
4162             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4163             {
4164               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4165                 MovDir[x][y] = direction[0][i];
4166               else
4167                 MovDir[x][y] = direction[1][i];
4168
4169               break;
4170             }
4171           }
4172         }                
4173       }
4174       else
4175       {
4176         MovDir[x][y] = 1 << RND(4);
4177
4178         if (element != EL_BUG &&
4179             element != EL_SPACESHIP &&
4180             element != EL_BD_BUTTERFLY &&
4181             element != EL_BD_FIREFLY)
4182           break;
4183
4184         for (i = 0; i < NUM_DIRECTIONS; i++)
4185         {
4186           int x1 = x + xy[i][0];
4187           int y1 = y + xy[i][1];
4188
4189           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4190           {
4191             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4192             {
4193               MovDir[x][y] = direction[0][i];
4194               break;
4195             }
4196             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4197                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4198             {
4199               MovDir[x][y] = direction[1][i];
4200               break;
4201             }
4202           }
4203         }
4204       }
4205       break;
4206   }
4207
4208   GfxDir[x][y] = MovDir[x][y];
4209 }
4210
4211 void InitAmoebaNr(int x, int y)
4212 {
4213   int i;
4214   int group_nr = AmoebeNachbarNr(x, y);
4215
4216   if (group_nr == 0)
4217   {
4218     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4219     {
4220       if (AmoebaCnt[i] == 0)
4221       {
4222         group_nr = i;
4223         break;
4224       }
4225     }
4226   }
4227
4228   AmoebaNr[x][y] = group_nr;
4229   AmoebaCnt[group_nr]++;
4230   AmoebaCnt2[group_nr]++;
4231 }
4232
4233 static void PlayerWins(struct PlayerInfo *player)
4234 {
4235   player->LevelSolved = TRUE;
4236   player->GameOver = TRUE;
4237
4238   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4239                          level.native_em_level->lev->score : player->score);
4240
4241   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4242                                       TimeLeft);
4243   player->LevelSolved_CountingScore = player->score_final;
4244 }
4245
4246 void GameWon()
4247 {
4248   static int time, time_final;
4249   static int score, score_final;
4250   static int game_over_delay_1 = 0;
4251   static int game_over_delay_2 = 0;
4252   int game_over_delay_value_1 = 50;
4253   int game_over_delay_value_2 = 50;
4254
4255   if (!local_player->LevelSolved_GameWon)
4256   {
4257     int i;
4258
4259     /* do not start end game actions before the player stops moving (to exit) */
4260     if (local_player->MovPos)
4261       return;
4262
4263     local_player->LevelSolved_GameWon = TRUE;
4264     local_player->LevelSolved_SaveTape = tape.recording;
4265     local_player->LevelSolved_SaveScore = !tape.playing;
4266
4267     if (!tape.playing)
4268     {
4269       LevelStats_incSolved(level_nr);
4270
4271       SaveLevelSetup_SeriesInfo();
4272     }
4273
4274     if (tape.auto_play)         /* tape might already be stopped here */
4275       tape.auto_play_level_solved = TRUE;
4276
4277     TapeStop();
4278
4279     game_over_delay_1 = game_over_delay_value_1;
4280     game_over_delay_2 = game_over_delay_value_2;
4281
4282     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4283     score = score_final = local_player->score_final;
4284
4285     if (TimeLeft > 0)
4286     {
4287       time_final = 0;
4288       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4289     }
4290     else if (game.no_time_limit && TimePlayed < 999)
4291     {
4292       time_final = 999;
4293       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4294     }
4295
4296     local_player->score_final = score_final;
4297
4298     if (level_editor_test_game)
4299     {
4300       time = time_final;
4301       score = score_final;
4302
4303       local_player->LevelSolved_CountingTime = time;
4304       local_player->LevelSolved_CountingScore = score;
4305
4306       game_panel_controls[GAME_PANEL_TIME].value = time;
4307       game_panel_controls[GAME_PANEL_SCORE].value = score;
4308
4309       DisplayGameControlValues();
4310     }
4311
4312     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4313     {
4314       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4315       {
4316         /* close exit door after last player */
4317         if ((AllPlayersGone &&
4318              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4319               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4320               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4321             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4322             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4323         {
4324           int element = Feld[ExitX][ExitY];
4325
4326           Feld[ExitX][ExitY] =
4327             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4328              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4329              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4330              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4331              EL_EM_STEEL_EXIT_CLOSING);
4332
4333           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4334         }
4335
4336         /* player disappears */
4337         DrawLevelField(ExitX, ExitY);
4338       }
4339
4340       for (i = 0; i < MAX_PLAYERS; i++)
4341       {
4342         struct PlayerInfo *player = &stored_player[i];
4343
4344         if (player->present)
4345         {
4346           RemovePlayer(player);
4347
4348           /* player disappears */
4349           DrawLevelField(player->jx, player->jy);
4350         }
4351       }
4352     }
4353
4354     PlaySound(SND_GAME_WINNING);
4355   }
4356
4357   if (game_over_delay_1 > 0)
4358   {
4359     game_over_delay_1--;
4360
4361     return;
4362   }
4363
4364   if (time != time_final)
4365   {
4366     int time_to_go = ABS(time_final - time);
4367     int time_count_dir = (time < time_final ? +1 : -1);
4368     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4369
4370     time  += time_count_steps * time_count_dir;
4371     score += time_count_steps * level.score[SC_TIME_BONUS];
4372
4373     local_player->LevelSolved_CountingTime = time;
4374     local_player->LevelSolved_CountingScore = score;
4375
4376     game_panel_controls[GAME_PANEL_TIME].value = time;
4377     game_panel_controls[GAME_PANEL_SCORE].value = score;
4378
4379     DisplayGameControlValues();
4380
4381     if (time == time_final)
4382       StopSound(SND_GAME_LEVELTIME_BONUS);
4383     else if (setup.sound_loops)
4384       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4385     else
4386       PlaySound(SND_GAME_LEVELTIME_BONUS);
4387
4388     return;
4389   }
4390
4391   local_player->LevelSolved_PanelOff = TRUE;
4392
4393   if (game_over_delay_2 > 0)
4394   {
4395     game_over_delay_2--;
4396
4397     return;
4398   }
4399
4400   GameEnd();
4401 }
4402
4403 void GameEnd()
4404 {
4405   int hi_pos;
4406   boolean raise_level = FALSE;
4407
4408   local_player->LevelSolved_GameEnd = TRUE;
4409
4410   if (!global.use_envelope_request)
4411     CloseDoor(DOOR_CLOSE_1);
4412
4413   if (local_player->LevelSolved_SaveTape)
4414   {
4415     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4416   }
4417
4418   CloseDoor(DOOR_CLOSE_ALL);
4419
4420   if (level_editor_test_game)
4421   {
4422     game_status = GAME_MODE_MAIN;
4423
4424     DrawMainMenu();
4425
4426     return;
4427   }
4428
4429   if (!local_player->LevelSolved_SaveScore)
4430   {
4431     FadeOut(REDRAW_FIELD);
4432
4433     game_status = GAME_MODE_MAIN;
4434
4435     DrawMainMenu();
4436
4437     return;
4438   }
4439
4440   if (level_nr == leveldir_current->handicap_level)
4441   {
4442     leveldir_current->handicap_level++;
4443
4444     SaveLevelSetup_SeriesInfo();
4445   }
4446
4447   if (level_nr < leveldir_current->last_level)
4448     raise_level = TRUE;                 /* advance to next level */
4449
4450   if ((hi_pos = NewHiScore()) >= 0) 
4451   {
4452     game_status = GAME_MODE_SCORES;
4453
4454     DrawHallOfFame(hi_pos);
4455
4456     if (raise_level)
4457     {
4458       level_nr++;
4459       TapeErase();
4460     }
4461   }
4462   else
4463   {
4464     FadeOut(REDRAW_FIELD);
4465
4466     game_status = GAME_MODE_MAIN;
4467
4468     if (raise_level)
4469     {
4470       level_nr++;
4471       TapeErase();
4472     }
4473
4474     DrawMainMenu();
4475   }
4476 }
4477
4478 int NewHiScore()
4479 {
4480   int k, l;
4481   int position = -1;
4482
4483   LoadScore(level_nr);
4484
4485   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4486       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4487     return -1;
4488
4489   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4490   {
4491     if (local_player->score_final > highscore[k].Score)
4492     {
4493       /* player has made it to the hall of fame */
4494
4495       if (k < MAX_SCORE_ENTRIES - 1)
4496       {
4497         int m = MAX_SCORE_ENTRIES - 1;
4498
4499 #ifdef ONE_PER_NAME
4500         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4501           if (strEqual(setup.player_name, highscore[l].Name))
4502             m = l;
4503         if (m == k)     /* player's new highscore overwrites his old one */
4504           goto put_into_list;
4505 #endif
4506
4507         for (l = m; l > k; l--)
4508         {
4509           strcpy(highscore[l].Name, highscore[l - 1].Name);
4510           highscore[l].Score = highscore[l - 1].Score;
4511         }
4512       }
4513
4514 #ifdef ONE_PER_NAME
4515       put_into_list:
4516 #endif
4517       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4518       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4519       highscore[k].Score = local_player->score_final; 
4520       position = k;
4521       break;
4522     }
4523
4524 #ifdef ONE_PER_NAME
4525     else if (!strncmp(setup.player_name, highscore[k].Name,
4526                       MAX_PLAYER_NAME_LEN))
4527       break;    /* player already there with a higher score */
4528 #endif
4529
4530   }
4531
4532   if (position >= 0) 
4533     SaveScore(level_nr);
4534
4535   return position;
4536 }
4537
4538 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4539 {
4540   int element = Feld[x][y];
4541   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4542   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4543   int horiz_move = (dx != 0);
4544   int sign = (horiz_move ? dx : dy);
4545   int step = sign * element_info[element].move_stepsize;
4546
4547   /* special values for move stepsize for spring and things on conveyor belt */
4548   if (horiz_move)
4549   {
4550     if (CAN_FALL(element) &&
4551         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4552       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4553     else if (element == EL_SPRING)
4554       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4555   }
4556
4557   return step;
4558 }
4559
4560 inline static int getElementMoveStepsize(int x, int y)
4561 {
4562   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4563 }
4564
4565 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4566 {
4567   if (player->GfxAction != action || player->GfxDir != dir)
4568   {
4569     player->GfxAction = action;
4570     player->GfxDir = dir;
4571     player->Frame = 0;
4572     player->StepFrame = 0;
4573   }
4574 }
4575
4576 static void ResetGfxFrame(int x, int y, boolean redraw)
4577 {
4578   int element = Feld[x][y];
4579   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4580   int last_gfx_frame = GfxFrame[x][y];
4581
4582   if (graphic_info[graphic].anim_global_sync)
4583     GfxFrame[x][y] = FrameCounter;
4584   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4585     GfxFrame[x][y] = CustomValue[x][y];
4586   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4587     GfxFrame[x][y] = element_info[element].collect_score;
4588   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4589     GfxFrame[x][y] = ChangeDelay[x][y];
4590
4591   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4592     DrawLevelGraphicAnimation(x, y, graphic);
4593 }
4594
4595 static void ResetGfxAnimation(int x, int y)
4596 {
4597   GfxAction[x][y] = ACTION_DEFAULT;
4598   GfxDir[x][y] = MovDir[x][y];
4599   GfxFrame[x][y] = 0;
4600
4601   ResetGfxFrame(x, y, FALSE);
4602 }
4603
4604 static void ResetRandomAnimationValue(int x, int y)
4605 {
4606   GfxRandom[x][y] = INIT_GFX_RANDOM();
4607 }
4608
4609 void InitMovingField(int x, int y, int direction)
4610 {
4611   int element = Feld[x][y];
4612   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4613   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4614   int newx = x + dx;
4615   int newy = y + dy;
4616   boolean is_moving_before, is_moving_after;
4617
4618   /* check if element was/is moving or being moved before/after mode change */
4619   is_moving_before = (WasJustMoving[x][y] != 0);
4620   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4621
4622   /* reset animation only for moving elements which change direction of moving
4623      or which just started or stopped moving
4624      (else CEs with property "can move" / "not moving" are reset each frame) */
4625   if (is_moving_before != is_moving_after ||
4626       direction != MovDir[x][y])
4627     ResetGfxAnimation(x, y);
4628
4629   MovDir[x][y] = direction;
4630   GfxDir[x][y] = direction;
4631
4632   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4633                      direction == MV_DOWN && CAN_FALL(element) ?
4634                      ACTION_FALLING : ACTION_MOVING);
4635
4636   /* this is needed for CEs with property "can move" / "not moving" */
4637
4638   if (is_moving_after)
4639   {
4640     if (Feld[newx][newy] == EL_EMPTY)
4641       Feld[newx][newy] = EL_BLOCKED;
4642
4643     MovDir[newx][newy] = MovDir[x][y];
4644
4645     CustomValue[newx][newy] = CustomValue[x][y];
4646
4647     GfxFrame[newx][newy] = GfxFrame[x][y];
4648     GfxRandom[newx][newy] = GfxRandom[x][y];
4649     GfxAction[newx][newy] = GfxAction[x][y];
4650     GfxDir[newx][newy] = GfxDir[x][y];
4651   }
4652 }
4653
4654 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4655 {
4656   int direction = MovDir[x][y];
4657   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4658   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4659
4660   *goes_to_x = newx;
4661   *goes_to_y = newy;
4662 }
4663
4664 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4665 {
4666   int oldx = x, oldy = y;
4667   int direction = MovDir[x][y];
4668
4669   if (direction == MV_LEFT)
4670     oldx++;
4671   else if (direction == MV_RIGHT)
4672     oldx--;
4673   else if (direction == MV_UP)
4674     oldy++;
4675   else if (direction == MV_DOWN)
4676     oldy--;
4677
4678   *comes_from_x = oldx;
4679   *comes_from_y = oldy;
4680 }
4681
4682 int MovingOrBlocked2Element(int x, int y)
4683 {
4684   int element = Feld[x][y];
4685
4686   if (element == EL_BLOCKED)
4687   {
4688     int oldx, oldy;
4689
4690     Blocked2Moving(x, y, &oldx, &oldy);
4691     return Feld[oldx][oldy];
4692   }
4693   else
4694     return element;
4695 }
4696
4697 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4698 {
4699   /* like MovingOrBlocked2Element(), but if element is moving
4700      and (x,y) is the field the moving element is just leaving,
4701      return EL_BLOCKED instead of the element value */
4702   int element = Feld[x][y];
4703
4704   if (IS_MOVING(x, y))
4705   {
4706     if (element == EL_BLOCKED)
4707     {
4708       int oldx, oldy;
4709
4710       Blocked2Moving(x, y, &oldx, &oldy);
4711       return Feld[oldx][oldy];
4712     }
4713     else
4714       return EL_BLOCKED;
4715   }
4716   else
4717     return element;
4718 }
4719
4720 static void RemoveField(int x, int y)
4721 {
4722   Feld[x][y] = EL_EMPTY;
4723
4724   MovPos[x][y] = 0;
4725   MovDir[x][y] = 0;
4726   MovDelay[x][y] = 0;
4727
4728   CustomValue[x][y] = 0;
4729
4730   AmoebaNr[x][y] = 0;
4731   ChangeDelay[x][y] = 0;
4732   ChangePage[x][y] = -1;
4733   Pushed[x][y] = FALSE;
4734
4735   GfxElement[x][y] = EL_UNDEFINED;
4736   GfxAction[x][y] = ACTION_DEFAULT;
4737   GfxDir[x][y] = MV_NONE;
4738 }
4739
4740 void RemoveMovingField(int x, int y)
4741 {
4742   int oldx = x, oldy = y, newx = x, newy = y;
4743   int element = Feld[x][y];
4744   int next_element = EL_UNDEFINED;
4745
4746   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4747     return;
4748
4749   if (IS_MOVING(x, y))
4750   {
4751     Moving2Blocked(x, y, &newx, &newy);
4752
4753     if (Feld[newx][newy] != EL_BLOCKED)
4754     {
4755       /* element is moving, but target field is not free (blocked), but
4756          already occupied by something different (example: acid pool);
4757          in this case, only remove the moving field, but not the target */
4758
4759       RemoveField(oldx, oldy);
4760
4761       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4762
4763       TEST_DrawLevelField(oldx, oldy);
4764
4765       return;
4766     }
4767   }
4768   else if (element == EL_BLOCKED)
4769   {
4770     Blocked2Moving(x, y, &oldx, &oldy);
4771     if (!IS_MOVING(oldx, oldy))
4772       return;
4773   }
4774
4775   if (element == EL_BLOCKED &&
4776       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4777        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4778        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4779        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4780        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4781        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4782     next_element = get_next_element(Feld[oldx][oldy]);
4783
4784   RemoveField(oldx, oldy);
4785   RemoveField(newx, newy);
4786
4787   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4788
4789   if (next_element != EL_UNDEFINED)
4790     Feld[oldx][oldy] = next_element;
4791
4792   TEST_DrawLevelField(oldx, oldy);
4793   TEST_DrawLevelField(newx, newy);
4794 }
4795
4796 void DrawDynamite(int x, int y)
4797 {
4798   int sx = SCREENX(x), sy = SCREENY(y);
4799   int graphic = el2img(Feld[x][y]);
4800   int frame;
4801
4802   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4803     return;
4804
4805   if (IS_WALKABLE_INSIDE(Back[x][y]))
4806     return;
4807
4808   if (Back[x][y])
4809     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4810   else if (Store[x][y])
4811     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4812
4813   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4814
4815   if (Back[x][y] || Store[x][y])
4816     DrawGraphicThruMask(sx, sy, graphic, frame);
4817   else
4818     DrawGraphic(sx, sy, graphic, frame);
4819 }
4820
4821 void CheckDynamite(int x, int y)
4822 {
4823   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4824   {
4825     MovDelay[x][y]--;
4826
4827     if (MovDelay[x][y] != 0)
4828     {
4829       DrawDynamite(x, y);
4830       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4831
4832       return;
4833     }
4834   }
4835
4836   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4837
4838   Bang(x, y);
4839 }
4840
4841 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4842 {
4843   boolean num_checked_players = 0;
4844   int i;
4845
4846   for (i = 0; i < MAX_PLAYERS; i++)
4847   {
4848     if (stored_player[i].active)
4849     {
4850       int sx = stored_player[i].jx;
4851       int sy = stored_player[i].jy;
4852
4853       if (num_checked_players == 0)
4854       {
4855         *sx1 = *sx2 = sx;
4856         *sy1 = *sy2 = sy;
4857       }
4858       else
4859       {
4860         *sx1 = MIN(*sx1, sx);
4861         *sy1 = MIN(*sy1, sy);
4862         *sx2 = MAX(*sx2, sx);
4863         *sy2 = MAX(*sy2, sy);
4864       }
4865
4866       num_checked_players++;
4867     }
4868   }
4869 }
4870
4871 static boolean checkIfAllPlayersFitToScreen_RND()
4872 {
4873   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4874
4875   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4876
4877   return (sx2 - sx1 < SCR_FIELDX &&
4878           sy2 - sy1 < SCR_FIELDY);
4879 }
4880
4881 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4882 {
4883   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4884
4885   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4886
4887   *sx = (sx1 + sx2) / 2;
4888   *sy = (sy1 + sy2) / 2;
4889 }
4890
4891 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4892                         boolean center_screen, boolean quick_relocation)
4893 {
4894   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4895   boolean no_delay = (tape.warp_forward);
4896   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4897   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4898
4899   if (level.lazy_relocation && IN_VIS_FIELD(SCREENX(x), SCREENY(y)))
4900   {
4901     RedrawPlayfield();
4902   }
4903   else if (quick_relocation)
4904   {
4905     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4906     {
4907       if (!level.shifted_relocation || center_screen)
4908       {
4909         /* quick relocation (without scrolling), with centering of screen */
4910
4911         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4912                     x > SBX_Right + MIDPOSX ? SBX_Right :
4913                     x - MIDPOSX);
4914
4915         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4916                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4917                     y - MIDPOSY);
4918       }
4919       else
4920       {
4921         /* quick relocation (without scrolling), but do not center screen */
4922
4923         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4924                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4925                                old_x - MIDPOSX);
4926
4927         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4928                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4929                                old_y - MIDPOSY);
4930
4931         int offset_x = x + (scroll_x - center_scroll_x);
4932         int offset_y = y + (scroll_y - center_scroll_y);
4933
4934         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4935                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4936                     offset_x - MIDPOSX);
4937
4938         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4939                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4940                     offset_y - MIDPOSY);
4941       }
4942     }
4943     else
4944     {
4945       if (!level.shifted_relocation || center_screen)
4946       {
4947         /* quick relocation (without scrolling), with centering of screen */
4948
4949         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4950                     x > SBX_Right + MIDPOSX ? SBX_Right :
4951                     x - MIDPOSX);
4952
4953         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4954                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4955                     y - MIDPOSY);
4956       }
4957       else
4958       {
4959         /* quick relocation (without scrolling), but do not center screen */
4960
4961         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4962                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4963                                old_x - MIDPOSX);
4964
4965         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4966                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4967                                old_y - MIDPOSY);
4968
4969         int offset_x = x + (scroll_x - center_scroll_x);
4970         int offset_y = y + (scroll_y - center_scroll_y);
4971
4972         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4973                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4974                     offset_x - MIDPOSX);
4975
4976         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4977                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4978                     offset_y - MIDPOSY);
4979       }
4980     }
4981
4982     RedrawPlayfield();
4983   }
4984   else
4985   {
4986     int scroll_xx, scroll_yy;
4987
4988     if (!level.shifted_relocation || center_screen)
4989     {
4990       /* visible relocation (with scrolling), with centering of screen */
4991
4992       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4993                    x > SBX_Right + MIDPOSX ? SBX_Right :
4994                    x - MIDPOSX);
4995
4996       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4997                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4998                    y - MIDPOSY);
4999     }
5000     else
5001     {
5002       /* visible relocation (with scrolling), but do not center screen */
5003
5004       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
5005                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
5006                              old_x - MIDPOSX);
5007
5008       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5009                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5010                              old_y - MIDPOSY);
5011
5012       int offset_x = x + (scroll_x - center_scroll_x);
5013       int offset_y = y + (scroll_y - center_scroll_y);
5014
5015       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
5016                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
5017                    offset_x - MIDPOSX);
5018
5019       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
5020                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
5021                    offset_y - MIDPOSY);
5022     }
5023
5024     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5025
5026     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5027     {
5028       int dx = 0, dy = 0;
5029       int fx = FX, fy = FY;
5030
5031       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5032       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5033
5034       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5035         break;
5036
5037       scroll_x -= dx;
5038       scroll_y -= dy;
5039
5040       fx += dx * TILEX / 2;
5041       fy += dy * TILEY / 2;
5042
5043       ScrollLevel(dx, dy);
5044       DrawAllPlayers();
5045
5046       /* scroll in two steps of half tile size to make things smoother */
5047       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5048       Delay(wait_delay_value);
5049
5050       /* scroll second step to align at full tile size */
5051       BackToFront();
5052       Delay(wait_delay_value);
5053     }
5054
5055     DrawAllPlayers();
5056     BackToFront();
5057     Delay(wait_delay_value);
5058   }
5059 }
5060
5061 void RelocatePlayer(int jx, int jy, int el_player_raw)
5062 {
5063   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5064   int player_nr = GET_PLAYER_NR(el_player);
5065   struct PlayerInfo *player = &stored_player[player_nr];
5066   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5067   boolean no_delay = (tape.warp_forward);
5068   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5069   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5070   int old_jx = player->jx;
5071   int old_jy = player->jy;
5072   int old_element = Feld[old_jx][old_jy];
5073   int element = Feld[jx][jy];
5074   boolean player_relocated = (old_jx != jx || old_jy != jy);
5075
5076   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5077   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5078   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5079   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5080   int leave_side_horiz = move_dir_horiz;
5081   int leave_side_vert  = move_dir_vert;
5082   int enter_side = enter_side_horiz | enter_side_vert;
5083   int leave_side = leave_side_horiz | leave_side_vert;
5084
5085   if (player->GameOver)         /* do not reanimate dead player */
5086     return;
5087
5088   if (!player_relocated)        /* no need to relocate the player */
5089     return;
5090
5091   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5092   {
5093     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5094     DrawLevelField(jx, jy);
5095   }
5096
5097   if (player->present)
5098   {
5099     while (player->MovPos)
5100     {
5101       ScrollPlayer(player, SCROLL_GO_ON);
5102       ScrollScreen(NULL, SCROLL_GO_ON);
5103
5104       AdvanceFrameAndPlayerCounters(player->index_nr);
5105
5106       DrawPlayer(player);
5107
5108       BackToFront();
5109       Delay(wait_delay_value);
5110     }
5111
5112     DrawPlayer(player);         /* needed here only to cleanup last field */
5113     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5114
5115     player->is_moving = FALSE;
5116   }
5117
5118   if (IS_CUSTOM_ELEMENT(old_element))
5119     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5120                                CE_LEFT_BY_PLAYER,
5121                                player->index_bit, leave_side);
5122
5123   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5124                                       CE_PLAYER_LEAVES_X,
5125                                       player->index_bit, leave_side);
5126
5127   Feld[jx][jy] = el_player;
5128   InitPlayerField(jx, jy, el_player, TRUE);
5129
5130   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5131      possible that the relocation target field did not contain a player element,
5132      but a walkable element, to which the new player was relocated -- in this
5133      case, restore that (already initialized!) element on the player field */
5134   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5135   {
5136     Feld[jx][jy] = element;     /* restore previously existing element */
5137   }
5138
5139   /* only visually relocate centered player */
5140   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5141                      FALSE, level.instant_relocation);
5142
5143   TestIfPlayerTouchesBadThing(jx, jy);
5144   TestIfPlayerTouchesCustomElement(jx, jy);
5145
5146   if (IS_CUSTOM_ELEMENT(element))
5147     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5148                                player->index_bit, enter_side);
5149
5150   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5151                                       player->index_bit, enter_side);
5152
5153   if (player->is_switching)
5154   {
5155     /* ensure that relocation while still switching an element does not cause
5156        a new element to be treated as also switched directly after relocation
5157        (this is important for teleporter switches that teleport the player to
5158        a place where another teleporter switch is in the same direction, which
5159        would then incorrectly be treated as immediately switched before the
5160        direction key that caused the switch was released) */
5161
5162     player->switch_x += jx - old_jx;
5163     player->switch_y += jy - old_jy;
5164   }
5165 }
5166
5167 void Explode(int ex, int ey, int phase, int mode)
5168 {
5169   int x, y;
5170   int last_phase;
5171   int border_element;
5172
5173   /* !!! eliminate this variable !!! */
5174   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5175
5176   if (game.explosions_delayed)
5177   {
5178     ExplodeField[ex][ey] = mode;
5179     return;
5180   }
5181
5182   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5183   {
5184     int center_element = Feld[ex][ey];
5185     int artwork_element, explosion_element;     /* set these values later */
5186
5187     /* remove things displayed in background while burning dynamite */
5188     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5189       Back[ex][ey] = 0;
5190
5191     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5192     {
5193       /* put moving element to center field (and let it explode there) */
5194       center_element = MovingOrBlocked2Element(ex, ey);
5195       RemoveMovingField(ex, ey);
5196       Feld[ex][ey] = center_element;
5197     }
5198
5199     /* now "center_element" is finally determined -- set related values now */
5200     artwork_element = center_element;           /* for custom player artwork */
5201     explosion_element = center_element;         /* for custom player artwork */
5202
5203     if (IS_PLAYER(ex, ey))
5204     {
5205       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5206
5207       artwork_element = stored_player[player_nr].artwork_element;
5208
5209       if (level.use_explosion_element[player_nr])
5210       {
5211         explosion_element = level.explosion_element[player_nr];
5212         artwork_element = explosion_element;
5213       }
5214     }
5215
5216     if (mode == EX_TYPE_NORMAL ||
5217         mode == EX_TYPE_CENTER ||
5218         mode == EX_TYPE_CROSS)
5219       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5220
5221     last_phase = element_info[explosion_element].explosion_delay + 1;
5222
5223     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5224     {
5225       int xx = x - ex + 1;
5226       int yy = y - ey + 1;
5227       int element;
5228
5229       if (!IN_LEV_FIELD(x, y) ||
5230           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5231           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5232         continue;
5233
5234       element = Feld[x][y];
5235
5236       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5237       {
5238         element = MovingOrBlocked2Element(x, y);
5239
5240         if (!IS_EXPLOSION_PROOF(element))
5241           RemoveMovingField(x, y);
5242       }
5243
5244       /* indestructible elements can only explode in center (but not flames) */
5245       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5246                                            mode == EX_TYPE_BORDER)) ||
5247           element == EL_FLAMES)
5248         continue;
5249
5250       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5251          behaviour, for example when touching a yamyam that explodes to rocks
5252          with active deadly shield, a rock is created under the player !!! */
5253       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5254 #if 0
5255       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5256           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5257            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5258 #else
5259       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5260 #endif
5261       {
5262         if (IS_ACTIVE_BOMB(element))
5263         {
5264           /* re-activate things under the bomb like gate or penguin */
5265           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5266           Back[x][y] = 0;
5267         }
5268
5269         continue;
5270       }
5271
5272       /* save walkable background elements while explosion on same tile */
5273       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5274           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5275         Back[x][y] = element;
5276
5277       /* ignite explodable elements reached by other explosion */
5278       if (element == EL_EXPLOSION)
5279         element = Store2[x][y];
5280
5281       if (AmoebaNr[x][y] &&
5282           (element == EL_AMOEBA_FULL ||
5283            element == EL_BD_AMOEBA ||
5284            element == EL_AMOEBA_GROWING))
5285       {
5286         AmoebaCnt[AmoebaNr[x][y]]--;
5287         AmoebaCnt2[AmoebaNr[x][y]]--;
5288       }
5289
5290       RemoveField(x, y);
5291
5292       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5293       {
5294         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5295
5296         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5297
5298         if (PLAYERINFO(ex, ey)->use_murphy)
5299           Store[x][y] = EL_EMPTY;
5300       }
5301
5302       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5303          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5304       else if (ELEM_IS_PLAYER(center_element))
5305         Store[x][y] = EL_EMPTY;
5306       else if (center_element == EL_YAMYAM)
5307         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5308       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5309         Store[x][y] = element_info[center_element].content.e[xx][yy];
5310 #if 1
5311       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5312          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5313          otherwise) -- FIX THIS !!! */
5314       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5315         Store[x][y] = element_info[element].content.e[1][1];
5316 #else
5317       else if (!CAN_EXPLODE(element))
5318         Store[x][y] = element_info[element].content.e[1][1];
5319 #endif
5320       else
5321         Store[x][y] = EL_EMPTY;
5322
5323       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5324           center_element == EL_AMOEBA_TO_DIAMOND)
5325         Store2[x][y] = element;
5326
5327       Feld[x][y] = EL_EXPLOSION;
5328       GfxElement[x][y] = artwork_element;
5329
5330       ExplodePhase[x][y] = 1;
5331       ExplodeDelay[x][y] = last_phase;
5332
5333       Stop[x][y] = TRUE;
5334     }
5335
5336     if (center_element == EL_YAMYAM)
5337       game.yamyam_content_nr =
5338         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5339
5340     return;
5341   }
5342
5343   if (Stop[ex][ey])
5344     return;
5345
5346   x = ex;
5347   y = ey;
5348
5349   if (phase == 1)
5350     GfxFrame[x][y] = 0;         /* restart explosion animation */
5351
5352   last_phase = ExplodeDelay[x][y];
5353
5354   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5355
5356   /* this can happen if the player leaves an explosion just in time */
5357   if (GfxElement[x][y] == EL_UNDEFINED)
5358     GfxElement[x][y] = EL_EMPTY;
5359
5360   border_element = Store2[x][y];
5361   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5362     border_element = StorePlayer[x][y];
5363
5364   if (phase == element_info[border_element].ignition_delay ||
5365       phase == last_phase)
5366   {
5367     boolean border_explosion = FALSE;
5368
5369     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5370         !PLAYER_EXPLOSION_PROTECTED(x, y))
5371     {
5372       KillPlayerUnlessExplosionProtected(x, y);
5373       border_explosion = TRUE;
5374     }
5375     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5376     {
5377       Feld[x][y] = Store2[x][y];
5378       Store2[x][y] = 0;
5379       Bang(x, y);
5380       border_explosion = TRUE;
5381     }
5382     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5383     {
5384       AmoebeUmwandeln(x, y);
5385       Store2[x][y] = 0;
5386       border_explosion = TRUE;
5387     }
5388
5389     /* if an element just explodes due to another explosion (chain-reaction),
5390        do not immediately end the new explosion when it was the last frame of
5391        the explosion (as it would be done in the following "if"-statement!) */
5392     if (border_explosion && phase == last_phase)
5393       return;
5394   }
5395
5396   if (phase == last_phase)
5397   {
5398     int element;
5399
5400     element = Feld[x][y] = Store[x][y];
5401     Store[x][y] = Store2[x][y] = 0;
5402     GfxElement[x][y] = EL_UNDEFINED;
5403
5404     /* player can escape from explosions and might therefore be still alive */
5405     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5406         element <= EL_PLAYER_IS_EXPLODING_4)
5407     {
5408       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5409       int explosion_element = EL_PLAYER_1 + player_nr;
5410       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5411       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5412
5413       if (level.use_explosion_element[player_nr])
5414         explosion_element = level.explosion_element[player_nr];
5415
5416       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5417                     element_info[explosion_element].content.e[xx][yy]);
5418     }
5419
5420     /* restore probably existing indestructible background element */
5421     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5422       element = Feld[x][y] = Back[x][y];
5423     Back[x][y] = 0;
5424
5425     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5426     GfxDir[x][y] = MV_NONE;
5427     ChangeDelay[x][y] = 0;
5428     ChangePage[x][y] = -1;
5429
5430     CustomValue[x][y] = 0;
5431
5432     InitField_WithBug2(x, y, FALSE);
5433
5434     TEST_DrawLevelField(x, y);
5435
5436     TestIfElementTouchesCustomElement(x, y);
5437
5438     if (GFX_CRUMBLED(element))
5439       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5440
5441     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5442       StorePlayer[x][y] = 0;
5443
5444     if (ELEM_IS_PLAYER(element))
5445       RelocatePlayer(x, y, element);
5446   }
5447   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5448   {
5449     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5450     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5451
5452     if (phase == delay)
5453       TEST_DrawLevelFieldCrumbled(x, y);
5454
5455     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5456     {
5457       DrawLevelElement(x, y, Back[x][y]);
5458       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5459     }
5460     else if (IS_WALKABLE_UNDER(Back[x][y]))
5461     {
5462       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5463       DrawLevelElementThruMask(x, y, Back[x][y]);
5464     }
5465     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5466       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5467   }
5468 }
5469
5470 void DynaExplode(int ex, int ey)
5471 {
5472   int i, j;
5473   int dynabomb_element = Feld[ex][ey];
5474   int dynabomb_size = 1;
5475   boolean dynabomb_xl = FALSE;
5476   struct PlayerInfo *player;
5477   static int xy[4][2] =
5478   {
5479     { 0, -1 },
5480     { -1, 0 },
5481     { +1, 0 },
5482     { 0, +1 }
5483   };
5484
5485   if (IS_ACTIVE_BOMB(dynabomb_element))
5486   {
5487     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5488     dynabomb_size = player->dynabomb_size;
5489     dynabomb_xl = player->dynabomb_xl;
5490     player->dynabombs_left++;
5491   }
5492
5493   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5494
5495   for (i = 0; i < NUM_DIRECTIONS; i++)
5496   {
5497     for (j = 1; j <= dynabomb_size; j++)
5498     {
5499       int x = ex + j * xy[i][0];
5500       int y = ey + j * xy[i][1];
5501       int element;
5502
5503       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5504         break;
5505
5506       element = Feld[x][y];
5507
5508       /* do not restart explosions of fields with active bombs */
5509       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5510         continue;
5511
5512       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5513
5514       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5515           !IS_DIGGABLE(element) && !dynabomb_xl)
5516         break;
5517     }
5518   }
5519 }
5520
5521 void Bang(int x, int y)
5522 {
5523   int element = MovingOrBlocked2Element(x, y);
5524   int explosion_type = EX_TYPE_NORMAL;
5525
5526   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5527   {
5528     struct PlayerInfo *player = PLAYERINFO(x, y);
5529
5530     element = Feld[x][y] = player->initial_element;
5531
5532     if (level.use_explosion_element[player->index_nr])
5533     {
5534       int explosion_element = level.explosion_element[player->index_nr];
5535
5536       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5537         explosion_type = EX_TYPE_CROSS;
5538       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5539         explosion_type = EX_TYPE_CENTER;
5540     }
5541   }
5542
5543   switch (element)
5544   {
5545     case EL_BUG:
5546     case EL_SPACESHIP:
5547     case EL_BD_BUTTERFLY:
5548     case EL_BD_FIREFLY:
5549     case EL_YAMYAM:
5550     case EL_DARK_YAMYAM:
5551     case EL_ROBOT:
5552     case EL_PACMAN:
5553     case EL_MOLE:
5554       RaiseScoreElement(element);
5555       break;
5556
5557     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5558     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5559     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5560     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5561     case EL_DYNABOMB_INCREASE_NUMBER:
5562     case EL_DYNABOMB_INCREASE_SIZE:
5563     case EL_DYNABOMB_INCREASE_POWER:
5564       explosion_type = EX_TYPE_DYNA;
5565       break;
5566
5567     case EL_DC_LANDMINE:
5568       explosion_type = EX_TYPE_CENTER;
5569       break;
5570
5571     case EL_PENGUIN:
5572     case EL_LAMP:
5573     case EL_LAMP_ACTIVE:
5574     case EL_AMOEBA_TO_DIAMOND:
5575       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5576         explosion_type = EX_TYPE_CENTER;
5577       break;
5578
5579     default:
5580       if (element_info[element].explosion_type == EXPLODES_CROSS)
5581         explosion_type = EX_TYPE_CROSS;
5582       else if (element_info[element].explosion_type == EXPLODES_1X1)
5583         explosion_type = EX_TYPE_CENTER;
5584       break;
5585   }
5586
5587   if (explosion_type == EX_TYPE_DYNA)
5588     DynaExplode(x, y);
5589   else
5590     Explode(x, y, EX_PHASE_START, explosion_type);
5591
5592   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5593 }
5594
5595 void SplashAcid(int x, int y)
5596 {
5597   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5598       (!IN_LEV_FIELD(x - 1, y - 2) ||
5599        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5600     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5601
5602   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5603       (!IN_LEV_FIELD(x + 1, y - 2) ||
5604        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5605     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5606
5607   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5608 }
5609
5610 static void InitBeltMovement()
5611 {
5612   static int belt_base_element[4] =
5613   {
5614     EL_CONVEYOR_BELT_1_LEFT,
5615     EL_CONVEYOR_BELT_2_LEFT,
5616     EL_CONVEYOR_BELT_3_LEFT,
5617     EL_CONVEYOR_BELT_4_LEFT
5618   };
5619   static int belt_base_active_element[4] =
5620   {
5621     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5622     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5623     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5624     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5625   };
5626
5627   int x, y, i, j;
5628
5629   /* set frame order for belt animation graphic according to belt direction */
5630   for (i = 0; i < NUM_BELTS; i++)
5631   {
5632     int belt_nr = i;
5633
5634     for (j = 0; j < NUM_BELT_PARTS; j++)
5635     {
5636       int element = belt_base_active_element[belt_nr] + j;
5637       int graphic_1 = el2img(element);
5638       int graphic_2 = el2panelimg(element);
5639
5640       if (game.belt_dir[i] == MV_LEFT)
5641       {
5642         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5643         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5644       }
5645       else
5646       {
5647         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5648         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5649       }
5650     }
5651   }
5652
5653   SCAN_PLAYFIELD(x, y)
5654   {
5655     int element = Feld[x][y];
5656
5657     for (i = 0; i < NUM_BELTS; i++)
5658     {
5659       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5660       {
5661         int e_belt_nr = getBeltNrFromBeltElement(element);
5662         int belt_nr = i;
5663
5664         if (e_belt_nr == belt_nr)
5665         {
5666           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5667
5668           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5669         }
5670       }
5671     }
5672   }
5673 }
5674
5675 static void ToggleBeltSwitch(int x, int y)
5676 {
5677   static int belt_base_element[4] =
5678   {
5679     EL_CONVEYOR_BELT_1_LEFT,
5680     EL_CONVEYOR_BELT_2_LEFT,
5681     EL_CONVEYOR_BELT_3_LEFT,
5682     EL_CONVEYOR_BELT_4_LEFT
5683   };
5684   static int belt_base_active_element[4] =
5685   {
5686     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5687     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5688     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5689     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5690   };
5691   static int belt_base_switch_element[4] =
5692   {
5693     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5694     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5695     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5696     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5697   };
5698   static int belt_move_dir[4] =
5699   {
5700     MV_LEFT,
5701     MV_NONE,
5702     MV_RIGHT,
5703     MV_NONE,
5704   };
5705
5706   int element = Feld[x][y];
5707   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5708   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5709   int belt_dir = belt_move_dir[belt_dir_nr];
5710   int xx, yy, i;
5711
5712   if (!IS_BELT_SWITCH(element))
5713     return;
5714
5715   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5716   game.belt_dir[belt_nr] = belt_dir;
5717
5718   if (belt_dir_nr == 3)
5719     belt_dir_nr = 1;
5720
5721   /* set frame order for belt animation graphic according to belt direction */
5722   for (i = 0; i < NUM_BELT_PARTS; i++)
5723   {
5724     int element = belt_base_active_element[belt_nr] + i;
5725     int graphic_1 = el2img(element);
5726     int graphic_2 = el2panelimg(element);
5727
5728     if (belt_dir == MV_LEFT)
5729     {
5730       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5731       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5732     }
5733     else
5734     {
5735       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5736       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5737     }
5738   }
5739
5740   SCAN_PLAYFIELD(xx, yy)
5741   {
5742     int element = Feld[xx][yy];
5743
5744     if (IS_BELT_SWITCH(element))
5745     {
5746       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5747
5748       if (e_belt_nr == belt_nr)
5749       {
5750         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5751         TEST_DrawLevelField(xx, yy);
5752       }
5753     }
5754     else if (IS_BELT(element) && belt_dir != MV_NONE)
5755     {
5756       int e_belt_nr = getBeltNrFromBeltElement(element);
5757
5758       if (e_belt_nr == belt_nr)
5759       {
5760         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5761
5762         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5763         TEST_DrawLevelField(xx, yy);
5764       }
5765     }
5766     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5767     {
5768       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5769
5770       if (e_belt_nr == belt_nr)
5771       {
5772         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5773
5774         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5775         TEST_DrawLevelField(xx, yy);
5776       }
5777     }
5778   }
5779 }
5780
5781 static void ToggleSwitchgateSwitch(int x, int y)
5782 {
5783   int xx, yy;
5784
5785   game.switchgate_pos = !game.switchgate_pos;
5786
5787   SCAN_PLAYFIELD(xx, yy)
5788   {
5789     int element = Feld[xx][yy];
5790
5791     if (element == EL_SWITCHGATE_SWITCH_UP)
5792     {
5793       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5794       TEST_DrawLevelField(xx, yy);
5795     }
5796     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5797     {
5798       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5799       TEST_DrawLevelField(xx, yy);
5800     }
5801     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5802     {
5803       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5804       TEST_DrawLevelField(xx, yy);
5805     }
5806     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5807     {
5808       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5809       TEST_DrawLevelField(xx, yy);
5810     }
5811     else if (element == EL_SWITCHGATE_OPEN ||
5812              element == EL_SWITCHGATE_OPENING)
5813     {
5814       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5815
5816       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5817     }
5818     else if (element == EL_SWITCHGATE_CLOSED ||
5819              element == EL_SWITCHGATE_CLOSING)
5820     {
5821       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5822
5823       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5824     }
5825   }
5826 }
5827
5828 static int getInvisibleActiveFromInvisibleElement(int element)
5829 {
5830   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5831           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5832           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5833           element);
5834 }
5835
5836 static int getInvisibleFromInvisibleActiveElement(int element)
5837 {
5838   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5839           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5840           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5841           element);
5842 }
5843
5844 static void RedrawAllLightSwitchesAndInvisibleElements()
5845 {
5846   int x, y;
5847
5848   SCAN_PLAYFIELD(x, y)
5849   {
5850     int element = Feld[x][y];
5851
5852     if (element == EL_LIGHT_SWITCH &&
5853         game.light_time_left > 0)
5854     {
5855       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5856       TEST_DrawLevelField(x, y);
5857     }
5858     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5859              game.light_time_left == 0)
5860     {
5861       Feld[x][y] = EL_LIGHT_SWITCH;
5862       TEST_DrawLevelField(x, y);
5863     }
5864     else if (element == EL_EMC_DRIPPER &&
5865              game.light_time_left > 0)
5866     {
5867       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5868       TEST_DrawLevelField(x, y);
5869     }
5870     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5871              game.light_time_left == 0)
5872     {
5873       Feld[x][y] = EL_EMC_DRIPPER;
5874       TEST_DrawLevelField(x, y);
5875     }
5876     else if (element == EL_INVISIBLE_STEELWALL ||
5877              element == EL_INVISIBLE_WALL ||
5878              element == EL_INVISIBLE_SAND)
5879     {
5880       if (game.light_time_left > 0)
5881         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5882
5883       TEST_DrawLevelField(x, y);
5884
5885       /* uncrumble neighbour fields, if needed */
5886       if (element == EL_INVISIBLE_SAND)
5887         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5888     }
5889     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5890              element == EL_INVISIBLE_WALL_ACTIVE ||
5891              element == EL_INVISIBLE_SAND_ACTIVE)
5892     {
5893       if (game.light_time_left == 0)
5894         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5895
5896       TEST_DrawLevelField(x, y);
5897
5898       /* re-crumble neighbour fields, if needed */
5899       if (element == EL_INVISIBLE_SAND)
5900         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5901     }
5902   }
5903 }
5904
5905 static void RedrawAllInvisibleElementsForLenses()
5906 {
5907   int x, y;
5908
5909   SCAN_PLAYFIELD(x, y)
5910   {
5911     int element = Feld[x][y];
5912
5913     if (element == EL_EMC_DRIPPER &&
5914         game.lenses_time_left > 0)
5915     {
5916       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5917       TEST_DrawLevelField(x, y);
5918     }
5919     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5920              game.lenses_time_left == 0)
5921     {
5922       Feld[x][y] = EL_EMC_DRIPPER;
5923       TEST_DrawLevelField(x, y);
5924     }
5925     else if (element == EL_INVISIBLE_STEELWALL ||
5926              element == EL_INVISIBLE_WALL ||
5927              element == EL_INVISIBLE_SAND)
5928     {
5929       if (game.lenses_time_left > 0)
5930         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5931
5932       TEST_DrawLevelField(x, y);
5933
5934       /* uncrumble neighbour fields, if needed */
5935       if (element == EL_INVISIBLE_SAND)
5936         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5937     }
5938     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5939              element == EL_INVISIBLE_WALL_ACTIVE ||
5940              element == EL_INVISIBLE_SAND_ACTIVE)
5941     {
5942       if (game.lenses_time_left == 0)
5943         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5944
5945       TEST_DrawLevelField(x, y);
5946
5947       /* re-crumble neighbour fields, if needed */
5948       if (element == EL_INVISIBLE_SAND)
5949         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5950     }
5951   }
5952 }
5953
5954 static void RedrawAllInvisibleElementsForMagnifier()
5955 {
5956   int x, y;
5957
5958   SCAN_PLAYFIELD(x, y)
5959   {
5960     int element = Feld[x][y];
5961
5962     if (element == EL_EMC_FAKE_GRASS &&
5963         game.magnify_time_left > 0)
5964     {
5965       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5966       TEST_DrawLevelField(x, y);
5967     }
5968     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5969              game.magnify_time_left == 0)
5970     {
5971       Feld[x][y] = EL_EMC_FAKE_GRASS;
5972       TEST_DrawLevelField(x, y);
5973     }
5974     else if (IS_GATE_GRAY(element) &&
5975              game.magnify_time_left > 0)
5976     {
5977       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5978                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5979                     IS_EM_GATE_GRAY(element) ?
5980                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5981                     IS_EMC_GATE_GRAY(element) ?
5982                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5983                     IS_DC_GATE_GRAY(element) ?
5984                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5985                     element);
5986       TEST_DrawLevelField(x, y);
5987     }
5988     else if (IS_GATE_GRAY_ACTIVE(element) &&
5989              game.magnify_time_left == 0)
5990     {
5991       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5992                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5993                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5994                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5995                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5996                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5997                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5998                     EL_DC_GATE_WHITE_GRAY :
5999                     element);
6000       TEST_DrawLevelField(x, y);
6001     }
6002   }
6003 }
6004
6005 static void ToggleLightSwitch(int x, int y)
6006 {
6007   int element = Feld[x][y];
6008
6009   game.light_time_left =
6010     (element == EL_LIGHT_SWITCH ?
6011      level.time_light * FRAMES_PER_SECOND : 0);
6012
6013   RedrawAllLightSwitchesAndInvisibleElements();
6014 }
6015
6016 static void ActivateTimegateSwitch(int x, int y)
6017 {
6018   int xx, yy;
6019
6020   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
6021
6022   SCAN_PLAYFIELD(xx, yy)
6023   {
6024     int element = Feld[xx][yy];
6025
6026     if (element == EL_TIMEGATE_CLOSED ||
6027         element == EL_TIMEGATE_CLOSING)
6028     {
6029       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6030       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6031     }
6032
6033     /*
6034     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6035     {
6036       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6037       TEST_DrawLevelField(xx, yy);
6038     }
6039     */
6040
6041   }
6042
6043   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6044                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6045 }
6046
6047 void Impact(int x, int y)
6048 {
6049   boolean last_line = (y == lev_fieldy - 1);
6050   boolean object_hit = FALSE;
6051   boolean impact = (last_line || object_hit);
6052   int element = Feld[x][y];
6053   int smashed = EL_STEELWALL;
6054
6055   if (!last_line)       /* check if element below was hit */
6056   {
6057     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6058       return;
6059
6060     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6061                                          MovDir[x][y + 1] != MV_DOWN ||
6062                                          MovPos[x][y + 1] <= TILEY / 2));
6063
6064     /* do not smash moving elements that left the smashed field in time */
6065     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6066         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6067       object_hit = FALSE;
6068
6069 #if USE_QUICKSAND_IMPACT_BUGFIX
6070     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6071     {
6072       RemoveMovingField(x, y + 1);
6073       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6074       Feld[x][y + 2] = EL_ROCK;
6075       TEST_DrawLevelField(x, y + 2);
6076
6077       object_hit = TRUE;
6078     }
6079
6080     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6081     {
6082       RemoveMovingField(x, y + 1);
6083       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6084       Feld[x][y + 2] = EL_ROCK;
6085       TEST_DrawLevelField(x, y + 2);
6086
6087       object_hit = TRUE;
6088     }
6089 #endif
6090
6091     if (object_hit)
6092       smashed = MovingOrBlocked2Element(x, y + 1);
6093
6094     impact = (last_line || object_hit);
6095   }
6096
6097   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6098   {
6099     SplashAcid(x, y + 1);
6100     return;
6101   }
6102
6103   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6104   /* only reset graphic animation if graphic really changes after impact */
6105   if (impact &&
6106       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6107   {
6108     ResetGfxAnimation(x, y);
6109     TEST_DrawLevelField(x, y);
6110   }
6111
6112   if (impact && CAN_EXPLODE_IMPACT(element))
6113   {
6114     Bang(x, y);
6115     return;
6116   }
6117   else if (impact && element == EL_PEARL &&
6118            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6119   {
6120     ResetGfxAnimation(x, y);
6121
6122     Feld[x][y] = EL_PEARL_BREAKING;
6123     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6124     return;
6125   }
6126   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6127   {
6128     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6129
6130     return;
6131   }
6132
6133   if (impact && element == EL_AMOEBA_DROP)
6134   {
6135     if (object_hit && IS_PLAYER(x, y + 1))
6136       KillPlayerUnlessEnemyProtected(x, y + 1);
6137     else if (object_hit && smashed == EL_PENGUIN)
6138       Bang(x, y + 1);
6139     else
6140     {
6141       Feld[x][y] = EL_AMOEBA_GROWING;
6142       Store[x][y] = EL_AMOEBA_WET;
6143
6144       ResetRandomAnimationValue(x, y);
6145     }
6146     return;
6147   }
6148
6149   if (object_hit)               /* check which object was hit */
6150   {
6151     if ((CAN_PASS_MAGIC_WALL(element) && 
6152          (smashed == EL_MAGIC_WALL ||
6153           smashed == EL_BD_MAGIC_WALL)) ||
6154         (CAN_PASS_DC_MAGIC_WALL(element) &&
6155          smashed == EL_DC_MAGIC_WALL))
6156     {
6157       int xx, yy;
6158       int activated_magic_wall =
6159         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6160          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6161          EL_DC_MAGIC_WALL_ACTIVE);
6162
6163       /* activate magic wall / mill */
6164       SCAN_PLAYFIELD(xx, yy)
6165       {
6166         if (Feld[xx][yy] == smashed)
6167           Feld[xx][yy] = activated_magic_wall;
6168       }
6169
6170       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6171       game.magic_wall_active = TRUE;
6172
6173       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6174                             SND_MAGIC_WALL_ACTIVATING :
6175                             smashed == EL_BD_MAGIC_WALL ?
6176                             SND_BD_MAGIC_WALL_ACTIVATING :
6177                             SND_DC_MAGIC_WALL_ACTIVATING));
6178     }
6179
6180     if (IS_PLAYER(x, y + 1))
6181     {
6182       if (CAN_SMASH_PLAYER(element))
6183       {
6184         KillPlayerUnlessEnemyProtected(x, y + 1);
6185         return;
6186       }
6187     }
6188     else if (smashed == EL_PENGUIN)
6189     {
6190       if (CAN_SMASH_PLAYER(element))
6191       {
6192         Bang(x, y + 1);
6193         return;
6194       }
6195     }
6196     else if (element == EL_BD_DIAMOND)
6197     {
6198       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6199       {
6200         Bang(x, y + 1);
6201         return;
6202       }
6203     }
6204     else if (((element == EL_SP_INFOTRON ||
6205                element == EL_SP_ZONK) &&
6206               (smashed == EL_SP_SNIKSNAK ||
6207                smashed == EL_SP_ELECTRON ||
6208                smashed == EL_SP_DISK_ORANGE)) ||
6209              (element == EL_SP_INFOTRON &&
6210               smashed == EL_SP_DISK_YELLOW))
6211     {
6212       Bang(x, y + 1);
6213       return;
6214     }
6215     else if (CAN_SMASH_EVERYTHING(element))
6216     {
6217       if (IS_CLASSIC_ENEMY(smashed) ||
6218           CAN_EXPLODE_SMASHED(smashed))
6219       {
6220         Bang(x, y + 1);
6221         return;
6222       }
6223       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6224       {
6225         if (smashed == EL_LAMP ||
6226             smashed == EL_LAMP_ACTIVE)
6227         {
6228           Bang(x, y + 1);
6229           return;
6230         }
6231         else if (smashed == EL_NUT)
6232         {
6233           Feld[x][y + 1] = EL_NUT_BREAKING;
6234           PlayLevelSound(x, y, SND_NUT_BREAKING);
6235           RaiseScoreElement(EL_NUT);
6236           return;
6237         }
6238         else if (smashed == EL_PEARL)
6239         {
6240           ResetGfxAnimation(x, y);
6241
6242           Feld[x][y + 1] = EL_PEARL_BREAKING;
6243           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6244           return;
6245         }
6246         else if (smashed == EL_DIAMOND)
6247         {
6248           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6249           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6250           return;
6251         }
6252         else if (IS_BELT_SWITCH(smashed))
6253         {
6254           ToggleBeltSwitch(x, y + 1);
6255         }
6256         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6257                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6258                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6259                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6260         {
6261           ToggleSwitchgateSwitch(x, y + 1);
6262         }
6263         else if (smashed == EL_LIGHT_SWITCH ||
6264                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6265         {
6266           ToggleLightSwitch(x, y + 1);
6267         }
6268         else
6269         {
6270           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6271
6272           CheckElementChangeBySide(x, y + 1, smashed, element,
6273                                    CE_SWITCHED, CH_SIDE_TOP);
6274           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6275                                             CH_SIDE_TOP);
6276         }
6277       }
6278       else
6279       {
6280         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6281       }
6282     }
6283   }
6284
6285   /* play sound of magic wall / mill */
6286   if (!last_line &&
6287       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6288        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6289        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6290   {
6291     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6292       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6293     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6294       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6295     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6296       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6297
6298     return;
6299   }
6300
6301   /* play sound of object that hits the ground */
6302   if (last_line || object_hit)
6303     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6304 }
6305
6306 inline static void TurnRoundExt(int x, int y)
6307 {
6308   static struct
6309   {
6310     int dx, dy;
6311   } move_xy[] =
6312   {
6313     {  0,  0 },
6314     { -1,  0 },
6315     { +1,  0 },
6316     {  0,  0 },
6317     {  0, -1 },
6318     {  0,  0 }, { 0, 0 }, { 0, 0 },
6319     {  0, +1 }
6320   };
6321   static struct
6322   {
6323     int left, right, back;
6324   } turn[] =
6325   {
6326     { 0,        0,              0        },
6327     { MV_DOWN,  MV_UP,          MV_RIGHT },
6328     { MV_UP,    MV_DOWN,        MV_LEFT  },
6329     { 0,        0,              0        },
6330     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6331     { 0,        0,              0        },
6332     { 0,        0,              0        },
6333     { 0,        0,              0        },
6334     { MV_RIGHT, MV_LEFT,        MV_UP    }
6335   };
6336
6337   int element = Feld[x][y];
6338   int move_pattern = element_info[element].move_pattern;
6339
6340   int old_move_dir = MovDir[x][y];
6341   int left_dir  = turn[old_move_dir].left;
6342   int right_dir = turn[old_move_dir].right;
6343   int back_dir  = turn[old_move_dir].back;
6344
6345   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6346   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6347   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6348   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6349
6350   int left_x  = x + left_dx,  left_y  = y + left_dy;
6351   int right_x = x + right_dx, right_y = y + right_dy;
6352   int move_x  = x + move_dx,  move_y  = y + move_dy;
6353
6354   int xx, yy;
6355
6356   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6357   {
6358     TestIfBadThingTouchesOtherBadThing(x, y);
6359
6360     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6361       MovDir[x][y] = right_dir;
6362     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6363       MovDir[x][y] = left_dir;
6364
6365     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6366       MovDelay[x][y] = 9;
6367     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6368       MovDelay[x][y] = 1;
6369   }
6370   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6371   {
6372     TestIfBadThingTouchesOtherBadThing(x, y);
6373
6374     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6375       MovDir[x][y] = left_dir;
6376     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6377       MovDir[x][y] = right_dir;
6378
6379     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6380       MovDelay[x][y] = 9;
6381     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6382       MovDelay[x][y] = 1;
6383   }
6384   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6385   {
6386     TestIfBadThingTouchesOtherBadThing(x, y);
6387
6388     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6389       MovDir[x][y] = left_dir;
6390     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6391       MovDir[x][y] = right_dir;
6392
6393     if (MovDir[x][y] != old_move_dir)
6394       MovDelay[x][y] = 9;
6395   }
6396   else if (element == EL_YAMYAM)
6397   {
6398     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6399     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6400
6401     if (can_turn_left && can_turn_right)
6402       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6403     else if (can_turn_left)
6404       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6405     else if (can_turn_right)
6406       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6407     else
6408       MovDir[x][y] = back_dir;
6409
6410     MovDelay[x][y] = 16 + 16 * RND(3);
6411   }
6412   else if (element == EL_DARK_YAMYAM)
6413   {
6414     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6415                                                          left_x, left_y);
6416     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6417                                                          right_x, right_y);
6418
6419     if (can_turn_left && can_turn_right)
6420       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6421     else if (can_turn_left)
6422       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6423     else if (can_turn_right)
6424       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6425     else
6426       MovDir[x][y] = back_dir;
6427
6428     MovDelay[x][y] = 16 + 16 * RND(3);
6429   }
6430   else if (element == EL_PACMAN)
6431   {
6432     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6433     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6434
6435     if (can_turn_left && can_turn_right)
6436       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6437     else if (can_turn_left)
6438       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6439     else if (can_turn_right)
6440       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6441     else
6442       MovDir[x][y] = back_dir;
6443
6444     MovDelay[x][y] = 6 + RND(40);
6445   }
6446   else if (element == EL_PIG)
6447   {
6448     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6449     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6450     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6451     boolean should_turn_left, should_turn_right, should_move_on;
6452     int rnd_value = 24;
6453     int rnd = RND(rnd_value);
6454
6455     should_turn_left = (can_turn_left &&
6456                         (!can_move_on ||
6457                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6458                                                    y + back_dy + left_dy)));
6459     should_turn_right = (can_turn_right &&
6460                          (!can_move_on ||
6461                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6462                                                     y + back_dy + right_dy)));
6463     should_move_on = (can_move_on &&
6464                       (!can_turn_left ||
6465                        !can_turn_right ||
6466                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6467                                                  y + move_dy + left_dy) ||
6468                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6469                                                  y + move_dy + right_dy)));
6470
6471     if (should_turn_left || should_turn_right || should_move_on)
6472     {
6473       if (should_turn_left && should_turn_right && should_move_on)
6474         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6475                         rnd < 2 * rnd_value / 3 ? right_dir :
6476                         old_move_dir);
6477       else if (should_turn_left && should_turn_right)
6478         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6479       else if (should_turn_left && should_move_on)
6480         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6481       else if (should_turn_right && should_move_on)
6482         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6483       else if (should_turn_left)
6484         MovDir[x][y] = left_dir;
6485       else if (should_turn_right)
6486         MovDir[x][y] = right_dir;
6487       else if (should_move_on)
6488         MovDir[x][y] = old_move_dir;
6489     }
6490     else if (can_move_on && rnd > rnd_value / 8)
6491       MovDir[x][y] = old_move_dir;
6492     else if (can_turn_left && can_turn_right)
6493       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6494     else if (can_turn_left && rnd > rnd_value / 8)
6495       MovDir[x][y] = left_dir;
6496     else if (can_turn_right && rnd > rnd_value/8)
6497       MovDir[x][y] = right_dir;
6498     else
6499       MovDir[x][y] = back_dir;
6500
6501     xx = x + move_xy[MovDir[x][y]].dx;
6502     yy = y + move_xy[MovDir[x][y]].dy;
6503
6504     if (!IN_LEV_FIELD(xx, yy) ||
6505         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6506       MovDir[x][y] = old_move_dir;
6507
6508     MovDelay[x][y] = 0;
6509   }
6510   else if (element == EL_DRAGON)
6511   {
6512     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6513     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6514     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6515     int rnd_value = 24;
6516     int rnd = RND(rnd_value);
6517
6518     if (can_move_on && rnd > rnd_value / 8)
6519       MovDir[x][y] = old_move_dir;
6520     else if (can_turn_left && can_turn_right)
6521       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6522     else if (can_turn_left && rnd > rnd_value / 8)
6523       MovDir[x][y] = left_dir;
6524     else if (can_turn_right && rnd > rnd_value / 8)
6525       MovDir[x][y] = right_dir;
6526     else
6527       MovDir[x][y] = back_dir;
6528
6529     xx = x + move_xy[MovDir[x][y]].dx;
6530     yy = y + move_xy[MovDir[x][y]].dy;
6531
6532     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6533       MovDir[x][y] = old_move_dir;
6534
6535     MovDelay[x][y] = 0;
6536   }
6537   else if (element == EL_MOLE)
6538   {
6539     boolean can_move_on =
6540       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6541                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6542                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6543     if (!can_move_on)
6544     {
6545       boolean can_turn_left =
6546         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6547                               IS_AMOEBOID(Feld[left_x][left_y])));
6548
6549       boolean can_turn_right =
6550         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6551                               IS_AMOEBOID(Feld[right_x][right_y])));
6552
6553       if (can_turn_left && can_turn_right)
6554         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6555       else if (can_turn_left)
6556         MovDir[x][y] = left_dir;
6557       else
6558         MovDir[x][y] = right_dir;
6559     }
6560
6561     if (MovDir[x][y] != old_move_dir)
6562       MovDelay[x][y] = 9;
6563   }
6564   else if (element == EL_BALLOON)
6565   {
6566     MovDir[x][y] = game.wind_direction;
6567     MovDelay[x][y] = 0;
6568   }
6569   else if (element == EL_SPRING)
6570   {
6571     if (MovDir[x][y] & MV_HORIZONTAL)
6572     {
6573       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6574           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6575       {
6576         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6577         ResetGfxAnimation(move_x, move_y);
6578         TEST_DrawLevelField(move_x, move_y);
6579
6580         MovDir[x][y] = back_dir;
6581       }
6582       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6583                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6584         MovDir[x][y] = MV_NONE;
6585     }
6586
6587     MovDelay[x][y] = 0;
6588   }
6589   else if (element == EL_ROBOT ||
6590            element == EL_SATELLITE ||
6591            element == EL_PENGUIN ||
6592            element == EL_EMC_ANDROID)
6593   {
6594     int attr_x = -1, attr_y = -1;
6595
6596     if (AllPlayersGone)
6597     {
6598       attr_x = ExitX;
6599       attr_y = ExitY;
6600     }
6601     else
6602     {
6603       int i;
6604
6605       for (i = 0; i < MAX_PLAYERS; i++)
6606       {
6607         struct PlayerInfo *player = &stored_player[i];
6608         int jx = player->jx, jy = player->jy;
6609
6610         if (!player->active)
6611           continue;
6612
6613         if (attr_x == -1 ||
6614             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6615         {
6616           attr_x = jx;
6617           attr_y = jy;
6618         }
6619       }
6620     }
6621
6622     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6623         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6624          game.engine_version < VERSION_IDENT(3,1,0,0)))
6625     {
6626       attr_x = ZX;
6627       attr_y = ZY;
6628     }
6629
6630     if (element == EL_PENGUIN)
6631     {
6632       int i;
6633       static int xy[4][2] =
6634       {
6635         { 0, -1 },
6636         { -1, 0 },
6637         { +1, 0 },
6638         { 0, +1 }
6639       };
6640
6641       for (i = 0; i < NUM_DIRECTIONS; i++)
6642       {
6643         int ex = x + xy[i][0];
6644         int ey = y + xy[i][1];
6645
6646         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6647                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6648                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6649                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6650         {
6651           attr_x = ex;
6652           attr_y = ey;
6653           break;
6654         }
6655       }
6656     }
6657
6658     MovDir[x][y] = MV_NONE;
6659     if (attr_x < x)
6660       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6661     else if (attr_x > x)
6662       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6663     if (attr_y < y)
6664       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6665     else if (attr_y > y)
6666       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6667
6668     if (element == EL_ROBOT)
6669     {
6670       int newx, newy;
6671
6672       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6673         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6674       Moving2Blocked(x, y, &newx, &newy);
6675
6676       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6677         MovDelay[x][y] = 8 + 8 * !RND(3);
6678       else
6679         MovDelay[x][y] = 16;
6680     }
6681     else if (element == EL_PENGUIN)
6682     {
6683       int newx, newy;
6684
6685       MovDelay[x][y] = 1;
6686
6687       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6688       {
6689         boolean first_horiz = RND(2);
6690         int new_move_dir = MovDir[x][y];
6691
6692         MovDir[x][y] =
6693           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6694         Moving2Blocked(x, y, &newx, &newy);
6695
6696         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6697           return;
6698
6699         MovDir[x][y] =
6700           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6701         Moving2Blocked(x, y, &newx, &newy);
6702
6703         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6704           return;
6705
6706         MovDir[x][y] = old_move_dir;
6707         return;
6708       }
6709     }
6710     else if (element == EL_SATELLITE)
6711     {
6712       int newx, newy;
6713
6714       MovDelay[x][y] = 1;
6715
6716       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6717       {
6718         boolean first_horiz = RND(2);
6719         int new_move_dir = MovDir[x][y];
6720
6721         MovDir[x][y] =
6722           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6723         Moving2Blocked(x, y, &newx, &newy);
6724
6725         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6726           return;
6727
6728         MovDir[x][y] =
6729           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6730         Moving2Blocked(x, y, &newx, &newy);
6731
6732         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6733           return;
6734
6735         MovDir[x][y] = old_move_dir;
6736         return;
6737       }
6738     }
6739     else if (element == EL_EMC_ANDROID)
6740     {
6741       static int check_pos[16] =
6742       {
6743         -1,             /*  0 => (invalid)          */
6744         7,              /*  1 => MV_LEFT            */
6745         3,              /*  2 => MV_RIGHT           */
6746         -1,             /*  3 => (invalid)          */
6747         1,              /*  4 =>            MV_UP   */
6748         0,              /*  5 => MV_LEFT  | MV_UP   */
6749         2,              /*  6 => MV_RIGHT | MV_UP   */
6750         -1,             /*  7 => (invalid)          */
6751         5,              /*  8 =>            MV_DOWN */
6752         6,              /*  9 => MV_LEFT  | MV_DOWN */
6753         4,              /* 10 => MV_RIGHT | MV_DOWN */
6754         -1,             /* 11 => (invalid)          */
6755         -1,             /* 12 => (invalid)          */
6756         -1,             /* 13 => (invalid)          */
6757         -1,             /* 14 => (invalid)          */
6758         -1,             /* 15 => (invalid)          */
6759       };
6760       static struct
6761       {
6762         int dx, dy;
6763         int dir;
6764       } check_xy[8] =
6765       {
6766         { -1, -1,       MV_LEFT  | MV_UP   },
6767         {  0, -1,                  MV_UP   },
6768         { +1, -1,       MV_RIGHT | MV_UP   },
6769         { +1,  0,       MV_RIGHT           },
6770         { +1, +1,       MV_RIGHT | MV_DOWN },
6771         {  0, +1,                  MV_DOWN },
6772         { -1, +1,       MV_LEFT  | MV_DOWN },
6773         { -1,  0,       MV_LEFT            },
6774       };
6775       int start_pos, check_order;
6776       boolean can_clone = FALSE;
6777       int i;
6778
6779       /* check if there is any free field around current position */
6780       for (i = 0; i < 8; i++)
6781       {
6782         int newx = x + check_xy[i].dx;
6783         int newy = y + check_xy[i].dy;
6784
6785         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6786         {
6787           can_clone = TRUE;
6788
6789           break;
6790         }
6791       }
6792
6793       if (can_clone)            /* randomly find an element to clone */
6794       {
6795         can_clone = FALSE;
6796
6797         start_pos = check_pos[RND(8)];
6798         check_order = (RND(2) ? -1 : +1);
6799
6800         for (i = 0; i < 8; i++)
6801         {
6802           int pos_raw = start_pos + i * check_order;
6803           int pos = (pos_raw + 8) % 8;
6804           int newx = x + check_xy[pos].dx;
6805           int newy = y + check_xy[pos].dy;
6806
6807           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6808           {
6809             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6810             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6811
6812             Store[x][y] = Feld[newx][newy];
6813
6814             can_clone = TRUE;
6815
6816             break;
6817           }
6818         }
6819       }
6820
6821       if (can_clone)            /* randomly find a direction to move */
6822       {
6823         can_clone = FALSE;
6824
6825         start_pos = check_pos[RND(8)];
6826         check_order = (RND(2) ? -1 : +1);
6827
6828         for (i = 0; i < 8; i++)
6829         {
6830           int pos_raw = start_pos + i * check_order;
6831           int pos = (pos_raw + 8) % 8;
6832           int newx = x + check_xy[pos].dx;
6833           int newy = y + check_xy[pos].dy;
6834           int new_move_dir = check_xy[pos].dir;
6835
6836           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6837           {
6838             MovDir[x][y] = new_move_dir;
6839             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6840
6841             can_clone = TRUE;
6842
6843             break;
6844           }
6845         }
6846       }
6847
6848       if (can_clone)            /* cloning and moving successful */
6849         return;
6850
6851       /* cannot clone -- try to move towards player */
6852
6853       start_pos = check_pos[MovDir[x][y] & 0x0f];
6854       check_order = (RND(2) ? -1 : +1);
6855
6856       for (i = 0; i < 3; i++)
6857       {
6858         /* first check start_pos, then previous/next or (next/previous) pos */
6859         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6860         int pos = (pos_raw + 8) % 8;
6861         int newx = x + check_xy[pos].dx;
6862         int newy = y + check_xy[pos].dy;
6863         int new_move_dir = check_xy[pos].dir;
6864
6865         if (IS_PLAYER(newx, newy))
6866           break;
6867
6868         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6869         {
6870           MovDir[x][y] = new_move_dir;
6871           MovDelay[x][y] = level.android_move_time * 8 + 1;
6872
6873           break;
6874         }
6875       }
6876     }
6877   }
6878   else if (move_pattern == MV_TURNING_LEFT ||
6879            move_pattern == MV_TURNING_RIGHT ||
6880            move_pattern == MV_TURNING_LEFT_RIGHT ||
6881            move_pattern == MV_TURNING_RIGHT_LEFT ||
6882            move_pattern == MV_TURNING_RANDOM ||
6883            move_pattern == MV_ALL_DIRECTIONS)
6884   {
6885     boolean can_turn_left =
6886       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6887     boolean can_turn_right =
6888       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6889
6890     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6891       return;
6892
6893     if (move_pattern == MV_TURNING_LEFT)
6894       MovDir[x][y] = left_dir;
6895     else if (move_pattern == MV_TURNING_RIGHT)
6896       MovDir[x][y] = right_dir;
6897     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6898       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6899     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6900       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6901     else if (move_pattern == MV_TURNING_RANDOM)
6902       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6903                       can_turn_right && !can_turn_left ? right_dir :
6904                       RND(2) ? left_dir : right_dir);
6905     else if (can_turn_left && can_turn_right)
6906       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6907     else if (can_turn_left)
6908       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6909     else if (can_turn_right)
6910       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6911     else
6912       MovDir[x][y] = back_dir;
6913
6914     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6915   }
6916   else if (move_pattern == MV_HORIZONTAL ||
6917            move_pattern == MV_VERTICAL)
6918   {
6919     if (move_pattern & old_move_dir)
6920       MovDir[x][y] = back_dir;
6921     else if (move_pattern == MV_HORIZONTAL)
6922       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6923     else if (move_pattern == MV_VERTICAL)
6924       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6925
6926     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6927   }
6928   else if (move_pattern & MV_ANY_DIRECTION)
6929   {
6930     MovDir[x][y] = move_pattern;
6931     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6932   }
6933   else if (move_pattern & MV_WIND_DIRECTION)
6934   {
6935     MovDir[x][y] = game.wind_direction;
6936     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6937   }
6938   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6939   {
6940     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6941       MovDir[x][y] = left_dir;
6942     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6943       MovDir[x][y] = right_dir;
6944
6945     if (MovDir[x][y] != old_move_dir)
6946       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6947   }
6948   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6949   {
6950     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6951       MovDir[x][y] = right_dir;
6952     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6953       MovDir[x][y] = left_dir;
6954
6955     if (MovDir[x][y] != old_move_dir)
6956       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6957   }
6958   else if (move_pattern == MV_TOWARDS_PLAYER ||
6959            move_pattern == MV_AWAY_FROM_PLAYER)
6960   {
6961     int attr_x = -1, attr_y = -1;
6962     int newx, newy;
6963     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6964
6965     if (AllPlayersGone)
6966     {
6967       attr_x = ExitX;
6968       attr_y = ExitY;
6969     }
6970     else
6971     {
6972       int i;
6973
6974       for (i = 0; i < MAX_PLAYERS; i++)
6975       {
6976         struct PlayerInfo *player = &stored_player[i];
6977         int jx = player->jx, jy = player->jy;
6978
6979         if (!player->active)
6980           continue;
6981
6982         if (attr_x == -1 ||
6983             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6984         {
6985           attr_x = jx;
6986           attr_y = jy;
6987         }
6988       }
6989     }
6990
6991     MovDir[x][y] = MV_NONE;
6992     if (attr_x < x)
6993       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6994     else if (attr_x > x)
6995       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6996     if (attr_y < y)
6997       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6998     else if (attr_y > y)
6999       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
7000
7001     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7002
7003     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
7004     {
7005       boolean first_horiz = RND(2);
7006       int new_move_dir = MovDir[x][y];
7007
7008       if (element_info[element].move_stepsize == 0)     /* "not moving" */
7009       {
7010         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
7011         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7012
7013         return;
7014       }
7015
7016       MovDir[x][y] =
7017         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7018       Moving2Blocked(x, y, &newx, &newy);
7019
7020       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7021         return;
7022
7023       MovDir[x][y] =
7024         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7025       Moving2Blocked(x, y, &newx, &newy);
7026
7027       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7028         return;
7029
7030       MovDir[x][y] = old_move_dir;
7031     }
7032   }
7033   else if (move_pattern == MV_WHEN_PUSHED ||
7034            move_pattern == MV_WHEN_DROPPED)
7035   {
7036     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7037       MovDir[x][y] = MV_NONE;
7038
7039     MovDelay[x][y] = 0;
7040   }
7041   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7042   {
7043     static int test_xy[7][2] =
7044     {
7045       { 0, -1 },
7046       { -1, 0 },
7047       { +1, 0 },
7048       { 0, +1 },
7049       { 0, -1 },
7050       { -1, 0 },
7051       { +1, 0 },
7052     };
7053     static int test_dir[7] =
7054     {
7055       MV_UP,
7056       MV_LEFT,
7057       MV_RIGHT,
7058       MV_DOWN,
7059       MV_UP,
7060       MV_LEFT,
7061       MV_RIGHT,
7062     };
7063     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7064     int move_preference = -1000000;     /* start with very low preference */
7065     int new_move_dir = MV_NONE;
7066     int start_test = RND(4);
7067     int i;
7068
7069     for (i = 0; i < NUM_DIRECTIONS; i++)
7070     {
7071       int move_dir = test_dir[start_test + i];
7072       int move_dir_preference;
7073
7074       xx = x + test_xy[start_test + i][0];
7075       yy = y + test_xy[start_test + i][1];
7076
7077       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7078           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7079       {
7080         new_move_dir = move_dir;
7081
7082         break;
7083       }
7084
7085       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7086         continue;
7087
7088       move_dir_preference = -1 * RunnerVisit[xx][yy];
7089       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7090         move_dir_preference = PlayerVisit[xx][yy];
7091
7092       if (move_dir_preference > move_preference)
7093       {
7094         /* prefer field that has not been visited for the longest time */
7095         move_preference = move_dir_preference;
7096         new_move_dir = move_dir;
7097       }
7098       else if (move_dir_preference == move_preference &&
7099                move_dir == old_move_dir)
7100       {
7101         /* prefer last direction when all directions are preferred equally */
7102         move_preference = move_dir_preference;
7103         new_move_dir = move_dir;
7104       }
7105     }
7106
7107     MovDir[x][y] = new_move_dir;
7108     if (old_move_dir != new_move_dir)
7109       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7110   }
7111 }
7112
7113 static void TurnRound(int x, int y)
7114 {
7115   int direction = MovDir[x][y];
7116
7117   TurnRoundExt(x, y);
7118
7119   GfxDir[x][y] = MovDir[x][y];
7120
7121   if (direction != MovDir[x][y])
7122     GfxFrame[x][y] = 0;
7123
7124   if (MovDelay[x][y])
7125     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7126
7127   ResetGfxFrame(x, y, FALSE);
7128 }
7129
7130 static boolean JustBeingPushed(int x, int y)
7131 {
7132   int i;
7133
7134   for (i = 0; i < MAX_PLAYERS; i++)
7135   {
7136     struct PlayerInfo *player = &stored_player[i];
7137
7138     if (player->active && player->is_pushing && player->MovPos)
7139     {
7140       int next_jx = player->jx + (player->jx - player->last_jx);
7141       int next_jy = player->jy + (player->jy - player->last_jy);
7142
7143       if (x == next_jx && y == next_jy)
7144         return TRUE;
7145     }
7146   }
7147
7148   return FALSE;
7149 }
7150
7151 void StartMoving(int x, int y)
7152 {
7153   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7154   int element = Feld[x][y];
7155
7156   if (Stop[x][y])
7157     return;
7158
7159   if (MovDelay[x][y] == 0)
7160     GfxAction[x][y] = ACTION_DEFAULT;
7161
7162   if (CAN_FALL(element) && y < lev_fieldy - 1)
7163   {
7164     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7165         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7166       if (JustBeingPushed(x, y))
7167         return;
7168
7169     if (element == EL_QUICKSAND_FULL)
7170     {
7171       if (IS_FREE(x, y + 1))
7172       {
7173         InitMovingField(x, y, MV_DOWN);
7174         started_moving = TRUE;
7175
7176         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7177 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7178         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7179           Store[x][y] = EL_ROCK;
7180 #else
7181         Store[x][y] = EL_ROCK;
7182 #endif
7183
7184         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7185       }
7186       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7187       {
7188         if (!MovDelay[x][y])
7189         {
7190           MovDelay[x][y] = TILEY + 1;
7191
7192           ResetGfxAnimation(x, y);
7193           ResetGfxAnimation(x, y + 1);
7194         }
7195
7196         if (MovDelay[x][y])
7197         {
7198           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7199           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7200
7201           MovDelay[x][y]--;
7202           if (MovDelay[x][y])
7203             return;
7204         }
7205
7206         Feld[x][y] = EL_QUICKSAND_EMPTY;
7207         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7208         Store[x][y + 1] = Store[x][y];
7209         Store[x][y] = 0;
7210
7211         PlayLevelSoundAction(x, y, ACTION_FILLING);
7212       }
7213       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7214       {
7215         if (!MovDelay[x][y])
7216         {
7217           MovDelay[x][y] = TILEY + 1;
7218
7219           ResetGfxAnimation(x, y);
7220           ResetGfxAnimation(x, y + 1);
7221         }
7222
7223         if (MovDelay[x][y])
7224         {
7225           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7226           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7227
7228           MovDelay[x][y]--;
7229           if (MovDelay[x][y])
7230             return;
7231         }
7232
7233         Feld[x][y] = EL_QUICKSAND_EMPTY;
7234         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7235         Store[x][y + 1] = Store[x][y];
7236         Store[x][y] = 0;
7237
7238         PlayLevelSoundAction(x, y, ACTION_FILLING);
7239       }
7240     }
7241     else if (element == EL_QUICKSAND_FAST_FULL)
7242     {
7243       if (IS_FREE(x, y + 1))
7244       {
7245         InitMovingField(x, y, MV_DOWN);
7246         started_moving = TRUE;
7247
7248         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7249 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7250         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7251           Store[x][y] = EL_ROCK;
7252 #else
7253         Store[x][y] = EL_ROCK;
7254 #endif
7255
7256         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7257       }
7258       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7259       {
7260         if (!MovDelay[x][y])
7261         {
7262           MovDelay[x][y] = TILEY + 1;
7263
7264           ResetGfxAnimation(x, y);
7265           ResetGfxAnimation(x, y + 1);
7266         }
7267
7268         if (MovDelay[x][y])
7269         {
7270           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7271           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7272
7273           MovDelay[x][y]--;
7274           if (MovDelay[x][y])
7275             return;
7276         }
7277
7278         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7279         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7280         Store[x][y + 1] = Store[x][y];
7281         Store[x][y] = 0;
7282
7283         PlayLevelSoundAction(x, y, ACTION_FILLING);
7284       }
7285       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7286       {
7287         if (!MovDelay[x][y])
7288         {
7289           MovDelay[x][y] = TILEY + 1;
7290
7291           ResetGfxAnimation(x, y);
7292           ResetGfxAnimation(x, y + 1);
7293         }
7294
7295         if (MovDelay[x][y])
7296         {
7297           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7298           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7299
7300           MovDelay[x][y]--;
7301           if (MovDelay[x][y])
7302             return;
7303         }
7304
7305         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7306         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7307         Store[x][y + 1] = Store[x][y];
7308         Store[x][y] = 0;
7309
7310         PlayLevelSoundAction(x, y, ACTION_FILLING);
7311       }
7312     }
7313     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7314              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7315     {
7316       InitMovingField(x, y, MV_DOWN);
7317       started_moving = TRUE;
7318
7319       Feld[x][y] = EL_QUICKSAND_FILLING;
7320       Store[x][y] = element;
7321
7322       PlayLevelSoundAction(x, y, ACTION_FILLING);
7323     }
7324     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7325              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7326     {
7327       InitMovingField(x, y, MV_DOWN);
7328       started_moving = TRUE;
7329
7330       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7331       Store[x][y] = element;
7332
7333       PlayLevelSoundAction(x, y, ACTION_FILLING);
7334     }
7335     else if (element == EL_MAGIC_WALL_FULL)
7336     {
7337       if (IS_FREE(x, y + 1))
7338       {
7339         InitMovingField(x, y, MV_DOWN);
7340         started_moving = TRUE;
7341
7342         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7343         Store[x][y] = EL_CHANGED(Store[x][y]);
7344       }
7345       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7346       {
7347         if (!MovDelay[x][y])
7348           MovDelay[x][y] = TILEY / 4 + 1;
7349
7350         if (MovDelay[x][y])
7351         {
7352           MovDelay[x][y]--;
7353           if (MovDelay[x][y])
7354             return;
7355         }
7356
7357         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7358         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7359         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7360         Store[x][y] = 0;
7361       }
7362     }
7363     else if (element == EL_BD_MAGIC_WALL_FULL)
7364     {
7365       if (IS_FREE(x, y + 1))
7366       {
7367         InitMovingField(x, y, MV_DOWN);
7368         started_moving = TRUE;
7369
7370         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7371         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7372       }
7373       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7374       {
7375         if (!MovDelay[x][y])
7376           MovDelay[x][y] = TILEY / 4 + 1;
7377
7378         if (MovDelay[x][y])
7379         {
7380           MovDelay[x][y]--;
7381           if (MovDelay[x][y])
7382             return;
7383         }
7384
7385         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7386         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7387         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7388         Store[x][y] = 0;
7389       }
7390     }
7391     else if (element == EL_DC_MAGIC_WALL_FULL)
7392     {
7393       if (IS_FREE(x, y + 1))
7394       {
7395         InitMovingField(x, y, MV_DOWN);
7396         started_moving = TRUE;
7397
7398         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7399         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7400       }
7401       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7402       {
7403         if (!MovDelay[x][y])
7404           MovDelay[x][y] = TILEY / 4 + 1;
7405
7406         if (MovDelay[x][y])
7407         {
7408           MovDelay[x][y]--;
7409           if (MovDelay[x][y])
7410             return;
7411         }
7412
7413         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7414         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7415         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7416         Store[x][y] = 0;
7417       }
7418     }
7419     else if ((CAN_PASS_MAGIC_WALL(element) &&
7420               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7421                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7422              (CAN_PASS_DC_MAGIC_WALL(element) &&
7423               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7424
7425     {
7426       InitMovingField(x, y, MV_DOWN);
7427       started_moving = TRUE;
7428
7429       Feld[x][y] =
7430         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7431          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7432          EL_DC_MAGIC_WALL_FILLING);
7433       Store[x][y] = element;
7434     }
7435     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7436     {
7437       SplashAcid(x, y + 1);
7438
7439       InitMovingField(x, y, MV_DOWN);
7440       started_moving = TRUE;
7441
7442       Store[x][y] = EL_ACID;
7443     }
7444     else if (
7445              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7446               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7447              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7448               CAN_FALL(element) && WasJustFalling[x][y] &&
7449               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7450
7451              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7452               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7453               (Feld[x][y + 1] == EL_BLOCKED)))
7454     {
7455       /* this is needed for a special case not covered by calling "Impact()"
7456          from "ContinueMoving()": if an element moves to a tile directly below
7457          another element which was just falling on that tile (which was empty
7458          in the previous frame), the falling element above would just stop
7459          instead of smashing the element below (in previous version, the above
7460          element was just checked for "moving" instead of "falling", resulting
7461          in incorrect smashes caused by horizontal movement of the above
7462          element; also, the case of the player being the element to smash was
7463          simply not covered here... :-/ ) */
7464
7465       CheckCollision[x][y] = 0;
7466       CheckImpact[x][y] = 0;
7467
7468       Impact(x, y);
7469     }
7470     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7471     {
7472       if (MovDir[x][y] == MV_NONE)
7473       {
7474         InitMovingField(x, y, MV_DOWN);
7475         started_moving = TRUE;
7476       }
7477     }
7478     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7479     {
7480       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7481         MovDir[x][y] = MV_DOWN;
7482
7483       InitMovingField(x, y, MV_DOWN);
7484       started_moving = TRUE;
7485     }
7486     else if (element == EL_AMOEBA_DROP)
7487     {
7488       Feld[x][y] = EL_AMOEBA_GROWING;
7489       Store[x][y] = EL_AMOEBA_WET;
7490     }
7491     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7492               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7493              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7494              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7495     {
7496       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7497                                 (IS_FREE(x - 1, y + 1) ||
7498                                  Feld[x - 1][y + 1] == EL_ACID));
7499       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7500                                 (IS_FREE(x + 1, y + 1) ||
7501                                  Feld[x + 1][y + 1] == EL_ACID));
7502       boolean can_fall_any  = (can_fall_left || can_fall_right);
7503       boolean can_fall_both = (can_fall_left && can_fall_right);
7504       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7505
7506       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7507       {
7508         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7509           can_fall_right = FALSE;
7510         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7511           can_fall_left = FALSE;
7512         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7513           can_fall_right = FALSE;
7514         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7515           can_fall_left = FALSE;
7516
7517         can_fall_any  = (can_fall_left || can_fall_right);
7518         can_fall_both = FALSE;
7519       }
7520
7521       if (can_fall_both)
7522       {
7523         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7524           can_fall_right = FALSE;       /* slip down on left side */
7525         else
7526           can_fall_left = !(can_fall_right = RND(2));
7527
7528         can_fall_both = FALSE;
7529       }
7530
7531       if (can_fall_any)
7532       {
7533         /* if not determined otherwise, prefer left side for slipping down */
7534         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7535         started_moving = TRUE;
7536       }
7537     }
7538     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7539     {
7540       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7541       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7542       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7543       int belt_dir = game.belt_dir[belt_nr];
7544
7545       if ((belt_dir == MV_LEFT  && left_is_free) ||
7546           (belt_dir == MV_RIGHT && right_is_free))
7547       {
7548         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7549
7550         InitMovingField(x, y, belt_dir);
7551         started_moving = TRUE;
7552
7553         Pushed[x][y] = TRUE;
7554         Pushed[nextx][y] = TRUE;
7555
7556         GfxAction[x][y] = ACTION_DEFAULT;
7557       }
7558       else
7559       {
7560         MovDir[x][y] = 0;       /* if element was moving, stop it */
7561       }
7562     }
7563   }
7564
7565   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7566   if (CAN_MOVE(element) && !started_moving)
7567   {
7568     int move_pattern = element_info[element].move_pattern;
7569     int newx, newy;
7570
7571     Moving2Blocked(x, y, &newx, &newy);
7572
7573     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7574       return;
7575
7576     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7577         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7578     {
7579       WasJustMoving[x][y] = 0;
7580       CheckCollision[x][y] = 0;
7581
7582       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7583
7584       if (Feld[x][y] != element)        /* element has changed */
7585         return;
7586     }
7587
7588     if (!MovDelay[x][y])        /* start new movement phase */
7589     {
7590       /* all objects that can change their move direction after each step
7591          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7592
7593       if (element != EL_YAMYAM &&
7594           element != EL_DARK_YAMYAM &&
7595           element != EL_PACMAN &&
7596           !(move_pattern & MV_ANY_DIRECTION) &&
7597           move_pattern != MV_TURNING_LEFT &&
7598           move_pattern != MV_TURNING_RIGHT &&
7599           move_pattern != MV_TURNING_LEFT_RIGHT &&
7600           move_pattern != MV_TURNING_RIGHT_LEFT &&
7601           move_pattern != MV_TURNING_RANDOM)
7602       {
7603         TurnRound(x, y);
7604
7605         if (MovDelay[x][y] && (element == EL_BUG ||
7606                                element == EL_SPACESHIP ||
7607                                element == EL_SP_SNIKSNAK ||
7608                                element == EL_SP_ELECTRON ||
7609                                element == EL_MOLE))
7610           TEST_DrawLevelField(x, y);
7611       }
7612     }
7613
7614     if (MovDelay[x][y])         /* wait some time before next movement */
7615     {
7616       MovDelay[x][y]--;
7617
7618       if (element == EL_ROBOT ||
7619           element == EL_YAMYAM ||
7620           element == EL_DARK_YAMYAM)
7621       {
7622         DrawLevelElementAnimationIfNeeded(x, y, element);
7623         PlayLevelSoundAction(x, y, ACTION_WAITING);
7624       }
7625       else if (element == EL_SP_ELECTRON)
7626         DrawLevelElementAnimationIfNeeded(x, y, element);
7627       else if (element == EL_DRAGON)
7628       {
7629         int i;
7630         int dir = MovDir[x][y];
7631         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7632         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7633         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7634                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7635                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7636                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7637         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7638
7639         GfxAction[x][y] = ACTION_ATTACKING;
7640
7641         if (IS_PLAYER(x, y))
7642           DrawPlayerField(x, y);
7643         else
7644           TEST_DrawLevelField(x, y);
7645
7646         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7647
7648         for (i = 1; i <= 3; i++)
7649         {
7650           int xx = x + i * dx;
7651           int yy = y + i * dy;
7652           int sx = SCREENX(xx);
7653           int sy = SCREENY(yy);
7654           int flame_graphic = graphic + (i - 1);
7655
7656           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7657             break;
7658
7659           if (MovDelay[x][y])
7660           {
7661             int flamed = MovingOrBlocked2Element(xx, yy);
7662
7663             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7664               Bang(xx, yy);
7665             else
7666               RemoveMovingField(xx, yy);
7667
7668             ChangeDelay[xx][yy] = 0;
7669
7670             Feld[xx][yy] = EL_FLAMES;
7671
7672             if (IN_SCR_FIELD(sx, sy))
7673             {
7674               TEST_DrawLevelFieldCrumbled(xx, yy);
7675               DrawGraphic(sx, sy, flame_graphic, frame);
7676             }
7677           }
7678           else
7679           {
7680             if (Feld[xx][yy] == EL_FLAMES)
7681               Feld[xx][yy] = EL_EMPTY;
7682             TEST_DrawLevelField(xx, yy);
7683           }
7684         }
7685       }
7686
7687       if (MovDelay[x][y])       /* element still has to wait some time */
7688       {
7689         PlayLevelSoundAction(x, y, ACTION_WAITING);
7690
7691         return;
7692       }
7693     }
7694
7695     /* now make next step */
7696
7697     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7698
7699     if (DONT_COLLIDE_WITH(element) &&
7700         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7701         !PLAYER_ENEMY_PROTECTED(newx, newy))
7702     {
7703       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7704
7705       return;
7706     }
7707
7708     else if (CAN_MOVE_INTO_ACID(element) &&
7709              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7710              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7711              (MovDir[x][y] == MV_DOWN ||
7712               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7713     {
7714       SplashAcid(newx, newy);
7715       Store[x][y] = EL_ACID;
7716     }
7717     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7718     {
7719       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7720           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7721           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7722           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7723       {
7724         RemoveField(x, y);
7725         TEST_DrawLevelField(x, y);
7726
7727         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7728         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7729           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7730
7731         local_player->friends_still_needed--;
7732         if (!local_player->friends_still_needed &&
7733             !local_player->GameOver && AllPlayersGone)
7734           PlayerWins(local_player);
7735
7736         return;
7737       }
7738       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7739       {
7740         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7741           TEST_DrawLevelField(newx, newy);
7742         else
7743           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7744       }
7745       else if (!IS_FREE(newx, newy))
7746       {
7747         GfxAction[x][y] = ACTION_WAITING;
7748
7749         if (IS_PLAYER(x, y))
7750           DrawPlayerField(x, y);
7751         else
7752           TEST_DrawLevelField(x, y);
7753
7754         return;
7755       }
7756     }
7757     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7758     {
7759       if (IS_FOOD_PIG(Feld[newx][newy]))
7760       {
7761         if (IS_MOVING(newx, newy))
7762           RemoveMovingField(newx, newy);
7763         else
7764         {
7765           Feld[newx][newy] = EL_EMPTY;
7766           TEST_DrawLevelField(newx, newy);
7767         }
7768
7769         PlayLevelSound(x, y, SND_PIG_DIGGING);
7770       }
7771       else if (!IS_FREE(newx, newy))
7772       {
7773         if (IS_PLAYER(x, y))
7774           DrawPlayerField(x, y);
7775         else
7776           TEST_DrawLevelField(x, y);
7777
7778         return;
7779       }
7780     }
7781     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7782     {
7783       if (Store[x][y] != EL_EMPTY)
7784       {
7785         boolean can_clone = FALSE;
7786         int xx, yy;
7787
7788         /* check if element to clone is still there */
7789         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7790         {
7791           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7792           {
7793             can_clone = TRUE;
7794
7795             break;
7796           }
7797         }
7798
7799         /* cannot clone or target field not free anymore -- do not clone */
7800         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7801           Store[x][y] = EL_EMPTY;
7802       }
7803
7804       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7805       {
7806         if (IS_MV_DIAGONAL(MovDir[x][y]))
7807         {
7808           int diagonal_move_dir = MovDir[x][y];
7809           int stored = Store[x][y];
7810           int change_delay = 8;
7811           int graphic;
7812
7813           /* android is moving diagonally */
7814
7815           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7816
7817           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7818           GfxElement[x][y] = EL_EMC_ANDROID;
7819           GfxAction[x][y] = ACTION_SHRINKING;
7820           GfxDir[x][y] = diagonal_move_dir;
7821           ChangeDelay[x][y] = change_delay;
7822
7823           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7824                                    GfxDir[x][y]);
7825
7826           DrawLevelGraphicAnimation(x, y, graphic);
7827           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7828
7829           if (Feld[newx][newy] == EL_ACID)
7830           {
7831             SplashAcid(newx, newy);
7832
7833             return;
7834           }
7835
7836           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7837
7838           Store[newx][newy] = EL_EMC_ANDROID;
7839           GfxElement[newx][newy] = EL_EMC_ANDROID;
7840           GfxAction[newx][newy] = ACTION_GROWING;
7841           GfxDir[newx][newy] = diagonal_move_dir;
7842           ChangeDelay[newx][newy] = change_delay;
7843
7844           graphic = el_act_dir2img(GfxElement[newx][newy],
7845                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7846
7847           DrawLevelGraphicAnimation(newx, newy, graphic);
7848           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7849
7850           return;
7851         }
7852         else
7853         {
7854           Feld[newx][newy] = EL_EMPTY;
7855           TEST_DrawLevelField(newx, newy);
7856
7857           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7858         }
7859       }
7860       else if (!IS_FREE(newx, newy))
7861       {
7862         return;
7863       }
7864     }
7865     else if (IS_CUSTOM_ELEMENT(element) &&
7866              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7867     {
7868       if (!DigFieldByCE(newx, newy, element))
7869         return;
7870
7871       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7872       {
7873         RunnerVisit[x][y] = FrameCounter;
7874         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7875       }
7876     }
7877     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7878     {
7879       if (!IS_FREE(newx, newy))
7880       {
7881         if (IS_PLAYER(x, y))
7882           DrawPlayerField(x, y);
7883         else
7884           TEST_DrawLevelField(x, y);
7885
7886         return;
7887       }
7888       else
7889       {
7890         boolean wanna_flame = !RND(10);
7891         int dx = newx - x, dy = newy - y;
7892         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7893         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7894         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7895                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7896         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7897                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7898
7899         if ((wanna_flame ||
7900              IS_CLASSIC_ENEMY(element1) ||
7901              IS_CLASSIC_ENEMY(element2)) &&
7902             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7903             element1 != EL_FLAMES && element2 != EL_FLAMES)
7904         {
7905           ResetGfxAnimation(x, y);
7906           GfxAction[x][y] = ACTION_ATTACKING;
7907
7908           if (IS_PLAYER(x, y))
7909             DrawPlayerField(x, y);
7910           else
7911             TEST_DrawLevelField(x, y);
7912
7913           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7914
7915           MovDelay[x][y] = 50;
7916
7917           Feld[newx][newy] = EL_FLAMES;
7918           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7919             Feld[newx1][newy1] = EL_FLAMES;
7920           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7921             Feld[newx2][newy2] = EL_FLAMES;
7922
7923           return;
7924         }
7925       }
7926     }
7927     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7928              Feld[newx][newy] == EL_DIAMOND)
7929     {
7930       if (IS_MOVING(newx, newy))
7931         RemoveMovingField(newx, newy);
7932       else
7933       {
7934         Feld[newx][newy] = EL_EMPTY;
7935         TEST_DrawLevelField(newx, newy);
7936       }
7937
7938       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7939     }
7940     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7941              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7942     {
7943       if (AmoebaNr[newx][newy])
7944       {
7945         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7946         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7947             Feld[newx][newy] == EL_BD_AMOEBA)
7948           AmoebaCnt[AmoebaNr[newx][newy]]--;
7949       }
7950
7951       if (IS_MOVING(newx, newy))
7952       {
7953         RemoveMovingField(newx, newy);
7954       }
7955       else
7956       {
7957         Feld[newx][newy] = EL_EMPTY;
7958         TEST_DrawLevelField(newx, newy);
7959       }
7960
7961       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7962     }
7963     else if ((element == EL_PACMAN || element == EL_MOLE)
7964              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(Feld[newx][newy]))
7965     {
7966       if (AmoebaNr[newx][newy])
7967       {
7968         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7969         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7970             Feld[newx][newy] == EL_BD_AMOEBA)
7971           AmoebaCnt[AmoebaNr[newx][newy]]--;
7972       }
7973
7974       if (element == EL_MOLE)
7975       {
7976         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7977         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7978
7979         ResetGfxAnimation(x, y);
7980         GfxAction[x][y] = ACTION_DIGGING;
7981         TEST_DrawLevelField(x, y);
7982
7983         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7984
7985         return;                         /* wait for shrinking amoeba */
7986       }
7987       else      /* element == EL_PACMAN */
7988       {
7989         Feld[newx][newy] = EL_EMPTY;
7990         TEST_DrawLevelField(newx, newy);
7991         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7992       }
7993     }
7994     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7995              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7996               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7997     {
7998       /* wait for shrinking amoeba to completely disappear */
7999       return;
8000     }
8001     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
8002     {
8003       /* object was running against a wall */
8004
8005       TurnRound(x, y);
8006
8007       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
8008         DrawLevelElementAnimation(x, y, element);
8009
8010       if (DONT_TOUCH(element))
8011         TestIfBadThingTouchesPlayer(x, y);
8012
8013       return;
8014     }
8015
8016     InitMovingField(x, y, MovDir[x][y]);
8017
8018     PlayLevelSoundAction(x, y, ACTION_MOVING);
8019   }
8020
8021   if (MovDir[x][y])
8022     ContinueMoving(x, y);
8023 }
8024
8025 void ContinueMoving(int x, int y)
8026 {
8027   int element = Feld[x][y];
8028   struct ElementInfo *ei = &element_info[element];
8029   int direction = MovDir[x][y];
8030   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8031   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8032   int newx = x + dx, newy = y + dy;
8033   int stored = Store[x][y];
8034   int stored_new = Store[newx][newy];
8035   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8036   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8037   boolean last_line = (newy == lev_fieldy - 1);
8038
8039   MovPos[x][y] += getElementMoveStepsize(x, y);
8040
8041   if (pushed_by_player) /* special case: moving object pushed by player */
8042     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8043
8044   if (ABS(MovPos[x][y]) < TILEX)
8045   {
8046     TEST_DrawLevelField(x, y);
8047
8048     return;     /* element is still moving */
8049   }
8050
8051   /* element reached destination field */
8052
8053   Feld[x][y] = EL_EMPTY;
8054   Feld[newx][newy] = element;
8055   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8056
8057   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8058   {
8059     element = Feld[newx][newy] = EL_ACID;
8060   }
8061   else if (element == EL_MOLE)
8062   {
8063     Feld[x][y] = EL_SAND;
8064
8065     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8066   }
8067   else if (element == EL_QUICKSAND_FILLING)
8068   {
8069     element = Feld[newx][newy] = get_next_element(element);
8070     Store[newx][newy] = Store[x][y];
8071   }
8072   else if (element == EL_QUICKSAND_EMPTYING)
8073   {
8074     Feld[x][y] = get_next_element(element);
8075     element = Feld[newx][newy] = Store[x][y];
8076   }
8077   else if (element == EL_QUICKSAND_FAST_FILLING)
8078   {
8079     element = Feld[newx][newy] = get_next_element(element);
8080     Store[newx][newy] = Store[x][y];
8081   }
8082   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8083   {
8084     Feld[x][y] = get_next_element(element);
8085     element = Feld[newx][newy] = Store[x][y];
8086   }
8087   else if (element == EL_MAGIC_WALL_FILLING)
8088   {
8089     element = Feld[newx][newy] = get_next_element(element);
8090     if (!game.magic_wall_active)
8091       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8092     Store[newx][newy] = Store[x][y];
8093   }
8094   else if (element == EL_MAGIC_WALL_EMPTYING)
8095   {
8096     Feld[x][y] = get_next_element(element);
8097     if (!game.magic_wall_active)
8098       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8099     element = Feld[newx][newy] = Store[x][y];
8100
8101     InitField(newx, newy, FALSE);
8102   }
8103   else if (element == EL_BD_MAGIC_WALL_FILLING)
8104   {
8105     element = Feld[newx][newy] = get_next_element(element);
8106     if (!game.magic_wall_active)
8107       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8108     Store[newx][newy] = Store[x][y];
8109   }
8110   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8111   {
8112     Feld[x][y] = get_next_element(element);
8113     if (!game.magic_wall_active)
8114       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8115     element = Feld[newx][newy] = Store[x][y];
8116
8117     InitField(newx, newy, FALSE);
8118   }
8119   else if (element == EL_DC_MAGIC_WALL_FILLING)
8120   {
8121     element = Feld[newx][newy] = get_next_element(element);
8122     if (!game.magic_wall_active)
8123       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8124     Store[newx][newy] = Store[x][y];
8125   }
8126   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8127   {
8128     Feld[x][y] = get_next_element(element);
8129     if (!game.magic_wall_active)
8130       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8131     element = Feld[newx][newy] = Store[x][y];
8132
8133     InitField(newx, newy, FALSE);
8134   }
8135   else if (element == EL_AMOEBA_DROPPING)
8136   {
8137     Feld[x][y] = get_next_element(element);
8138     element = Feld[newx][newy] = Store[x][y];
8139   }
8140   else if (element == EL_SOKOBAN_OBJECT)
8141   {
8142     if (Back[x][y])
8143       Feld[x][y] = Back[x][y];
8144
8145     if (Back[newx][newy])
8146       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8147
8148     Back[x][y] = Back[newx][newy] = 0;
8149   }
8150
8151   Store[x][y] = EL_EMPTY;
8152   MovPos[x][y] = 0;
8153   MovDir[x][y] = 0;
8154   MovDelay[x][y] = 0;
8155
8156   MovDelay[newx][newy] = 0;
8157
8158   if (CAN_CHANGE_OR_HAS_ACTION(element))
8159   {
8160     /* copy element change control values to new field */
8161     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8162     ChangePage[newx][newy]  = ChangePage[x][y];
8163     ChangeCount[newx][newy] = ChangeCount[x][y];
8164     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8165   }
8166
8167   CustomValue[newx][newy] = CustomValue[x][y];
8168
8169   ChangeDelay[x][y] = 0;
8170   ChangePage[x][y] = -1;
8171   ChangeCount[x][y] = 0;
8172   ChangeEvent[x][y] = -1;
8173
8174   CustomValue[x][y] = 0;
8175
8176   /* copy animation control values to new field */
8177   GfxFrame[newx][newy]  = GfxFrame[x][y];
8178   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8179   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8180   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8181
8182   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8183
8184   /* some elements can leave other elements behind after moving */
8185   if (ei->move_leave_element != EL_EMPTY &&
8186       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8187       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8188   {
8189     int move_leave_element = ei->move_leave_element;
8190
8191     /* this makes it possible to leave the removed element again */
8192     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8193       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8194
8195     Feld[x][y] = move_leave_element;
8196
8197     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8198       MovDir[x][y] = direction;
8199
8200     InitField(x, y, FALSE);
8201
8202     if (GFX_CRUMBLED(Feld[x][y]))
8203       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8204
8205     if (ELEM_IS_PLAYER(move_leave_element))
8206       RelocatePlayer(x, y, move_leave_element);
8207   }
8208
8209   /* do this after checking for left-behind element */
8210   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8211
8212   if (!CAN_MOVE(element) ||
8213       (CAN_FALL(element) && direction == MV_DOWN &&
8214        (element == EL_SPRING ||
8215         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8216         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8217     GfxDir[x][y] = MovDir[newx][newy] = 0;
8218
8219   TEST_DrawLevelField(x, y);
8220   TEST_DrawLevelField(newx, newy);
8221
8222   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8223
8224   /* prevent pushed element from moving on in pushed direction */
8225   if (pushed_by_player && CAN_MOVE(element) &&
8226       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8227       !(element_info[element].move_pattern & direction))
8228     TurnRound(newx, newy);
8229
8230   /* prevent elements on conveyor belt from moving on in last direction */
8231   if (pushed_by_conveyor && CAN_FALL(element) &&
8232       direction & MV_HORIZONTAL)
8233     MovDir[newx][newy] = 0;
8234
8235   if (!pushed_by_player)
8236   {
8237     int nextx = newx + dx, nexty = newy + dy;
8238     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8239
8240     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8241
8242     if (CAN_FALL(element) && direction == MV_DOWN)
8243       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8244
8245     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8246       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8247
8248     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8249       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8250   }
8251
8252   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8253   {
8254     TestIfBadThingTouchesPlayer(newx, newy);
8255     TestIfBadThingTouchesFriend(newx, newy);
8256
8257     if (!IS_CUSTOM_ELEMENT(element))
8258       TestIfBadThingTouchesOtherBadThing(newx, newy);
8259   }
8260   else if (element == EL_PENGUIN)
8261     TestIfFriendTouchesBadThing(newx, newy);
8262
8263   if (DONT_GET_HIT_BY(element))
8264   {
8265     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8266   }
8267
8268   /* give the player one last chance (one more frame) to move away */
8269   if (CAN_FALL(element) && direction == MV_DOWN &&
8270       (last_line || (!IS_FREE(x, newy + 1) &&
8271                      (!IS_PLAYER(x, newy + 1) ||
8272                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8273     Impact(x, newy);
8274
8275   if (pushed_by_player && !game.use_change_when_pushing_bug)
8276   {
8277     int push_side = MV_DIR_OPPOSITE(direction);
8278     struct PlayerInfo *player = PLAYERINFO(x, y);
8279
8280     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8281                                player->index_bit, push_side);
8282     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8283                                         player->index_bit, push_side);
8284   }
8285
8286   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8287     MovDelay[newx][newy] = 1;
8288
8289   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8290
8291   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8292   TestIfElementHitsCustomElement(newx, newy, direction);
8293   TestIfPlayerTouchesCustomElement(newx, newy);
8294   TestIfElementTouchesCustomElement(newx, newy);
8295
8296   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8297       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8298     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8299                              MV_DIR_OPPOSITE(direction));
8300 }
8301
8302 int AmoebeNachbarNr(int ax, int ay)
8303 {
8304   int i;
8305   int element = Feld[ax][ay];
8306   int group_nr = 0;
8307   static int xy[4][2] =
8308   {
8309     { 0, -1 },
8310     { -1, 0 },
8311     { +1, 0 },
8312     { 0, +1 }
8313   };
8314
8315   for (i = 0; i < NUM_DIRECTIONS; i++)
8316   {
8317     int x = ax + xy[i][0];
8318     int y = ay + xy[i][1];
8319
8320     if (!IN_LEV_FIELD(x, y))
8321       continue;
8322
8323     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8324       group_nr = AmoebaNr[x][y];
8325   }
8326
8327   return group_nr;
8328 }
8329
8330 void AmoebenVereinigen(int ax, int ay)
8331 {
8332   int i, x, y, xx, yy;
8333   int new_group_nr = AmoebaNr[ax][ay];
8334   static int xy[4][2] =
8335   {
8336     { 0, -1 },
8337     { -1, 0 },
8338     { +1, 0 },
8339     { 0, +1 }
8340   };
8341
8342   if (new_group_nr == 0)
8343     return;
8344
8345   for (i = 0; i < NUM_DIRECTIONS; i++)
8346   {
8347     x = ax + xy[i][0];
8348     y = ay + xy[i][1];
8349
8350     if (!IN_LEV_FIELD(x, y))
8351       continue;
8352
8353     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8354          Feld[x][y] == EL_BD_AMOEBA ||
8355          Feld[x][y] == EL_AMOEBA_DEAD) &&
8356         AmoebaNr[x][y] != new_group_nr)
8357     {
8358       int old_group_nr = AmoebaNr[x][y];
8359
8360       if (old_group_nr == 0)
8361         return;
8362
8363       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8364       AmoebaCnt[old_group_nr] = 0;
8365       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8366       AmoebaCnt2[old_group_nr] = 0;
8367
8368       SCAN_PLAYFIELD(xx, yy)
8369       {
8370         if (AmoebaNr[xx][yy] == old_group_nr)
8371           AmoebaNr[xx][yy] = new_group_nr;
8372       }
8373     }
8374   }
8375 }
8376
8377 void AmoebeUmwandeln(int ax, int ay)
8378 {
8379   int i, x, y;
8380
8381   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8382   {
8383     int group_nr = AmoebaNr[ax][ay];
8384
8385 #ifdef DEBUG
8386     if (group_nr == 0)
8387     {
8388       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8389       printf("AmoebeUmwandeln(): This should never happen!\n");
8390       return;
8391     }
8392 #endif
8393
8394     SCAN_PLAYFIELD(x, y)
8395     {
8396       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8397       {
8398         AmoebaNr[x][y] = 0;
8399         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8400       }
8401     }
8402
8403     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8404                             SND_AMOEBA_TURNING_TO_GEM :
8405                             SND_AMOEBA_TURNING_TO_ROCK));
8406     Bang(ax, ay);
8407   }
8408   else
8409   {
8410     static int xy[4][2] =
8411     {
8412       { 0, -1 },
8413       { -1, 0 },
8414       { +1, 0 },
8415       { 0, +1 }
8416     };
8417
8418     for (i = 0; i < NUM_DIRECTIONS; i++)
8419     {
8420       x = ax + xy[i][0];
8421       y = ay + xy[i][1];
8422
8423       if (!IN_LEV_FIELD(x, y))
8424         continue;
8425
8426       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8427       {
8428         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8429                               SND_AMOEBA_TURNING_TO_GEM :
8430                               SND_AMOEBA_TURNING_TO_ROCK));
8431         Bang(x, y);
8432       }
8433     }
8434   }
8435 }
8436
8437 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8438 {
8439   int x, y;
8440   int group_nr = AmoebaNr[ax][ay];
8441   boolean done = FALSE;
8442
8443 #ifdef DEBUG
8444   if (group_nr == 0)
8445   {
8446     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8447     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8448     return;
8449   }
8450 #endif
8451
8452   SCAN_PLAYFIELD(x, y)
8453   {
8454     if (AmoebaNr[x][y] == group_nr &&
8455         (Feld[x][y] == EL_AMOEBA_DEAD ||
8456          Feld[x][y] == EL_BD_AMOEBA ||
8457          Feld[x][y] == EL_AMOEBA_GROWING))
8458     {
8459       AmoebaNr[x][y] = 0;
8460       Feld[x][y] = new_element;
8461       InitField(x, y, FALSE);
8462       TEST_DrawLevelField(x, y);
8463       done = TRUE;
8464     }
8465   }
8466
8467   if (done)
8468     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8469                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8470                             SND_BD_AMOEBA_TURNING_TO_GEM));
8471 }
8472
8473 void AmoebeWaechst(int x, int y)
8474 {
8475   static unsigned int sound_delay = 0;
8476   static unsigned int sound_delay_value = 0;
8477
8478   if (!MovDelay[x][y])          /* start new growing cycle */
8479   {
8480     MovDelay[x][y] = 7;
8481
8482     if (DelayReached(&sound_delay, sound_delay_value))
8483     {
8484       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8485       sound_delay_value = 30;
8486     }
8487   }
8488
8489   if (MovDelay[x][y])           /* wait some time before growing bigger */
8490   {
8491     MovDelay[x][y]--;
8492     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8493     {
8494       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8495                                            6 - MovDelay[x][y]);
8496
8497       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8498     }
8499
8500     if (!MovDelay[x][y])
8501     {
8502       Feld[x][y] = Store[x][y];
8503       Store[x][y] = 0;
8504       TEST_DrawLevelField(x, y);
8505     }
8506   }
8507 }
8508
8509 void AmoebaDisappearing(int x, int y)
8510 {
8511   static unsigned int sound_delay = 0;
8512   static unsigned int sound_delay_value = 0;
8513
8514   if (!MovDelay[x][y])          /* start new shrinking cycle */
8515   {
8516     MovDelay[x][y] = 7;
8517
8518     if (DelayReached(&sound_delay, sound_delay_value))
8519       sound_delay_value = 30;
8520   }
8521
8522   if (MovDelay[x][y])           /* wait some time before shrinking */
8523   {
8524     MovDelay[x][y]--;
8525     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8526     {
8527       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8528                                            6 - MovDelay[x][y]);
8529
8530       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8531     }
8532
8533     if (!MovDelay[x][y])
8534     {
8535       Feld[x][y] = EL_EMPTY;
8536       TEST_DrawLevelField(x, y);
8537
8538       /* don't let mole enter this field in this cycle;
8539          (give priority to objects falling to this field from above) */
8540       Stop[x][y] = TRUE;
8541     }
8542   }
8543 }
8544
8545 void AmoebeAbleger(int ax, int ay)
8546 {
8547   int i;
8548   int element = Feld[ax][ay];
8549   int graphic = el2img(element);
8550   int newax = ax, neway = ay;
8551   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8552   static int xy[4][2] =
8553   {
8554     { 0, -1 },
8555     { -1, 0 },
8556     { +1, 0 },
8557     { 0, +1 }
8558   };
8559
8560   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8561   {
8562     Feld[ax][ay] = EL_AMOEBA_DEAD;
8563     TEST_DrawLevelField(ax, ay);
8564     return;
8565   }
8566
8567   if (IS_ANIMATED(graphic))
8568     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8569
8570   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8571     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8572
8573   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8574   {
8575     MovDelay[ax][ay]--;
8576     if (MovDelay[ax][ay])
8577       return;
8578   }
8579
8580   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8581   {
8582     int start = RND(4);
8583     int x = ax + xy[start][0];
8584     int y = ay + xy[start][1];
8585
8586     if (!IN_LEV_FIELD(x, y))
8587       return;
8588
8589     if (IS_FREE(x, y) ||
8590         CAN_GROW_INTO(Feld[x][y]) ||
8591         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8592         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8593     {
8594       newax = x;
8595       neway = y;
8596     }
8597
8598     if (newax == ax && neway == ay)
8599       return;
8600   }
8601   else                          /* normal or "filled" (BD style) amoeba */
8602   {
8603     int start = RND(4);
8604     boolean waiting_for_player = FALSE;
8605
8606     for (i = 0; i < NUM_DIRECTIONS; i++)
8607     {
8608       int j = (start + i) % 4;
8609       int x = ax + xy[j][0];
8610       int y = ay + xy[j][1];
8611
8612       if (!IN_LEV_FIELD(x, y))
8613         continue;
8614
8615       if (IS_FREE(x, y) ||
8616           CAN_GROW_INTO(Feld[x][y]) ||
8617           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8618           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8619       {
8620         newax = x;
8621         neway = y;
8622         break;
8623       }
8624       else if (IS_PLAYER(x, y))
8625         waiting_for_player = TRUE;
8626     }
8627
8628     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8629     {
8630       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8631       {
8632         Feld[ax][ay] = EL_AMOEBA_DEAD;
8633         TEST_DrawLevelField(ax, ay);
8634         AmoebaCnt[AmoebaNr[ax][ay]]--;
8635
8636         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8637         {
8638           if (element == EL_AMOEBA_FULL)
8639             AmoebeUmwandeln(ax, ay);
8640           else if (element == EL_BD_AMOEBA)
8641             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8642         }
8643       }
8644       return;
8645     }
8646     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8647     {
8648       /* amoeba gets larger by growing in some direction */
8649
8650       int new_group_nr = AmoebaNr[ax][ay];
8651
8652 #ifdef DEBUG
8653   if (new_group_nr == 0)
8654   {
8655     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8656     printf("AmoebeAbleger(): This should never happen!\n");
8657     return;
8658   }
8659 #endif
8660
8661       AmoebaNr[newax][neway] = new_group_nr;
8662       AmoebaCnt[new_group_nr]++;
8663       AmoebaCnt2[new_group_nr]++;
8664
8665       /* if amoeba touches other amoeba(s) after growing, unify them */
8666       AmoebenVereinigen(newax, neway);
8667
8668       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8669       {
8670         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8671         return;
8672       }
8673     }
8674   }
8675
8676   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8677       (neway == lev_fieldy - 1 && newax != ax))
8678   {
8679     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8680     Store[newax][neway] = element;
8681   }
8682   else if (neway == ay || element == EL_EMC_DRIPPER)
8683   {
8684     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8685
8686     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8687   }
8688   else
8689   {
8690     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8691     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8692     Store[ax][ay] = EL_AMOEBA_DROP;
8693     ContinueMoving(ax, ay);
8694     return;
8695   }
8696
8697   TEST_DrawLevelField(newax, neway);
8698 }
8699
8700 void Life(int ax, int ay)
8701 {
8702   int x1, y1, x2, y2;
8703   int life_time = 40;
8704   int element = Feld[ax][ay];
8705   int graphic = el2img(element);
8706   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8707                          level.biomaze);
8708   boolean changed = FALSE;
8709
8710   if (IS_ANIMATED(graphic))
8711     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8712
8713   if (Stop[ax][ay])
8714     return;
8715
8716   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8717     MovDelay[ax][ay] = life_time;
8718
8719   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8720   {
8721     MovDelay[ax][ay]--;
8722     if (MovDelay[ax][ay])
8723       return;
8724   }
8725
8726   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8727   {
8728     int xx = ax+x1, yy = ay+y1;
8729     int nachbarn = 0;
8730
8731     if (!IN_LEV_FIELD(xx, yy))
8732       continue;
8733
8734     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8735     {
8736       int x = xx+x2, y = yy+y2;
8737
8738       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8739         continue;
8740
8741       if (((Feld[x][y] == element ||
8742             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8743            !Stop[x][y]) ||
8744           (IS_FREE(x, y) && Stop[x][y]))
8745         nachbarn++;
8746     }
8747
8748     if (xx == ax && yy == ay)           /* field in the middle */
8749     {
8750       if (nachbarn < life_parameter[0] ||
8751           nachbarn > life_parameter[1])
8752       {
8753         Feld[xx][yy] = EL_EMPTY;
8754         if (!Stop[xx][yy])
8755           TEST_DrawLevelField(xx, yy);
8756         Stop[xx][yy] = TRUE;
8757         changed = TRUE;
8758       }
8759     }
8760     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8761     {                                   /* free border field */
8762       if (nachbarn >= life_parameter[2] &&
8763           nachbarn <= life_parameter[3])
8764       {
8765         Feld[xx][yy] = element;
8766         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8767         if (!Stop[xx][yy])
8768           TEST_DrawLevelField(xx, yy);
8769         Stop[xx][yy] = TRUE;
8770         changed = TRUE;
8771       }
8772     }
8773   }
8774
8775   if (changed)
8776     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8777                    SND_GAME_OF_LIFE_GROWING);
8778 }
8779
8780 static void InitRobotWheel(int x, int y)
8781 {
8782   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8783 }
8784
8785 static void RunRobotWheel(int x, int y)
8786 {
8787   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8788 }
8789
8790 static void StopRobotWheel(int x, int y)
8791 {
8792   if (ZX == x && ZY == y)
8793   {
8794     ZX = ZY = -1;
8795
8796     game.robot_wheel_active = FALSE;
8797   }
8798 }
8799
8800 static void InitTimegateWheel(int x, int y)
8801 {
8802   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8803 }
8804
8805 static void RunTimegateWheel(int x, int y)
8806 {
8807   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8808 }
8809
8810 static void InitMagicBallDelay(int x, int y)
8811 {
8812   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8813 }
8814
8815 static void ActivateMagicBall(int bx, int by)
8816 {
8817   int x, y;
8818
8819   if (level.ball_random)
8820   {
8821     int pos_border = RND(8);    /* select one of the eight border elements */
8822     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8823     int xx = pos_content % 3;
8824     int yy = pos_content / 3;
8825
8826     x = bx - 1 + xx;
8827     y = by - 1 + yy;
8828
8829     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8830       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8831   }
8832   else
8833   {
8834     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8835     {
8836       int xx = x - bx + 1;
8837       int yy = y - by + 1;
8838
8839       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8840         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8841     }
8842   }
8843
8844   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8845 }
8846
8847 void CheckExit(int x, int y)
8848 {
8849   if (local_player->gems_still_needed > 0 ||
8850       local_player->sokobanfields_still_needed > 0 ||
8851       local_player->lights_still_needed > 0)
8852   {
8853     int element = Feld[x][y];
8854     int graphic = el2img(element);
8855
8856     if (IS_ANIMATED(graphic))
8857       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8858
8859     return;
8860   }
8861
8862   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8863     return;
8864
8865   Feld[x][y] = EL_EXIT_OPENING;
8866
8867   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8868 }
8869
8870 void CheckExitEM(int x, int y)
8871 {
8872   if (local_player->gems_still_needed > 0 ||
8873       local_player->sokobanfields_still_needed > 0 ||
8874       local_player->lights_still_needed > 0)
8875   {
8876     int element = Feld[x][y];
8877     int graphic = el2img(element);
8878
8879     if (IS_ANIMATED(graphic))
8880       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8881
8882     return;
8883   }
8884
8885   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8886     return;
8887
8888   Feld[x][y] = EL_EM_EXIT_OPENING;
8889
8890   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8891 }
8892
8893 void CheckExitSteel(int x, int y)
8894 {
8895   if (local_player->gems_still_needed > 0 ||
8896       local_player->sokobanfields_still_needed > 0 ||
8897       local_player->lights_still_needed > 0)
8898   {
8899     int element = Feld[x][y];
8900     int graphic = el2img(element);
8901
8902     if (IS_ANIMATED(graphic))
8903       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8904
8905     return;
8906   }
8907
8908   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8909     return;
8910
8911   Feld[x][y] = EL_STEEL_EXIT_OPENING;
8912
8913   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8914 }
8915
8916 void CheckExitSteelEM(int x, int y)
8917 {
8918   if (local_player->gems_still_needed > 0 ||
8919       local_player->sokobanfields_still_needed > 0 ||
8920       local_player->lights_still_needed > 0)
8921   {
8922     int element = Feld[x][y];
8923     int graphic = el2img(element);
8924
8925     if (IS_ANIMATED(graphic))
8926       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8927
8928     return;
8929   }
8930
8931   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8932     return;
8933
8934   Feld[x][y] = EL_EM_STEEL_EXIT_OPENING;
8935
8936   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8937 }
8938
8939 void CheckExitSP(int x, int y)
8940 {
8941   if (local_player->gems_still_needed > 0)
8942   {
8943     int element = Feld[x][y];
8944     int graphic = el2img(element);
8945
8946     if (IS_ANIMATED(graphic))
8947       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8948
8949     return;
8950   }
8951
8952   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8953     return;
8954
8955   Feld[x][y] = EL_SP_EXIT_OPENING;
8956
8957   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8958 }
8959
8960 static void CloseAllOpenTimegates()
8961 {
8962   int x, y;
8963
8964   SCAN_PLAYFIELD(x, y)
8965   {
8966     int element = Feld[x][y];
8967
8968     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8969     {
8970       Feld[x][y] = EL_TIMEGATE_CLOSING;
8971
8972       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8973     }
8974   }
8975 }
8976
8977 void DrawTwinkleOnField(int x, int y)
8978 {
8979   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8980     return;
8981
8982   if (Feld[x][y] == EL_BD_DIAMOND)
8983     return;
8984
8985   if (MovDelay[x][y] == 0)      /* next animation frame */
8986     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8987
8988   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8989   {
8990     MovDelay[x][y]--;
8991
8992     DrawLevelElementAnimation(x, y, Feld[x][y]);
8993
8994     if (MovDelay[x][y] != 0)
8995     {
8996       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8997                                            10 - MovDelay[x][y]);
8998
8999       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
9000     }
9001   }
9002 }
9003
9004 void MauerWaechst(int x, int y)
9005 {
9006   int delay = 6;
9007
9008   if (!MovDelay[x][y])          /* next animation frame */
9009     MovDelay[x][y] = 3 * delay;
9010
9011   if (MovDelay[x][y])           /* wait some time before next frame */
9012   {
9013     MovDelay[x][y]--;
9014
9015     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
9016     {
9017       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
9018       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
9019
9020       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
9021     }
9022
9023     if (!MovDelay[x][y])
9024     {
9025       if (MovDir[x][y] == MV_LEFT)
9026       {
9027         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9028           TEST_DrawLevelField(x - 1, y);
9029       }
9030       else if (MovDir[x][y] == MV_RIGHT)
9031       {
9032         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9033           TEST_DrawLevelField(x + 1, y);
9034       }
9035       else if (MovDir[x][y] == MV_UP)
9036       {
9037         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9038           TEST_DrawLevelField(x, y - 1);
9039       }
9040       else
9041       {
9042         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9043           TEST_DrawLevelField(x, y + 1);
9044       }
9045
9046       Feld[x][y] = Store[x][y];
9047       Store[x][y] = 0;
9048       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9049       TEST_DrawLevelField(x, y);
9050     }
9051   }
9052 }
9053
9054 void MauerAbleger(int ax, int ay)
9055 {
9056   int element = Feld[ax][ay];
9057   int graphic = el2img(element);
9058   boolean oben_frei = FALSE, unten_frei = FALSE;
9059   boolean links_frei = FALSE, rechts_frei = FALSE;
9060   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9061   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9062   boolean new_wall = FALSE;
9063
9064   if (IS_ANIMATED(graphic))
9065     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9066
9067   if (!MovDelay[ax][ay])        /* start building new wall */
9068     MovDelay[ax][ay] = 6;
9069
9070   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9071   {
9072     MovDelay[ax][ay]--;
9073     if (MovDelay[ax][ay])
9074       return;
9075   }
9076
9077   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9078     oben_frei = TRUE;
9079   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9080     unten_frei = TRUE;
9081   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9082     links_frei = TRUE;
9083   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9084     rechts_frei = TRUE;
9085
9086   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9087       element == EL_EXPANDABLE_WALL_ANY)
9088   {
9089     if (oben_frei)
9090     {
9091       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9092       Store[ax][ay-1] = element;
9093       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9094       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9095         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9096                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9097       new_wall = TRUE;
9098     }
9099     if (unten_frei)
9100     {
9101       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9102       Store[ax][ay+1] = element;
9103       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9104       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9105         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9106                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9107       new_wall = TRUE;
9108     }
9109   }
9110
9111   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9112       element == EL_EXPANDABLE_WALL_ANY ||
9113       element == EL_EXPANDABLE_WALL ||
9114       element == EL_BD_EXPANDABLE_WALL)
9115   {
9116     if (links_frei)
9117     {
9118       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9119       Store[ax-1][ay] = element;
9120       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9121       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9122         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9123                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9124       new_wall = TRUE;
9125     }
9126
9127     if (rechts_frei)
9128     {
9129       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9130       Store[ax+1][ay] = element;
9131       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9132       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9133         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9134                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9135       new_wall = TRUE;
9136     }
9137   }
9138
9139   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9140     TEST_DrawLevelField(ax, ay);
9141
9142   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9143     oben_massiv = TRUE;
9144   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9145     unten_massiv = TRUE;
9146   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9147     links_massiv = TRUE;
9148   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9149     rechts_massiv = TRUE;
9150
9151   if (((oben_massiv && unten_massiv) ||
9152        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9153        element == EL_EXPANDABLE_WALL) &&
9154       ((links_massiv && rechts_massiv) ||
9155        element == EL_EXPANDABLE_WALL_VERTICAL))
9156     Feld[ax][ay] = EL_WALL;
9157
9158   if (new_wall)
9159     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9160 }
9161
9162 void MauerAblegerStahl(int ax, int ay)
9163 {
9164   int element = Feld[ax][ay];
9165   int graphic = el2img(element);
9166   boolean oben_frei = FALSE, unten_frei = FALSE;
9167   boolean links_frei = FALSE, rechts_frei = FALSE;
9168   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9169   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9170   boolean new_wall = FALSE;
9171
9172   if (IS_ANIMATED(graphic))
9173     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9174
9175   if (!MovDelay[ax][ay])        /* start building new wall */
9176     MovDelay[ax][ay] = 6;
9177
9178   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9179   {
9180     MovDelay[ax][ay]--;
9181     if (MovDelay[ax][ay])
9182       return;
9183   }
9184
9185   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9186     oben_frei = TRUE;
9187   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9188     unten_frei = TRUE;
9189   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9190     links_frei = TRUE;
9191   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9192     rechts_frei = TRUE;
9193
9194   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9195       element == EL_EXPANDABLE_STEELWALL_ANY)
9196   {
9197     if (oben_frei)
9198     {
9199       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9200       Store[ax][ay-1] = element;
9201       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9202       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9203         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9204                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9205       new_wall = TRUE;
9206     }
9207     if (unten_frei)
9208     {
9209       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9210       Store[ax][ay+1] = element;
9211       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9212       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9213         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9214                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9215       new_wall = TRUE;
9216     }
9217   }
9218
9219   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9220       element == EL_EXPANDABLE_STEELWALL_ANY)
9221   {
9222     if (links_frei)
9223     {
9224       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9225       Store[ax-1][ay] = element;
9226       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9227       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9228         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9229                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9230       new_wall = TRUE;
9231     }
9232
9233     if (rechts_frei)
9234     {
9235       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9236       Store[ax+1][ay] = element;
9237       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9238       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9239         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9240                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9241       new_wall = TRUE;
9242     }
9243   }
9244
9245   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9246     oben_massiv = TRUE;
9247   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9248     unten_massiv = TRUE;
9249   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9250     links_massiv = TRUE;
9251   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9252     rechts_massiv = TRUE;
9253
9254   if (((oben_massiv && unten_massiv) ||
9255        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9256       ((links_massiv && rechts_massiv) ||
9257        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9258     Feld[ax][ay] = EL_STEELWALL;
9259
9260   if (new_wall)
9261     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9262 }
9263
9264 void CheckForDragon(int x, int y)
9265 {
9266   int i, j;
9267   boolean dragon_found = FALSE;
9268   static int xy[4][2] =
9269   {
9270     { 0, -1 },
9271     { -1, 0 },
9272     { +1, 0 },
9273     { 0, +1 }
9274   };
9275
9276   for (i = 0; i < NUM_DIRECTIONS; i++)
9277   {
9278     for (j = 0; j < 4; j++)
9279     {
9280       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9281
9282       if (IN_LEV_FIELD(xx, yy) &&
9283           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9284       {
9285         if (Feld[xx][yy] == EL_DRAGON)
9286           dragon_found = TRUE;
9287       }
9288       else
9289         break;
9290     }
9291   }
9292
9293   if (!dragon_found)
9294   {
9295     for (i = 0; i < NUM_DIRECTIONS; i++)
9296     {
9297       for (j = 0; j < 3; j++)
9298       {
9299         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9300   
9301         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9302         {
9303           Feld[xx][yy] = EL_EMPTY;
9304           TEST_DrawLevelField(xx, yy);
9305         }
9306         else
9307           break;
9308       }
9309     }
9310   }
9311 }
9312
9313 static void InitBuggyBase(int x, int y)
9314 {
9315   int element = Feld[x][y];
9316   int activating_delay = FRAMES_PER_SECOND / 4;
9317
9318   ChangeDelay[x][y] =
9319     (element == EL_SP_BUGGY_BASE ?
9320      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9321      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9322      activating_delay :
9323      element == EL_SP_BUGGY_BASE_ACTIVE ?
9324      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9325 }
9326
9327 static void WarnBuggyBase(int x, int y)
9328 {
9329   int i;
9330   static int xy[4][2] =
9331   {
9332     { 0, -1 },
9333     { -1, 0 },
9334     { +1, 0 },
9335     { 0, +1 }
9336   };
9337
9338   for (i = 0; i < NUM_DIRECTIONS; i++)
9339   {
9340     int xx = x + xy[i][0];
9341     int yy = y + xy[i][1];
9342
9343     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9344     {
9345       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9346
9347       break;
9348     }
9349   }
9350 }
9351
9352 static void InitTrap(int x, int y)
9353 {
9354   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9355 }
9356
9357 static void ActivateTrap(int x, int y)
9358 {
9359   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9360 }
9361
9362 static void ChangeActiveTrap(int x, int y)
9363 {
9364   int graphic = IMG_TRAP_ACTIVE;
9365
9366   /* if new animation frame was drawn, correct crumbled sand border */
9367   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9368     TEST_DrawLevelFieldCrumbled(x, y);
9369 }
9370
9371 static int getSpecialActionElement(int element, int number, int base_element)
9372 {
9373   return (element != EL_EMPTY ? element :
9374           number != -1 ? base_element + number - 1 :
9375           EL_EMPTY);
9376 }
9377
9378 static int getModifiedActionNumber(int value_old, int operator, int operand,
9379                                    int value_min, int value_max)
9380 {
9381   int value_new = (operator == CA_MODE_SET      ? operand :
9382                    operator == CA_MODE_ADD      ? value_old + operand :
9383                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9384                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9385                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9386                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9387                    value_old);
9388
9389   return (value_new < value_min ? value_min :
9390           value_new > value_max ? value_max :
9391           value_new);
9392 }
9393
9394 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9395 {
9396   struct ElementInfo *ei = &element_info[element];
9397   struct ElementChangeInfo *change = &ei->change_page[page];
9398   int target_element = change->target_element;
9399   int action_type = change->action_type;
9400   int action_mode = change->action_mode;
9401   int action_arg = change->action_arg;
9402   int action_element = change->action_element;
9403   int i;
9404
9405   if (!change->has_action)
9406     return;
9407
9408   /* ---------- determine action paramater values -------------------------- */
9409
9410   int level_time_value =
9411     (level.time > 0 ? TimeLeft :
9412      TimePlayed);
9413
9414   int action_arg_element_raw =
9415     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9416      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9417      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9418      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9419      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9420      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9421      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9422      EL_EMPTY);
9423   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9424
9425   int action_arg_direction =
9426     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9427      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9428      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9429      change->actual_trigger_side :
9430      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9431      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9432      MV_NONE);
9433
9434   int action_arg_number_min =
9435     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9436      CA_ARG_MIN);
9437
9438   int action_arg_number_max =
9439     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9440      action_type == CA_SET_LEVEL_GEMS ? 999 :
9441      action_type == CA_SET_LEVEL_TIME ? 9999 :
9442      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9443      action_type == CA_SET_CE_VALUE ? 9999 :
9444      action_type == CA_SET_CE_SCORE ? 9999 :
9445      CA_ARG_MAX);
9446
9447   int action_arg_number_reset =
9448     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9449      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9450      action_type == CA_SET_LEVEL_TIME ? level.time :
9451      action_type == CA_SET_LEVEL_SCORE ? 0 :
9452      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9453      action_type == CA_SET_CE_SCORE ? 0 :
9454      0);
9455
9456   int action_arg_number =
9457     (action_arg <= CA_ARG_MAX ? action_arg :
9458      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9459      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9460      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9461      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9462      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9463      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9464      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9465      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9466      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9467      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9468      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9469      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9470      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9471      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9472      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9473      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9474      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9475      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9476      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9477      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9478      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9479      -1);
9480
9481   int action_arg_number_old =
9482     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9483      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9484      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9485      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9486      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9487      0);
9488
9489   int action_arg_number_new =
9490     getModifiedActionNumber(action_arg_number_old,
9491                             action_mode, action_arg_number,
9492                             action_arg_number_min, action_arg_number_max);
9493
9494   int trigger_player_bits =
9495     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9496      change->actual_trigger_player_bits : change->trigger_player);
9497
9498   int action_arg_player_bits =
9499     (action_arg >= CA_ARG_PLAYER_1 &&
9500      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9501      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9502      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9503      PLAYER_BITS_ANY);
9504
9505   /* ---------- execute action  -------------------------------------------- */
9506
9507   switch (action_type)
9508   {
9509     case CA_NO_ACTION:
9510     {
9511       return;
9512     }
9513
9514     /* ---------- level actions  ------------------------------------------- */
9515
9516     case CA_RESTART_LEVEL:
9517     {
9518       game.restart_level = TRUE;
9519
9520       break;
9521     }
9522
9523     case CA_SHOW_ENVELOPE:
9524     {
9525       int element = getSpecialActionElement(action_arg_element,
9526                                             action_arg_number, EL_ENVELOPE_1);
9527
9528       if (IS_ENVELOPE(element))
9529         local_player->show_envelope = element;
9530
9531       break;
9532     }
9533
9534     case CA_SET_LEVEL_TIME:
9535     {
9536       if (level.time > 0)       /* only modify limited time value */
9537       {
9538         TimeLeft = action_arg_number_new;
9539
9540         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9541
9542         DisplayGameControlValues();
9543
9544         if (!TimeLeft && setup.time_limit)
9545           for (i = 0; i < MAX_PLAYERS; i++)
9546             KillPlayer(&stored_player[i]);
9547       }
9548
9549       break;
9550     }
9551
9552     case CA_SET_LEVEL_SCORE:
9553     {
9554       local_player->score = action_arg_number_new;
9555
9556       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9557
9558       DisplayGameControlValues();
9559
9560       break;
9561     }
9562
9563     case CA_SET_LEVEL_GEMS:
9564     {
9565       local_player->gems_still_needed = action_arg_number_new;
9566
9567       game_panel_controls[GAME_PANEL_GEMS].value =
9568         local_player->gems_still_needed;
9569
9570       DisplayGameControlValues();
9571
9572       break;
9573     }
9574
9575     case CA_SET_LEVEL_WIND:
9576     {
9577       game.wind_direction = action_arg_direction;
9578
9579       break;
9580     }
9581
9582     case CA_SET_LEVEL_RANDOM_SEED:
9583     {
9584       /* ensure that setting a new random seed while playing is predictable */
9585       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9586
9587       break;
9588     }
9589
9590     /* ---------- player actions  ------------------------------------------ */
9591
9592     case CA_MOVE_PLAYER:
9593     {
9594       /* automatically move to the next field in specified direction */
9595       for (i = 0; i < MAX_PLAYERS; i++)
9596         if (trigger_player_bits & (1 << i))
9597           stored_player[i].programmed_action = action_arg_direction;
9598
9599       break;
9600     }
9601
9602     case CA_EXIT_PLAYER:
9603     {
9604       for (i = 0; i < MAX_PLAYERS; i++)
9605         if (action_arg_player_bits & (1 << i))
9606           PlayerWins(&stored_player[i]);
9607
9608       break;
9609     }
9610
9611     case CA_KILL_PLAYER:
9612     {
9613       for (i = 0; i < MAX_PLAYERS; i++)
9614         if (action_arg_player_bits & (1 << i))
9615           KillPlayer(&stored_player[i]);
9616
9617       break;
9618     }
9619
9620     case CA_SET_PLAYER_KEYS:
9621     {
9622       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9623       int element = getSpecialActionElement(action_arg_element,
9624                                             action_arg_number, EL_KEY_1);
9625
9626       if (IS_KEY(element))
9627       {
9628         for (i = 0; i < MAX_PLAYERS; i++)
9629         {
9630           if (trigger_player_bits & (1 << i))
9631           {
9632             stored_player[i].key[KEY_NR(element)] = key_state;
9633
9634             DrawGameDoorValues();
9635           }
9636         }
9637       }
9638
9639       break;
9640     }
9641
9642     case CA_SET_PLAYER_SPEED:
9643     {
9644       for (i = 0; i < MAX_PLAYERS; i++)
9645       {
9646         if (trigger_player_bits & (1 << i))
9647         {
9648           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9649
9650           if (action_arg == CA_ARG_SPEED_FASTER &&
9651               stored_player[i].cannot_move)
9652           {
9653             action_arg_number = STEPSIZE_VERY_SLOW;
9654           }
9655           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9656                    action_arg == CA_ARG_SPEED_FASTER)
9657           {
9658             action_arg_number = 2;
9659             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9660                            CA_MODE_MULTIPLY);
9661           }
9662           else if (action_arg == CA_ARG_NUMBER_RESET)
9663           {
9664             action_arg_number = level.initial_player_stepsize[i];
9665           }
9666
9667           move_stepsize =
9668             getModifiedActionNumber(move_stepsize,
9669                                     action_mode,
9670                                     action_arg_number,
9671                                     action_arg_number_min,
9672                                     action_arg_number_max);
9673
9674           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9675         }
9676       }
9677
9678       break;
9679     }
9680
9681     case CA_SET_PLAYER_SHIELD:
9682     {
9683       for (i = 0; i < MAX_PLAYERS; i++)
9684       {
9685         if (trigger_player_bits & (1 << i))
9686         {
9687           if (action_arg == CA_ARG_SHIELD_OFF)
9688           {
9689             stored_player[i].shield_normal_time_left = 0;
9690             stored_player[i].shield_deadly_time_left = 0;
9691           }
9692           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9693           {
9694             stored_player[i].shield_normal_time_left = 999999;
9695           }
9696           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9697           {
9698             stored_player[i].shield_normal_time_left = 999999;
9699             stored_player[i].shield_deadly_time_left = 999999;
9700           }
9701         }
9702       }
9703
9704       break;
9705     }
9706
9707     case CA_SET_PLAYER_GRAVITY:
9708     {
9709       for (i = 0; i < MAX_PLAYERS; i++)
9710       {
9711         if (trigger_player_bits & (1 << i))
9712         {
9713           stored_player[i].gravity =
9714             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9715              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9716              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9717              stored_player[i].gravity);
9718         }
9719       }
9720
9721       break;
9722     }
9723
9724     case CA_SET_PLAYER_ARTWORK:
9725     {
9726       for (i = 0; i < MAX_PLAYERS; i++)
9727       {
9728         if (trigger_player_bits & (1 << i))
9729         {
9730           int artwork_element = action_arg_element;
9731
9732           if (action_arg == CA_ARG_ELEMENT_RESET)
9733             artwork_element =
9734               (level.use_artwork_element[i] ? level.artwork_element[i] :
9735                stored_player[i].element_nr);
9736
9737           if (stored_player[i].artwork_element != artwork_element)
9738             stored_player[i].Frame = 0;
9739
9740           stored_player[i].artwork_element = artwork_element;
9741
9742           SetPlayerWaiting(&stored_player[i], FALSE);
9743
9744           /* set number of special actions for bored and sleeping animation */
9745           stored_player[i].num_special_action_bored =
9746             get_num_special_action(artwork_element,
9747                                    ACTION_BORING_1, ACTION_BORING_LAST);
9748           stored_player[i].num_special_action_sleeping =
9749             get_num_special_action(artwork_element,
9750                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9751         }
9752       }
9753
9754       break;
9755     }
9756
9757     case CA_SET_PLAYER_INVENTORY:
9758     {
9759       for (i = 0; i < MAX_PLAYERS; i++)
9760       {
9761         struct PlayerInfo *player = &stored_player[i];
9762         int j, k;
9763
9764         if (trigger_player_bits & (1 << i))
9765         {
9766           int inventory_element = action_arg_element;
9767
9768           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9769               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9770               action_arg == CA_ARG_ELEMENT_ACTION)
9771           {
9772             int element = inventory_element;
9773             int collect_count = element_info[element].collect_count_initial;
9774
9775             if (!IS_CUSTOM_ELEMENT(element))
9776               collect_count = 1;
9777
9778             if (collect_count == 0)
9779               player->inventory_infinite_element = element;
9780             else
9781               for (k = 0; k < collect_count; k++)
9782                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9783                   player->inventory_element[player->inventory_size++] =
9784                     element;
9785           }
9786           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9787                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9788                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9789           {
9790             if (player->inventory_infinite_element != EL_UNDEFINED &&
9791                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9792                                      action_arg_element_raw))
9793               player->inventory_infinite_element = EL_UNDEFINED;
9794
9795             for (k = 0, j = 0; j < player->inventory_size; j++)
9796             {
9797               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9798                                         action_arg_element_raw))
9799                 player->inventory_element[k++] = player->inventory_element[j];
9800             }
9801
9802             player->inventory_size = k;
9803           }
9804           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9805           {
9806             if (player->inventory_size > 0)
9807             {
9808               for (j = 0; j < player->inventory_size - 1; j++)
9809                 player->inventory_element[j] = player->inventory_element[j + 1];
9810
9811               player->inventory_size--;
9812             }
9813           }
9814           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9815           {
9816             if (player->inventory_size > 0)
9817               player->inventory_size--;
9818           }
9819           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9820           {
9821             player->inventory_infinite_element = EL_UNDEFINED;
9822             player->inventory_size = 0;
9823           }
9824           else if (action_arg == CA_ARG_INVENTORY_RESET)
9825           {
9826             player->inventory_infinite_element = EL_UNDEFINED;
9827             player->inventory_size = 0;
9828
9829             if (level.use_initial_inventory[i])
9830             {
9831               for (j = 0; j < level.initial_inventory_size[i]; j++)
9832               {
9833                 int element = level.initial_inventory_content[i][j];
9834                 int collect_count = element_info[element].collect_count_initial;
9835
9836                 if (!IS_CUSTOM_ELEMENT(element))
9837                   collect_count = 1;
9838
9839                 if (collect_count == 0)
9840                   player->inventory_infinite_element = element;
9841                 else
9842                   for (k = 0; k < collect_count; k++)
9843                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9844                       player->inventory_element[player->inventory_size++] =
9845                         element;
9846               }
9847             }
9848           }
9849         }
9850       }
9851
9852       break;
9853     }
9854
9855     /* ---------- CE actions  ---------------------------------------------- */
9856
9857     case CA_SET_CE_VALUE:
9858     {
9859       int last_ce_value = CustomValue[x][y];
9860
9861       CustomValue[x][y] = action_arg_number_new;
9862
9863       if (CustomValue[x][y] != last_ce_value)
9864       {
9865         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9866         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9867
9868         if (CustomValue[x][y] == 0)
9869         {
9870           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9871           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9872         }
9873       }
9874
9875       break;
9876     }
9877
9878     case CA_SET_CE_SCORE:
9879     {
9880       int last_ce_score = ei->collect_score;
9881
9882       ei->collect_score = action_arg_number_new;
9883
9884       if (ei->collect_score != last_ce_score)
9885       {
9886         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9887         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9888
9889         if (ei->collect_score == 0)
9890         {
9891           int xx, yy;
9892
9893           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9894           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9895
9896           /*
9897             This is a very special case that seems to be a mixture between
9898             CheckElementChange() and CheckTriggeredElementChange(): while
9899             the first one only affects single elements that are triggered
9900             directly, the second one affects multiple elements in the playfield
9901             that are triggered indirectly by another element. This is a third
9902             case: Changing the CE score always affects multiple identical CEs,
9903             so every affected CE must be checked, not only the single CE for
9904             which the CE score was changed in the first place (as every instance
9905             of that CE shares the same CE score, and therefore also can change)!
9906           */
9907           SCAN_PLAYFIELD(xx, yy)
9908           {
9909             if (Feld[xx][yy] == element)
9910               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9911                                  CE_SCORE_GETS_ZERO);
9912           }
9913         }
9914       }
9915
9916       break;
9917     }
9918
9919     case CA_SET_CE_ARTWORK:
9920     {
9921       int artwork_element = action_arg_element;
9922       boolean reset_frame = FALSE;
9923       int xx, yy;
9924
9925       if (action_arg == CA_ARG_ELEMENT_RESET)
9926         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9927                            element);
9928
9929       if (ei->gfx_element != artwork_element)
9930         reset_frame = TRUE;
9931
9932       ei->gfx_element = artwork_element;
9933
9934       SCAN_PLAYFIELD(xx, yy)
9935       {
9936         if (Feld[xx][yy] == element)
9937         {
9938           if (reset_frame)
9939           {
9940             ResetGfxAnimation(xx, yy);
9941             ResetRandomAnimationValue(xx, yy);
9942           }
9943
9944           TEST_DrawLevelField(xx, yy);
9945         }
9946       }
9947
9948       break;
9949     }
9950
9951     /* ---------- engine actions  ------------------------------------------ */
9952
9953     case CA_SET_ENGINE_SCAN_MODE:
9954     {
9955       InitPlayfieldScanMode(action_arg);
9956
9957       break;
9958     }
9959
9960     default:
9961       break;
9962   }
9963 }
9964
9965 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9966 {
9967   int old_element = Feld[x][y];
9968   int new_element = GetElementFromGroupElement(element);
9969   int previous_move_direction = MovDir[x][y];
9970   int last_ce_value = CustomValue[x][y];
9971   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9972   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9973   boolean add_player_onto_element = (new_element_is_player &&
9974                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9975                                      IS_WALKABLE(old_element));
9976
9977   if (!add_player_onto_element)
9978   {
9979     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9980       RemoveMovingField(x, y);
9981     else
9982       RemoveField(x, y);
9983
9984     Feld[x][y] = new_element;
9985
9986     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9987       MovDir[x][y] = previous_move_direction;
9988
9989     if (element_info[new_element].use_last_ce_value)
9990       CustomValue[x][y] = last_ce_value;
9991
9992     InitField_WithBug1(x, y, FALSE);
9993
9994     new_element = Feld[x][y];   /* element may have changed */
9995
9996     ResetGfxAnimation(x, y);
9997     ResetRandomAnimationValue(x, y);
9998
9999     TEST_DrawLevelField(x, y);
10000
10001     if (GFX_CRUMBLED(new_element))
10002       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
10003   }
10004
10005   /* check if element under the player changes from accessible to unaccessible
10006      (needed for special case of dropping element which then changes) */
10007   /* (must be checked after creating new element for walkable group elements) */
10008   if (IS_PLAYER(x, y) && !player_explosion_protected &&
10009       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
10010   {
10011     Bang(x, y);
10012
10013     return;
10014   }
10015
10016   /* "ChangeCount" not set yet to allow "entered by player" change one time */
10017   if (new_element_is_player)
10018     RelocatePlayer(x, y, new_element);
10019
10020   if (is_change)
10021     ChangeCount[x][y]++;        /* count number of changes in the same frame */
10022
10023   TestIfBadThingTouchesPlayer(x, y);
10024   TestIfPlayerTouchesCustomElement(x, y);
10025   TestIfElementTouchesCustomElement(x, y);
10026 }
10027
10028 static void CreateField(int x, int y, int element)
10029 {
10030   CreateFieldExt(x, y, element, FALSE);
10031 }
10032
10033 static void CreateElementFromChange(int x, int y, int element)
10034 {
10035   element = GET_VALID_RUNTIME_ELEMENT(element);
10036
10037   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10038   {
10039     int old_element = Feld[x][y];
10040
10041     /* prevent changed element from moving in same engine frame
10042        unless both old and new element can either fall or move */
10043     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10044         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10045       Stop[x][y] = TRUE;
10046   }
10047
10048   CreateFieldExt(x, y, element, TRUE);
10049 }
10050
10051 static boolean ChangeElement(int x, int y, int element, int page)
10052 {
10053   struct ElementInfo *ei = &element_info[element];
10054   struct ElementChangeInfo *change = &ei->change_page[page];
10055   int ce_value = CustomValue[x][y];
10056   int ce_score = ei->collect_score;
10057   int target_element;
10058   int old_element = Feld[x][y];
10059
10060   /* always use default change event to prevent running into a loop */
10061   if (ChangeEvent[x][y] == -1)
10062     ChangeEvent[x][y] = CE_DELAY;
10063
10064   if (ChangeEvent[x][y] == CE_DELAY)
10065   {
10066     /* reset actual trigger element, trigger player and action element */
10067     change->actual_trigger_element = EL_EMPTY;
10068     change->actual_trigger_player = EL_EMPTY;
10069     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10070     change->actual_trigger_side = CH_SIDE_NONE;
10071     change->actual_trigger_ce_value = 0;
10072     change->actual_trigger_ce_score = 0;
10073   }
10074
10075   /* do not change elements more than a specified maximum number of changes */
10076   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10077     return FALSE;
10078
10079   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10080
10081   if (change->explode)
10082   {
10083     Bang(x, y);
10084
10085     return TRUE;
10086   }
10087
10088   if (change->use_target_content)
10089   {
10090     boolean complete_replace = TRUE;
10091     boolean can_replace[3][3];
10092     int xx, yy;
10093
10094     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10095     {
10096       boolean is_empty;
10097       boolean is_walkable;
10098       boolean is_diggable;
10099       boolean is_collectible;
10100       boolean is_removable;
10101       boolean is_destructible;
10102       int ex = x + xx - 1;
10103       int ey = y + yy - 1;
10104       int content_element = change->target_content.e[xx][yy];
10105       int e;
10106
10107       can_replace[xx][yy] = TRUE;
10108
10109       if (ex == x && ey == y)   /* do not check changing element itself */
10110         continue;
10111
10112       if (content_element == EL_EMPTY_SPACE)
10113       {
10114         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10115
10116         continue;
10117       }
10118
10119       if (!IN_LEV_FIELD(ex, ey))
10120       {
10121         can_replace[xx][yy] = FALSE;
10122         complete_replace = FALSE;
10123
10124         continue;
10125       }
10126
10127       e = Feld[ex][ey];
10128
10129       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10130         e = MovingOrBlocked2Element(ex, ey);
10131
10132       is_empty = (IS_FREE(ex, ey) ||
10133                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10134
10135       is_walkable     = (is_empty || IS_WALKABLE(e));
10136       is_diggable     = (is_empty || IS_DIGGABLE(e));
10137       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10138       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10139       is_removable    = (is_diggable || is_collectible);
10140
10141       can_replace[xx][yy] =
10142         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10143           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10144           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10145           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10146           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10147           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10148          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10149
10150       if (!can_replace[xx][yy])
10151         complete_replace = FALSE;
10152     }
10153
10154     if (!change->only_if_complete || complete_replace)
10155     {
10156       boolean something_has_changed = FALSE;
10157
10158       if (change->only_if_complete && change->use_random_replace &&
10159           RND(100) < change->random_percentage)
10160         return FALSE;
10161
10162       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10163       {
10164         int ex = x + xx - 1;
10165         int ey = y + yy - 1;
10166         int content_element;
10167
10168         if (can_replace[xx][yy] && (!change->use_random_replace ||
10169                                     RND(100) < change->random_percentage))
10170         {
10171           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10172             RemoveMovingField(ex, ey);
10173
10174           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10175
10176           content_element = change->target_content.e[xx][yy];
10177           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10178                                               ce_value, ce_score);
10179
10180           CreateElementFromChange(ex, ey, target_element);
10181
10182           something_has_changed = TRUE;
10183
10184           /* for symmetry reasons, freeze newly created border elements */
10185           if (ex != x || ey != y)
10186             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10187         }
10188       }
10189
10190       if (something_has_changed)
10191       {
10192         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10193         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10194       }
10195     }
10196   }
10197   else
10198   {
10199     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10200                                         ce_value, ce_score);
10201
10202     if (element == EL_DIAGONAL_GROWING ||
10203         element == EL_DIAGONAL_SHRINKING)
10204     {
10205       target_element = Store[x][y];
10206
10207       Store[x][y] = EL_EMPTY;
10208     }
10209
10210     CreateElementFromChange(x, y, target_element);
10211
10212     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10213     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10214   }
10215
10216   /* this uses direct change before indirect change */
10217   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10218
10219   return TRUE;
10220 }
10221
10222 static void HandleElementChange(int x, int y, int page)
10223 {
10224   int element = MovingOrBlocked2Element(x, y);
10225   struct ElementInfo *ei = &element_info[element];
10226   struct ElementChangeInfo *change = &ei->change_page[page];
10227   boolean handle_action_before_change = FALSE;
10228
10229 #ifdef DEBUG
10230   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10231       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10232   {
10233     printf("\n\n");
10234     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10235            x, y, element, element_info[element].token_name);
10236     printf("HandleElementChange(): This should never happen!\n");
10237     printf("\n\n");
10238   }
10239 #endif
10240
10241   /* this can happen with classic bombs on walkable, changing elements */
10242   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10243   {
10244     return;
10245   }
10246
10247   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10248   {
10249     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10250
10251     if (change->can_change)
10252     {
10253       /* !!! not clear why graphic animation should be reset at all here !!! */
10254       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10255       /* !!! SOLUTION: do not reset if graphics engine set to 4 or above !!! */
10256
10257       /*
10258         GRAPHICAL BUG ADDRESSED BY CHECKING GRAPHICS ENGINE VERSION:
10259
10260         When using an animation frame delay of 1 (this only happens with
10261         "sp_zonk.moving.left/right" in the classic graphics), the default
10262         (non-moving) animation shows wrong animation frames (while the
10263         moving animation, like "sp_zonk.moving.left/right", is correct,
10264         so this graphical bug never shows up with the classic graphics).
10265         For an animation with 4 frames, this causes wrong frames 0,0,1,2
10266         be drawn instead of the correct frames 0,1,2,3. This is caused by
10267         "GfxFrame[][]" being reset *twice* (in two successive frames) after
10268         an element change: First when the change delay ("ChangeDelay[][]")
10269         counter has reached zero after decrementing, then a second time in
10270         the next frame (after "GfxFrame[][]" was already incremented) when
10271         "ChangeDelay[][]" is reset to the initial delay value again.
10272
10273         This causes frame 0 to be drawn twice, while the last frame won't
10274         be drawn anymore, resulting in the wrong frame sequence 0,0,1,2.
10275
10276         As some animations may already be cleverly designed around this bug
10277         (at least the "Snake Bite" snake tail animation does this), it cannot
10278         simply be fixed here without breaking such existing animations.
10279         Unfortunately, it cannot easily be detected if a graphics set was
10280         designed "before" or "after" the bug was fixed. As a workaround,
10281         a new graphics set option "game.graphics_engine_version" was added
10282         to be able to specify the game's major release version for which the
10283         graphics set was designed, which can then be used to decide if the
10284         bugfix should be used (version 4 and above) or not (version 3 or
10285         below, or if no version was specified at all, as with old sets).
10286
10287         (The wrong/fixed animation frames can be tested with the test level set
10288         "test_gfxframe" and level "000", which contains a specially prepared
10289         custom element at level position (x/y) == (11/9) which uses the zonk
10290         animation mentioned above. Using "game.graphics_engine_version: 4"
10291         fixes the wrong animation frames, showing the correct frames 0,1,2,3.
10292         This can also be seen from the debug output for this test element.)
10293       */
10294
10295       /* when a custom element is about to change (for example by change delay),
10296          do not reset graphic animation when the custom element is moving */
10297       if (game.graphics_engine_version < 4 &&
10298           !IS_MOVING(x, y))
10299       {
10300         ResetGfxAnimation(x, y);
10301         ResetRandomAnimationValue(x, y);
10302       }
10303
10304       if (change->pre_change_function)
10305         change->pre_change_function(x, y);
10306     }
10307   }
10308
10309   ChangeDelay[x][y]--;
10310
10311   if (ChangeDelay[x][y] != 0)           /* continue element change */
10312   {
10313     if (change->can_change)
10314     {
10315       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10316
10317       if (IS_ANIMATED(graphic))
10318         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10319
10320       if (change->change_function)
10321         change->change_function(x, y);
10322     }
10323   }
10324   else                                  /* finish element change */
10325   {
10326     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10327     {
10328       page = ChangePage[x][y];
10329       ChangePage[x][y] = -1;
10330
10331       change = &ei->change_page[page];
10332     }
10333
10334     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10335     {
10336       ChangeDelay[x][y] = 1;            /* try change after next move step */
10337       ChangePage[x][y] = page;          /* remember page to use for change */
10338
10339       return;
10340     }
10341
10342     /* special case: set new level random seed before changing element */
10343     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10344       handle_action_before_change = TRUE;
10345
10346     if (change->has_action && handle_action_before_change)
10347       ExecuteCustomElementAction(x, y, element, page);
10348
10349     if (change->can_change)
10350     {
10351       if (ChangeElement(x, y, element, page))
10352       {
10353         if (change->post_change_function)
10354           change->post_change_function(x, y);
10355       }
10356     }
10357
10358     if (change->has_action && !handle_action_before_change)
10359       ExecuteCustomElementAction(x, y, element, page);
10360   }
10361 }
10362
10363 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10364                                               int trigger_element,
10365                                               int trigger_event,
10366                                               int trigger_player,
10367                                               int trigger_side,
10368                                               int trigger_page)
10369 {
10370   boolean change_done_any = FALSE;
10371   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10372   int i;
10373
10374   if (!(trigger_events[trigger_element][trigger_event]))
10375     return FALSE;
10376
10377   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10378
10379   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10380   {
10381     int element = EL_CUSTOM_START + i;
10382     boolean change_done = FALSE;
10383     int p;
10384
10385     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10386         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10387       continue;
10388
10389     for (p = 0; p < element_info[element].num_change_pages; p++)
10390     {
10391       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10392
10393       if (change->can_change_or_has_action &&
10394           change->has_event[trigger_event] &&
10395           change->trigger_side & trigger_side &&
10396           change->trigger_player & trigger_player &&
10397           change->trigger_page & trigger_page_bits &&
10398           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10399       {
10400         change->actual_trigger_element = trigger_element;
10401         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10402         change->actual_trigger_player_bits = trigger_player;
10403         change->actual_trigger_side = trigger_side;
10404         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10405         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10406
10407         if ((change->can_change && !change_done) || change->has_action)
10408         {
10409           int x, y;
10410
10411           SCAN_PLAYFIELD(x, y)
10412           {
10413             if (Feld[x][y] == element)
10414             {
10415               if (change->can_change && !change_done)
10416               {
10417                 /* if element already changed in this frame, not only prevent
10418                    another element change (checked in ChangeElement()), but
10419                    also prevent additional element actions for this element */
10420
10421                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10422                     !level.use_action_after_change_bug)
10423                   continue;
10424
10425                 ChangeDelay[x][y] = 1;
10426                 ChangeEvent[x][y] = trigger_event;
10427
10428                 HandleElementChange(x, y, p);
10429               }
10430               else if (change->has_action)
10431               {
10432                 /* if element already changed in this frame, not only prevent
10433                    another element change (checked in ChangeElement()), but
10434                    also prevent additional element actions for this element */
10435
10436                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10437                     !level.use_action_after_change_bug)
10438                   continue;
10439
10440                 ExecuteCustomElementAction(x, y, element, p);
10441                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10442               }
10443             }
10444           }
10445
10446           if (change->can_change)
10447           {
10448             change_done = TRUE;
10449             change_done_any = TRUE;
10450           }
10451         }
10452       }
10453     }
10454   }
10455
10456   RECURSION_LOOP_DETECTION_END();
10457
10458   return change_done_any;
10459 }
10460
10461 static boolean CheckElementChangeExt(int x, int y,
10462                                      int element,
10463                                      int trigger_element,
10464                                      int trigger_event,
10465                                      int trigger_player,
10466                                      int trigger_side)
10467 {
10468   boolean change_done = FALSE;
10469   int p;
10470
10471   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10472       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10473     return FALSE;
10474
10475   if (Feld[x][y] == EL_BLOCKED)
10476   {
10477     Blocked2Moving(x, y, &x, &y);
10478     element = Feld[x][y];
10479   }
10480
10481   /* check if element has already changed or is about to change after moving */
10482   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10483        Feld[x][y] != element) ||
10484
10485       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10486        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10487         ChangePage[x][y] != -1)))
10488     return FALSE;
10489
10490   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10491
10492   for (p = 0; p < element_info[element].num_change_pages; p++)
10493   {
10494     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10495
10496     /* check trigger element for all events where the element that is checked
10497        for changing interacts with a directly adjacent element -- this is
10498        different to element changes that affect other elements to change on the
10499        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10500     boolean check_trigger_element =
10501       (trigger_event == CE_TOUCHING_X ||
10502        trigger_event == CE_HITTING_X ||
10503        trigger_event == CE_HIT_BY_X ||
10504        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10505
10506     if (change->can_change_or_has_action &&
10507         change->has_event[trigger_event] &&
10508         change->trigger_side & trigger_side &&
10509         change->trigger_player & trigger_player &&
10510         (!check_trigger_element ||
10511          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10512     {
10513       change->actual_trigger_element = trigger_element;
10514       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10515       change->actual_trigger_player_bits = trigger_player;
10516       change->actual_trigger_side = trigger_side;
10517       change->actual_trigger_ce_value = CustomValue[x][y];
10518       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10519
10520       /* special case: trigger element not at (x,y) position for some events */
10521       if (check_trigger_element)
10522       {
10523         static struct
10524         {
10525           int dx, dy;
10526         } move_xy[] =
10527           {
10528             {  0,  0 },
10529             { -1,  0 },
10530             { +1,  0 },
10531             {  0,  0 },
10532             {  0, -1 },
10533             {  0,  0 }, { 0, 0 }, { 0, 0 },
10534             {  0, +1 }
10535           };
10536
10537         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10538         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10539
10540         change->actual_trigger_ce_value = CustomValue[xx][yy];
10541         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10542       }
10543
10544       if (change->can_change && !change_done)
10545       {
10546         ChangeDelay[x][y] = 1;
10547         ChangeEvent[x][y] = trigger_event;
10548
10549         HandleElementChange(x, y, p);
10550
10551         change_done = TRUE;
10552       }
10553       else if (change->has_action)
10554       {
10555         ExecuteCustomElementAction(x, y, element, p);
10556         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10557       }
10558     }
10559   }
10560
10561   RECURSION_LOOP_DETECTION_END();
10562
10563   return change_done;
10564 }
10565
10566 static void PlayPlayerSound(struct PlayerInfo *player)
10567 {
10568   int jx = player->jx, jy = player->jy;
10569   int sound_element = player->artwork_element;
10570   int last_action = player->last_action_waiting;
10571   int action = player->action_waiting;
10572
10573   if (player->is_waiting)
10574   {
10575     if (action != last_action)
10576       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10577     else
10578       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10579   }
10580   else
10581   {
10582     if (action != last_action)
10583       StopSound(element_info[sound_element].sound[last_action]);
10584
10585     if (last_action == ACTION_SLEEPING)
10586       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10587   }
10588 }
10589
10590 static void PlayAllPlayersSound()
10591 {
10592   int i;
10593
10594   for (i = 0; i < MAX_PLAYERS; i++)
10595     if (stored_player[i].active)
10596       PlayPlayerSound(&stored_player[i]);
10597 }
10598
10599 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10600 {
10601   boolean last_waiting = player->is_waiting;
10602   int move_dir = player->MovDir;
10603
10604   player->dir_waiting = move_dir;
10605   player->last_action_waiting = player->action_waiting;
10606
10607   if (is_waiting)
10608   {
10609     if (!last_waiting)          /* not waiting -> waiting */
10610     {
10611       player->is_waiting = TRUE;
10612
10613       player->frame_counter_bored =
10614         FrameCounter +
10615         game.player_boring_delay_fixed +
10616         GetSimpleRandom(game.player_boring_delay_random);
10617       player->frame_counter_sleeping =
10618         FrameCounter +
10619         game.player_sleeping_delay_fixed +
10620         GetSimpleRandom(game.player_sleeping_delay_random);
10621
10622       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10623     }
10624
10625     if (game.player_sleeping_delay_fixed +
10626         game.player_sleeping_delay_random > 0 &&
10627         player->anim_delay_counter == 0 &&
10628         player->post_delay_counter == 0 &&
10629         FrameCounter >= player->frame_counter_sleeping)
10630       player->is_sleeping = TRUE;
10631     else if (game.player_boring_delay_fixed +
10632              game.player_boring_delay_random > 0 &&
10633              FrameCounter >= player->frame_counter_bored)
10634       player->is_bored = TRUE;
10635
10636     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10637                               player->is_bored ? ACTION_BORING :
10638                               ACTION_WAITING);
10639
10640     if (player->is_sleeping && player->use_murphy)
10641     {
10642       /* special case for sleeping Murphy when leaning against non-free tile */
10643
10644       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10645           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10646            !IS_MOVING(player->jx - 1, player->jy)))
10647         move_dir = MV_LEFT;
10648       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10649                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10650                 !IS_MOVING(player->jx + 1, player->jy)))
10651         move_dir = MV_RIGHT;
10652       else
10653         player->is_sleeping = FALSE;
10654
10655       player->dir_waiting = move_dir;
10656     }
10657
10658     if (player->is_sleeping)
10659     {
10660       if (player->num_special_action_sleeping > 0)
10661       {
10662         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10663         {
10664           int last_special_action = player->special_action_sleeping;
10665           int num_special_action = player->num_special_action_sleeping;
10666           int special_action =
10667             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10668              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10669              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10670              last_special_action + 1 : ACTION_SLEEPING);
10671           int special_graphic =
10672             el_act_dir2img(player->artwork_element, special_action, move_dir);
10673
10674           player->anim_delay_counter =
10675             graphic_info[special_graphic].anim_delay_fixed +
10676             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10677           player->post_delay_counter =
10678             graphic_info[special_graphic].post_delay_fixed +
10679             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10680
10681           player->special_action_sleeping = special_action;
10682         }
10683
10684         if (player->anim_delay_counter > 0)
10685         {
10686           player->action_waiting = player->special_action_sleeping;
10687           player->anim_delay_counter--;
10688         }
10689         else if (player->post_delay_counter > 0)
10690         {
10691           player->post_delay_counter--;
10692         }
10693       }
10694     }
10695     else if (player->is_bored)
10696     {
10697       if (player->num_special_action_bored > 0)
10698       {
10699         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10700         {
10701           int special_action =
10702             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10703           int special_graphic =
10704             el_act_dir2img(player->artwork_element, special_action, move_dir);
10705
10706           player->anim_delay_counter =
10707             graphic_info[special_graphic].anim_delay_fixed +
10708             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10709           player->post_delay_counter =
10710             graphic_info[special_graphic].post_delay_fixed +
10711             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10712
10713           player->special_action_bored = special_action;
10714         }
10715
10716         if (player->anim_delay_counter > 0)
10717         {
10718           player->action_waiting = player->special_action_bored;
10719           player->anim_delay_counter--;
10720         }
10721         else if (player->post_delay_counter > 0)
10722         {
10723           player->post_delay_counter--;
10724         }
10725       }
10726     }
10727   }
10728   else if (last_waiting)        /* waiting -> not waiting */
10729   {
10730     player->is_waiting = FALSE;
10731     player->is_bored = FALSE;
10732     player->is_sleeping = FALSE;
10733
10734     player->frame_counter_bored = -1;
10735     player->frame_counter_sleeping = -1;
10736
10737     player->anim_delay_counter = 0;
10738     player->post_delay_counter = 0;
10739
10740     player->dir_waiting = player->MovDir;
10741     player->action_waiting = ACTION_DEFAULT;
10742
10743     player->special_action_bored = ACTION_DEFAULT;
10744     player->special_action_sleeping = ACTION_DEFAULT;
10745   }
10746 }
10747
10748 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10749 {
10750   static boolean player_was_moving = FALSE;
10751   static boolean player_was_snapping = FALSE;
10752   static boolean player_was_dropping = FALSE;
10753
10754   if ((!player->is_moving  && player_was_moving) ||
10755       (player->MovPos == 0 && player_was_moving) ||
10756       (player->is_snapping && !player_was_snapping) ||
10757       (player->is_dropping && !player_was_dropping))
10758   {
10759     if (!SaveEngineSnapshotToList())
10760       return;
10761
10762     player_was_moving = FALSE;
10763     player_was_snapping = TRUE;
10764     player_was_dropping = TRUE;
10765   }
10766   else
10767   {
10768     if (player->is_moving)
10769       player_was_moving = TRUE;
10770
10771     if (!player->is_snapping)
10772       player_was_snapping = FALSE;
10773
10774     if (!player->is_dropping)
10775       player_was_dropping = FALSE;
10776   }
10777 }
10778
10779 static void CheckSingleStepMode(struct PlayerInfo *player)
10780 {
10781   if (tape.single_step && tape.recording && !tape.pausing)
10782   {
10783     /* as it is called "single step mode", just return to pause mode when the
10784        player stopped moving after one tile (or never starts moving at all) */
10785     if (!player->is_moving && !player->is_pushing)
10786     {
10787       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10788       SnapField(player, 0, 0);                  /* stop snapping */
10789     }
10790   }
10791
10792   CheckSaveEngineSnapshot(player);
10793 }
10794
10795 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10796 {
10797   int left      = player_action & JOY_LEFT;
10798   int right     = player_action & JOY_RIGHT;
10799   int up        = player_action & JOY_UP;
10800   int down      = player_action & JOY_DOWN;
10801   int button1   = player_action & JOY_BUTTON_1;
10802   int button2   = player_action & JOY_BUTTON_2;
10803   int dx        = (left ? -1 : right ? 1 : 0);
10804   int dy        = (up   ? -1 : down  ? 1 : 0);
10805
10806   if (!player->active || tape.pausing)
10807     return 0;
10808
10809   if (player_action)
10810   {
10811     if (button1)
10812       SnapField(player, dx, dy);
10813     else
10814     {
10815       if (button2)
10816         DropElement(player);
10817
10818       MovePlayer(player, dx, dy);
10819     }
10820
10821     CheckSingleStepMode(player);
10822
10823     SetPlayerWaiting(player, FALSE);
10824
10825     return player_action;
10826   }
10827   else
10828   {
10829     /* no actions for this player (no input at player's configured device) */
10830
10831     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10832     SnapField(player, 0, 0);
10833     CheckGravityMovementWhenNotMoving(player);
10834
10835     if (player->MovPos == 0)
10836       SetPlayerWaiting(player, TRUE);
10837
10838     if (player->MovPos == 0)    /* needed for tape.playing */
10839       player->is_moving = FALSE;
10840
10841     player->is_dropping = FALSE;
10842     player->is_dropping_pressed = FALSE;
10843     player->drop_pressed_delay = 0;
10844
10845     CheckSingleStepMode(player);
10846
10847     return 0;
10848   }
10849 }
10850
10851 static void CheckLevelTime()
10852 {
10853   int i;
10854
10855   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10856   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10857   {
10858     if (level.native_em_level->lev->home == 0)  /* all players at home */
10859     {
10860       PlayerWins(local_player);
10861
10862       AllPlayersGone = TRUE;
10863
10864       level.native_em_level->lev->home = -1;
10865     }
10866
10867     if (level.native_em_level->ply[0]->alive == 0 &&
10868         level.native_em_level->ply[1]->alive == 0 &&
10869         level.native_em_level->ply[2]->alive == 0 &&
10870         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10871       AllPlayersGone = TRUE;
10872   }
10873   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10874   {
10875     if (game_sp.LevelSolved &&
10876         !game_sp.GameOver)                              /* game won */
10877     {
10878       PlayerWins(local_player);
10879
10880       game_sp.GameOver = TRUE;
10881
10882       AllPlayersGone = TRUE;
10883     }
10884
10885     if (game_sp.GameOver)                               /* game lost */
10886       AllPlayersGone = TRUE;
10887   }
10888
10889   if (TimeFrames >= FRAMES_PER_SECOND)
10890   {
10891     TimeFrames = 0;
10892     TapeTime++;
10893
10894     for (i = 0; i < MAX_PLAYERS; i++)
10895     {
10896       struct PlayerInfo *player = &stored_player[i];
10897
10898       if (SHIELD_ON(player))
10899       {
10900         player->shield_normal_time_left--;
10901
10902         if (player->shield_deadly_time_left > 0)
10903           player->shield_deadly_time_left--;
10904       }
10905     }
10906
10907     if (!local_player->LevelSolved && !level.use_step_counter)
10908     {
10909       TimePlayed++;
10910
10911       if (TimeLeft > 0)
10912       {
10913         TimeLeft--;
10914
10915         if (TimeLeft <= 10 && setup.time_limit)
10916           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10917
10918         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10919            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10920
10921         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10922
10923         if (!TimeLeft && setup.time_limit)
10924         {
10925           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10926             level.native_em_level->lev->killed_out_of_time = TRUE;
10927           else
10928             for (i = 0; i < MAX_PLAYERS; i++)
10929               KillPlayer(&stored_player[i]);
10930         }
10931       }
10932       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10933       {
10934         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10935       }
10936
10937       level.native_em_level->lev->time =
10938         (game.no_time_limit ? TimePlayed : TimeLeft);
10939     }
10940
10941     if (tape.recording || tape.playing)
10942       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10943   }
10944
10945   if (tape.recording || tape.playing)
10946     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10947
10948   UpdateAndDisplayGameControlValues();
10949 }
10950
10951 void AdvanceFrameAndPlayerCounters(int player_nr)
10952 {
10953   int i;
10954
10955   /* advance frame counters (global frame counter and time frame counter) */
10956   FrameCounter++;
10957   TimeFrames++;
10958
10959   /* advance player counters (counters for move delay, move animation etc.) */
10960   for (i = 0; i < MAX_PLAYERS; i++)
10961   {
10962     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10963     int move_delay_value = stored_player[i].move_delay_value;
10964     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10965
10966     if (!advance_player_counters)       /* not all players may be affected */
10967       continue;
10968
10969     if (move_frames == 0)       /* less than one move per game frame */
10970     {
10971       int stepsize = TILEX / move_delay_value;
10972       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10973       int count = (stored_player[i].is_moving ?
10974                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10975
10976       if (count % delay == 0)
10977         move_frames = 1;
10978     }
10979
10980     stored_player[i].Frame += move_frames;
10981
10982     if (stored_player[i].MovPos != 0)
10983       stored_player[i].StepFrame += move_frames;
10984
10985     if (stored_player[i].move_delay > 0)
10986       stored_player[i].move_delay--;
10987
10988     /* due to bugs in previous versions, counter must count up, not down */
10989     if (stored_player[i].push_delay != -1)
10990       stored_player[i].push_delay++;
10991
10992     if (stored_player[i].drop_delay > 0)
10993       stored_player[i].drop_delay--;
10994
10995     if (stored_player[i].is_dropping_pressed)
10996       stored_player[i].drop_pressed_delay++;
10997   }
10998 }
10999
11000 void StartGameActions(boolean init_network_game, boolean record_tape,
11001                       int random_seed)
11002 {
11003   unsigned int new_random_seed = InitRND(random_seed);
11004
11005   if (record_tape)
11006     TapeStartRecording(new_random_seed);
11007
11008 #if defined(NETWORK_AVALIABLE)
11009   if (init_network_game)
11010   {
11011     SendToServer_StartPlaying();
11012
11013     return;
11014   }
11015 #endif
11016
11017   InitGame();
11018 }
11019
11020 void GameActions()
11021 {
11022   static unsigned int game_frame_delay = 0;
11023   unsigned int game_frame_delay_value;
11024   byte *recorded_player_action;
11025   byte summarized_player_action = 0;
11026   byte tape_action[MAX_PLAYERS];
11027   int i;
11028
11029   /* detect endless loops, caused by custom element programming */
11030   if (recursion_loop_detected && recursion_loop_depth == 0)
11031   {
11032     char *message = getStringCat3("Internal Error! Element ",
11033                                   EL_NAME(recursion_loop_element),
11034                                   " caused endless loop! Quit the game?");
11035
11036     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
11037           EL_NAME(recursion_loop_element));
11038
11039     RequestQuitGameExt(FALSE, level_editor_test_game, message);
11040
11041     recursion_loop_detected = FALSE;    /* if game should be continued */
11042
11043     free(message);
11044
11045     return;
11046   }
11047
11048   if (game.restart_level)
11049     StartGameActions(options.network, setup.autorecord, level.random_seed);
11050
11051   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11052   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11053   {
11054     if (level.native_em_level->lev->home == 0)  /* all players at home */
11055     {
11056       PlayerWins(local_player);
11057
11058       AllPlayersGone = TRUE;
11059
11060       level.native_em_level->lev->home = -1;
11061     }
11062
11063     if (level.native_em_level->ply[0]->alive == 0 &&
11064         level.native_em_level->ply[1]->alive == 0 &&
11065         level.native_em_level->ply[2]->alive == 0 &&
11066         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11067       AllPlayersGone = TRUE;
11068   }
11069   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11070   {
11071     if (game_sp.LevelSolved &&
11072         !game_sp.GameOver)                              /* game won */
11073     {
11074       PlayerWins(local_player);
11075
11076       game_sp.GameOver = TRUE;
11077
11078       AllPlayersGone = TRUE;
11079     }
11080
11081     if (game_sp.GameOver)                               /* game lost */
11082       AllPlayersGone = TRUE;
11083   }
11084
11085   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11086     GameWon();
11087
11088   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11089     TapeStop();
11090
11091   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11092     return;
11093
11094   game_frame_delay_value =
11095     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11096
11097   if (tape.playing && tape.warp_forward && !tape.pausing)
11098     game_frame_delay_value = 0;
11099
11100 #if 0
11101   /* ---------- main game synchronization point ---------- */
11102
11103   int skip = WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11104
11105   printf("::: skip == %d\n", skip);
11106
11107 #else
11108   /* ---------- main game synchronization point ---------- */
11109
11110   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11111 #endif
11112
11113   if (network_playing && !network_player_action_received)
11114   {
11115     /* try to get network player actions in time */
11116
11117 #if defined(NETWORK_AVALIABLE)
11118     /* last chance to get network player actions without main loop delay */
11119     HandleNetworking();
11120 #endif
11121
11122     /* game was quit by network peer */
11123     if (game_status != GAME_MODE_PLAYING)
11124       return;
11125
11126     if (!network_player_action_received)
11127       return;           /* failed to get network player actions in time */
11128
11129     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11130   }
11131
11132   if (tape.pausing)
11133     return;
11134
11135   /* at this point we know that we really continue executing the game */
11136
11137   network_player_action_received = FALSE;
11138
11139   /* when playing tape, read previously recorded player input from tape data */
11140   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11141
11142   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11143   if (tape.pausing)
11144     return;
11145
11146   if (tape.set_centered_player)
11147   {
11148     game.centered_player_nr_next = tape.centered_player_nr_next;
11149     game.set_centered_player = TRUE;
11150   }
11151
11152   for (i = 0; i < MAX_PLAYERS; i++)
11153   {
11154     summarized_player_action |= stored_player[i].action;
11155
11156     if (!network_playing && (game.team_mode || tape.playing))
11157       stored_player[i].effective_action = stored_player[i].action;
11158   }
11159
11160 #if defined(NETWORK_AVALIABLE)
11161   if (network_playing)
11162     SendToServer_MovePlayer(summarized_player_action);
11163 #endif
11164
11165   if (!options.network && !game.team_mode)
11166     local_player->effective_action = summarized_player_action;
11167
11168   if (tape.recording &&
11169       setup.team_mode &&
11170       setup.input_on_focus &&
11171       game.centered_player_nr != -1)
11172   {
11173     for (i = 0; i < MAX_PLAYERS; i++)
11174       stored_player[i].effective_action =
11175         (i == game.centered_player_nr ? summarized_player_action : 0);
11176   }
11177
11178   if (recorded_player_action != NULL)
11179     for (i = 0; i < MAX_PLAYERS; i++)
11180       stored_player[i].effective_action = recorded_player_action[i];
11181
11182   for (i = 0; i < MAX_PLAYERS; i++)
11183   {
11184     tape_action[i] = stored_player[i].effective_action;
11185
11186     /* (this may happen in the RND game engine if a player was not present on
11187        the playfield on level start, but appeared later from a custom element */
11188     if (setup.team_mode &&
11189         tape.recording &&
11190         tape_action[i] &&
11191         !tape.player_participates[i])
11192       tape.player_participates[i] = TRUE;
11193   }
11194
11195   /* only record actions from input devices, but not programmed actions */
11196   if (tape.recording)
11197     TapeRecordAction(tape_action);
11198
11199 #if USE_NEW_PLAYER_ASSIGNMENTS
11200   // !!! also map player actions in single player mode !!!
11201   // if (game.team_mode)
11202   {
11203     byte mapped_action[MAX_PLAYERS];
11204
11205 #if DEBUG_PLAYER_ACTIONS
11206     printf(":::");
11207     for (i = 0; i < MAX_PLAYERS; i++)
11208       printf(" %d, ", stored_player[i].effective_action);
11209 #endif
11210
11211     for (i = 0; i < MAX_PLAYERS; i++)
11212       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11213
11214     for (i = 0; i < MAX_PLAYERS; i++)
11215       stored_player[i].effective_action = mapped_action[i];
11216
11217 #if DEBUG_PLAYER_ACTIONS
11218     printf(" =>");
11219     for (i = 0; i < MAX_PLAYERS; i++)
11220       printf(" %d, ", stored_player[i].effective_action);
11221     printf("\n");
11222 #endif
11223   }
11224 #if DEBUG_PLAYER_ACTIONS
11225   else
11226   {
11227     printf(":::");
11228     for (i = 0; i < MAX_PLAYERS; i++)
11229       printf(" %d, ", stored_player[i].effective_action);
11230     printf("\n");
11231   }
11232 #endif
11233 #endif
11234
11235   for (i = 0; i < MAX_PLAYERS; i++)
11236   {
11237     // allow engine snapshot in case of changed movement attempt
11238     if ((game.snapshot.last_action[i] & KEY_MOTION) !=
11239         (stored_player[i].effective_action & KEY_MOTION))
11240       game.snapshot.changed_action = TRUE;
11241
11242     // allow engine snapshot in case of snapping/dropping attempt
11243     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
11244         (stored_player[i].effective_action & KEY_BUTTON) != 0)
11245       game.snapshot.changed_action = TRUE;
11246
11247     game.snapshot.last_action[i] = stored_player[i].effective_action;
11248   }
11249
11250   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11251   {
11252     GameActions_EM_Main();
11253   }
11254   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11255   {
11256     GameActions_SP_Main();
11257   }
11258   else
11259   {
11260     GameActions_RND_Main();
11261   }
11262
11263   BlitScreenToBitmap(backbuffer);
11264
11265   CheckLevelTime();
11266
11267   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11268
11269   if (options.debug)                    /* calculate frames per second */
11270   {
11271     static unsigned int fps_counter = 0;
11272     static int fps_frames = 0;
11273     unsigned int fps_delay_ms = Counter() - fps_counter;
11274
11275     fps_frames++;
11276
11277     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11278     {
11279       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11280
11281       fps_frames = 0;
11282       fps_counter = Counter();
11283     }
11284
11285     redraw_mask |= REDRAW_FPS;
11286   }
11287 }
11288
11289 void GameActions_EM_Main()
11290 {
11291   byte effective_action[MAX_PLAYERS];
11292   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11293   int i;
11294
11295   for (i = 0; i < MAX_PLAYERS; i++)
11296     effective_action[i] = stored_player[i].effective_action;
11297
11298   GameActions_EM(effective_action, warp_mode);
11299 }
11300
11301 void GameActions_SP_Main()
11302 {
11303   byte effective_action[MAX_PLAYERS];
11304   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11305   int i;
11306
11307   for (i = 0; i < MAX_PLAYERS; i++)
11308     effective_action[i] = stored_player[i].effective_action;
11309
11310   GameActions_SP(effective_action, warp_mode);
11311 }
11312
11313 void GameActions_RND_Main()
11314 {
11315   GameActions_RND();
11316 }
11317
11318 void GameActions_RND()
11319 {
11320   int magic_wall_x = 0, magic_wall_y = 0;
11321   int i, x, y, element, graphic;
11322
11323   InitPlayfieldScanModeVars();
11324
11325   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11326   {
11327     SCAN_PLAYFIELD(x, y)
11328     {
11329       ChangeCount[x][y] = 0;
11330       ChangeEvent[x][y] = -1;
11331     }
11332   }
11333
11334   if (game.set_centered_player)
11335   {
11336     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11337
11338     /* switching to "all players" only possible if all players fit to screen */
11339     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11340     {
11341       game.centered_player_nr_next = game.centered_player_nr;
11342       game.set_centered_player = FALSE;
11343     }
11344
11345     /* do not switch focus to non-existing (or non-active) player */
11346     if (game.centered_player_nr_next >= 0 &&
11347         !stored_player[game.centered_player_nr_next].active)
11348     {
11349       game.centered_player_nr_next = game.centered_player_nr;
11350       game.set_centered_player = FALSE;
11351     }
11352   }
11353
11354   if (game.set_centered_player &&
11355       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11356   {
11357     int sx, sy;
11358
11359     if (game.centered_player_nr_next == -1)
11360     {
11361       setScreenCenteredToAllPlayers(&sx, &sy);
11362     }
11363     else
11364     {
11365       sx = stored_player[game.centered_player_nr_next].jx;
11366       sy = stored_player[game.centered_player_nr_next].jy;
11367     }
11368
11369     game.centered_player_nr = game.centered_player_nr_next;
11370     game.set_centered_player = FALSE;
11371
11372     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11373     DrawGameDoorValues();
11374   }
11375
11376   for (i = 0; i < MAX_PLAYERS; i++)
11377   {
11378     int actual_player_action = stored_player[i].effective_action;
11379
11380 #if 1
11381     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11382        - rnd_equinox_tetrachloride 048
11383        - rnd_equinox_tetrachloride_ii 096
11384        - rnd_emanuel_schmieg 002
11385        - doctor_sloan_ww 001, 020
11386     */
11387     if (stored_player[i].MovPos == 0)
11388       CheckGravityMovement(&stored_player[i]);
11389 #endif
11390
11391     /* overwrite programmed action with tape action */
11392     if (stored_player[i].programmed_action)
11393       actual_player_action = stored_player[i].programmed_action;
11394
11395     PlayerActions(&stored_player[i], actual_player_action);
11396
11397     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11398   }
11399
11400   ScrollScreen(NULL, SCROLL_GO_ON);
11401
11402   /* for backwards compatibility, the following code emulates a fixed bug that
11403      occured when pushing elements (causing elements that just made their last
11404      pushing step to already (if possible) make their first falling step in the
11405      same game frame, which is bad); this code is also needed to use the famous
11406      "spring push bug" which is used in older levels and might be wanted to be
11407      used also in newer levels, but in this case the buggy pushing code is only
11408      affecting the "spring" element and no other elements */
11409
11410   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11411   {
11412     for (i = 0; i < MAX_PLAYERS; i++)
11413     {
11414       struct PlayerInfo *player = &stored_player[i];
11415       int x = player->jx;
11416       int y = player->jy;
11417
11418       if (player->active && player->is_pushing && player->is_moving &&
11419           IS_MOVING(x, y) &&
11420           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11421            Feld[x][y] == EL_SPRING))
11422       {
11423         ContinueMoving(x, y);
11424
11425         /* continue moving after pushing (this is actually a bug) */
11426         if (!IS_MOVING(x, y))
11427           Stop[x][y] = FALSE;
11428       }
11429     }
11430   }
11431
11432   SCAN_PLAYFIELD(x, y)
11433   {
11434     ChangeCount[x][y] = 0;
11435     ChangeEvent[x][y] = -1;
11436
11437     /* this must be handled before main playfield loop */
11438     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11439     {
11440       MovDelay[x][y]--;
11441       if (MovDelay[x][y] <= 0)
11442         RemoveField(x, y);
11443     }
11444
11445     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11446     {
11447       MovDelay[x][y]--;
11448       if (MovDelay[x][y] <= 0)
11449       {
11450         RemoveField(x, y);
11451         TEST_DrawLevelField(x, y);
11452
11453         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11454       }
11455     }
11456
11457 #if DEBUG
11458     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11459     {
11460       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11461       printf("GameActions(): This should never happen!\n");
11462
11463       ChangePage[x][y] = -1;
11464     }
11465 #endif
11466
11467     Stop[x][y] = FALSE;
11468     if (WasJustMoving[x][y] > 0)
11469       WasJustMoving[x][y]--;
11470     if (WasJustFalling[x][y] > 0)
11471       WasJustFalling[x][y]--;
11472     if (CheckCollision[x][y] > 0)
11473       CheckCollision[x][y]--;
11474     if (CheckImpact[x][y] > 0)
11475       CheckImpact[x][y]--;
11476
11477     GfxFrame[x][y]++;
11478
11479     /* reset finished pushing action (not done in ContinueMoving() to allow
11480        continuous pushing animation for elements with zero push delay) */
11481     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11482     {
11483       ResetGfxAnimation(x, y);
11484       TEST_DrawLevelField(x, y);
11485     }
11486
11487 #if DEBUG
11488     if (IS_BLOCKED(x, y))
11489     {
11490       int oldx, oldy;
11491
11492       Blocked2Moving(x, y, &oldx, &oldy);
11493       if (!IS_MOVING(oldx, oldy))
11494       {
11495         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11496         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11497         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11498         printf("GameActions(): This should never happen!\n");
11499       }
11500     }
11501 #endif
11502   }
11503
11504   SCAN_PLAYFIELD(x, y)
11505   {
11506     element = Feld[x][y];
11507     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11508
11509     ResetGfxFrame(x, y, TRUE);
11510
11511     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11512         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11513       ResetRandomAnimationValue(x, y);
11514
11515     SetRandomAnimationValue(x, y);
11516
11517     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11518
11519     if (IS_INACTIVE(element))
11520     {
11521       if (IS_ANIMATED(graphic))
11522         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11523
11524       continue;
11525     }
11526
11527     /* this may take place after moving, so 'element' may have changed */
11528     if (IS_CHANGING(x, y) &&
11529         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11530     {
11531       int page = element_info[element].event_page_nr[CE_DELAY];
11532
11533       HandleElementChange(x, y, page);
11534
11535       element = Feld[x][y];
11536       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11537     }
11538
11539     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11540     {
11541       StartMoving(x, y);
11542
11543       element = Feld[x][y];
11544       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11545
11546       if (IS_ANIMATED(graphic) &&
11547           !IS_MOVING(x, y) &&
11548           !Stop[x][y])
11549         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11550
11551       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11552         TEST_DrawTwinkleOnField(x, y);
11553     }
11554     else if ((element == EL_ACID ||
11555               element == EL_EXIT_OPEN ||
11556               element == EL_EM_EXIT_OPEN ||
11557               element == EL_SP_EXIT_OPEN ||
11558               element == EL_STEEL_EXIT_OPEN ||
11559               element == EL_EM_STEEL_EXIT_OPEN ||
11560               element == EL_SP_TERMINAL ||
11561               element == EL_SP_TERMINAL_ACTIVE ||
11562               element == EL_EXTRA_TIME ||
11563               element == EL_SHIELD_NORMAL ||
11564               element == EL_SHIELD_DEADLY) &&
11565              IS_ANIMATED(graphic))
11566       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11567     else if (IS_MOVING(x, y))
11568       ContinueMoving(x, y);
11569     else if (IS_ACTIVE_BOMB(element))
11570       CheckDynamite(x, y);
11571     else if (element == EL_AMOEBA_GROWING)
11572       AmoebeWaechst(x, y);
11573     else if (element == EL_AMOEBA_SHRINKING)
11574       AmoebaDisappearing(x, y);
11575
11576 #if !USE_NEW_AMOEBA_CODE
11577     else if (IS_AMOEBALIVE(element))
11578       AmoebeAbleger(x, y);
11579 #endif
11580
11581     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11582       Life(x, y);
11583     else if (element == EL_EXIT_CLOSED)
11584       CheckExit(x, y);
11585     else if (element == EL_EM_EXIT_CLOSED)
11586       CheckExitEM(x, y);
11587     else if (element == EL_STEEL_EXIT_CLOSED)
11588       CheckExitSteel(x, y);
11589     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11590       CheckExitSteelEM(x, y);
11591     else if (element == EL_SP_EXIT_CLOSED)
11592       CheckExitSP(x, y);
11593     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11594              element == EL_EXPANDABLE_STEELWALL_GROWING)
11595       MauerWaechst(x, y);
11596     else if (element == EL_EXPANDABLE_WALL ||
11597              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11598              element == EL_EXPANDABLE_WALL_VERTICAL ||
11599              element == EL_EXPANDABLE_WALL_ANY ||
11600              element == EL_BD_EXPANDABLE_WALL)
11601       MauerAbleger(x, y);
11602     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11603              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11604              element == EL_EXPANDABLE_STEELWALL_ANY)
11605       MauerAblegerStahl(x, y);
11606     else if (element == EL_FLAMES)
11607       CheckForDragon(x, y);
11608     else if (element == EL_EXPLOSION)
11609       ; /* drawing of correct explosion animation is handled separately */
11610     else if (element == EL_ELEMENT_SNAPPING ||
11611              element == EL_DIAGONAL_SHRINKING ||
11612              element == EL_DIAGONAL_GROWING)
11613     {
11614       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11615
11616       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11617     }
11618     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11619       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11620
11621     if (IS_BELT_ACTIVE(element))
11622       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11623
11624     if (game.magic_wall_active)
11625     {
11626       int jx = local_player->jx, jy = local_player->jy;
11627
11628       /* play the element sound at the position nearest to the player */
11629       if ((element == EL_MAGIC_WALL_FULL ||
11630            element == EL_MAGIC_WALL_ACTIVE ||
11631            element == EL_MAGIC_WALL_EMPTYING ||
11632            element == EL_BD_MAGIC_WALL_FULL ||
11633            element == EL_BD_MAGIC_WALL_ACTIVE ||
11634            element == EL_BD_MAGIC_WALL_EMPTYING ||
11635            element == EL_DC_MAGIC_WALL_FULL ||
11636            element == EL_DC_MAGIC_WALL_ACTIVE ||
11637            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11638           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11639       {
11640         magic_wall_x = x;
11641         magic_wall_y = y;
11642       }
11643     }
11644   }
11645
11646 #if USE_NEW_AMOEBA_CODE
11647   /* new experimental amoeba growth stuff */
11648   if (!(FrameCounter % 8))
11649   {
11650     static unsigned int random = 1684108901;
11651
11652     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11653     {
11654       x = RND(lev_fieldx);
11655       y = RND(lev_fieldy);
11656       element = Feld[x][y];
11657
11658       if (!IS_PLAYER(x,y) &&
11659           (element == EL_EMPTY ||
11660            CAN_GROW_INTO(element) ||
11661            element == EL_QUICKSAND_EMPTY ||
11662            element == EL_QUICKSAND_FAST_EMPTY ||
11663            element == EL_ACID_SPLASH_LEFT ||
11664            element == EL_ACID_SPLASH_RIGHT))
11665       {
11666         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11667             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11668             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11669             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11670           Feld[x][y] = EL_AMOEBA_DROP;
11671       }
11672
11673       random = random * 129 + 1;
11674     }
11675   }
11676 #endif
11677
11678   game.explosions_delayed = FALSE;
11679
11680   SCAN_PLAYFIELD(x, y)
11681   {
11682     element = Feld[x][y];
11683
11684     if (ExplodeField[x][y])
11685       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11686     else if (element == EL_EXPLOSION)
11687       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11688
11689     ExplodeField[x][y] = EX_TYPE_NONE;
11690   }
11691
11692   game.explosions_delayed = TRUE;
11693
11694   if (game.magic_wall_active)
11695   {
11696     if (!(game.magic_wall_time_left % 4))
11697     {
11698       int element = Feld[magic_wall_x][magic_wall_y];
11699
11700       if (element == EL_BD_MAGIC_WALL_FULL ||
11701           element == EL_BD_MAGIC_WALL_ACTIVE ||
11702           element == EL_BD_MAGIC_WALL_EMPTYING)
11703         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11704       else if (element == EL_DC_MAGIC_WALL_FULL ||
11705                element == EL_DC_MAGIC_WALL_ACTIVE ||
11706                element == EL_DC_MAGIC_WALL_EMPTYING)
11707         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11708       else
11709         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11710     }
11711
11712     if (game.magic_wall_time_left > 0)
11713     {
11714       game.magic_wall_time_left--;
11715
11716       if (!game.magic_wall_time_left)
11717       {
11718         SCAN_PLAYFIELD(x, y)
11719         {
11720           element = Feld[x][y];
11721
11722           if (element == EL_MAGIC_WALL_ACTIVE ||
11723               element == EL_MAGIC_WALL_FULL)
11724           {
11725             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11726             TEST_DrawLevelField(x, y);
11727           }
11728           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11729                    element == EL_BD_MAGIC_WALL_FULL)
11730           {
11731             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11732             TEST_DrawLevelField(x, y);
11733           }
11734           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11735                    element == EL_DC_MAGIC_WALL_FULL)
11736           {
11737             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11738             TEST_DrawLevelField(x, y);
11739           }
11740         }
11741
11742         game.magic_wall_active = FALSE;
11743       }
11744     }
11745   }
11746
11747   if (game.light_time_left > 0)
11748   {
11749     game.light_time_left--;
11750
11751     if (game.light_time_left == 0)
11752       RedrawAllLightSwitchesAndInvisibleElements();
11753   }
11754
11755   if (game.timegate_time_left > 0)
11756   {
11757     game.timegate_time_left--;
11758
11759     if (game.timegate_time_left == 0)
11760       CloseAllOpenTimegates();
11761   }
11762
11763   if (game.lenses_time_left > 0)
11764   {
11765     game.lenses_time_left--;
11766
11767     if (game.lenses_time_left == 0)
11768       RedrawAllInvisibleElementsForLenses();
11769   }
11770
11771   if (game.magnify_time_left > 0)
11772   {
11773     game.magnify_time_left--;
11774
11775     if (game.magnify_time_left == 0)
11776       RedrawAllInvisibleElementsForMagnifier();
11777   }
11778
11779   for (i = 0; i < MAX_PLAYERS; i++)
11780   {
11781     struct PlayerInfo *player = &stored_player[i];
11782
11783     if (SHIELD_ON(player))
11784     {
11785       if (player->shield_deadly_time_left)
11786         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11787       else if (player->shield_normal_time_left)
11788         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11789     }
11790   }
11791
11792 #if USE_DELAYED_GFX_REDRAW
11793   SCAN_PLAYFIELD(x, y)
11794   {
11795     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11796     {
11797       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11798          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11799
11800       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11801         DrawLevelField(x, y);
11802
11803       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11804         DrawLevelFieldCrumbled(x, y);
11805
11806       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11807         DrawLevelFieldCrumbledNeighbours(x, y);
11808
11809       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11810         DrawTwinkleOnField(x, y);
11811     }
11812
11813     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11814   }
11815 #endif
11816
11817   DrawAllPlayers();
11818   PlayAllPlayersSound();
11819
11820   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11821   {
11822     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11823
11824     local_player->show_envelope = 0;
11825   }
11826
11827   /* use random number generator in every frame to make it less predictable */
11828   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11829     RND(1);
11830 }
11831
11832 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11833 {
11834   int min_x = x, min_y = y, max_x = x, max_y = y;
11835   int i;
11836
11837   for (i = 0; i < MAX_PLAYERS; i++)
11838   {
11839     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11840
11841     if (!stored_player[i].active || &stored_player[i] == player)
11842       continue;
11843
11844     min_x = MIN(min_x, jx);
11845     min_y = MIN(min_y, jy);
11846     max_x = MAX(max_x, jx);
11847     max_y = MAX(max_y, jy);
11848   }
11849
11850   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11851 }
11852
11853 static boolean AllPlayersInVisibleScreen()
11854 {
11855   int i;
11856
11857   for (i = 0; i < MAX_PLAYERS; i++)
11858   {
11859     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11860
11861     if (!stored_player[i].active)
11862       continue;
11863
11864     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11865       return FALSE;
11866   }
11867
11868   return TRUE;
11869 }
11870
11871 void ScrollLevel(int dx, int dy)
11872 {
11873   int scroll_offset = 2 * TILEX_VAR;
11874   int x, y;
11875
11876   BlitBitmap(drawto_field, drawto_field,
11877              FX + TILEX_VAR * (dx == -1) - scroll_offset,
11878              FY + TILEY_VAR * (dy == -1) - scroll_offset,
11879              SXSIZE - TILEX_VAR * (dx != 0) + 2 * scroll_offset,
11880              SYSIZE - TILEY_VAR * (dy != 0) + 2 * scroll_offset,
11881              FX + TILEX_VAR * (dx == 1) - scroll_offset,
11882              FY + TILEY_VAR * (dy == 1) - scroll_offset);
11883
11884   if (dx != 0)
11885   {
11886     x = (dx == 1 ? BX1 : BX2);
11887     for (y = BY1; y <= BY2; y++)
11888       DrawScreenField(x, y);
11889   }
11890
11891   if (dy != 0)
11892   {
11893     y = (dy == 1 ? BY1 : BY2);
11894     for (x = BX1; x <= BX2; x++)
11895       DrawScreenField(x, y);
11896   }
11897
11898   redraw_mask |= REDRAW_FIELD;
11899 }
11900
11901 static boolean canFallDown(struct PlayerInfo *player)
11902 {
11903   int jx = player->jx, jy = player->jy;
11904
11905   return (IN_LEV_FIELD(jx, jy + 1) &&
11906           (IS_FREE(jx, jy + 1) ||
11907            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11908           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11909           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11910 }
11911
11912 static boolean canPassField(int x, int y, int move_dir)
11913 {
11914   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11915   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11916   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11917   int nextx = x + dx;
11918   int nexty = y + dy;
11919   int element = Feld[x][y];
11920
11921   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11922           !CAN_MOVE(element) &&
11923           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11924           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11925           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11926 }
11927
11928 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11929 {
11930   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11931   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11932   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11933   int newx = x + dx;
11934   int newy = y + dy;
11935
11936   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11937           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11938           (IS_DIGGABLE(Feld[newx][newy]) ||
11939            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11940            canPassField(newx, newy, move_dir)));
11941 }
11942
11943 static void CheckGravityMovement(struct PlayerInfo *player)
11944 {
11945   if (player->gravity && !player->programmed_action)
11946   {
11947     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11948     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11949     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11950     int jx = player->jx, jy = player->jy;
11951     boolean player_is_moving_to_valid_field =
11952       (!player_is_snapping &&
11953        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11954         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11955     boolean player_can_fall_down = canFallDown(player);
11956
11957     if (player_can_fall_down &&
11958         !player_is_moving_to_valid_field)
11959       player->programmed_action = MV_DOWN;
11960   }
11961 }
11962
11963 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11964 {
11965   return CheckGravityMovement(player);
11966
11967   if (player->gravity && !player->programmed_action)
11968   {
11969     int jx = player->jx, jy = player->jy;
11970     boolean field_under_player_is_free =
11971       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11972     boolean player_is_standing_on_valid_field =
11973       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11974        (IS_WALKABLE(Feld[jx][jy]) &&
11975         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11976
11977     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11978       player->programmed_action = MV_DOWN;
11979   }
11980 }
11981
11982 /*
11983   MovePlayerOneStep()
11984   -----------------------------------------------------------------------------
11985   dx, dy:               direction (non-diagonal) to try to move the player to
11986   real_dx, real_dy:     direction as read from input device (can be diagonal)
11987 */
11988
11989 boolean MovePlayerOneStep(struct PlayerInfo *player,
11990                           int dx, int dy, int real_dx, int real_dy)
11991 {
11992   int jx = player->jx, jy = player->jy;
11993   int new_jx = jx + dx, new_jy = jy + dy;
11994   int can_move;
11995   boolean player_can_move = !player->cannot_move;
11996
11997   if (!player->active || (!dx && !dy))
11998     return MP_NO_ACTION;
11999
12000   player->MovDir = (dx < 0 ? MV_LEFT :
12001                     dx > 0 ? MV_RIGHT :
12002                     dy < 0 ? MV_UP :
12003                     dy > 0 ? MV_DOWN :  MV_NONE);
12004
12005   if (!IN_LEV_FIELD(new_jx, new_jy))
12006     return MP_NO_ACTION;
12007
12008   if (!player_can_move)
12009   {
12010     if (player->MovPos == 0)
12011     {
12012       player->is_moving = FALSE;
12013       player->is_digging = FALSE;
12014       player->is_collecting = FALSE;
12015       player->is_snapping = FALSE;
12016       player->is_pushing = FALSE;
12017     }
12018   }
12019
12020   if (!options.network && game.centered_player_nr == -1 &&
12021       !AllPlayersInSight(player, new_jx, new_jy))
12022     return MP_NO_ACTION;
12023
12024   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
12025   if (can_move != MP_MOVING)
12026     return can_move;
12027
12028   /* check if DigField() has caused relocation of the player */
12029   if (player->jx != jx || player->jy != jy)
12030     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
12031
12032   StorePlayer[jx][jy] = 0;
12033   player->last_jx = jx;
12034   player->last_jy = jy;
12035   player->jx = new_jx;
12036   player->jy = new_jy;
12037   StorePlayer[new_jx][new_jy] = player->element_nr;
12038
12039   if (player->move_delay_value_next != -1)
12040   {
12041     player->move_delay_value = player->move_delay_value_next;
12042     player->move_delay_value_next = -1;
12043   }
12044
12045   player->MovPos =
12046     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
12047
12048   player->step_counter++;
12049
12050   PlayerVisit[jx][jy] = FrameCounter;
12051
12052   player->is_moving = TRUE;
12053
12054 #if 1
12055   /* should better be called in MovePlayer(), but this breaks some tapes */
12056   ScrollPlayer(player, SCROLL_INIT);
12057 #endif
12058
12059   return MP_MOVING;
12060 }
12061
12062 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
12063 {
12064   int jx = player->jx, jy = player->jy;
12065   int old_jx = jx, old_jy = jy;
12066   int moved = MP_NO_ACTION;
12067
12068   if (!player->active)
12069     return FALSE;
12070
12071   if (!dx && !dy)
12072   {
12073     if (player->MovPos == 0)
12074     {
12075       player->is_moving = FALSE;
12076       player->is_digging = FALSE;
12077       player->is_collecting = FALSE;
12078       player->is_snapping = FALSE;
12079       player->is_pushing = FALSE;
12080     }
12081
12082     return FALSE;
12083   }
12084
12085   if (player->move_delay > 0)
12086     return FALSE;
12087
12088   player->move_delay = -1;              /* set to "uninitialized" value */
12089
12090   /* store if player is automatically moved to next field */
12091   player->is_auto_moving = (player->programmed_action != MV_NONE);
12092
12093   /* remove the last programmed player action */
12094   player->programmed_action = 0;
12095
12096   if (player->MovPos)
12097   {
12098     /* should only happen if pre-1.2 tape recordings are played */
12099     /* this is only for backward compatibility */
12100
12101     int original_move_delay_value = player->move_delay_value;
12102
12103 #if DEBUG
12104     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12105            tape.counter);
12106 #endif
12107
12108     /* scroll remaining steps with finest movement resolution */
12109     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12110
12111     while (player->MovPos)
12112     {
12113       ScrollPlayer(player, SCROLL_GO_ON);
12114       ScrollScreen(NULL, SCROLL_GO_ON);
12115
12116       AdvanceFrameAndPlayerCounters(player->index_nr);
12117
12118       DrawAllPlayers();
12119       BackToFront();
12120     }
12121
12122     player->move_delay_value = original_move_delay_value;
12123   }
12124
12125   player->is_active = FALSE;
12126
12127   if (player->last_move_dir & MV_HORIZONTAL)
12128   {
12129     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12130       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12131   }
12132   else
12133   {
12134     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12135       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12136   }
12137
12138   if (!moved && !player->is_active)
12139   {
12140     player->is_moving = FALSE;
12141     player->is_digging = FALSE;
12142     player->is_collecting = FALSE;
12143     player->is_snapping = FALSE;
12144     player->is_pushing = FALSE;
12145   }
12146
12147   jx = player->jx;
12148   jy = player->jy;
12149
12150   if (moved & MP_MOVING && !ScreenMovPos &&
12151       (player->index_nr == game.centered_player_nr ||
12152        game.centered_player_nr == -1))
12153   {
12154     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12155     int offset = game.scroll_delay_value;
12156
12157     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12158     {
12159       /* actual player has left the screen -- scroll in that direction */
12160       if (jx != old_jx)         /* player has moved horizontally */
12161         scroll_x += (jx - old_jx);
12162       else                      /* player has moved vertically */
12163         scroll_y += (jy - old_jy);
12164     }
12165     else
12166     {
12167       if (jx != old_jx)         /* player has moved horizontally */
12168       {
12169         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12170             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12171           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12172
12173         /* don't scroll over playfield boundaries */
12174         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12175           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12176
12177         /* don't scroll more than one field at a time */
12178         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12179
12180         /* don't scroll against the player's moving direction */
12181         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12182             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12183           scroll_x = old_scroll_x;
12184       }
12185       else                      /* player has moved vertically */
12186       {
12187         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12188             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12189           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12190
12191         /* don't scroll over playfield boundaries */
12192         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12193           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12194
12195         /* don't scroll more than one field at a time */
12196         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12197
12198         /* don't scroll against the player's moving direction */
12199         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12200             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12201           scroll_y = old_scroll_y;
12202       }
12203     }
12204
12205     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12206     {
12207       if (!options.network && game.centered_player_nr == -1 &&
12208           !AllPlayersInVisibleScreen())
12209       {
12210         scroll_x = old_scroll_x;
12211         scroll_y = old_scroll_y;
12212       }
12213       else
12214       {
12215         ScrollScreen(player, SCROLL_INIT);
12216         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12217       }
12218     }
12219   }
12220
12221   player->StepFrame = 0;
12222
12223   if (moved & MP_MOVING)
12224   {
12225     if (old_jx != jx && old_jy == jy)
12226       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12227     else if (old_jx == jx && old_jy != jy)
12228       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12229
12230     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12231
12232     player->last_move_dir = player->MovDir;
12233     player->is_moving = TRUE;
12234     player->is_snapping = FALSE;
12235     player->is_switching = FALSE;
12236     player->is_dropping = FALSE;
12237     player->is_dropping_pressed = FALSE;
12238     player->drop_pressed_delay = 0;
12239
12240 #if 0
12241     /* should better be called here than above, but this breaks some tapes */
12242     ScrollPlayer(player, SCROLL_INIT);
12243 #endif
12244   }
12245   else
12246   {
12247     CheckGravityMovementWhenNotMoving(player);
12248
12249     player->is_moving = FALSE;
12250
12251     /* at this point, the player is allowed to move, but cannot move right now
12252        (e.g. because of something blocking the way) -- ensure that the player
12253        is also allowed to move in the next frame (in old versions before 3.1.1,
12254        the player was forced to wait again for eight frames before next try) */
12255
12256     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12257       player->move_delay = 0;   /* allow direct movement in the next frame */
12258   }
12259
12260   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12261     player->move_delay = player->move_delay_value;
12262
12263   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12264   {
12265     TestIfPlayerTouchesBadThing(jx, jy);
12266     TestIfPlayerTouchesCustomElement(jx, jy);
12267   }
12268
12269   if (!player->active)
12270     RemovePlayer(player);
12271
12272   return moved;
12273 }
12274
12275 void ScrollPlayer(struct PlayerInfo *player, int mode)
12276 {
12277   int jx = player->jx, jy = player->jy;
12278   int last_jx = player->last_jx, last_jy = player->last_jy;
12279   int move_stepsize = TILEX / player->move_delay_value;
12280
12281   if (!player->active)
12282     return;
12283
12284   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12285     return;
12286
12287   if (mode == SCROLL_INIT)
12288   {
12289     player->actual_frame_counter = FrameCounter;
12290     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12291
12292     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12293         Feld[last_jx][last_jy] == EL_EMPTY)
12294     {
12295       int last_field_block_delay = 0;   /* start with no blocking at all */
12296       int block_delay_adjustment = player->block_delay_adjustment;
12297
12298       /* if player blocks last field, add delay for exactly one move */
12299       if (player->block_last_field)
12300       {
12301         last_field_block_delay += player->move_delay_value;
12302
12303         /* when blocking enabled, prevent moving up despite gravity */
12304         if (player->gravity && player->MovDir == MV_UP)
12305           block_delay_adjustment = -1;
12306       }
12307
12308       /* add block delay adjustment (also possible when not blocking) */
12309       last_field_block_delay += block_delay_adjustment;
12310
12311       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12312       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12313     }
12314
12315     if (player->MovPos != 0)    /* player has not yet reached destination */
12316       return;
12317   }
12318   else if (!FrameReached(&player->actual_frame_counter, 1))
12319     return;
12320
12321   if (player->MovPos != 0)
12322   {
12323     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12324     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12325
12326     /* before DrawPlayer() to draw correct player graphic for this case */
12327     if (player->MovPos == 0)
12328       CheckGravityMovement(player);
12329   }
12330
12331   if (player->MovPos == 0)      /* player reached destination field */
12332   {
12333     if (player->move_delay_reset_counter > 0)
12334     {
12335       player->move_delay_reset_counter--;
12336
12337       if (player->move_delay_reset_counter == 0)
12338       {
12339         /* continue with normal speed after quickly moving through gate */
12340         HALVE_PLAYER_SPEED(player);
12341
12342         /* be able to make the next move without delay */
12343         player->move_delay = 0;
12344       }
12345     }
12346
12347     player->last_jx = jx;
12348     player->last_jy = jy;
12349
12350     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12351         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12352         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12353         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12354         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12355         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12356         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12357         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12358     {
12359       DrawPlayer(player);       /* needed here only to cleanup last field */
12360       RemovePlayer(player);
12361
12362       if (local_player->friends_still_needed == 0 ||
12363           IS_SP_ELEMENT(Feld[jx][jy]))
12364         PlayerWins(player);
12365     }
12366
12367     /* this breaks one level: "machine", level 000 */
12368     {
12369       int move_direction = player->MovDir;
12370       int enter_side = MV_DIR_OPPOSITE(move_direction);
12371       int leave_side = move_direction;
12372       int old_jx = last_jx;
12373       int old_jy = last_jy;
12374       int old_element = Feld[old_jx][old_jy];
12375       int new_element = Feld[jx][jy];
12376
12377       if (IS_CUSTOM_ELEMENT(old_element))
12378         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12379                                    CE_LEFT_BY_PLAYER,
12380                                    player->index_bit, leave_side);
12381
12382       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12383                                           CE_PLAYER_LEAVES_X,
12384                                           player->index_bit, leave_side);
12385
12386       if (IS_CUSTOM_ELEMENT(new_element))
12387         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12388                                    player->index_bit, enter_side);
12389
12390       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12391                                           CE_PLAYER_ENTERS_X,
12392                                           player->index_bit, enter_side);
12393
12394       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12395                                         CE_MOVE_OF_X, move_direction);
12396     }
12397
12398     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12399     {
12400       TestIfPlayerTouchesBadThing(jx, jy);
12401       TestIfPlayerTouchesCustomElement(jx, jy);
12402
12403       /* needed because pushed element has not yet reached its destination,
12404          so it would trigger a change event at its previous field location */
12405       if (!player->is_pushing)
12406         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12407
12408       if (!player->active)
12409         RemovePlayer(player);
12410     }
12411
12412     if (!local_player->LevelSolved && level.use_step_counter)
12413     {
12414       int i;
12415
12416       TimePlayed++;
12417
12418       if (TimeLeft > 0)
12419       {
12420         TimeLeft--;
12421
12422         if (TimeLeft <= 10 && setup.time_limit)
12423           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12424
12425         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12426
12427         DisplayGameControlValues();
12428
12429         if (!TimeLeft && setup.time_limit)
12430           for (i = 0; i < MAX_PLAYERS; i++)
12431             KillPlayer(&stored_player[i]);
12432       }
12433       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12434       {
12435         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12436
12437         DisplayGameControlValues();
12438       }
12439     }
12440
12441     if (tape.single_step && tape.recording && !tape.pausing &&
12442         !player->programmed_action)
12443       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12444
12445     if (!player->programmed_action)
12446       CheckSaveEngineSnapshot(player);
12447   }
12448 }
12449
12450 void ScrollScreen(struct PlayerInfo *player, int mode)
12451 {
12452   static unsigned int screen_frame_counter = 0;
12453
12454   if (mode == SCROLL_INIT)
12455   {
12456     /* set scrolling step size according to actual player's moving speed */
12457     ScrollStepSize = TILEX / player->move_delay_value;
12458
12459     screen_frame_counter = FrameCounter;
12460     ScreenMovDir = player->MovDir;
12461     ScreenMovPos = player->MovPos;
12462     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12463     return;
12464   }
12465   else if (!FrameReached(&screen_frame_counter, 1))
12466     return;
12467
12468   if (ScreenMovPos)
12469   {
12470     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12471     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12472     redraw_mask |= REDRAW_FIELD;
12473   }
12474   else
12475     ScreenMovDir = MV_NONE;
12476 }
12477
12478 void TestIfPlayerTouchesCustomElement(int x, int y)
12479 {
12480   static int xy[4][2] =
12481   {
12482     { 0, -1 },
12483     { -1, 0 },
12484     { +1, 0 },
12485     { 0, +1 }
12486   };
12487   static int trigger_sides[4][2] =
12488   {
12489     /* center side       border side */
12490     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12491     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12492     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12493     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12494   };
12495   static int touch_dir[4] =
12496   {
12497     MV_LEFT | MV_RIGHT,
12498     MV_UP   | MV_DOWN,
12499     MV_UP   | MV_DOWN,
12500     MV_LEFT | MV_RIGHT
12501   };
12502   int center_element = Feld[x][y];      /* should always be non-moving! */
12503   int i;
12504
12505   for (i = 0; i < NUM_DIRECTIONS; i++)
12506   {
12507     int xx = x + xy[i][0];
12508     int yy = y + xy[i][1];
12509     int center_side = trigger_sides[i][0];
12510     int border_side = trigger_sides[i][1];
12511     int border_element;
12512
12513     if (!IN_LEV_FIELD(xx, yy))
12514       continue;
12515
12516     if (IS_PLAYER(x, y))                /* player found at center element */
12517     {
12518       struct PlayerInfo *player = PLAYERINFO(x, y);
12519
12520       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12521         border_element = Feld[xx][yy];          /* may be moving! */
12522       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12523         border_element = Feld[xx][yy];
12524       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12525         border_element = MovingOrBlocked2Element(xx, yy);
12526       else
12527         continue;               /* center and border element do not touch */
12528
12529       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12530                                  player->index_bit, border_side);
12531       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12532                                           CE_PLAYER_TOUCHES_X,
12533                                           player->index_bit, border_side);
12534
12535       {
12536         /* use player element that is initially defined in the level playfield,
12537            not the player element that corresponds to the runtime player number
12538            (example: a level that contains EL_PLAYER_3 as the only player would
12539            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12540         int player_element = PLAYERINFO(x, y)->initial_element;
12541
12542         CheckElementChangeBySide(xx, yy, border_element, player_element,
12543                                  CE_TOUCHING_X, border_side);
12544       }
12545     }
12546     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12547     {
12548       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12549
12550       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12551       {
12552         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12553           continue;             /* center and border element do not touch */
12554       }
12555
12556       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12557                                  player->index_bit, center_side);
12558       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12559                                           CE_PLAYER_TOUCHES_X,
12560                                           player->index_bit, center_side);
12561
12562       {
12563         /* use player element that is initially defined in the level playfield,
12564            not the player element that corresponds to the runtime player number
12565            (example: a level that contains EL_PLAYER_3 as the only player would
12566            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12567         int player_element = PLAYERINFO(xx, yy)->initial_element;
12568
12569         CheckElementChangeBySide(x, y, center_element, player_element,
12570                                  CE_TOUCHING_X, center_side);
12571       }
12572
12573       break;
12574     }
12575   }
12576 }
12577
12578 void TestIfElementTouchesCustomElement(int x, int y)
12579 {
12580   static int xy[4][2] =
12581   {
12582     { 0, -1 },
12583     { -1, 0 },
12584     { +1, 0 },
12585     { 0, +1 }
12586   };
12587   static int trigger_sides[4][2] =
12588   {
12589     /* center side      border side */
12590     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12591     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12592     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12593     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12594   };
12595   static int touch_dir[4] =
12596   {
12597     MV_LEFT | MV_RIGHT,
12598     MV_UP   | MV_DOWN,
12599     MV_UP   | MV_DOWN,
12600     MV_LEFT | MV_RIGHT
12601   };
12602   boolean change_center_element = FALSE;
12603   int center_element = Feld[x][y];      /* should always be non-moving! */
12604   int border_element_old[NUM_DIRECTIONS];
12605   int i;
12606
12607   for (i = 0; i < NUM_DIRECTIONS; i++)
12608   {
12609     int xx = x + xy[i][0];
12610     int yy = y + xy[i][1];
12611     int border_element;
12612
12613     border_element_old[i] = -1;
12614
12615     if (!IN_LEV_FIELD(xx, yy))
12616       continue;
12617
12618     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12619       border_element = Feld[xx][yy];    /* may be moving! */
12620     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12621       border_element = Feld[xx][yy];
12622     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12623       border_element = MovingOrBlocked2Element(xx, yy);
12624     else
12625       continue;                 /* center and border element do not touch */
12626
12627     border_element_old[i] = border_element;
12628   }
12629
12630   for (i = 0; i < NUM_DIRECTIONS; i++)
12631   {
12632     int xx = x + xy[i][0];
12633     int yy = y + xy[i][1];
12634     int center_side = trigger_sides[i][0];
12635     int border_element = border_element_old[i];
12636
12637     if (border_element == -1)
12638       continue;
12639
12640     /* check for change of border element */
12641     CheckElementChangeBySide(xx, yy, border_element, center_element,
12642                              CE_TOUCHING_X, center_side);
12643
12644     /* (center element cannot be player, so we dont have to check this here) */
12645   }
12646
12647   for (i = 0; i < NUM_DIRECTIONS; i++)
12648   {
12649     int xx = x + xy[i][0];
12650     int yy = y + xy[i][1];
12651     int border_side = trigger_sides[i][1];
12652     int border_element = border_element_old[i];
12653
12654     if (border_element == -1)
12655       continue;
12656
12657     /* check for change of center element (but change it only once) */
12658     if (!change_center_element)
12659       change_center_element =
12660         CheckElementChangeBySide(x, y, center_element, border_element,
12661                                  CE_TOUCHING_X, border_side);
12662
12663     if (IS_PLAYER(xx, yy))
12664     {
12665       /* use player element that is initially defined in the level playfield,
12666          not the player element that corresponds to the runtime player number
12667          (example: a level that contains EL_PLAYER_3 as the only player would
12668          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12669       int player_element = PLAYERINFO(xx, yy)->initial_element;
12670
12671       CheckElementChangeBySide(x, y, center_element, player_element,
12672                                CE_TOUCHING_X, border_side);
12673     }
12674   }
12675 }
12676
12677 void TestIfElementHitsCustomElement(int x, int y, int direction)
12678 {
12679   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12680   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12681   int hitx = x + dx, hity = y + dy;
12682   int hitting_element = Feld[x][y];
12683   int touched_element;
12684
12685   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12686     return;
12687
12688   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12689                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12690
12691   if (IN_LEV_FIELD(hitx, hity))
12692   {
12693     int opposite_direction = MV_DIR_OPPOSITE(direction);
12694     int hitting_side = direction;
12695     int touched_side = opposite_direction;
12696     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12697                           MovDir[hitx][hity] != direction ||
12698                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12699
12700     object_hit = TRUE;
12701
12702     if (object_hit)
12703     {
12704       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12705                                CE_HITTING_X, touched_side);
12706
12707       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12708                                CE_HIT_BY_X, hitting_side);
12709
12710       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12711                                CE_HIT_BY_SOMETHING, opposite_direction);
12712
12713       if (IS_PLAYER(hitx, hity))
12714       {
12715         /* use player element that is initially defined in the level playfield,
12716            not the player element that corresponds to the runtime player number
12717            (example: a level that contains EL_PLAYER_3 as the only player would
12718            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12719         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12720
12721         CheckElementChangeBySide(x, y, hitting_element, player_element,
12722                                  CE_HITTING_X, touched_side);
12723       }
12724     }
12725   }
12726
12727   /* "hitting something" is also true when hitting the playfield border */
12728   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12729                            CE_HITTING_SOMETHING, direction);
12730 }
12731
12732 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12733 {
12734   int i, kill_x = -1, kill_y = -1;
12735
12736   int bad_element = -1;
12737   static int test_xy[4][2] =
12738   {
12739     { 0, -1 },
12740     { -1, 0 },
12741     { +1, 0 },
12742     { 0, +1 }
12743   };
12744   static int test_dir[4] =
12745   {
12746     MV_UP,
12747     MV_LEFT,
12748     MV_RIGHT,
12749     MV_DOWN
12750   };
12751
12752   for (i = 0; i < NUM_DIRECTIONS; i++)
12753   {
12754     int test_x, test_y, test_move_dir, test_element;
12755
12756     test_x = good_x + test_xy[i][0];
12757     test_y = good_y + test_xy[i][1];
12758
12759     if (!IN_LEV_FIELD(test_x, test_y))
12760       continue;
12761
12762     test_move_dir =
12763       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12764
12765     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12766
12767     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12768        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12769     */
12770     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12771         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12772     {
12773       kill_x = test_x;
12774       kill_y = test_y;
12775       bad_element = test_element;
12776
12777       break;
12778     }
12779   }
12780
12781   if (kill_x != -1 || kill_y != -1)
12782   {
12783     if (IS_PLAYER(good_x, good_y))
12784     {
12785       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12786
12787       if (player->shield_deadly_time_left > 0 &&
12788           !IS_INDESTRUCTIBLE(bad_element))
12789         Bang(kill_x, kill_y);
12790       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12791         KillPlayer(player);
12792     }
12793     else
12794       Bang(good_x, good_y);
12795   }
12796 }
12797
12798 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12799 {
12800   int i, kill_x = -1, kill_y = -1;
12801   int bad_element = Feld[bad_x][bad_y];
12802   static int test_xy[4][2] =
12803   {
12804     { 0, -1 },
12805     { -1, 0 },
12806     { +1, 0 },
12807     { 0, +1 }
12808   };
12809   static int touch_dir[4] =
12810   {
12811     MV_LEFT | MV_RIGHT,
12812     MV_UP   | MV_DOWN,
12813     MV_UP   | MV_DOWN,
12814     MV_LEFT | MV_RIGHT
12815   };
12816   static int test_dir[4] =
12817   {
12818     MV_UP,
12819     MV_LEFT,
12820     MV_RIGHT,
12821     MV_DOWN
12822   };
12823
12824   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12825     return;
12826
12827   for (i = 0; i < NUM_DIRECTIONS; i++)
12828   {
12829     int test_x, test_y, test_move_dir, test_element;
12830
12831     test_x = bad_x + test_xy[i][0];
12832     test_y = bad_y + test_xy[i][1];
12833
12834     if (!IN_LEV_FIELD(test_x, test_y))
12835       continue;
12836
12837     test_move_dir =
12838       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12839
12840     test_element = Feld[test_x][test_y];
12841
12842     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12843        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12844     */
12845     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12846         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12847     {
12848       /* good thing is player or penguin that does not move away */
12849       if (IS_PLAYER(test_x, test_y))
12850       {
12851         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12852
12853         if (bad_element == EL_ROBOT && player->is_moving)
12854           continue;     /* robot does not kill player if he is moving */
12855
12856         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12857         {
12858           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12859             continue;           /* center and border element do not touch */
12860         }
12861
12862         kill_x = test_x;
12863         kill_y = test_y;
12864
12865         break;
12866       }
12867       else if (test_element == EL_PENGUIN)
12868       {
12869         kill_x = test_x;
12870         kill_y = test_y;
12871
12872         break;
12873       }
12874     }
12875   }
12876
12877   if (kill_x != -1 || kill_y != -1)
12878   {
12879     if (IS_PLAYER(kill_x, kill_y))
12880     {
12881       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12882
12883       if (player->shield_deadly_time_left > 0 &&
12884           !IS_INDESTRUCTIBLE(bad_element))
12885         Bang(bad_x, bad_y);
12886       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12887         KillPlayer(player);
12888     }
12889     else
12890       Bang(kill_x, kill_y);
12891   }
12892 }
12893
12894 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12895 {
12896   int bad_element = Feld[bad_x][bad_y];
12897   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12898   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12899   int test_x = bad_x + dx, test_y = bad_y + dy;
12900   int test_move_dir, test_element;
12901   int kill_x = -1, kill_y = -1;
12902
12903   if (!IN_LEV_FIELD(test_x, test_y))
12904     return;
12905
12906   test_move_dir =
12907     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12908
12909   test_element = Feld[test_x][test_y];
12910
12911   if (test_move_dir != bad_move_dir)
12912   {
12913     /* good thing can be player or penguin that does not move away */
12914     if (IS_PLAYER(test_x, test_y))
12915     {
12916       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12917
12918       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12919          player as being hit when he is moving towards the bad thing, because
12920          the "get hit by" condition would be lost after the player stops) */
12921       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12922         return;         /* player moves away from bad thing */
12923
12924       kill_x = test_x;
12925       kill_y = test_y;
12926     }
12927     else if (test_element == EL_PENGUIN)
12928     {
12929       kill_x = test_x;
12930       kill_y = test_y;
12931     }
12932   }
12933
12934   if (kill_x != -1 || kill_y != -1)
12935   {
12936     if (IS_PLAYER(kill_x, kill_y))
12937     {
12938       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12939
12940       if (player->shield_deadly_time_left > 0 &&
12941           !IS_INDESTRUCTIBLE(bad_element))
12942         Bang(bad_x, bad_y);
12943       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12944         KillPlayer(player);
12945     }
12946     else
12947       Bang(kill_x, kill_y);
12948   }
12949 }
12950
12951 void TestIfPlayerTouchesBadThing(int x, int y)
12952 {
12953   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12954 }
12955
12956 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12957 {
12958   TestIfGoodThingHitsBadThing(x, y, move_dir);
12959 }
12960
12961 void TestIfBadThingTouchesPlayer(int x, int y)
12962 {
12963   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12964 }
12965
12966 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12967 {
12968   TestIfBadThingHitsGoodThing(x, y, move_dir);
12969 }
12970
12971 void TestIfFriendTouchesBadThing(int x, int y)
12972 {
12973   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12974 }
12975
12976 void TestIfBadThingTouchesFriend(int x, int y)
12977 {
12978   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12979 }
12980
12981 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12982 {
12983   int i, kill_x = bad_x, kill_y = bad_y;
12984   static int xy[4][2] =
12985   {
12986     { 0, -1 },
12987     { -1, 0 },
12988     { +1, 0 },
12989     { 0, +1 }
12990   };
12991
12992   for (i = 0; i < NUM_DIRECTIONS; i++)
12993   {
12994     int x, y, element;
12995
12996     x = bad_x + xy[i][0];
12997     y = bad_y + xy[i][1];
12998     if (!IN_LEV_FIELD(x, y))
12999       continue;
13000
13001     element = Feld[x][y];
13002     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
13003         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
13004     {
13005       kill_x = x;
13006       kill_y = y;
13007       break;
13008     }
13009   }
13010
13011   if (kill_x != bad_x || kill_y != bad_y)
13012     Bang(bad_x, bad_y);
13013 }
13014
13015 void KillPlayer(struct PlayerInfo *player)
13016 {
13017   int jx = player->jx, jy = player->jy;
13018
13019   if (!player->active)
13020     return;
13021
13022 #if 0
13023   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
13024          player->killed, player->active, player->reanimated);
13025 #endif
13026
13027   /* the following code was introduced to prevent an infinite loop when calling
13028      -> Bang()
13029      -> CheckTriggeredElementChangeExt()
13030      -> ExecuteCustomElementAction()
13031      -> KillPlayer()
13032      -> (infinitely repeating the above sequence of function calls)
13033      which occurs when killing the player while having a CE with the setting
13034      "kill player X when explosion of <player X>"; the solution using a new
13035      field "player->killed" was chosen for backwards compatibility, although
13036      clever use of the fields "player->active" etc. would probably also work */
13037 #if 1
13038   if (player->killed)
13039     return;
13040 #endif
13041
13042   player->killed = TRUE;
13043
13044   /* remove accessible field at the player's position */
13045   Feld[jx][jy] = EL_EMPTY;
13046
13047   /* deactivate shield (else Bang()/Explode() would not work right) */
13048   player->shield_normal_time_left = 0;
13049   player->shield_deadly_time_left = 0;
13050
13051 #if 0
13052   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
13053          player->killed, player->active, player->reanimated);
13054 #endif
13055
13056   Bang(jx, jy);
13057
13058 #if 0
13059   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
13060          player->killed, player->active, player->reanimated);
13061 #endif
13062
13063   if (player->reanimated)       /* killed player may have been reanimated */
13064     player->killed = player->reanimated = FALSE;
13065   else
13066     BuryPlayer(player);
13067 }
13068
13069 static void KillPlayerUnlessEnemyProtected(int x, int y)
13070 {
13071   if (!PLAYER_ENEMY_PROTECTED(x, y))
13072     KillPlayer(PLAYERINFO(x, y));
13073 }
13074
13075 static void KillPlayerUnlessExplosionProtected(int x, int y)
13076 {
13077   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13078     KillPlayer(PLAYERINFO(x, y));
13079 }
13080
13081 void BuryPlayer(struct PlayerInfo *player)
13082 {
13083   int jx = player->jx, jy = player->jy;
13084
13085   if (!player->active)
13086     return;
13087
13088   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13089   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13090
13091   player->GameOver = TRUE;
13092   RemovePlayer(player);
13093 }
13094
13095 void RemovePlayer(struct PlayerInfo *player)
13096 {
13097   int jx = player->jx, jy = player->jy;
13098   int i, found = FALSE;
13099
13100   player->present = FALSE;
13101   player->active = FALSE;
13102
13103   if (!ExplodeField[jx][jy])
13104     StorePlayer[jx][jy] = 0;
13105
13106   if (player->is_moving)
13107     TEST_DrawLevelField(player->last_jx, player->last_jy);
13108
13109   for (i = 0; i < MAX_PLAYERS; i++)
13110     if (stored_player[i].active)
13111       found = TRUE;
13112
13113   if (!found)
13114     AllPlayersGone = TRUE;
13115
13116   ExitX = ZX = jx;
13117   ExitY = ZY = jy;
13118 }
13119
13120 static void setFieldForSnapping(int x, int y, int element, int direction)
13121 {
13122   struct ElementInfo *ei = &element_info[element];
13123   int direction_bit = MV_DIR_TO_BIT(direction);
13124   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13125   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13126                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13127
13128   Feld[x][y] = EL_ELEMENT_SNAPPING;
13129   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13130
13131   ResetGfxAnimation(x, y);
13132
13133   GfxElement[x][y] = element;
13134   GfxAction[x][y] = action;
13135   GfxDir[x][y] = direction;
13136   GfxFrame[x][y] = -1;
13137 }
13138
13139 /*
13140   =============================================================================
13141   checkDiagonalPushing()
13142   -----------------------------------------------------------------------------
13143   check if diagonal input device direction results in pushing of object
13144   (by checking if the alternative direction is walkable, diggable, ...)
13145   =============================================================================
13146 */
13147
13148 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13149                                     int x, int y, int real_dx, int real_dy)
13150 {
13151   int jx, jy, dx, dy, xx, yy;
13152
13153   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13154     return TRUE;
13155
13156   /* diagonal direction: check alternative direction */
13157   jx = player->jx;
13158   jy = player->jy;
13159   dx = x - jx;
13160   dy = y - jy;
13161   xx = jx + (dx == 0 ? real_dx : 0);
13162   yy = jy + (dy == 0 ? real_dy : 0);
13163
13164   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13165 }
13166
13167 /*
13168   =============================================================================
13169   DigField()
13170   -----------------------------------------------------------------------------
13171   x, y:                 field next to player (non-diagonal) to try to dig to
13172   real_dx, real_dy:     direction as read from input device (can be diagonal)
13173   =============================================================================
13174 */
13175
13176 static int DigField(struct PlayerInfo *player,
13177                     int oldx, int oldy, int x, int y,
13178                     int real_dx, int real_dy, int mode)
13179 {
13180   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13181   boolean player_was_pushing = player->is_pushing;
13182   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13183   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13184   int jx = oldx, jy = oldy;
13185   int dx = x - jx, dy = y - jy;
13186   int nextx = x + dx, nexty = y + dy;
13187   int move_direction = (dx == -1 ? MV_LEFT  :
13188                         dx == +1 ? MV_RIGHT :
13189                         dy == -1 ? MV_UP    :
13190                         dy == +1 ? MV_DOWN  : MV_NONE);
13191   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13192   int dig_side = MV_DIR_OPPOSITE(move_direction);
13193   int old_element = Feld[jx][jy];
13194   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13195   int collect_count;
13196
13197   if (is_player)                /* function can also be called by EL_PENGUIN */
13198   {
13199     if (player->MovPos == 0)
13200     {
13201       player->is_digging = FALSE;
13202       player->is_collecting = FALSE;
13203     }
13204
13205     if (player->MovPos == 0)    /* last pushing move finished */
13206       player->is_pushing = FALSE;
13207
13208     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13209     {
13210       player->is_switching = FALSE;
13211       player->push_delay = -1;
13212
13213       return MP_NO_ACTION;
13214     }
13215   }
13216
13217   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13218     old_element = Back[jx][jy];
13219
13220   /* in case of element dropped at player position, check background */
13221   else if (Back[jx][jy] != EL_EMPTY &&
13222            game.engine_version >= VERSION_IDENT(2,2,0,0))
13223     old_element = Back[jx][jy];
13224
13225   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13226     return MP_NO_ACTION;        /* field has no opening in this direction */
13227
13228   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13229     return MP_NO_ACTION;        /* field has no opening in this direction */
13230
13231   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13232   {
13233     SplashAcid(x, y);
13234
13235     Feld[jx][jy] = player->artwork_element;
13236     InitMovingField(jx, jy, MV_DOWN);
13237     Store[jx][jy] = EL_ACID;
13238     ContinueMoving(jx, jy);
13239     BuryPlayer(player);
13240
13241     return MP_DONT_RUN_INTO;
13242   }
13243
13244   if (player_can_move && DONT_RUN_INTO(element))
13245   {
13246     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13247
13248     return MP_DONT_RUN_INTO;
13249   }
13250
13251   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13252     return MP_NO_ACTION;
13253
13254   collect_count = element_info[element].collect_count_initial;
13255
13256   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13257     return MP_NO_ACTION;
13258
13259   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13260     player_can_move = player_can_move_or_snap;
13261
13262   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13263       game.engine_version >= VERSION_IDENT(2,2,0,0))
13264   {
13265     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13266                                player->index_bit, dig_side);
13267     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13268                                         player->index_bit, dig_side);
13269
13270     if (element == EL_DC_LANDMINE)
13271       Bang(x, y);
13272
13273     if (Feld[x][y] != element)          /* field changed by snapping */
13274       return MP_ACTION;
13275
13276     return MP_NO_ACTION;
13277   }
13278
13279   if (player->gravity && is_player && !player->is_auto_moving &&
13280       canFallDown(player) && move_direction != MV_DOWN &&
13281       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13282     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13283
13284   if (player_can_move &&
13285       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13286   {
13287     int sound_element = SND_ELEMENT(element);
13288     int sound_action = ACTION_WALKING;
13289
13290     if (IS_RND_GATE(element))
13291     {
13292       if (!player->key[RND_GATE_NR(element)])
13293         return MP_NO_ACTION;
13294     }
13295     else if (IS_RND_GATE_GRAY(element))
13296     {
13297       if (!player->key[RND_GATE_GRAY_NR(element)])
13298         return MP_NO_ACTION;
13299     }
13300     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13301     {
13302       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13303         return MP_NO_ACTION;
13304     }
13305     else if (element == EL_EXIT_OPEN ||
13306              element == EL_EM_EXIT_OPEN ||
13307              element == EL_EM_EXIT_OPENING ||
13308              element == EL_STEEL_EXIT_OPEN ||
13309              element == EL_EM_STEEL_EXIT_OPEN ||
13310              element == EL_EM_STEEL_EXIT_OPENING ||
13311              element == EL_SP_EXIT_OPEN ||
13312              element == EL_SP_EXIT_OPENING)
13313     {
13314       sound_action = ACTION_PASSING;    /* player is passing exit */
13315     }
13316     else if (element == EL_EMPTY)
13317     {
13318       sound_action = ACTION_MOVING;             /* nothing to walk on */
13319     }
13320
13321     /* play sound from background or player, whatever is available */
13322     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13323       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13324     else
13325       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13326   }
13327   else if (player_can_move &&
13328            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13329   {
13330     if (!ACCESS_FROM(element, opposite_direction))
13331       return MP_NO_ACTION;      /* field not accessible from this direction */
13332
13333     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13334       return MP_NO_ACTION;
13335
13336     if (IS_EM_GATE(element))
13337     {
13338       if (!player->key[EM_GATE_NR(element)])
13339         return MP_NO_ACTION;
13340     }
13341     else if (IS_EM_GATE_GRAY(element))
13342     {
13343       if (!player->key[EM_GATE_GRAY_NR(element)])
13344         return MP_NO_ACTION;
13345     }
13346     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13347     {
13348       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13349         return MP_NO_ACTION;
13350     }
13351     else if (IS_EMC_GATE(element))
13352     {
13353       if (!player->key[EMC_GATE_NR(element)])
13354         return MP_NO_ACTION;
13355     }
13356     else if (IS_EMC_GATE_GRAY(element))
13357     {
13358       if (!player->key[EMC_GATE_GRAY_NR(element)])
13359         return MP_NO_ACTION;
13360     }
13361     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13362     {
13363       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13364         return MP_NO_ACTION;
13365     }
13366     else if (element == EL_DC_GATE_WHITE ||
13367              element == EL_DC_GATE_WHITE_GRAY ||
13368              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13369     {
13370       if (player->num_white_keys == 0)
13371         return MP_NO_ACTION;
13372
13373       player->num_white_keys--;
13374     }
13375     else if (IS_SP_PORT(element))
13376     {
13377       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13378           element == EL_SP_GRAVITY_PORT_RIGHT ||
13379           element == EL_SP_GRAVITY_PORT_UP ||
13380           element == EL_SP_GRAVITY_PORT_DOWN)
13381         player->gravity = !player->gravity;
13382       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13383                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13384                element == EL_SP_GRAVITY_ON_PORT_UP ||
13385                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13386         player->gravity = TRUE;
13387       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13388                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13389                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13390                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13391         player->gravity = FALSE;
13392     }
13393
13394     /* automatically move to the next field with double speed */
13395     player->programmed_action = move_direction;
13396
13397     if (player->move_delay_reset_counter == 0)
13398     {
13399       player->move_delay_reset_counter = 2;     /* two double speed steps */
13400
13401       DOUBLE_PLAYER_SPEED(player);
13402     }
13403
13404     PlayLevelSoundAction(x, y, ACTION_PASSING);
13405   }
13406   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13407   {
13408     RemoveField(x, y);
13409
13410     if (mode != DF_SNAP)
13411     {
13412       GfxElement[x][y] = GFX_ELEMENT(element);
13413       player->is_digging = TRUE;
13414     }
13415
13416     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13417
13418     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13419                                         player->index_bit, dig_side);
13420
13421     if (mode == DF_SNAP)
13422     {
13423       if (level.block_snap_field)
13424         setFieldForSnapping(x, y, element, move_direction);
13425       else
13426         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13427
13428       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13429                                           player->index_bit, dig_side);
13430     }
13431   }
13432   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13433   {
13434     RemoveField(x, y);
13435
13436     if (is_player && mode != DF_SNAP)
13437     {
13438       GfxElement[x][y] = element;
13439       player->is_collecting = TRUE;
13440     }
13441
13442     if (element == EL_SPEED_PILL)
13443     {
13444       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13445     }
13446     else if (element == EL_EXTRA_TIME && level.time > 0)
13447     {
13448       TimeLeft += level.extra_time;
13449
13450       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13451
13452       DisplayGameControlValues();
13453     }
13454     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13455     {
13456       player->shield_normal_time_left += level.shield_normal_time;
13457       if (element == EL_SHIELD_DEADLY)
13458         player->shield_deadly_time_left += level.shield_deadly_time;
13459     }
13460     else if (element == EL_DYNAMITE ||
13461              element == EL_EM_DYNAMITE ||
13462              element == EL_SP_DISK_RED)
13463     {
13464       if (player->inventory_size < MAX_INVENTORY_SIZE)
13465         player->inventory_element[player->inventory_size++] = element;
13466
13467       DrawGameDoorValues();
13468     }
13469     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13470     {
13471       player->dynabomb_count++;
13472       player->dynabombs_left++;
13473     }
13474     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13475     {
13476       player->dynabomb_size++;
13477     }
13478     else if (element == EL_DYNABOMB_INCREASE_POWER)
13479     {
13480       player->dynabomb_xl = TRUE;
13481     }
13482     else if (IS_KEY(element))
13483     {
13484       player->key[KEY_NR(element)] = TRUE;
13485
13486       DrawGameDoorValues();
13487     }
13488     else if (element == EL_DC_KEY_WHITE)
13489     {
13490       player->num_white_keys++;
13491
13492       /* display white keys? */
13493       /* DrawGameDoorValues(); */
13494     }
13495     else if (IS_ENVELOPE(element))
13496     {
13497       player->show_envelope = element;
13498     }
13499     else if (element == EL_EMC_LENSES)
13500     {
13501       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13502
13503       RedrawAllInvisibleElementsForLenses();
13504     }
13505     else if (element == EL_EMC_MAGNIFIER)
13506     {
13507       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13508
13509       RedrawAllInvisibleElementsForMagnifier();
13510     }
13511     else if (IS_DROPPABLE(element) ||
13512              IS_THROWABLE(element))     /* can be collected and dropped */
13513     {
13514       int i;
13515
13516       if (collect_count == 0)
13517         player->inventory_infinite_element = element;
13518       else
13519         for (i = 0; i < collect_count; i++)
13520           if (player->inventory_size < MAX_INVENTORY_SIZE)
13521             player->inventory_element[player->inventory_size++] = element;
13522
13523       DrawGameDoorValues();
13524     }
13525     else if (collect_count > 0)
13526     {
13527       local_player->gems_still_needed -= collect_count;
13528       if (local_player->gems_still_needed < 0)
13529         local_player->gems_still_needed = 0;
13530
13531       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13532
13533       DisplayGameControlValues();
13534     }
13535
13536     RaiseScoreElement(element);
13537     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13538
13539     if (is_player)
13540       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13541                                           player->index_bit, dig_side);
13542
13543     if (mode == DF_SNAP)
13544     {
13545       if (level.block_snap_field)
13546         setFieldForSnapping(x, y, element, move_direction);
13547       else
13548         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13549
13550       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13551                                           player->index_bit, dig_side);
13552     }
13553   }
13554   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13555   {
13556     if (mode == DF_SNAP && element != EL_BD_ROCK)
13557       return MP_NO_ACTION;
13558
13559     if (CAN_FALL(element) && dy)
13560       return MP_NO_ACTION;
13561
13562     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13563         !(element == EL_SPRING && level.use_spring_bug))
13564       return MP_NO_ACTION;
13565
13566     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13567         ((move_direction & MV_VERTICAL &&
13568           ((element_info[element].move_pattern & MV_LEFT &&
13569             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13570            (element_info[element].move_pattern & MV_RIGHT &&
13571             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13572          (move_direction & MV_HORIZONTAL &&
13573           ((element_info[element].move_pattern & MV_UP &&
13574             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13575            (element_info[element].move_pattern & MV_DOWN &&
13576             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13577       return MP_NO_ACTION;
13578
13579     /* do not push elements already moving away faster than player */
13580     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13581         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13582       return MP_NO_ACTION;
13583
13584     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13585     {
13586       if (player->push_delay_value == -1 || !player_was_pushing)
13587         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13588     }
13589     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13590     {
13591       if (player->push_delay_value == -1)
13592         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13593     }
13594     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13595     {
13596       if (!player->is_pushing)
13597         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13598     }
13599
13600     player->is_pushing = TRUE;
13601     player->is_active = TRUE;
13602
13603     if (!(IN_LEV_FIELD(nextx, nexty) &&
13604           (IS_FREE(nextx, nexty) ||
13605            (IS_SB_ELEMENT(element) &&
13606             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13607            (IS_CUSTOM_ELEMENT(element) &&
13608             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13609       return MP_NO_ACTION;
13610
13611     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13612       return MP_NO_ACTION;
13613
13614     if (player->push_delay == -1)       /* new pushing; restart delay */
13615       player->push_delay = 0;
13616
13617     if (player->push_delay < player->push_delay_value &&
13618         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13619         element != EL_SPRING && element != EL_BALLOON)
13620     {
13621       /* make sure that there is no move delay before next try to push */
13622       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13623         player->move_delay = 0;
13624
13625       return MP_NO_ACTION;
13626     }
13627
13628     if (IS_CUSTOM_ELEMENT(element) &&
13629         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13630     {
13631       if (!DigFieldByCE(nextx, nexty, element))
13632         return MP_NO_ACTION;
13633     }
13634
13635     if (IS_SB_ELEMENT(element))
13636     {
13637       if (element == EL_SOKOBAN_FIELD_FULL)
13638       {
13639         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13640         local_player->sokobanfields_still_needed++;
13641       }
13642
13643       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13644       {
13645         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13646         local_player->sokobanfields_still_needed--;
13647       }
13648
13649       Feld[x][y] = EL_SOKOBAN_OBJECT;
13650
13651       if (Back[x][y] == Back[nextx][nexty])
13652         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13653       else if (Back[x][y] != 0)
13654         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13655                                     ACTION_EMPTYING);
13656       else
13657         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13658                                     ACTION_FILLING);
13659
13660       if (local_player->sokobanfields_still_needed == 0 &&
13661           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13662       {
13663         PlayerWins(player);
13664
13665         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13666       }
13667     }
13668     else
13669       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13670
13671     InitMovingField(x, y, move_direction);
13672     GfxAction[x][y] = ACTION_PUSHING;
13673
13674     if (mode == DF_SNAP)
13675       ContinueMoving(x, y);
13676     else
13677       MovPos[x][y] = (dx != 0 ? dx : dy);
13678
13679     Pushed[x][y] = TRUE;
13680     Pushed[nextx][nexty] = TRUE;
13681
13682     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13683       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13684     else
13685       player->push_delay_value = -1;    /* get new value later */
13686
13687     /* check for element change _after_ element has been pushed */
13688     if (game.use_change_when_pushing_bug)
13689     {
13690       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13691                                  player->index_bit, dig_side);
13692       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13693                                           player->index_bit, dig_side);
13694     }
13695   }
13696   else if (IS_SWITCHABLE(element))
13697   {
13698     if (PLAYER_SWITCHING(player, x, y))
13699     {
13700       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13701                                           player->index_bit, dig_side);
13702
13703       return MP_ACTION;
13704     }
13705
13706     player->is_switching = TRUE;
13707     player->switch_x = x;
13708     player->switch_y = y;
13709
13710     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13711
13712     if (element == EL_ROBOT_WHEEL)
13713     {
13714       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13715       ZX = x;
13716       ZY = y;
13717
13718       game.robot_wheel_active = TRUE;
13719
13720       TEST_DrawLevelField(x, y);
13721     }
13722     else if (element == EL_SP_TERMINAL)
13723     {
13724       int xx, yy;
13725
13726       SCAN_PLAYFIELD(xx, yy)
13727       {
13728         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13729           Bang(xx, yy);
13730         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13731           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13732       }
13733     }
13734     else if (IS_BELT_SWITCH(element))
13735     {
13736       ToggleBeltSwitch(x, y);
13737     }
13738     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13739              element == EL_SWITCHGATE_SWITCH_DOWN ||
13740              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13741              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13742     {
13743       ToggleSwitchgateSwitch(x, y);
13744     }
13745     else if (element == EL_LIGHT_SWITCH ||
13746              element == EL_LIGHT_SWITCH_ACTIVE)
13747     {
13748       ToggleLightSwitch(x, y);
13749     }
13750     else if (element == EL_TIMEGATE_SWITCH ||
13751              element == EL_DC_TIMEGATE_SWITCH)
13752     {
13753       ActivateTimegateSwitch(x, y);
13754     }
13755     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13756              element == EL_BALLOON_SWITCH_RIGHT ||
13757              element == EL_BALLOON_SWITCH_UP    ||
13758              element == EL_BALLOON_SWITCH_DOWN  ||
13759              element == EL_BALLOON_SWITCH_NONE  ||
13760              element == EL_BALLOON_SWITCH_ANY)
13761     {
13762       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13763                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13764                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13765                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13766                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13767                              move_direction);
13768     }
13769     else if (element == EL_LAMP)
13770     {
13771       Feld[x][y] = EL_LAMP_ACTIVE;
13772       local_player->lights_still_needed--;
13773
13774       ResetGfxAnimation(x, y);
13775       TEST_DrawLevelField(x, y);
13776     }
13777     else if (element == EL_TIME_ORB_FULL)
13778     {
13779       Feld[x][y] = EL_TIME_ORB_EMPTY;
13780
13781       if (level.time > 0 || level.use_time_orb_bug)
13782       {
13783         TimeLeft += level.time_orb_time;
13784         game.no_time_limit = FALSE;
13785
13786         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13787
13788         DisplayGameControlValues();
13789       }
13790
13791       ResetGfxAnimation(x, y);
13792       TEST_DrawLevelField(x, y);
13793     }
13794     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13795              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13796     {
13797       int xx, yy;
13798
13799       game.ball_state = !game.ball_state;
13800
13801       SCAN_PLAYFIELD(xx, yy)
13802       {
13803         int e = Feld[xx][yy];
13804
13805         if (game.ball_state)
13806         {
13807           if (e == EL_EMC_MAGIC_BALL)
13808             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13809           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13810             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13811         }
13812         else
13813         {
13814           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13815             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13816           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13817             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13818         }
13819       }
13820     }
13821
13822     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13823                                         player->index_bit, dig_side);
13824
13825     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13826                                         player->index_bit, dig_side);
13827
13828     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13829                                         player->index_bit, dig_side);
13830
13831     return MP_ACTION;
13832   }
13833   else
13834   {
13835     if (!PLAYER_SWITCHING(player, x, y))
13836     {
13837       player->is_switching = TRUE;
13838       player->switch_x = x;
13839       player->switch_y = y;
13840
13841       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13842                                  player->index_bit, dig_side);
13843       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13844                                           player->index_bit, dig_side);
13845
13846       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13847                                  player->index_bit, dig_side);
13848       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13849                                           player->index_bit, dig_side);
13850     }
13851
13852     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13853                                player->index_bit, dig_side);
13854     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13855                                         player->index_bit, dig_side);
13856
13857     return MP_NO_ACTION;
13858   }
13859
13860   player->push_delay = -1;
13861
13862   if (is_player)                /* function can also be called by EL_PENGUIN */
13863   {
13864     if (Feld[x][y] != element)          /* really digged/collected something */
13865     {
13866       player->is_collecting = !player->is_digging;
13867       player->is_active = TRUE;
13868     }
13869   }
13870
13871   return MP_MOVING;
13872 }
13873
13874 static boolean DigFieldByCE(int x, int y, int digging_element)
13875 {
13876   int element = Feld[x][y];
13877
13878   if (!IS_FREE(x, y))
13879   {
13880     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13881                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13882                   ACTION_BREAKING);
13883
13884     /* no element can dig solid indestructible elements */
13885     if (IS_INDESTRUCTIBLE(element) &&
13886         !IS_DIGGABLE(element) &&
13887         !IS_COLLECTIBLE(element))
13888       return FALSE;
13889
13890     if (AmoebaNr[x][y] &&
13891         (element == EL_AMOEBA_FULL ||
13892          element == EL_BD_AMOEBA ||
13893          element == EL_AMOEBA_GROWING))
13894     {
13895       AmoebaCnt[AmoebaNr[x][y]]--;
13896       AmoebaCnt2[AmoebaNr[x][y]]--;
13897     }
13898
13899     if (IS_MOVING(x, y))
13900       RemoveMovingField(x, y);
13901     else
13902     {
13903       RemoveField(x, y);
13904       TEST_DrawLevelField(x, y);
13905     }
13906
13907     /* if digged element was about to explode, prevent the explosion */
13908     ExplodeField[x][y] = EX_TYPE_NONE;
13909
13910     PlayLevelSoundAction(x, y, action);
13911   }
13912
13913   Store[x][y] = EL_EMPTY;
13914
13915   /* this makes it possible to leave the removed element again */
13916   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13917     Store[x][y] = element;
13918
13919   return TRUE;
13920 }
13921
13922 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13923 {
13924   int jx = player->jx, jy = player->jy;
13925   int x = jx + dx, y = jy + dy;
13926   int snap_direction = (dx == -1 ? MV_LEFT  :
13927                         dx == +1 ? MV_RIGHT :
13928                         dy == -1 ? MV_UP    :
13929                         dy == +1 ? MV_DOWN  : MV_NONE);
13930   boolean can_continue_snapping = (level.continuous_snapping &&
13931                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13932
13933   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13934     return FALSE;
13935
13936   if (!player->active || !IN_LEV_FIELD(x, y))
13937     return FALSE;
13938
13939   if (dx && dy)
13940     return FALSE;
13941
13942   if (!dx && !dy)
13943   {
13944     if (player->MovPos == 0)
13945       player->is_pushing = FALSE;
13946
13947     player->is_snapping = FALSE;
13948
13949     if (player->MovPos == 0)
13950     {
13951       player->is_moving = FALSE;
13952       player->is_digging = FALSE;
13953       player->is_collecting = FALSE;
13954     }
13955
13956     return FALSE;
13957   }
13958
13959   /* prevent snapping with already pressed snap key when not allowed */
13960   if (player->is_snapping && !can_continue_snapping)
13961     return FALSE;
13962
13963   player->MovDir = snap_direction;
13964
13965   if (player->MovPos == 0)
13966   {
13967     player->is_moving = FALSE;
13968     player->is_digging = FALSE;
13969     player->is_collecting = FALSE;
13970   }
13971
13972   player->is_dropping = FALSE;
13973   player->is_dropping_pressed = FALSE;
13974   player->drop_pressed_delay = 0;
13975
13976   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13977     return FALSE;
13978
13979   player->is_snapping = TRUE;
13980   player->is_active = TRUE;
13981
13982   if (player->MovPos == 0)
13983   {
13984     player->is_moving = FALSE;
13985     player->is_digging = FALSE;
13986     player->is_collecting = FALSE;
13987   }
13988
13989   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13990     TEST_DrawLevelField(player->last_jx, player->last_jy);
13991
13992   TEST_DrawLevelField(x, y);
13993
13994   return TRUE;
13995 }
13996
13997 static boolean DropElement(struct PlayerInfo *player)
13998 {
13999   int old_element, new_element;
14000   int dropx = player->jx, dropy = player->jy;
14001   int drop_direction = player->MovDir;
14002   int drop_side = drop_direction;
14003   int drop_element = get_next_dropped_element(player);
14004
14005   player->is_dropping_pressed = TRUE;
14006
14007   /* do not drop an element on top of another element; when holding drop key
14008      pressed without moving, dropped element must move away before the next
14009      element can be dropped (this is especially important if the next element
14010      is dynamite, which can be placed on background for historical reasons) */
14011   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
14012     return MP_ACTION;
14013
14014   if (IS_THROWABLE(drop_element))
14015   {
14016     dropx += GET_DX_FROM_DIR(drop_direction);
14017     dropy += GET_DY_FROM_DIR(drop_direction);
14018
14019     if (!IN_LEV_FIELD(dropx, dropy))
14020       return FALSE;
14021   }
14022
14023   old_element = Feld[dropx][dropy];     /* old element at dropping position */
14024   new_element = drop_element;           /* default: no change when dropping */
14025
14026   /* check if player is active, not moving and ready to drop */
14027   if (!player->active || player->MovPos || player->drop_delay > 0)
14028     return FALSE;
14029
14030   /* check if player has anything that can be dropped */
14031   if (new_element == EL_UNDEFINED)
14032     return FALSE;
14033
14034   /* check if drop key was pressed long enough for EM style dynamite */
14035   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
14036     return FALSE;
14037
14038   /* check if anything can be dropped at the current position */
14039   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
14040     return FALSE;
14041
14042   /* collected custom elements can only be dropped on empty fields */
14043   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
14044     return FALSE;
14045
14046   if (old_element != EL_EMPTY)
14047     Back[dropx][dropy] = old_element;   /* store old element on this field */
14048
14049   ResetGfxAnimation(dropx, dropy);
14050   ResetRandomAnimationValue(dropx, dropy);
14051
14052   if (player->inventory_size > 0 ||
14053       player->inventory_infinite_element != EL_UNDEFINED)
14054   {
14055     if (player->inventory_size > 0)
14056     {
14057       player->inventory_size--;
14058
14059       DrawGameDoorValues();
14060
14061       if (new_element == EL_DYNAMITE)
14062         new_element = EL_DYNAMITE_ACTIVE;
14063       else if (new_element == EL_EM_DYNAMITE)
14064         new_element = EL_EM_DYNAMITE_ACTIVE;
14065       else if (new_element == EL_SP_DISK_RED)
14066         new_element = EL_SP_DISK_RED_ACTIVE;
14067     }
14068
14069     Feld[dropx][dropy] = new_element;
14070
14071     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14072       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14073                           el2img(Feld[dropx][dropy]), 0);
14074
14075     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14076
14077     /* needed if previous element just changed to "empty" in the last frame */
14078     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14079
14080     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14081                                player->index_bit, drop_side);
14082     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14083                                         CE_PLAYER_DROPS_X,
14084                                         player->index_bit, drop_side);
14085
14086     TestIfElementTouchesCustomElement(dropx, dropy);
14087   }
14088   else          /* player is dropping a dyna bomb */
14089   {
14090     player->dynabombs_left--;
14091
14092     Feld[dropx][dropy] = new_element;
14093
14094     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14095       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14096                           el2img(Feld[dropx][dropy]), 0);
14097
14098     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14099   }
14100
14101   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14102     InitField_WithBug1(dropx, dropy, FALSE);
14103
14104   new_element = Feld[dropx][dropy];     /* element might have changed */
14105
14106   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14107       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14108   {
14109     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14110       MovDir[dropx][dropy] = drop_direction;
14111
14112     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14113
14114     /* do not cause impact style collision by dropping elements that can fall */
14115     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14116   }
14117
14118   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14119   player->is_dropping = TRUE;
14120
14121   player->drop_pressed_delay = 0;
14122   player->is_dropping_pressed = FALSE;
14123
14124   player->drop_x = dropx;
14125   player->drop_y = dropy;
14126
14127   return TRUE;
14128 }
14129
14130 /* ------------------------------------------------------------------------- */
14131 /* game sound playing functions                                              */
14132 /* ------------------------------------------------------------------------- */
14133
14134 static int *loop_sound_frame = NULL;
14135 static int *loop_sound_volume = NULL;
14136
14137 void InitPlayLevelSound()
14138 {
14139   int num_sounds = getSoundListSize();
14140
14141   checked_free(loop_sound_frame);
14142   checked_free(loop_sound_volume);
14143
14144   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14145   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14146 }
14147
14148 static void PlayLevelSound(int x, int y, int nr)
14149 {
14150   int sx = SCREENX(x), sy = SCREENY(y);
14151   int volume, stereo_position;
14152   int max_distance = 8;
14153   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14154
14155   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14156       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14157     return;
14158
14159   if (!IN_LEV_FIELD(x, y) ||
14160       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14161       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14162     return;
14163
14164   volume = SOUND_MAX_VOLUME;
14165
14166   if (!IN_SCR_FIELD(sx, sy))
14167   {
14168     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14169     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14170
14171     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14172   }
14173
14174   stereo_position = (SOUND_MAX_LEFT +
14175                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14176                      (SCR_FIELDX + 2 * max_distance));
14177
14178   if (IS_LOOP_SOUND(nr))
14179   {
14180     /* This assures that quieter loop sounds do not overwrite louder ones,
14181        while restarting sound volume comparison with each new game frame. */
14182
14183     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14184       return;
14185
14186     loop_sound_volume[nr] = volume;
14187     loop_sound_frame[nr] = FrameCounter;
14188   }
14189
14190   PlaySoundExt(nr, volume, stereo_position, type);
14191 }
14192
14193 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14194 {
14195   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14196                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14197                  y < LEVELY(BY1) ? LEVELY(BY1) :
14198                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14199                  sound_action);
14200 }
14201
14202 static void PlayLevelSoundAction(int x, int y, int action)
14203 {
14204   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14205 }
14206
14207 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14208 {
14209   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14210
14211   if (sound_effect != SND_UNDEFINED)
14212     PlayLevelSound(x, y, sound_effect);
14213 }
14214
14215 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14216                                               int action)
14217 {
14218   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14219
14220   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14221     PlayLevelSound(x, y, sound_effect);
14222 }
14223
14224 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14225 {
14226   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14227
14228   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14229     PlayLevelSound(x, y, sound_effect);
14230 }
14231
14232 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14233 {
14234   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14235
14236   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14237     StopSound(sound_effect);
14238 }
14239
14240 static void PlayLevelMusic()
14241 {
14242   if (levelset.music[level_nr] != MUS_UNDEFINED)
14243     PlayMusic(levelset.music[level_nr]);        /* from config file */
14244   else
14245     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14246 }
14247
14248 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14249 {
14250   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14251   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14252   int x = xx - 1 - offset;
14253   int y = yy - 1 - offset;
14254
14255   switch (sample)
14256   {
14257     case SAMPLE_blank:
14258       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14259       break;
14260
14261     case SAMPLE_roll:
14262       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14263       break;
14264
14265     case SAMPLE_stone:
14266       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14267       break;
14268
14269     case SAMPLE_nut:
14270       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14271       break;
14272
14273     case SAMPLE_crack:
14274       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14275       break;
14276
14277     case SAMPLE_bug:
14278       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14279       break;
14280
14281     case SAMPLE_tank:
14282       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14283       break;
14284
14285     case SAMPLE_android_clone:
14286       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14287       break;
14288
14289     case SAMPLE_android_move:
14290       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14291       break;
14292
14293     case SAMPLE_spring:
14294       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14295       break;
14296
14297     case SAMPLE_slurp:
14298       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14299       break;
14300
14301     case SAMPLE_eater:
14302       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14303       break;
14304
14305     case SAMPLE_eater_eat:
14306       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14307       break;
14308
14309     case SAMPLE_alien:
14310       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14311       break;
14312
14313     case SAMPLE_collect:
14314       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14315       break;
14316
14317     case SAMPLE_diamond:
14318       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14319       break;
14320
14321     case SAMPLE_squash:
14322       /* !!! CHECK THIS !!! */
14323 #if 1
14324       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14325 #else
14326       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14327 #endif
14328       break;
14329
14330     case SAMPLE_wonderfall:
14331       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14332       break;
14333
14334     case SAMPLE_drip:
14335       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14336       break;
14337
14338     case SAMPLE_push:
14339       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14340       break;
14341
14342     case SAMPLE_dirt:
14343       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14344       break;
14345
14346     case SAMPLE_acid:
14347       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14348       break;
14349
14350     case SAMPLE_ball:
14351       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14352       break;
14353
14354     case SAMPLE_grow:
14355       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14356       break;
14357
14358     case SAMPLE_wonder:
14359       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14360       break;
14361
14362     case SAMPLE_door:
14363       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14364       break;
14365
14366     case SAMPLE_exit_open:
14367       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14368       break;
14369
14370     case SAMPLE_exit_leave:
14371       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14372       break;
14373
14374     case SAMPLE_dynamite:
14375       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14376       break;
14377
14378     case SAMPLE_tick:
14379       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14380       break;
14381
14382     case SAMPLE_press:
14383       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14384       break;
14385
14386     case SAMPLE_wheel:
14387       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14388       break;
14389
14390     case SAMPLE_boom:
14391       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14392       break;
14393
14394     case SAMPLE_die:
14395       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14396       break;
14397
14398     case SAMPLE_time:
14399       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14400       break;
14401
14402     default:
14403       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14404       break;
14405   }
14406 }
14407
14408 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14409 {
14410   int element = map_element_SP_to_RND(element_sp);
14411   int action = map_action_SP_to_RND(action_sp);
14412   int offset = (setup.sp_show_border_elements ? 0 : 1);
14413   int x = xx - offset;
14414   int y = yy - offset;
14415
14416   PlayLevelSoundElementAction(x, y, element, action);
14417 }
14418
14419 void RaiseScore(int value)
14420 {
14421   local_player->score += value;
14422
14423   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14424
14425   DisplayGameControlValues();
14426 }
14427
14428 void RaiseScoreElement(int element)
14429 {
14430   switch (element)
14431   {
14432     case EL_EMERALD:
14433     case EL_BD_DIAMOND:
14434     case EL_EMERALD_YELLOW:
14435     case EL_EMERALD_RED:
14436     case EL_EMERALD_PURPLE:
14437     case EL_SP_INFOTRON:
14438       RaiseScore(level.score[SC_EMERALD]);
14439       break;
14440     case EL_DIAMOND:
14441       RaiseScore(level.score[SC_DIAMOND]);
14442       break;
14443     case EL_CRYSTAL:
14444       RaiseScore(level.score[SC_CRYSTAL]);
14445       break;
14446     case EL_PEARL:
14447       RaiseScore(level.score[SC_PEARL]);
14448       break;
14449     case EL_BUG:
14450     case EL_BD_BUTTERFLY:
14451     case EL_SP_ELECTRON:
14452       RaiseScore(level.score[SC_BUG]);
14453       break;
14454     case EL_SPACESHIP:
14455     case EL_BD_FIREFLY:
14456     case EL_SP_SNIKSNAK:
14457       RaiseScore(level.score[SC_SPACESHIP]);
14458       break;
14459     case EL_YAMYAM:
14460     case EL_DARK_YAMYAM:
14461       RaiseScore(level.score[SC_YAMYAM]);
14462       break;
14463     case EL_ROBOT:
14464       RaiseScore(level.score[SC_ROBOT]);
14465       break;
14466     case EL_PACMAN:
14467       RaiseScore(level.score[SC_PACMAN]);
14468       break;
14469     case EL_NUT:
14470       RaiseScore(level.score[SC_NUT]);
14471       break;
14472     case EL_DYNAMITE:
14473     case EL_EM_DYNAMITE:
14474     case EL_SP_DISK_RED:
14475     case EL_DYNABOMB_INCREASE_NUMBER:
14476     case EL_DYNABOMB_INCREASE_SIZE:
14477     case EL_DYNABOMB_INCREASE_POWER:
14478       RaiseScore(level.score[SC_DYNAMITE]);
14479       break;
14480     case EL_SHIELD_NORMAL:
14481     case EL_SHIELD_DEADLY:
14482       RaiseScore(level.score[SC_SHIELD]);
14483       break;
14484     case EL_EXTRA_TIME:
14485       RaiseScore(level.extra_time_score);
14486       break;
14487     case EL_KEY_1:
14488     case EL_KEY_2:
14489     case EL_KEY_3:
14490     case EL_KEY_4:
14491     case EL_EM_KEY_1:
14492     case EL_EM_KEY_2:
14493     case EL_EM_KEY_3:
14494     case EL_EM_KEY_4:
14495     case EL_EMC_KEY_5:
14496     case EL_EMC_KEY_6:
14497     case EL_EMC_KEY_7:
14498     case EL_EMC_KEY_8:
14499     case EL_DC_KEY_WHITE:
14500       RaiseScore(level.score[SC_KEY]);
14501       break;
14502     default:
14503       RaiseScore(element_info[element].collect_score);
14504       break;
14505   }
14506 }
14507
14508 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14509 {
14510   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14511   {
14512     /* closing door required in case of envelope style request dialogs */
14513     if (!skip_request)
14514       CloseDoor(DOOR_CLOSE_1);
14515
14516 #if defined(NETWORK_AVALIABLE)
14517     if (options.network)
14518       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14519     else
14520 #endif
14521     {
14522       if (quick_quit)
14523       {
14524         FadeSkipNextFadeIn();
14525
14526         game_status = GAME_MODE_MAIN;
14527
14528         DrawMainMenu();
14529       }
14530       else
14531       {
14532         game_status = GAME_MODE_MAIN;
14533
14534         DrawMainMenu();
14535       }
14536     }
14537   }
14538   else          /* continue playing the game */
14539   {
14540     if (tape.playing && tape.deactivate_display)
14541       TapeDeactivateDisplayOff(TRUE);
14542
14543     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14544
14545     if (tape.playing && tape.deactivate_display)
14546       TapeDeactivateDisplayOn();
14547   }
14548 }
14549
14550 void RequestQuitGame(boolean ask_if_really_quit)
14551 {
14552   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14553   boolean skip_request = AllPlayersGone || quick_quit;
14554
14555   RequestQuitGameExt(skip_request, quick_quit,
14556                      "Do you really want to quit the game?");
14557 }
14558
14559
14560 /* ------------------------------------------------------------------------- */
14561 /* random generator functions                                                */
14562 /* ------------------------------------------------------------------------- */
14563
14564 unsigned int InitEngineRandom_RND(int seed)
14565 {
14566   game.num_random_calls = 0;
14567
14568   return InitEngineRandom(seed);
14569 }
14570
14571 unsigned int RND(int max)
14572 {
14573   if (max > 0)
14574   {
14575     game.num_random_calls++;
14576
14577     return GetEngineRandom(max);
14578   }
14579
14580   return 0;
14581 }
14582
14583
14584 /* ------------------------------------------------------------------------- */
14585 /* game engine snapshot handling functions                                   */
14586 /* ------------------------------------------------------------------------- */
14587
14588 struct EngineSnapshotInfo
14589 {
14590   /* runtime values for custom element collect score */
14591   int collect_score[NUM_CUSTOM_ELEMENTS];
14592
14593   /* runtime values for group element choice position */
14594   int choice_pos[NUM_GROUP_ELEMENTS];
14595
14596   /* runtime values for belt position animations */
14597   int belt_graphic[4][NUM_BELT_PARTS];
14598   int belt_anim_mode[4][NUM_BELT_PARTS];
14599 };
14600
14601 static struct EngineSnapshotInfo engine_snapshot_rnd;
14602 static char *snapshot_level_identifier = NULL;
14603 static int snapshot_level_nr = -1;
14604
14605 static void SaveEngineSnapshotValues_RND()
14606 {
14607   static int belt_base_active_element[4] =
14608   {
14609     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14610     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14611     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14612     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14613   };
14614   int i, j;
14615
14616   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14617   {
14618     int element = EL_CUSTOM_START + i;
14619
14620     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14621   }
14622
14623   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14624   {
14625     int element = EL_GROUP_START + i;
14626
14627     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14628   }
14629
14630   for (i = 0; i < 4; i++)
14631   {
14632     for (j = 0; j < NUM_BELT_PARTS; j++)
14633     {
14634       int element = belt_base_active_element[i] + j;
14635       int graphic = el2img(element);
14636       int anim_mode = graphic_info[graphic].anim_mode;
14637
14638       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14639       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14640     }
14641   }
14642 }
14643
14644 static void LoadEngineSnapshotValues_RND()
14645 {
14646   unsigned int num_random_calls = game.num_random_calls;
14647   int i, j;
14648
14649   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14650   {
14651     int element = EL_CUSTOM_START + i;
14652
14653     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14654   }
14655
14656   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14657   {
14658     int element = EL_GROUP_START + i;
14659
14660     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14661   }
14662
14663   for (i = 0; i < 4; i++)
14664   {
14665     for (j = 0; j < NUM_BELT_PARTS; j++)
14666     {
14667       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14668       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14669
14670       graphic_info[graphic].anim_mode = anim_mode;
14671     }
14672   }
14673
14674   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14675   {
14676     InitRND(tape.random_seed);
14677     for (i = 0; i < num_random_calls; i++)
14678       RND(1);
14679   }
14680
14681   if (game.num_random_calls != num_random_calls)
14682   {
14683     Error(ERR_INFO, "number of random calls out of sync");
14684     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14685     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14686     Error(ERR_EXIT, "this should not happen -- please debug");
14687   }
14688 }
14689
14690 void FreeEngineSnapshotSingle()
14691 {
14692   FreeSnapshotSingle();
14693
14694   setString(&snapshot_level_identifier, NULL);
14695   snapshot_level_nr = -1;
14696 }
14697
14698 void FreeEngineSnapshotList()
14699 {
14700   FreeSnapshotList();
14701 }
14702
14703 ListNode *SaveEngineSnapshotBuffers()
14704 {
14705   ListNode *buffers = NULL;
14706
14707   /* copy some special values to a structure better suited for the snapshot */
14708
14709   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14710     SaveEngineSnapshotValues_RND();
14711   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14712     SaveEngineSnapshotValues_EM();
14713   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14714     SaveEngineSnapshotValues_SP(&buffers);
14715
14716   /* save values stored in special snapshot structure */
14717
14718   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14719     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14720   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14721     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14722   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14723     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14724
14725   /* save further RND engine values */
14726
14727   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14728   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14729   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14730
14731   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14732   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14733   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14734   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14735
14736   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14737   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14738   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14739   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14740   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14741
14742   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14743   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14744   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14745
14746   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14747
14748   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14749
14750   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14751   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14752
14753   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14754   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14755   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14756   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14757   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14758   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14759   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14760   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14761   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14762   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14763   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14764   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14765   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14766   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14767   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14768   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14769   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14770   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14771
14772   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14773   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14774
14775   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14776   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14777   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14778
14779   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14780   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14781
14782   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14783   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14784   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14785   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14786   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14787
14788   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14789   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14790
14791 #if 0
14792   ListNode *node = engine_snapshot_list_rnd;
14793   int num_bytes = 0;
14794
14795   while (node != NULL)
14796   {
14797     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14798
14799     node = node->next;
14800   }
14801
14802   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14803 #endif
14804
14805   return buffers;
14806 }
14807
14808 void SaveEngineSnapshotSingle()
14809 {
14810   ListNode *buffers = SaveEngineSnapshotBuffers();
14811
14812   /* finally save all snapshot buffers to single snapshot */
14813   SaveSnapshotSingle(buffers);
14814
14815   /* save level identification information */
14816   setString(&snapshot_level_identifier, leveldir_current->identifier);
14817   snapshot_level_nr = level_nr;
14818 }
14819
14820 static boolean SaveEngineSnapshotToListExt(boolean initial_snapshot)
14821 {
14822   boolean save_snapshot =
14823     (initial_snapshot ||
14824      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_STEP) ||
14825      (game.snapshot.mode == SNAPSHOT_MODE_EVERY_MOVE &&
14826       game.snapshot.changed_action));
14827
14828   game.snapshot.changed_action = FALSE;
14829
14830   if (game.snapshot.mode == SNAPSHOT_MODE_OFF ||
14831       tape.quick_resume ||
14832       !save_snapshot)
14833     return FALSE;
14834
14835   ListNode *buffers = SaveEngineSnapshotBuffers();
14836
14837   /* finally save all snapshot buffers to snapshot list */
14838   SaveSnapshotToList(buffers);
14839
14840   return TRUE;
14841 }
14842
14843 boolean SaveEngineSnapshotToList()
14844 {
14845   return SaveEngineSnapshotToListExt(FALSE);
14846 }
14847
14848 void SaveEngineSnapshotToListInitial()
14849 {
14850   FreeEngineSnapshotList();
14851
14852   SaveEngineSnapshotToListExt(TRUE);
14853 }
14854
14855 void LoadEngineSnapshotValues()
14856 {
14857   /* restore special values from snapshot structure */
14858
14859   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14860     LoadEngineSnapshotValues_RND();
14861   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14862     LoadEngineSnapshotValues_EM();
14863   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14864     LoadEngineSnapshotValues_SP();
14865 }
14866
14867 void LoadEngineSnapshotSingle()
14868 {
14869   LoadSnapshotSingle();
14870
14871   LoadEngineSnapshotValues();
14872 }
14873
14874 void LoadEngineSnapshot_Undo(int steps)
14875 {
14876   LoadSnapshotFromList_Older(steps);
14877
14878   LoadEngineSnapshotValues();
14879 }
14880
14881 void LoadEngineSnapshot_Redo(int steps)
14882 {
14883   LoadSnapshotFromList_Newer(steps);
14884
14885   LoadEngineSnapshotValues();
14886 }
14887
14888 boolean CheckEngineSnapshotSingle()
14889 {
14890   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14891           snapshot_level_nr == level_nr);
14892 }
14893
14894 boolean CheckEngineSnapshotList()
14895 {
14896   return CheckSnapshotList();
14897 }
14898
14899
14900 /* ---------- new game button stuff ---------------------------------------- */
14901
14902 static struct
14903 {
14904   int graphic;
14905   struct XY *pos;
14906   int gadget_id;
14907   char *infotext;
14908 } gamebutton_info[NUM_GAME_BUTTONS] =
14909 {
14910   {
14911     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14912     GAME_CTRL_ID_STOP,                  "stop game"
14913   },
14914   {
14915     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14916     GAME_CTRL_ID_PAUSE,                 "pause game"
14917   },
14918   {
14919     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14920     GAME_CTRL_ID_PLAY,                  "play game"
14921   },
14922   {
14923     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14924     GAME_CTRL_ID_UNDO,                  "undo step"
14925   },
14926   {
14927     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14928     GAME_CTRL_ID_REDO,                  "redo step"
14929   },
14930   {
14931     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14932     GAME_CTRL_ID_SAVE,                  "save game"
14933   },
14934   {
14935     IMG_GAME_BUTTON_GFX_PAUSE2,         &game.button.pause2,
14936     GAME_CTRL_ID_PAUSE2,                "pause game"
14937   },
14938   {
14939     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14940     GAME_CTRL_ID_LOAD,                  "load game"
14941   },
14942   {
14943     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14944     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14945   },
14946   {
14947     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14948     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14949   },
14950   {
14951     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14952     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14953   }
14954 };
14955
14956 void CreateGameButtons()
14957 {
14958   int i;
14959
14960   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14961   {
14962     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14963     struct XY *pos = gamebutton_info[i].pos;
14964     struct GadgetInfo *gi;
14965     int button_type;
14966     boolean checked;
14967     unsigned int event_mask;
14968     int base_x = (tape.show_game_buttons ? VX : DX);
14969     int base_y = (tape.show_game_buttons ? VY : DY);
14970     int gd_x   = gfx->src_x;
14971     int gd_y   = gfx->src_y;
14972     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14973     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14974     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14975     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14976     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14977     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14978     int id = i;
14979
14980     if (gfx->bitmap == NULL)
14981     {
14982       game_gadget[id] = NULL;
14983
14984       continue;
14985     }
14986
14987     if (id == GAME_CTRL_ID_STOP ||
14988         id == GAME_CTRL_ID_PLAY ||
14989         id == GAME_CTRL_ID_SAVE ||
14990         id == GAME_CTRL_ID_LOAD)
14991     {
14992       button_type = GD_TYPE_NORMAL_BUTTON;
14993       checked = FALSE;
14994       event_mask = GD_EVENT_RELEASED;
14995     }
14996     else if (id == GAME_CTRL_ID_UNDO ||
14997              id == GAME_CTRL_ID_REDO)
14998     {
14999       button_type = GD_TYPE_NORMAL_BUTTON;
15000       checked = FALSE;
15001       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
15002     }
15003     else
15004     {
15005       button_type = GD_TYPE_CHECK_BUTTON;
15006       checked =
15007         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
15008          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
15009          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
15010       event_mask = GD_EVENT_PRESSED;
15011     }
15012
15013     gi = CreateGadget(GDI_CUSTOM_ID, id,
15014                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
15015                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
15016                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
15017                       GDI_WIDTH, gfx->width,
15018                       GDI_HEIGHT, gfx->height,
15019                       GDI_TYPE, button_type,
15020                       GDI_STATE, GD_BUTTON_UNPRESSED,
15021                       GDI_CHECKED, checked,
15022                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
15023                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
15024                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
15025                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
15026                       GDI_DIRECT_DRAW, FALSE,
15027                       GDI_EVENT_MASK, event_mask,
15028                       GDI_CALLBACK_ACTION, HandleGameButtons,
15029                       GDI_END);
15030
15031     if (gi == NULL)
15032       Error(ERR_EXIT, "cannot create gadget");
15033
15034     game_gadget[id] = gi;
15035   }
15036 }
15037
15038 void FreeGameButtons()
15039 {
15040   int i;
15041
15042   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15043     FreeGadget(game_gadget[i]);
15044 }
15045
15046 static void MapGameButtonsAtSamePosition(int id)
15047 {
15048   int i;
15049
15050   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15051     if (i != id &&
15052         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15053         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15054       MapGadget(game_gadget[i]);
15055 }
15056
15057 static void UnmapGameButtonsAtSamePosition(int id)
15058 {
15059   int i;
15060
15061   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15062     if (i != id &&
15063         gamebutton_info[i].pos->x == gamebutton_info[id].pos->x &&
15064         gamebutton_info[i].pos->y == gamebutton_info[id].pos->y)
15065       UnmapGadget(game_gadget[i]);
15066 }
15067
15068 void MapUndoRedoButtons()
15069 {
15070   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15071   UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15072
15073   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15074   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15075 }
15076
15077 void UnmapUndoRedoButtons()
15078 {
15079   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
15080   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
15081
15082   MapGameButtonsAtSamePosition(GAME_CTRL_ID_UNDO);
15083   MapGameButtonsAtSamePosition(GAME_CTRL_ID_REDO);
15084 }
15085
15086 void MapGameButtons()
15087 {
15088   int i;
15089
15090   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15091     if (i != GAME_CTRL_ID_UNDO &&
15092         i != GAME_CTRL_ID_REDO)
15093       MapGadget(game_gadget[i]);
15094
15095   if (setup.show_snapshot_buttons)
15096   {
15097     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_SAVE);
15098     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE2);
15099     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_LOAD);
15100   }
15101   else
15102   {
15103     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_STOP);
15104     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PAUSE);
15105     UnmapGameButtonsAtSamePosition(GAME_CTRL_ID_PLAY);
15106   }
15107
15108   RedrawGameButtons();
15109 }
15110
15111 void UnmapGameButtons()
15112 {
15113   int i;
15114
15115   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15116     UnmapGadget(game_gadget[i]);
15117 }
15118
15119 void RedrawGameButtons()
15120 {
15121   int i;
15122
15123   for (i = 0; i < NUM_GAME_BUTTONS; i++)
15124     RedrawGadget(game_gadget[i]);
15125
15126   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
15127   redraw_mask &= ~REDRAW_ALL;
15128 }
15129
15130 void GameUndoRedoExt()
15131 {
15132   ClearPlayerAction();
15133
15134   tape.pausing = TRUE;
15135
15136   RedrawPlayfield();
15137   UpdateAndDisplayGameControlValues();
15138
15139   DrawCompleteVideoDisplay();
15140   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15141   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15142   DrawVideoDisplay((tape.single_step ? VIDEO_STATE_1STEP_ON :
15143                     VIDEO_STATE_1STEP_OFF), 0);
15144
15145   BackToFront();
15146 }
15147
15148 void GameUndo(int steps)
15149 {
15150   if (!CheckEngineSnapshotList())
15151     return;
15152
15153   LoadEngineSnapshot_Undo(steps);
15154
15155   GameUndoRedoExt();
15156 }
15157
15158 void GameRedo(int steps)
15159 {
15160   if (!CheckEngineSnapshotList())
15161     return;
15162
15163   LoadEngineSnapshot_Redo(steps);
15164
15165   GameUndoRedoExt();
15166 }
15167
15168 static void HandleGameButtonsExt(int id, int button)
15169 {
15170   int steps = BUTTON_STEPSIZE(button);
15171   boolean handle_game_buttons =
15172     (game_status == GAME_MODE_PLAYING ||
15173      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15174
15175   if (!handle_game_buttons)
15176     return;
15177
15178   switch (id)
15179   {
15180     case GAME_CTRL_ID_STOP:
15181       if (game_status == GAME_MODE_MAIN)
15182         break;
15183
15184       if (tape.playing)
15185         TapeStop();
15186       else
15187         RequestQuitGame(TRUE);
15188
15189       break;
15190
15191     case GAME_CTRL_ID_PAUSE:
15192     case GAME_CTRL_ID_PAUSE2:
15193       if (options.network && game_status == GAME_MODE_PLAYING)
15194       {
15195 #if defined(NETWORK_AVALIABLE)
15196         if (tape.pausing)
15197           SendToServer_ContinuePlaying();
15198         else
15199           SendToServer_PausePlaying();
15200 #endif
15201       }
15202       else
15203         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15204       break;
15205
15206     case GAME_CTRL_ID_PLAY:
15207       if (game_status == GAME_MODE_MAIN)
15208       {
15209         StartGameActions(options.network, setup.autorecord, level.random_seed);
15210       }
15211       else if (tape.pausing)
15212       {
15213 #if defined(NETWORK_AVALIABLE)
15214         if (options.network)
15215           SendToServer_ContinuePlaying();
15216         else
15217 #endif
15218           TapeTogglePause(TAPE_TOGGLE_MANUAL);
15219       }
15220       break;
15221
15222     case GAME_CTRL_ID_UNDO:
15223       GameUndo(steps);
15224       break;
15225
15226     case GAME_CTRL_ID_REDO:
15227       GameRedo(steps);
15228       break;
15229
15230     case GAME_CTRL_ID_SAVE:
15231       TapeQuickSave();
15232       break;
15233
15234     case GAME_CTRL_ID_LOAD:
15235       TapeQuickLoad();
15236       break;
15237
15238     case SOUND_CTRL_ID_MUSIC:
15239       if (setup.sound_music)
15240       { 
15241         setup.sound_music = FALSE;
15242
15243         FadeMusic();
15244       }
15245       else if (audio.music_available)
15246       { 
15247         setup.sound = setup.sound_music = TRUE;
15248
15249         SetAudioMode(setup.sound);
15250
15251         PlayLevelMusic();
15252       }
15253       break;
15254
15255     case SOUND_CTRL_ID_LOOPS:
15256       if (setup.sound_loops)
15257         setup.sound_loops = FALSE;
15258       else if (audio.loops_available)
15259       {
15260         setup.sound = setup.sound_loops = TRUE;
15261
15262         SetAudioMode(setup.sound);
15263       }
15264       break;
15265
15266     case SOUND_CTRL_ID_SIMPLE:
15267       if (setup.sound_simple)
15268         setup.sound_simple = FALSE;
15269       else if (audio.sound_available)
15270       {
15271         setup.sound = setup.sound_simple = TRUE;
15272
15273         SetAudioMode(setup.sound);
15274       }
15275       break;
15276
15277     default:
15278       break;
15279   }
15280 }
15281
15282 static void HandleGameButtons(struct GadgetInfo *gi)
15283 {
15284   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15285 }
15286
15287 void HandleSoundButtonKeys(Key key)
15288 {
15289
15290   if (key == setup.shortcut.sound_simple)
15291     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15292   else if (key == setup.shortcut.sound_loops)
15293     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15294   else if (key == setup.shortcut.sound_music)
15295     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15296 }