added movement-based engine snapshot mode to step-based mode
[rocksndiamonds.git] / src / game.c
1 // ============================================================================
2 // Rocks'n'Diamonds - McDuffin Strikes Back!
3 // ----------------------------------------------------------------------------
4 // (c) 1995-2014 by Artsoft Entertainment
5 //                  Holger Schemel
6 //                  info@artsoft.org
7 //                  http://www.artsoft.org/
8 // ----------------------------------------------------------------------------
9 // game.c
10 // ============================================================================
11
12 #include "libgame/libgame.h"
13
14 #include "game.h"
15 #include "init.h"
16 #include "tools.h"
17 #include "screens.h"
18 #include "events.h"
19 #include "files.h"
20 #include "tape.h"
21 #include "network.h"
22
23
24 /* DEBUG SETTINGS */
25 #define DEBUG_INIT_PLAYER       1
26 #define DEBUG_PLAYER_ACTIONS    0
27
28 /* EXPERIMENTAL STUFF */
29 #define USE_NEW_AMOEBA_CODE     FALSE
30
31 /* EXPERIMENTAL STUFF */
32 #define USE_QUICKSAND_BD_ROCK_BUGFIX    0
33 #define USE_QUICKSAND_IMPACT_BUGFIX     0
34 #define USE_DELAYED_GFX_REDRAW          0
35 #define USE_NEW_PLAYER_ASSIGNMENTS      1
36
37 #if USE_DELAYED_GFX_REDRAW
38 #define TEST_DrawLevelField(x, y)                               \
39         GfxRedraw[x][y] |= GFX_REDRAW_TILE
40 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
41         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED
42 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
43         GfxRedraw[x][y] |= GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS
44 #define TEST_DrawTwinkleOnField(x, y)                           \
45         GfxRedraw[x][y] |= GFX_REDRAW_TILE_TWINKLED
46 #else
47 #define TEST_DrawLevelField(x, y)                               \
48              DrawLevelField(x, y)
49 #define TEST_DrawLevelFieldCrumbled(x, y)                       \
50              DrawLevelFieldCrumbled(x, y)
51 #define TEST_DrawLevelFieldCrumbledNeighbours(x, y)             \
52              DrawLevelFieldCrumbledNeighbours(x, y)
53 #define TEST_DrawTwinkleOnField(x, y)                           \
54              DrawTwinkleOnField(x, y)
55 #endif
56
57
58 /* for DigField() */
59 #define DF_NO_PUSH              0
60 #define DF_DIG                  1
61 #define DF_SNAP                 2
62
63 /* for MovePlayer() */
64 #define MP_NO_ACTION            0
65 #define MP_MOVING               1
66 #define MP_ACTION               2
67 #define MP_DONT_RUN_INTO        (MP_MOVING | MP_ACTION)
68
69 /* for ScrollPlayer() */
70 #define SCROLL_INIT             0
71 #define SCROLL_GO_ON            1
72
73 /* for Bang()/Explode() */
74 #define EX_PHASE_START          0
75 #define EX_TYPE_NONE            0
76 #define EX_TYPE_NORMAL          (1 << 0)
77 #define EX_TYPE_CENTER          (1 << 1)
78 #define EX_TYPE_BORDER          (1 << 2)
79 #define EX_TYPE_CROSS           (1 << 3)
80 #define EX_TYPE_DYNA            (1 << 4)
81 #define EX_TYPE_SINGLE_TILE     (EX_TYPE_CENTER | EX_TYPE_BORDER)
82
83 #define PANEL_OFF()             (local_player->LevelSolved_PanelOff)
84 #define PANEL_DEACTIVATED(p)    ((p)->x < 0 || (p)->y < 0 || PANEL_OFF())
85 #define PANEL_XPOS(p)           (DX + ALIGNED_TEXT_XPOS(p))
86 #define PANEL_YPOS(p)           (DY + ALIGNED_TEXT_YPOS(p))
87
88 /* game panel display and control definitions */
89 #define GAME_PANEL_LEVEL_NUMBER                 0
90 #define GAME_PANEL_GEMS                         1
91 #define GAME_PANEL_INVENTORY_COUNT              2
92 #define GAME_PANEL_INVENTORY_FIRST_1            3
93 #define GAME_PANEL_INVENTORY_FIRST_2            4
94 #define GAME_PANEL_INVENTORY_FIRST_3            5
95 #define GAME_PANEL_INVENTORY_FIRST_4            6
96 #define GAME_PANEL_INVENTORY_FIRST_5            7
97 #define GAME_PANEL_INVENTORY_FIRST_6            8
98 #define GAME_PANEL_INVENTORY_FIRST_7            9
99 #define GAME_PANEL_INVENTORY_FIRST_8            10
100 #define GAME_PANEL_INVENTORY_LAST_1             11
101 #define GAME_PANEL_INVENTORY_LAST_2             12
102 #define GAME_PANEL_INVENTORY_LAST_3             13
103 #define GAME_PANEL_INVENTORY_LAST_4             14
104 #define GAME_PANEL_INVENTORY_LAST_5             15
105 #define GAME_PANEL_INVENTORY_LAST_6             16
106 #define GAME_PANEL_INVENTORY_LAST_7             17
107 #define GAME_PANEL_INVENTORY_LAST_8             18
108 #define GAME_PANEL_KEY_1                        19
109 #define GAME_PANEL_KEY_2                        20
110 #define GAME_PANEL_KEY_3                        21
111 #define GAME_PANEL_KEY_4                        22
112 #define GAME_PANEL_KEY_5                        23
113 #define GAME_PANEL_KEY_6                        24
114 #define GAME_PANEL_KEY_7                        25
115 #define GAME_PANEL_KEY_8                        26
116 #define GAME_PANEL_KEY_WHITE                    27
117 #define GAME_PANEL_KEY_WHITE_COUNT              28
118 #define GAME_PANEL_SCORE                        29
119 #define GAME_PANEL_HIGHSCORE                    30
120 #define GAME_PANEL_TIME                         31
121 #define GAME_PANEL_TIME_HH                      32
122 #define GAME_PANEL_TIME_MM                      33
123 #define GAME_PANEL_TIME_SS                      34
124 #define GAME_PANEL_FRAME                        35
125 #define GAME_PANEL_SHIELD_NORMAL                36
126 #define GAME_PANEL_SHIELD_NORMAL_TIME           37
127 #define GAME_PANEL_SHIELD_DEADLY                38
128 #define GAME_PANEL_SHIELD_DEADLY_TIME           39
129 #define GAME_PANEL_EXIT                         40
130 #define GAME_PANEL_EMC_MAGIC_BALL               41
131 #define GAME_PANEL_EMC_MAGIC_BALL_SWITCH        42
132 #define GAME_PANEL_LIGHT_SWITCH                 43
133 #define GAME_PANEL_LIGHT_SWITCH_TIME            44
134 #define GAME_PANEL_TIMEGATE_SWITCH              45
135 #define GAME_PANEL_TIMEGATE_SWITCH_TIME         46
136 #define GAME_PANEL_SWITCHGATE_SWITCH            47
137 #define GAME_PANEL_EMC_LENSES                   48
138 #define GAME_PANEL_EMC_LENSES_TIME              49
139 #define GAME_PANEL_EMC_MAGNIFIER                50
140 #define GAME_PANEL_EMC_MAGNIFIER_TIME           51
141 #define GAME_PANEL_BALLOON_SWITCH               52
142 #define GAME_PANEL_DYNABOMB_NUMBER              53
143 #define GAME_PANEL_DYNABOMB_SIZE                54
144 #define GAME_PANEL_DYNABOMB_POWER               55
145 #define GAME_PANEL_PENGUINS                     56
146 #define GAME_PANEL_SOKOBAN_OBJECTS              57
147 #define GAME_PANEL_SOKOBAN_FIELDS               58
148 #define GAME_PANEL_ROBOT_WHEEL                  59
149 #define GAME_PANEL_CONVEYOR_BELT_1              60
150 #define GAME_PANEL_CONVEYOR_BELT_2              61
151 #define GAME_PANEL_CONVEYOR_BELT_3              62
152 #define GAME_PANEL_CONVEYOR_BELT_4              63
153 #define GAME_PANEL_CONVEYOR_BELT_1_SWITCH       64
154 #define GAME_PANEL_CONVEYOR_BELT_2_SWITCH       65
155 #define GAME_PANEL_CONVEYOR_BELT_3_SWITCH       66
156 #define GAME_PANEL_CONVEYOR_BELT_4_SWITCH       67
157 #define GAME_PANEL_MAGIC_WALL                   68
158 #define GAME_PANEL_MAGIC_WALL_TIME              69
159 #define GAME_PANEL_GRAVITY_STATE                70
160 #define GAME_PANEL_GRAPHIC_1                    71
161 #define GAME_PANEL_GRAPHIC_2                    72
162 #define GAME_PANEL_GRAPHIC_3                    73
163 #define GAME_PANEL_GRAPHIC_4                    74
164 #define GAME_PANEL_GRAPHIC_5                    75
165 #define GAME_PANEL_GRAPHIC_6                    76
166 #define GAME_PANEL_GRAPHIC_7                    77
167 #define GAME_PANEL_GRAPHIC_8                    78
168 #define GAME_PANEL_ELEMENT_1                    79
169 #define GAME_PANEL_ELEMENT_2                    80
170 #define GAME_PANEL_ELEMENT_3                    81
171 #define GAME_PANEL_ELEMENT_4                    82
172 #define GAME_PANEL_ELEMENT_5                    83
173 #define GAME_PANEL_ELEMENT_6                    84
174 #define GAME_PANEL_ELEMENT_7                    85
175 #define GAME_PANEL_ELEMENT_8                    86
176 #define GAME_PANEL_ELEMENT_COUNT_1              87
177 #define GAME_PANEL_ELEMENT_COUNT_2              88
178 #define GAME_PANEL_ELEMENT_COUNT_3              89
179 #define GAME_PANEL_ELEMENT_COUNT_4              90
180 #define GAME_PANEL_ELEMENT_COUNT_5              91
181 #define GAME_PANEL_ELEMENT_COUNT_6              92
182 #define GAME_PANEL_ELEMENT_COUNT_7              93
183 #define GAME_PANEL_ELEMENT_COUNT_8              94
184 #define GAME_PANEL_CE_SCORE_1                   95
185 #define GAME_PANEL_CE_SCORE_2                   96
186 #define GAME_PANEL_CE_SCORE_3                   97
187 #define GAME_PANEL_CE_SCORE_4                   98
188 #define GAME_PANEL_CE_SCORE_5                   99
189 #define GAME_PANEL_CE_SCORE_6                   100
190 #define GAME_PANEL_CE_SCORE_7                   101
191 #define GAME_PANEL_CE_SCORE_8                   102
192 #define GAME_PANEL_CE_SCORE_1_ELEMENT           103
193 #define GAME_PANEL_CE_SCORE_2_ELEMENT           104
194 #define GAME_PANEL_CE_SCORE_3_ELEMENT           105
195 #define GAME_PANEL_CE_SCORE_4_ELEMENT           106
196 #define GAME_PANEL_CE_SCORE_5_ELEMENT           107
197 #define GAME_PANEL_CE_SCORE_6_ELEMENT           108
198 #define GAME_PANEL_CE_SCORE_7_ELEMENT           109
199 #define GAME_PANEL_CE_SCORE_8_ELEMENT           110
200 #define GAME_PANEL_PLAYER_NAME                  111
201 #define GAME_PANEL_LEVEL_NAME                   112
202 #define GAME_PANEL_LEVEL_AUTHOR                 113
203
204 #define NUM_GAME_PANEL_CONTROLS                 114
205
206 struct GamePanelOrderInfo
207 {
208   int nr;
209   int sort_priority;
210 };
211
212 static struct GamePanelOrderInfo game_panel_order[NUM_GAME_PANEL_CONTROLS];
213
214 struct GamePanelControlInfo
215 {
216   int nr;
217
218   struct TextPosInfo *pos;
219   int type;
220
221   int value, last_value;
222   int frame, last_frame;
223   int gfx_frame;
224   int gfx_random;
225 };
226
227 static struct GamePanelControlInfo game_panel_controls[] =
228 {
229   {
230     GAME_PANEL_LEVEL_NUMBER,
231     &game.panel.level_number,
232     TYPE_INTEGER,
233   },
234   {
235     GAME_PANEL_GEMS,
236     &game.panel.gems,
237     TYPE_INTEGER,
238   },
239   {
240     GAME_PANEL_INVENTORY_COUNT,
241     &game.panel.inventory_count,
242     TYPE_INTEGER,
243   },
244   {
245     GAME_PANEL_INVENTORY_FIRST_1,
246     &game.panel.inventory_first[0],
247     TYPE_ELEMENT,
248   },
249   {
250     GAME_PANEL_INVENTORY_FIRST_2,
251     &game.panel.inventory_first[1],
252     TYPE_ELEMENT,
253   },
254   {
255     GAME_PANEL_INVENTORY_FIRST_3,
256     &game.panel.inventory_first[2],
257     TYPE_ELEMENT,
258   },
259   {
260     GAME_PANEL_INVENTORY_FIRST_4,
261     &game.panel.inventory_first[3],
262     TYPE_ELEMENT,
263   },
264   {
265     GAME_PANEL_INVENTORY_FIRST_5,
266     &game.panel.inventory_first[4],
267     TYPE_ELEMENT,
268   },
269   {
270     GAME_PANEL_INVENTORY_FIRST_6,
271     &game.panel.inventory_first[5],
272     TYPE_ELEMENT,
273   },
274   {
275     GAME_PANEL_INVENTORY_FIRST_7,
276     &game.panel.inventory_first[6],
277     TYPE_ELEMENT,
278   },
279   {
280     GAME_PANEL_INVENTORY_FIRST_8,
281     &game.panel.inventory_first[7],
282     TYPE_ELEMENT,
283   },
284   {
285     GAME_PANEL_INVENTORY_LAST_1,
286     &game.panel.inventory_last[0],
287     TYPE_ELEMENT,
288   },
289   {
290     GAME_PANEL_INVENTORY_LAST_2,
291     &game.panel.inventory_last[1],
292     TYPE_ELEMENT,
293   },
294   {
295     GAME_PANEL_INVENTORY_LAST_3,
296     &game.panel.inventory_last[2],
297     TYPE_ELEMENT,
298   },
299   {
300     GAME_PANEL_INVENTORY_LAST_4,
301     &game.panel.inventory_last[3],
302     TYPE_ELEMENT,
303   },
304   {
305     GAME_PANEL_INVENTORY_LAST_5,
306     &game.panel.inventory_last[4],
307     TYPE_ELEMENT,
308   },
309   {
310     GAME_PANEL_INVENTORY_LAST_6,
311     &game.panel.inventory_last[5],
312     TYPE_ELEMENT,
313   },
314   {
315     GAME_PANEL_INVENTORY_LAST_7,
316     &game.panel.inventory_last[6],
317     TYPE_ELEMENT,
318   },
319   {
320     GAME_PANEL_INVENTORY_LAST_8,
321     &game.panel.inventory_last[7],
322     TYPE_ELEMENT,
323   },
324   {
325     GAME_PANEL_KEY_1,
326     &game.panel.key[0],
327     TYPE_ELEMENT,
328   },
329   {
330     GAME_PANEL_KEY_2,
331     &game.panel.key[1],
332     TYPE_ELEMENT,
333   },
334   {
335     GAME_PANEL_KEY_3,
336     &game.panel.key[2],
337     TYPE_ELEMENT,
338   },
339   {
340     GAME_PANEL_KEY_4,
341     &game.panel.key[3],
342     TYPE_ELEMENT,
343   },
344   {
345     GAME_PANEL_KEY_5,
346     &game.panel.key[4],
347     TYPE_ELEMENT,
348   },
349   {
350     GAME_PANEL_KEY_6,
351     &game.panel.key[5],
352     TYPE_ELEMENT,
353   },
354   {
355     GAME_PANEL_KEY_7,
356     &game.panel.key[6],
357     TYPE_ELEMENT,
358   },
359   {
360     GAME_PANEL_KEY_8,
361     &game.panel.key[7],
362     TYPE_ELEMENT,
363   },
364   {
365     GAME_PANEL_KEY_WHITE,
366     &game.panel.key_white,
367     TYPE_ELEMENT,
368   },
369   {
370     GAME_PANEL_KEY_WHITE_COUNT,
371     &game.panel.key_white_count,
372     TYPE_INTEGER,
373   },
374   {
375     GAME_PANEL_SCORE,
376     &game.panel.score,
377     TYPE_INTEGER,
378   },
379   {
380     GAME_PANEL_HIGHSCORE,
381     &game.panel.highscore,
382     TYPE_INTEGER,
383   },
384   {
385     GAME_PANEL_TIME,
386     &game.panel.time,
387     TYPE_INTEGER,
388   },
389   {
390     GAME_PANEL_TIME_HH,
391     &game.panel.time_hh,
392     TYPE_INTEGER,
393   },
394   {
395     GAME_PANEL_TIME_MM,
396     &game.panel.time_mm,
397     TYPE_INTEGER,
398   },
399   {
400     GAME_PANEL_TIME_SS,
401     &game.panel.time_ss,
402     TYPE_INTEGER,
403   },
404   {
405     GAME_PANEL_FRAME,
406     &game.panel.frame,
407     TYPE_INTEGER,
408   },
409   {
410     GAME_PANEL_SHIELD_NORMAL,
411     &game.panel.shield_normal,
412     TYPE_ELEMENT,
413   },
414   {
415     GAME_PANEL_SHIELD_NORMAL_TIME,
416     &game.panel.shield_normal_time,
417     TYPE_INTEGER,
418   },
419   {
420     GAME_PANEL_SHIELD_DEADLY,
421     &game.panel.shield_deadly,
422     TYPE_ELEMENT,
423   },
424   {
425     GAME_PANEL_SHIELD_DEADLY_TIME,
426     &game.panel.shield_deadly_time,
427     TYPE_INTEGER,
428   },
429   {
430     GAME_PANEL_EXIT,
431     &game.panel.exit,
432     TYPE_ELEMENT,
433   },
434   {
435     GAME_PANEL_EMC_MAGIC_BALL,
436     &game.panel.emc_magic_ball,
437     TYPE_ELEMENT,
438   },
439   {
440     GAME_PANEL_EMC_MAGIC_BALL_SWITCH,
441     &game.panel.emc_magic_ball_switch,
442     TYPE_ELEMENT,
443   },
444   {
445     GAME_PANEL_LIGHT_SWITCH,
446     &game.panel.light_switch,
447     TYPE_ELEMENT,
448   },
449   {
450     GAME_PANEL_LIGHT_SWITCH_TIME,
451     &game.panel.light_switch_time,
452     TYPE_INTEGER,
453   },
454   {
455     GAME_PANEL_TIMEGATE_SWITCH,
456     &game.panel.timegate_switch,
457     TYPE_ELEMENT,
458   },
459   {
460     GAME_PANEL_TIMEGATE_SWITCH_TIME,
461     &game.panel.timegate_switch_time,
462     TYPE_INTEGER,
463   },
464   {
465     GAME_PANEL_SWITCHGATE_SWITCH,
466     &game.panel.switchgate_switch,
467     TYPE_ELEMENT,
468   },
469   {
470     GAME_PANEL_EMC_LENSES,
471     &game.panel.emc_lenses,
472     TYPE_ELEMENT,
473   },
474   {
475     GAME_PANEL_EMC_LENSES_TIME,
476     &game.panel.emc_lenses_time,
477     TYPE_INTEGER,
478   },
479   {
480     GAME_PANEL_EMC_MAGNIFIER,
481     &game.panel.emc_magnifier,
482     TYPE_ELEMENT,
483   },
484   {
485     GAME_PANEL_EMC_MAGNIFIER_TIME,
486     &game.panel.emc_magnifier_time,
487     TYPE_INTEGER,
488   },
489   {
490     GAME_PANEL_BALLOON_SWITCH,
491     &game.panel.balloon_switch,
492     TYPE_ELEMENT,
493   },
494   {
495     GAME_PANEL_DYNABOMB_NUMBER,
496     &game.panel.dynabomb_number,
497     TYPE_INTEGER,
498   },
499   {
500     GAME_PANEL_DYNABOMB_SIZE,
501     &game.panel.dynabomb_size,
502     TYPE_INTEGER,
503   },
504   {
505     GAME_PANEL_DYNABOMB_POWER,
506     &game.panel.dynabomb_power,
507     TYPE_ELEMENT,
508   },
509   {
510     GAME_PANEL_PENGUINS,
511     &game.panel.penguins,
512     TYPE_INTEGER,
513   },
514   {
515     GAME_PANEL_SOKOBAN_OBJECTS,
516     &game.panel.sokoban_objects,
517     TYPE_INTEGER,
518   },
519   {
520     GAME_PANEL_SOKOBAN_FIELDS,
521     &game.panel.sokoban_fields,
522     TYPE_INTEGER,
523   },
524   {
525     GAME_PANEL_ROBOT_WHEEL,
526     &game.panel.robot_wheel,
527     TYPE_ELEMENT,
528   },
529   {
530     GAME_PANEL_CONVEYOR_BELT_1,
531     &game.panel.conveyor_belt[0],
532     TYPE_ELEMENT,
533   },
534   {
535     GAME_PANEL_CONVEYOR_BELT_2,
536     &game.panel.conveyor_belt[1],
537     TYPE_ELEMENT,
538   },
539   {
540     GAME_PANEL_CONVEYOR_BELT_3,
541     &game.panel.conveyor_belt[2],
542     TYPE_ELEMENT,
543   },
544   {
545     GAME_PANEL_CONVEYOR_BELT_4,
546     &game.panel.conveyor_belt[3],
547     TYPE_ELEMENT,
548   },
549   {
550     GAME_PANEL_CONVEYOR_BELT_1_SWITCH,
551     &game.panel.conveyor_belt_switch[0],
552     TYPE_ELEMENT,
553   },
554   {
555     GAME_PANEL_CONVEYOR_BELT_2_SWITCH,
556     &game.panel.conveyor_belt_switch[1],
557     TYPE_ELEMENT,
558   },
559   {
560     GAME_PANEL_CONVEYOR_BELT_3_SWITCH,
561     &game.panel.conveyor_belt_switch[2],
562     TYPE_ELEMENT,
563   },
564   {
565     GAME_PANEL_CONVEYOR_BELT_4_SWITCH,
566     &game.panel.conveyor_belt_switch[3],
567     TYPE_ELEMENT,
568   },
569   {
570     GAME_PANEL_MAGIC_WALL,
571     &game.panel.magic_wall,
572     TYPE_ELEMENT,
573   },
574   {
575     GAME_PANEL_MAGIC_WALL_TIME,
576     &game.panel.magic_wall_time,
577     TYPE_INTEGER,
578   },
579   {
580     GAME_PANEL_GRAVITY_STATE,
581     &game.panel.gravity_state,
582     TYPE_STRING,
583   },
584   {
585     GAME_PANEL_GRAPHIC_1,
586     &game.panel.graphic[0],
587     TYPE_ELEMENT,
588   },
589   {
590     GAME_PANEL_GRAPHIC_2,
591     &game.panel.graphic[1],
592     TYPE_ELEMENT,
593   },
594   {
595     GAME_PANEL_GRAPHIC_3,
596     &game.panel.graphic[2],
597     TYPE_ELEMENT,
598   },
599   {
600     GAME_PANEL_GRAPHIC_4,
601     &game.panel.graphic[3],
602     TYPE_ELEMENT,
603   },
604   {
605     GAME_PANEL_GRAPHIC_5,
606     &game.panel.graphic[4],
607     TYPE_ELEMENT,
608   },
609   {
610     GAME_PANEL_GRAPHIC_6,
611     &game.panel.graphic[5],
612     TYPE_ELEMENT,
613   },
614   {
615     GAME_PANEL_GRAPHIC_7,
616     &game.panel.graphic[6],
617     TYPE_ELEMENT,
618   },
619   {
620     GAME_PANEL_GRAPHIC_8,
621     &game.panel.graphic[7],
622     TYPE_ELEMENT,
623   },
624   {
625     GAME_PANEL_ELEMENT_1,
626     &game.panel.element[0],
627     TYPE_ELEMENT,
628   },
629   {
630     GAME_PANEL_ELEMENT_2,
631     &game.panel.element[1],
632     TYPE_ELEMENT,
633   },
634   {
635     GAME_PANEL_ELEMENT_3,
636     &game.panel.element[2],
637     TYPE_ELEMENT,
638   },
639   {
640     GAME_PANEL_ELEMENT_4,
641     &game.panel.element[3],
642     TYPE_ELEMENT,
643   },
644   {
645     GAME_PANEL_ELEMENT_5,
646     &game.panel.element[4],
647     TYPE_ELEMENT,
648   },
649   {
650     GAME_PANEL_ELEMENT_6,
651     &game.panel.element[5],
652     TYPE_ELEMENT,
653   },
654   {
655     GAME_PANEL_ELEMENT_7,
656     &game.panel.element[6],
657     TYPE_ELEMENT,
658   },
659   {
660     GAME_PANEL_ELEMENT_8,
661     &game.panel.element[7],
662     TYPE_ELEMENT,
663   },
664   {
665     GAME_PANEL_ELEMENT_COUNT_1,
666     &game.panel.element_count[0],
667     TYPE_INTEGER,
668   },
669   {
670     GAME_PANEL_ELEMENT_COUNT_2,
671     &game.panel.element_count[1],
672     TYPE_INTEGER,
673   },
674   {
675     GAME_PANEL_ELEMENT_COUNT_3,
676     &game.panel.element_count[2],
677     TYPE_INTEGER,
678   },
679   {
680     GAME_PANEL_ELEMENT_COUNT_4,
681     &game.panel.element_count[3],
682     TYPE_INTEGER,
683   },
684   {
685     GAME_PANEL_ELEMENT_COUNT_5,
686     &game.panel.element_count[4],
687     TYPE_INTEGER,
688   },
689   {
690     GAME_PANEL_ELEMENT_COUNT_6,
691     &game.panel.element_count[5],
692     TYPE_INTEGER,
693   },
694   {
695     GAME_PANEL_ELEMENT_COUNT_7,
696     &game.panel.element_count[6],
697     TYPE_INTEGER,
698   },
699   {
700     GAME_PANEL_ELEMENT_COUNT_8,
701     &game.panel.element_count[7],
702     TYPE_INTEGER,
703   },
704   {
705     GAME_PANEL_CE_SCORE_1,
706     &game.panel.ce_score[0],
707     TYPE_INTEGER,
708   },
709   {
710     GAME_PANEL_CE_SCORE_2,
711     &game.panel.ce_score[1],
712     TYPE_INTEGER,
713   },
714   {
715     GAME_PANEL_CE_SCORE_3,
716     &game.panel.ce_score[2],
717     TYPE_INTEGER,
718   },
719   {
720     GAME_PANEL_CE_SCORE_4,
721     &game.panel.ce_score[3],
722     TYPE_INTEGER,
723   },
724   {
725     GAME_PANEL_CE_SCORE_5,
726     &game.panel.ce_score[4],
727     TYPE_INTEGER,
728   },
729   {
730     GAME_PANEL_CE_SCORE_6,
731     &game.panel.ce_score[5],
732     TYPE_INTEGER,
733   },
734   {
735     GAME_PANEL_CE_SCORE_7,
736     &game.panel.ce_score[6],
737     TYPE_INTEGER,
738   },
739   {
740     GAME_PANEL_CE_SCORE_8,
741     &game.panel.ce_score[7],
742     TYPE_INTEGER,
743   },
744   {
745     GAME_PANEL_CE_SCORE_1_ELEMENT,
746     &game.panel.ce_score_element[0],
747     TYPE_ELEMENT,
748   },
749   {
750     GAME_PANEL_CE_SCORE_2_ELEMENT,
751     &game.panel.ce_score_element[1],
752     TYPE_ELEMENT,
753   },
754   {
755     GAME_PANEL_CE_SCORE_3_ELEMENT,
756     &game.panel.ce_score_element[2],
757     TYPE_ELEMENT,
758   },
759   {
760     GAME_PANEL_CE_SCORE_4_ELEMENT,
761     &game.panel.ce_score_element[3],
762     TYPE_ELEMENT,
763   },
764   {
765     GAME_PANEL_CE_SCORE_5_ELEMENT,
766     &game.panel.ce_score_element[4],
767     TYPE_ELEMENT,
768   },
769   {
770     GAME_PANEL_CE_SCORE_6_ELEMENT,
771     &game.panel.ce_score_element[5],
772     TYPE_ELEMENT,
773   },
774   {
775     GAME_PANEL_CE_SCORE_7_ELEMENT,
776     &game.panel.ce_score_element[6],
777     TYPE_ELEMENT,
778   },
779   {
780     GAME_PANEL_CE_SCORE_8_ELEMENT,
781     &game.panel.ce_score_element[7],
782     TYPE_ELEMENT,
783   },
784   {
785     GAME_PANEL_PLAYER_NAME,
786     &game.panel.player_name,
787     TYPE_STRING,
788   },
789   {
790     GAME_PANEL_LEVEL_NAME,
791     &game.panel.level_name,
792     TYPE_STRING,
793   },
794   {
795     GAME_PANEL_LEVEL_AUTHOR,
796     &game.panel.level_author,
797     TYPE_STRING,
798   },
799
800   {
801     -1,
802     NULL,
803     -1,
804   }
805 };
806
807 /* values for delayed check of falling and moving elements and for collision */
808 #define CHECK_DELAY_MOVING      3
809 #define CHECK_DELAY_FALLING     CHECK_DELAY_MOVING
810 #define CHECK_DELAY_COLLISION   2
811 #define CHECK_DELAY_IMPACT      CHECK_DELAY_COLLISION
812
813 /* values for initial player move delay (initial delay counter value) */
814 #define INITIAL_MOVE_DELAY_OFF  -1
815 #define INITIAL_MOVE_DELAY_ON   0
816
817 /* values for player movement speed (which is in fact a delay value) */
818 #define MOVE_DELAY_MIN_SPEED    32
819 #define MOVE_DELAY_NORMAL_SPEED 8
820 #define MOVE_DELAY_HIGH_SPEED   4
821 #define MOVE_DELAY_MAX_SPEED    1
822
823 #define DOUBLE_MOVE_DELAY(x)    (x = (x < MOVE_DELAY_MIN_SPEED ? x * 2 : x))
824 #define HALVE_MOVE_DELAY(x)     (x = (x > MOVE_DELAY_MAX_SPEED ? x / 2 : x))
825
826 #define DOUBLE_PLAYER_SPEED(p)  (HALVE_MOVE_DELAY( (p)->move_delay_value))
827 #define HALVE_PLAYER_SPEED(p)   (DOUBLE_MOVE_DELAY((p)->move_delay_value))
828
829 /* values for other actions */
830 #define MOVE_STEPSIZE_NORMAL    (TILEX / MOVE_DELAY_NORMAL_SPEED)
831 #define MOVE_STEPSIZE_MIN       (1)
832 #define MOVE_STEPSIZE_MAX       (TILEX)
833
834 #define GET_DX_FROM_DIR(d)      ((d) == MV_LEFT ? -1 : (d) == MV_RIGHT ? 1 : 0)
835 #define GET_DY_FROM_DIR(d)      ((d) == MV_UP   ? -1 : (d) == MV_DOWN  ? 1 : 0)
836
837 #define INIT_GFX_RANDOM()       (GetSimpleRandom(1000000))
838
839 #define GET_NEW_PUSH_DELAY(e)   (   (element_info[e].push_delay_fixed) + \
840                                  RND(element_info[e].push_delay_random))
841 #define GET_NEW_DROP_DELAY(e)   (   (element_info[e].drop_delay_fixed) + \
842                                  RND(element_info[e].drop_delay_random))
843 #define GET_NEW_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
844                                  RND(element_info[e].move_delay_random))
845 #define GET_MAX_MOVE_DELAY(e)   (   (element_info[e].move_delay_fixed) + \
846                                     (element_info[e].move_delay_random))
847 #define GET_NEW_CE_VALUE(e)     (   (element_info[e].ce_value_fixed_initial) +\
848                                  RND(element_info[e].ce_value_random_initial))
849 #define GET_CE_SCORE(e)         (   (element_info[e].collect_score))
850 #define GET_CHANGE_DELAY(c)     (   ((c)->delay_fixed  * (c)->delay_frames) + \
851                                  RND((c)->delay_random * (c)->delay_frames))
852 #define GET_CE_DELAY_VALUE(c)   (   ((c)->delay_fixed) + \
853                                  RND((c)->delay_random))
854
855
856 #define GET_VALID_RUNTIME_ELEMENT(e)                                    \
857          ((e) >= NUM_RUNTIME_ELEMENTS ? EL_UNKNOWN : (e))
858
859 #define RESOLVED_REFERENCE_ELEMENT(be, e)                               \
860         ((be) + (e) - EL_SELF < EL_CUSTOM_START ? EL_CUSTOM_START :     \
861          (be) + (e) - EL_SELF > EL_CUSTOM_END   ? EL_CUSTOM_END :       \
862          (be) + (e) - EL_SELF)
863
864 #define GET_PLAYER_FROM_BITS(p)                                         \
865         (EL_PLAYER_1 + ((p) != PLAYER_BITS_ANY ? log_2(p) : 0))
866
867 #define GET_TARGET_ELEMENT(be, e, ch, cv, cs)                           \
868         ((e) == EL_TRIGGER_PLAYER   ? (ch)->actual_trigger_player    :  \
869          (e) == EL_TRIGGER_ELEMENT  ? (ch)->actual_trigger_element   :  \
870          (e) == EL_TRIGGER_CE_VALUE ? (ch)->actual_trigger_ce_value  :  \
871          (e) == EL_TRIGGER_CE_SCORE ? (ch)->actual_trigger_ce_score  :  \
872          (e) == EL_CURRENT_CE_VALUE ? (cv) :                            \
873          (e) == EL_CURRENT_CE_SCORE ? (cs) :                            \
874          (e) >= EL_PREV_CE_8 && (e) <= EL_NEXT_CE_8 ?                   \
875          RESOLVED_REFERENCE_ELEMENT(be, e) :                            \
876          (e))
877
878 #define CAN_GROW_INTO(e)                                                \
879         ((e) == EL_SAND || (IS_DIGGABLE(e) && level.grow_into_diggable))
880
881 #define ELEMENT_CAN_ENTER_FIELD_BASE_X(x, y, condition)                 \
882                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
883                                         (condition)))
884
885 #define ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, condition)              \
886                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
887                                         (CAN_MOVE_INTO_ACID(e) &&       \
888                                          Feld[x][y] == EL_ACID) ||      \
889                                         (condition)))
890
891 #define ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, condition)              \
892                 (IN_LEV_FIELD(x, y) && (IS_FREE_OR_PLAYER(x, y) ||      \
893                                         (CAN_MOVE_INTO_ACID(e) &&       \
894                                          Feld[x][y] == EL_ACID) ||      \
895                                         (condition)))
896
897 #define ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, condition)              \
898                 (IN_LEV_FIELD(x, y) && (IS_FREE(x, y) ||                \
899                                         (condition) ||                  \
900                                         (CAN_MOVE_INTO_ACID(e) &&       \
901                                          Feld[x][y] == EL_ACID) ||      \
902                                         (DONT_COLLIDE_WITH(e) &&        \
903                                          IS_PLAYER(x, y) &&             \
904                                          !PLAYER_ENEMY_PROTECTED(x, y))))
905
906 #define ELEMENT_CAN_ENTER_FIELD(e, x, y)                                \
907         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, 0)
908
909 #define SATELLITE_CAN_ENTER_FIELD(x, y)                                 \
910         ELEMENT_CAN_ENTER_FIELD_BASE_2(EL_SATELLITE, x, y, 0)
911
912 #define ANDROID_CAN_ENTER_FIELD(e, x, y)                                \
913         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, Feld[x][y] == EL_EMC_PLANT)
914
915 #define ANDROID_CAN_CLONE_FIELD(x, y)                                   \
916         (IN_LEV_FIELD(x, y) && (CAN_BE_CLONED_BY_ANDROID(Feld[x][y]) || \
917                                 CAN_BE_CLONED_BY_ANDROID(EL_TRIGGER_ELEMENT)))
918
919 #define ENEMY_CAN_ENTER_FIELD(e, x, y)                                  \
920         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
921
922 #define YAMYAM_CAN_ENTER_FIELD(e, x, y)                                 \
923         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, Feld[x][y] == EL_DIAMOND)
924
925 #define DARK_YAMYAM_CAN_ENTER_FIELD(e, x, y)                            \
926         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x,y, IS_FOOD_DARK_YAMYAM(Feld[x][y]))
927
928 #define PACMAN_CAN_ENTER_FIELD(e, x, y)                                 \
929         ELEMENT_CAN_ENTER_FIELD_BASE_3(e, x, y, IS_AMOEBOID(Feld[x][y]))
930
931 #define PIG_CAN_ENTER_FIELD(e, x, y)                                    \
932         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, IS_FOOD_PIG(Feld[x][y]))
933
934 #define PENGUIN_CAN_ENTER_FIELD(e, x, y)                                \
935         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (Feld[x][y] == EL_EXIT_OPEN || \
936                                                  Feld[x][y] == EL_EM_EXIT_OPEN || \
937                                                  Feld[x][y] == EL_STEEL_EXIT_OPEN || \
938                                                  Feld[x][y] == EL_EM_STEEL_EXIT_OPEN || \
939                                                  IS_FOOD_PENGUIN(Feld[x][y])))
940 #define DRAGON_CAN_ENTER_FIELD(e, x, y)                                 \
941         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
942
943 #define MOLE_CAN_ENTER_FIELD(e, x, y, condition)                        \
944         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, (condition))
945
946 #define SPRING_CAN_ENTER_FIELD(e, x, y)                                 \
947         ELEMENT_CAN_ENTER_FIELD_BASE_2(e, x, y, 0)
948
949 #define SPRING_CAN_BUMP_FROM_FIELD(x, y)                                \
950         (IN_LEV_FIELD(x, y) && (Feld[x][y] == EL_EMC_SPRING_BUMPER ||   \
951                                 Feld[x][y] == EL_EMC_SPRING_BUMPER_ACTIVE))
952
953 #define MOVE_ENTER_EL(e)        (element_info[e].move_enter_element)
954
955 #define CE_ENTER_FIELD_COND(e, x, y)                                    \
956                 (!IS_PLAYER(x, y) &&                                    \
957                  IS_EQUAL_OR_IN_GROUP(Feld[x][y], MOVE_ENTER_EL(e)))
958
959 #define CUSTOM_ELEMENT_CAN_ENTER_FIELD(e, x, y)                         \
960         ELEMENT_CAN_ENTER_FIELD_BASE_4(e, x, y, CE_ENTER_FIELD_COND(e, x, y))
961
962 #define IN_LEV_FIELD_AND_IS_FREE(x, y)  (IN_LEV_FIELD(x, y) &&  IS_FREE(x, y))
963 #define IN_LEV_FIELD_AND_NOT_FREE(x, y) (IN_LEV_FIELD(x, y) && !IS_FREE(x, y))
964
965 #define ACCESS_FROM(e, d)               (element_info[e].access_direction &(d))
966 #define IS_WALKABLE_FROM(e, d)          (IS_WALKABLE(e)   && ACCESS_FROM(e, d))
967 #define IS_PASSABLE_FROM(e, d)          (IS_PASSABLE(e)   && ACCESS_FROM(e, d))
968 #define IS_ACCESSIBLE_FROM(e, d)        (IS_ACCESSIBLE(e) && ACCESS_FROM(e, d))
969
970 /* game button identifiers */
971 #define GAME_CTRL_ID_STOP               0
972 #define GAME_CTRL_ID_PAUSE              1
973 #define GAME_CTRL_ID_PLAY               2
974 #define GAME_CTRL_ID_UNDO               3
975 #define GAME_CTRL_ID_REDO               4
976 #define GAME_CTRL_ID_SAVE               5
977 #define GAME_CTRL_ID_LOAD               6
978 #define SOUND_CTRL_ID_MUSIC             7
979 #define SOUND_CTRL_ID_LOOPS             8
980 #define SOUND_CTRL_ID_SIMPLE            9
981
982 #define NUM_GAME_BUTTONS                10
983
984
985 /* forward declaration for internal use */
986
987 static void CreateField(int, int, int);
988
989 static void ResetGfxAnimation(int, int);
990
991 static void SetPlayerWaiting(struct PlayerInfo *, boolean);
992 static void AdvanceFrameAndPlayerCounters(int);
993
994 static boolean MovePlayerOneStep(struct PlayerInfo *, int, int, int, int);
995 static boolean MovePlayer(struct PlayerInfo *, int, int);
996 static void ScrollPlayer(struct PlayerInfo *, int);
997 static void ScrollScreen(struct PlayerInfo *, int);
998
999 static int DigField(struct PlayerInfo *, int, int, int, int, int, int, int);
1000 static boolean DigFieldByCE(int, int, int);
1001 static boolean SnapField(struct PlayerInfo *, int, int);
1002 static boolean DropElement(struct PlayerInfo *);
1003
1004 static void InitBeltMovement(void);
1005 static void CloseAllOpenTimegates(void);
1006 static void CheckGravityMovement(struct PlayerInfo *);
1007 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *);
1008 static void KillPlayerUnlessEnemyProtected(int, int);
1009 static void KillPlayerUnlessExplosionProtected(int, int);
1010
1011 static void TestIfPlayerTouchesCustomElement(int, int);
1012 static void TestIfElementTouchesCustomElement(int, int);
1013 static void TestIfElementHitsCustomElement(int, int, int);
1014
1015 static void HandleElementChange(int, int, int);
1016 static void ExecuteCustomElementAction(int, int, int, int);
1017 static boolean ChangeElement(int, int, int, int);
1018
1019 static boolean CheckTriggeredElementChangeExt(int, int, int, int, int,int,int);
1020 #define CheckTriggeredElementChange(x, y, e, ev)                        \
1021         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY,CH_SIDE_ANY, -1)
1022 #define CheckTriggeredElementChangeByPlayer(x, y, e, ev, p, s)          \
1023         CheckTriggeredElementChangeExt(x, y, e, ev, p, s, -1)
1024 #define CheckTriggeredElementChangeBySide(x, y, e, ev, s)               \
1025         CheckTriggeredElementChangeExt(x, y, e, ev, CH_PLAYER_ANY, s, -1)
1026 #define CheckTriggeredElementChangeByPage(x, y, e, ev, p)               \
1027         CheckTriggeredElementChangeExt(x,y,e,ev, CH_PLAYER_ANY, CH_SIDE_ANY, p)
1028
1029 static boolean CheckElementChangeExt(int, int, int, int, int, int, int);
1030 #define CheckElementChange(x, y, e, te, ev)                             \
1031         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, CH_SIDE_ANY)
1032 #define CheckElementChangeByPlayer(x, y, e, ev, p, s)                   \
1033         CheckElementChangeExt(x, y, e, EL_EMPTY, ev, p, s)
1034 #define CheckElementChangeBySide(x, y, e, te, ev, s)                    \
1035         CheckElementChangeExt(x, y, e, te, ev, CH_PLAYER_ANY, s)
1036
1037 static void PlayLevelSound(int, int, int);
1038 static void PlayLevelSoundNearest(int, int, int);
1039 static void PlayLevelSoundAction(int, int, int);
1040 static void PlayLevelSoundElementAction(int, int, int, int);
1041 static void PlayLevelSoundElementActionIfLoop(int, int, int, int);
1042 static void PlayLevelSoundActionIfLoop(int, int, int);
1043 static void StopLevelSoundActionIfLoop(int, int, int);
1044 static void PlayLevelMusic();
1045
1046 static void HandleGameButtons(struct GadgetInfo *);
1047
1048 int AmoebeNachbarNr(int, int);
1049 void AmoebeUmwandeln(int, int);
1050 void ContinueMoving(int, int);
1051 void Bang(int, int);
1052 void InitMovDir(int, int);
1053 void InitAmoebaNr(int, int);
1054 int NewHiScore(void);
1055
1056 void TestIfGoodThingHitsBadThing(int, int, int);
1057 void TestIfBadThingHitsGoodThing(int, int, int);
1058 void TestIfPlayerTouchesBadThing(int, int);
1059 void TestIfPlayerRunsIntoBadThing(int, int, int);
1060 void TestIfBadThingTouchesPlayer(int, int);
1061 void TestIfBadThingRunsIntoPlayer(int, int, int);
1062 void TestIfFriendTouchesBadThing(int, int);
1063 void TestIfBadThingTouchesFriend(int, int);
1064 void TestIfBadThingTouchesOtherBadThing(int, int);
1065 void TestIfGoodThingGetsHitByBadThing(int, int, int);
1066
1067 void KillPlayer(struct PlayerInfo *);
1068 void BuryPlayer(struct PlayerInfo *);
1069 void RemovePlayer(struct PlayerInfo *);
1070
1071 static int getInvisibleActiveFromInvisibleElement(int);
1072 static int getInvisibleFromInvisibleActiveElement(int);
1073
1074 static struct GadgetInfo *game_gadget[NUM_GAME_BUTTONS];
1075
1076 /* for detection of endless loops, caused by custom element programming */
1077 /* (using maximal playfield width x 10 is just a rough approximation) */
1078 #define MAX_ELEMENT_CHANGE_RECURSION_DEPTH      (MAX_PLAYFIELD_WIDTH * 10)
1079
1080 #define RECURSION_LOOP_DETECTION_START(e, rc)                           \
1081 {                                                                       \
1082   if (recursion_loop_detected)                                          \
1083     return (rc);                                                        \
1084                                                                         \
1085   if (recursion_loop_depth > MAX_ELEMENT_CHANGE_RECURSION_DEPTH)        \
1086   {                                                                     \
1087     recursion_loop_detected = TRUE;                                     \
1088     recursion_loop_element = (e);                                       \
1089   }                                                                     \
1090                                                                         \
1091   recursion_loop_depth++;                                               \
1092 }
1093
1094 #define RECURSION_LOOP_DETECTION_END()                                  \
1095 {                                                                       \
1096   recursion_loop_depth--;                                               \
1097 }
1098
1099 static int recursion_loop_depth;
1100 static boolean recursion_loop_detected;
1101 static boolean recursion_loop_element;
1102
1103 static int map_player_action[MAX_PLAYERS];
1104
1105
1106 /* ------------------------------------------------------------------------- */
1107 /* definition of elements that automatically change to other elements after  */
1108 /* a specified time, eventually calling a function when changing             */
1109 /* ------------------------------------------------------------------------- */
1110
1111 /* forward declaration for changer functions */
1112 static void InitBuggyBase(int, int);
1113 static void WarnBuggyBase(int, int);
1114
1115 static void InitTrap(int, int);
1116 static void ActivateTrap(int, int);
1117 static void ChangeActiveTrap(int, int);
1118
1119 static void InitRobotWheel(int, int);
1120 static void RunRobotWheel(int, int);
1121 static void StopRobotWheel(int, int);
1122
1123 static void InitTimegateWheel(int, int);
1124 static void RunTimegateWheel(int, int);
1125
1126 static void InitMagicBallDelay(int, int);
1127 static void ActivateMagicBall(int, int);
1128
1129 struct ChangingElementInfo
1130 {
1131   int element;
1132   int target_element;
1133   int change_delay;
1134   void (*pre_change_function)(int x, int y);
1135   void (*change_function)(int x, int y);
1136   void (*post_change_function)(int x, int y);
1137 };
1138
1139 static struct ChangingElementInfo change_delay_list[] =
1140 {
1141   {
1142     EL_NUT_BREAKING,
1143     EL_EMERALD,
1144     6,
1145     NULL,
1146     NULL,
1147     NULL
1148   },
1149   {
1150     EL_PEARL_BREAKING,
1151     EL_EMPTY,
1152     8,
1153     NULL,
1154     NULL,
1155     NULL
1156   },
1157   {
1158     EL_EXIT_OPENING,
1159     EL_EXIT_OPEN,
1160     29,
1161     NULL,
1162     NULL,
1163     NULL
1164   },
1165   {
1166     EL_EXIT_CLOSING,
1167     EL_EXIT_CLOSED,
1168     29,
1169     NULL,
1170     NULL,
1171     NULL
1172   },
1173   {
1174     EL_STEEL_EXIT_OPENING,
1175     EL_STEEL_EXIT_OPEN,
1176     29,
1177     NULL,
1178     NULL,
1179     NULL
1180   },
1181   {
1182     EL_STEEL_EXIT_CLOSING,
1183     EL_STEEL_EXIT_CLOSED,
1184     29,
1185     NULL,
1186     NULL,
1187     NULL
1188   },
1189   {
1190     EL_EM_EXIT_OPENING,
1191     EL_EM_EXIT_OPEN,
1192     29,
1193     NULL,
1194     NULL,
1195     NULL
1196   },
1197   {
1198     EL_EM_EXIT_CLOSING,
1199     EL_EMPTY,
1200     29,
1201     NULL,
1202     NULL,
1203     NULL
1204   },
1205   {
1206     EL_EM_STEEL_EXIT_OPENING,
1207     EL_EM_STEEL_EXIT_OPEN,
1208     29,
1209     NULL,
1210     NULL,
1211     NULL
1212   },
1213   {
1214     EL_EM_STEEL_EXIT_CLOSING,
1215     EL_STEELWALL,
1216     29,
1217     NULL,
1218     NULL,
1219     NULL
1220   },
1221   {
1222     EL_SP_EXIT_OPENING,
1223     EL_SP_EXIT_OPEN,
1224     29,
1225     NULL,
1226     NULL,
1227     NULL
1228   },
1229   {
1230     EL_SP_EXIT_CLOSING,
1231     EL_SP_EXIT_CLOSED,
1232     29,
1233     NULL,
1234     NULL,
1235     NULL
1236   },
1237   {
1238     EL_SWITCHGATE_OPENING,
1239     EL_SWITCHGATE_OPEN,
1240     29,
1241     NULL,
1242     NULL,
1243     NULL
1244   },
1245   {
1246     EL_SWITCHGATE_CLOSING,
1247     EL_SWITCHGATE_CLOSED,
1248     29,
1249     NULL,
1250     NULL,
1251     NULL
1252   },
1253   {
1254     EL_TIMEGATE_OPENING,
1255     EL_TIMEGATE_OPEN,
1256     29,
1257     NULL,
1258     NULL,
1259     NULL
1260   },
1261   {
1262     EL_TIMEGATE_CLOSING,
1263     EL_TIMEGATE_CLOSED,
1264     29,
1265     NULL,
1266     NULL,
1267     NULL
1268   },
1269
1270   {
1271     EL_ACID_SPLASH_LEFT,
1272     EL_EMPTY,
1273     8,
1274     NULL,
1275     NULL,
1276     NULL
1277   },
1278   {
1279     EL_ACID_SPLASH_RIGHT,
1280     EL_EMPTY,
1281     8,
1282     NULL,
1283     NULL,
1284     NULL
1285   },
1286   {
1287     EL_SP_BUGGY_BASE,
1288     EL_SP_BUGGY_BASE_ACTIVATING,
1289     0,
1290     InitBuggyBase,
1291     NULL,
1292     NULL
1293   },
1294   {
1295     EL_SP_BUGGY_BASE_ACTIVATING,
1296     EL_SP_BUGGY_BASE_ACTIVE,
1297     0,
1298     InitBuggyBase,
1299     NULL,
1300     NULL
1301   },
1302   {
1303     EL_SP_BUGGY_BASE_ACTIVE,
1304     EL_SP_BUGGY_BASE,
1305     0,
1306     InitBuggyBase,
1307     WarnBuggyBase,
1308     NULL
1309   },
1310   {
1311     EL_TRAP,
1312     EL_TRAP_ACTIVE,
1313     0,
1314     InitTrap,
1315     NULL,
1316     ActivateTrap
1317   },
1318   {
1319     EL_TRAP_ACTIVE,
1320     EL_TRAP,
1321     31,
1322     NULL,
1323     ChangeActiveTrap,
1324     NULL
1325   },
1326   {
1327     EL_ROBOT_WHEEL_ACTIVE,
1328     EL_ROBOT_WHEEL,
1329     0,
1330     InitRobotWheel,
1331     RunRobotWheel,
1332     StopRobotWheel
1333   },
1334   {
1335     EL_TIMEGATE_SWITCH_ACTIVE,
1336     EL_TIMEGATE_SWITCH,
1337     0,
1338     InitTimegateWheel,
1339     RunTimegateWheel,
1340     NULL
1341   },
1342   {
1343     EL_DC_TIMEGATE_SWITCH_ACTIVE,
1344     EL_DC_TIMEGATE_SWITCH,
1345     0,
1346     InitTimegateWheel,
1347     RunTimegateWheel,
1348     NULL
1349   },
1350   {
1351     EL_EMC_MAGIC_BALL_ACTIVE,
1352     EL_EMC_MAGIC_BALL_ACTIVE,
1353     0,
1354     InitMagicBallDelay,
1355     NULL,
1356     ActivateMagicBall
1357   },
1358   {
1359     EL_EMC_SPRING_BUMPER_ACTIVE,
1360     EL_EMC_SPRING_BUMPER,
1361     8,
1362     NULL,
1363     NULL,
1364     NULL
1365   },
1366   {
1367     EL_DIAGONAL_SHRINKING,
1368     EL_UNDEFINED,
1369     0,
1370     NULL,
1371     NULL,
1372     NULL
1373   },
1374   {
1375     EL_DIAGONAL_GROWING,
1376     EL_UNDEFINED,
1377     0,
1378     NULL,
1379     NULL,
1380     NULL,
1381   },
1382
1383   {
1384     EL_UNDEFINED,
1385     EL_UNDEFINED,
1386     -1,
1387     NULL,
1388     NULL,
1389     NULL
1390   }
1391 };
1392
1393 struct
1394 {
1395   int element;
1396   int push_delay_fixed, push_delay_random;
1397 }
1398 push_delay_list[] =
1399 {
1400   { EL_SPRING,                  0, 0 },
1401   { EL_BALLOON,                 0, 0 },
1402
1403   { EL_SOKOBAN_OBJECT,          2, 0 },
1404   { EL_SOKOBAN_FIELD_FULL,      2, 0 },
1405   { EL_SATELLITE,               2, 0 },
1406   { EL_SP_DISK_YELLOW,          2, 0 },
1407
1408   { EL_UNDEFINED,               0, 0 },
1409 };
1410
1411 struct
1412 {
1413   int element;
1414   int move_stepsize;
1415 }
1416 move_stepsize_list[] =
1417 {
1418   { EL_AMOEBA_DROP,             2 },
1419   { EL_AMOEBA_DROPPING,         2 },
1420   { EL_QUICKSAND_FILLING,       1 },
1421   { EL_QUICKSAND_EMPTYING,      1 },
1422   { EL_QUICKSAND_FAST_FILLING,  2 },
1423   { EL_QUICKSAND_FAST_EMPTYING, 2 },
1424   { EL_MAGIC_WALL_FILLING,      2 },
1425   { EL_MAGIC_WALL_EMPTYING,     2 },
1426   { EL_BD_MAGIC_WALL_FILLING,   2 },
1427   { EL_BD_MAGIC_WALL_EMPTYING,  2 },
1428   { EL_DC_MAGIC_WALL_FILLING,   2 },
1429   { EL_DC_MAGIC_WALL_EMPTYING,  2 },
1430
1431   { EL_UNDEFINED,               0 },
1432 };
1433
1434 struct
1435 {
1436   int element;
1437   int count;
1438 }
1439 collect_count_list[] =
1440 {
1441   { EL_EMERALD,                 1 },
1442   { EL_BD_DIAMOND,              1 },
1443   { EL_EMERALD_YELLOW,          1 },
1444   { EL_EMERALD_RED,             1 },
1445   { EL_EMERALD_PURPLE,          1 },
1446   { EL_DIAMOND,                 3 },
1447   { EL_SP_INFOTRON,             1 },
1448   { EL_PEARL,                   5 },
1449   { EL_CRYSTAL,                 8 },
1450
1451   { EL_UNDEFINED,               0 },
1452 };
1453
1454 struct
1455 {
1456   int element;
1457   int direction;
1458 }
1459 access_direction_list[] =
1460 {
1461   { EL_TUBE_ANY,                        MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1462   { EL_TUBE_VERTICAL,                                        MV_UP | MV_DOWN },
1463   { EL_TUBE_HORIZONTAL,                 MV_LEFT | MV_RIGHT                   },
1464   { EL_TUBE_VERTICAL_LEFT,              MV_LEFT |            MV_UP | MV_DOWN },
1465   { EL_TUBE_VERTICAL_RIGHT,                       MV_RIGHT | MV_UP | MV_DOWN },
1466   { EL_TUBE_HORIZONTAL_UP,              MV_LEFT | MV_RIGHT | MV_UP           },
1467   { EL_TUBE_HORIZONTAL_DOWN,            MV_LEFT | MV_RIGHT |         MV_DOWN },
1468   { EL_TUBE_LEFT_UP,                    MV_LEFT |            MV_UP           },
1469   { EL_TUBE_LEFT_DOWN,                  MV_LEFT |                    MV_DOWN },
1470   { EL_TUBE_RIGHT_UP,                             MV_RIGHT | MV_UP           },
1471   { EL_TUBE_RIGHT_DOWN,                           MV_RIGHT |         MV_DOWN },
1472
1473   { EL_SP_PORT_LEFT,                              MV_RIGHT                   },
1474   { EL_SP_PORT_RIGHT,                   MV_LEFT                              },
1475   { EL_SP_PORT_UP,                                                   MV_DOWN },
1476   { EL_SP_PORT_DOWN,                                         MV_UP           },
1477   { EL_SP_PORT_HORIZONTAL,              MV_LEFT | MV_RIGHT                   },
1478   { EL_SP_PORT_VERTICAL,                                     MV_UP | MV_DOWN },
1479   { EL_SP_PORT_ANY,                     MV_LEFT | MV_RIGHT | MV_UP | MV_DOWN },
1480   { EL_SP_GRAVITY_PORT_LEFT,                      MV_RIGHT                   },
1481   { EL_SP_GRAVITY_PORT_RIGHT,           MV_LEFT                              },
1482   { EL_SP_GRAVITY_PORT_UP,                                           MV_DOWN },
1483   { EL_SP_GRAVITY_PORT_DOWN,                                 MV_UP           },
1484   { EL_SP_GRAVITY_ON_PORT_LEFT,                   MV_RIGHT                   },
1485   { EL_SP_GRAVITY_ON_PORT_RIGHT,        MV_LEFT                              },
1486   { EL_SP_GRAVITY_ON_PORT_UP,                                        MV_DOWN },
1487   { EL_SP_GRAVITY_ON_PORT_DOWN,                              MV_UP           },
1488   { EL_SP_GRAVITY_OFF_PORT_LEFT,                  MV_RIGHT                   },
1489   { EL_SP_GRAVITY_OFF_PORT_RIGHT,       MV_LEFT                              },
1490   { EL_SP_GRAVITY_OFF_PORT_UP,                                       MV_DOWN },
1491   { EL_SP_GRAVITY_OFF_PORT_DOWN,                             MV_UP           },
1492
1493   { EL_UNDEFINED,                       MV_NONE                              }
1494 };
1495
1496 static boolean trigger_events[MAX_NUM_ELEMENTS][NUM_CHANGE_EVENTS];
1497
1498 #define IS_AUTO_CHANGING(e)     (element_info[e].has_change_event[CE_DELAY])
1499 #define IS_JUST_CHANGING(x, y)  (ChangeDelay[x][y] != 0)
1500 #define IS_CHANGING(x, y)       (IS_AUTO_CHANGING(Feld[x][y]) || \
1501                                  IS_JUST_CHANGING(x, y))
1502
1503 #define CE_PAGE(e, ce)          (element_info[e].event_page[ce])
1504
1505 /* static variables for playfield scan mode (scanning forward or backward) */
1506 static int playfield_scan_start_x = 0;
1507 static int playfield_scan_start_y = 0;
1508 static int playfield_scan_delta_x = 1;
1509 static int playfield_scan_delta_y = 1;
1510
1511 #define SCAN_PLAYFIELD(x, y)    for ((y) = playfield_scan_start_y;      \
1512                                      (y) >= 0 && (y) <= lev_fieldy - 1; \
1513                                      (y) += playfield_scan_delta_y)     \
1514                                 for ((x) = playfield_scan_start_x;      \
1515                                      (x) >= 0 && (x) <= lev_fieldx - 1; \
1516                                      (x) += playfield_scan_delta_x)
1517
1518 #ifdef DEBUG
1519 void DEBUG_SetMaximumDynamite()
1520 {
1521   int i;
1522
1523   for (i = 0; i < MAX_INVENTORY_SIZE; i++)
1524     if (local_player->inventory_size < MAX_INVENTORY_SIZE)
1525       local_player->inventory_element[local_player->inventory_size++] =
1526         EL_DYNAMITE;
1527 }
1528 #endif
1529
1530 static void InitPlayfieldScanModeVars()
1531 {
1532   if (game.use_reverse_scan_direction)
1533   {
1534     playfield_scan_start_x = lev_fieldx - 1;
1535     playfield_scan_start_y = lev_fieldy - 1;
1536
1537     playfield_scan_delta_x = -1;
1538     playfield_scan_delta_y = -1;
1539   }
1540   else
1541   {
1542     playfield_scan_start_x = 0;
1543     playfield_scan_start_y = 0;
1544
1545     playfield_scan_delta_x = 1;
1546     playfield_scan_delta_y = 1;
1547   }
1548 }
1549
1550 static void InitPlayfieldScanMode(int mode)
1551 {
1552   game.use_reverse_scan_direction =
1553     (mode == CA_ARG_SCAN_MODE_REVERSE ? TRUE : FALSE);
1554
1555   InitPlayfieldScanModeVars();
1556 }
1557
1558 static int get_move_delay_from_stepsize(int move_stepsize)
1559 {
1560   move_stepsize =
1561     MIN(MAX(MOVE_STEPSIZE_MIN, move_stepsize), MOVE_STEPSIZE_MAX);
1562
1563   /* make sure that stepsize value is always a power of 2 */
1564   move_stepsize = (1 << log_2(move_stepsize));
1565
1566   return TILEX / move_stepsize;
1567 }
1568
1569 static void SetPlayerMoveSpeed(struct PlayerInfo *player, int move_stepsize,
1570                                boolean init_game)
1571 {
1572   int player_nr = player->index_nr;
1573   int move_delay = get_move_delay_from_stepsize(move_stepsize);
1574   boolean cannot_move = (move_stepsize == STEPSIZE_NOT_MOVING ? TRUE : FALSE);
1575
1576   /* do no immediately change move delay -- the player might just be moving */
1577   player->move_delay_value_next = move_delay;
1578
1579   /* information if player can move must be set separately */
1580   player->cannot_move = cannot_move;
1581
1582   if (init_game)
1583   {
1584     player->move_delay       = game.initial_move_delay[player_nr];
1585     player->move_delay_value = game.initial_move_delay_value[player_nr];
1586
1587     player->move_delay_value_next = -1;
1588
1589     player->move_delay_reset_counter = 0;
1590   }
1591 }
1592
1593 void GetPlayerConfig()
1594 {
1595   GameFrameDelay = setup.game_frame_delay;
1596
1597   if (!audio.sound_available)
1598     setup.sound_simple = FALSE;
1599
1600   if (!audio.loops_available)
1601     setup.sound_loops = FALSE;
1602
1603   if (!audio.music_available)
1604     setup.sound_music = FALSE;
1605
1606   if (!video.fullscreen_available)
1607     setup.fullscreen = FALSE;
1608
1609   setup.sound = (setup.sound_simple || setup.sound_loops || setup.sound_music);
1610
1611   SetAudioMode(setup.sound);
1612   InitJoysticks();
1613 }
1614
1615 int GetElementFromGroupElement(int element)
1616 {
1617   if (IS_GROUP_ELEMENT(element))
1618   {
1619     struct ElementGroupInfo *group = element_info[element].group;
1620     int last_anim_random_frame = gfx.anim_random_frame;
1621     int element_pos;
1622
1623     if (group->choice_mode == ANIM_RANDOM)
1624       gfx.anim_random_frame = RND(group->num_elements_resolved);
1625
1626     element_pos = getAnimationFrame(group->num_elements_resolved, 1,
1627                                     group->choice_mode, 0,
1628                                     group->choice_pos);
1629
1630     if (group->choice_mode == ANIM_RANDOM)
1631       gfx.anim_random_frame = last_anim_random_frame;
1632
1633     group->choice_pos++;
1634
1635     element = group->element_resolved[element_pos];
1636   }
1637
1638   return element;
1639 }
1640
1641 static void InitPlayerField(int x, int y, int element, boolean init_game)
1642 {
1643   if (element == EL_SP_MURPHY)
1644   {
1645     if (init_game)
1646     {
1647       if (stored_player[0].present)
1648       {
1649         Feld[x][y] = EL_SP_MURPHY_CLONE;
1650
1651         return;
1652       }
1653       else
1654       {
1655         stored_player[0].initial_element = element;
1656         stored_player[0].use_murphy = TRUE;
1657
1658         if (!level.use_artwork_element[0])
1659           stored_player[0].artwork_element = EL_SP_MURPHY;
1660       }
1661
1662       Feld[x][y] = EL_PLAYER_1;
1663     }
1664   }
1665
1666   if (init_game)
1667   {
1668     struct PlayerInfo *player = &stored_player[Feld[x][y] - EL_PLAYER_1];
1669     int jx = player->jx, jy = player->jy;
1670
1671     player->present = TRUE;
1672
1673     player->block_last_field = (element == EL_SP_MURPHY ?
1674                                 level.sp_block_last_field :
1675                                 level.block_last_field);
1676
1677     /* ---------- initialize player's last field block delay --------------- */
1678
1679     /* always start with reliable default value (no adjustment needed) */
1680     player->block_delay_adjustment = 0;
1681
1682     /* special case 1: in Supaplex, Murphy blocks last field one more frame */
1683     if (player->block_last_field && element == EL_SP_MURPHY)
1684       player->block_delay_adjustment = 1;
1685
1686     /* special case 2: in game engines before 3.1.1, blocking was different */
1687     if (game.use_block_last_field_bug)
1688       player->block_delay_adjustment = (player->block_last_field ? -1 : 1);
1689
1690     if (!options.network || player->connected)
1691     {
1692       player->active = TRUE;
1693
1694       /* remove potentially duplicate players */
1695       if (StorePlayer[jx][jy] == Feld[x][y])
1696         StorePlayer[jx][jy] = 0;
1697
1698       StorePlayer[x][y] = Feld[x][y];
1699
1700 #if DEBUG_INIT_PLAYER
1701       if (options.debug)
1702       {
1703         printf("- player element %d activated", player->element_nr);
1704         printf(" (local player is %d and currently %s)\n",
1705                local_player->element_nr,
1706                local_player->active ? "active" : "not active");
1707       }
1708     }
1709 #endif
1710
1711     Feld[x][y] = EL_EMPTY;
1712
1713     player->jx = player->last_jx = x;
1714     player->jy = player->last_jy = y;
1715   }
1716
1717   if (!init_game)
1718   {
1719     int player_nr = GET_PLAYER_NR(element);
1720     struct PlayerInfo *player = &stored_player[player_nr];
1721
1722     if (player->active && player->killed)
1723       player->reanimated = TRUE; /* if player was just killed, reanimate him */
1724   }
1725 }
1726
1727 static void InitField(int x, int y, boolean init_game)
1728 {
1729   int element = Feld[x][y];
1730
1731   switch (element)
1732   {
1733     case EL_SP_MURPHY:
1734     case EL_PLAYER_1:
1735     case EL_PLAYER_2:
1736     case EL_PLAYER_3:
1737     case EL_PLAYER_4:
1738       InitPlayerField(x, y, element, init_game);
1739       break;
1740
1741     case EL_SOKOBAN_FIELD_PLAYER:
1742       element = Feld[x][y] = EL_PLAYER_1;
1743       InitField(x, y, init_game);
1744
1745       element = Feld[x][y] = EL_SOKOBAN_FIELD_EMPTY;
1746       InitField(x, y, init_game);
1747       break;
1748
1749     case EL_SOKOBAN_FIELD_EMPTY:
1750       local_player->sokobanfields_still_needed++;
1751       break;
1752
1753     case EL_STONEBLOCK:
1754       if (x < lev_fieldx-1 && Feld[x+1][y] == EL_ACID)
1755         Feld[x][y] = EL_ACID_POOL_TOPLEFT;
1756       else if (x > 0 && Feld[x-1][y] == EL_ACID)
1757         Feld[x][y] = EL_ACID_POOL_TOPRIGHT;
1758       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPLEFT)
1759         Feld[x][y] = EL_ACID_POOL_BOTTOMLEFT;
1760       else if (y > 0 && Feld[x][y-1] == EL_ACID)
1761         Feld[x][y] = EL_ACID_POOL_BOTTOM;
1762       else if (y > 0 && Feld[x][y-1] == EL_ACID_POOL_TOPRIGHT)
1763         Feld[x][y] = EL_ACID_POOL_BOTTOMRIGHT;
1764       break;
1765
1766     case EL_BUG:
1767     case EL_BUG_RIGHT:
1768     case EL_BUG_UP:
1769     case EL_BUG_LEFT:
1770     case EL_BUG_DOWN:
1771     case EL_SPACESHIP:
1772     case EL_SPACESHIP_RIGHT:
1773     case EL_SPACESHIP_UP:
1774     case EL_SPACESHIP_LEFT:
1775     case EL_SPACESHIP_DOWN:
1776     case EL_BD_BUTTERFLY:
1777     case EL_BD_BUTTERFLY_RIGHT:
1778     case EL_BD_BUTTERFLY_UP:
1779     case EL_BD_BUTTERFLY_LEFT:
1780     case EL_BD_BUTTERFLY_DOWN:
1781     case EL_BD_FIREFLY:
1782     case EL_BD_FIREFLY_RIGHT:
1783     case EL_BD_FIREFLY_UP:
1784     case EL_BD_FIREFLY_LEFT:
1785     case EL_BD_FIREFLY_DOWN:
1786     case EL_PACMAN_RIGHT:
1787     case EL_PACMAN_UP:
1788     case EL_PACMAN_LEFT:
1789     case EL_PACMAN_DOWN:
1790     case EL_YAMYAM:
1791     case EL_YAMYAM_LEFT:
1792     case EL_YAMYAM_RIGHT:
1793     case EL_YAMYAM_UP:
1794     case EL_YAMYAM_DOWN:
1795     case EL_DARK_YAMYAM:
1796     case EL_ROBOT:
1797     case EL_PACMAN:
1798     case EL_SP_SNIKSNAK:
1799     case EL_SP_ELECTRON:
1800     case EL_MOLE:
1801     case EL_MOLE_LEFT:
1802     case EL_MOLE_RIGHT:
1803     case EL_MOLE_UP:
1804     case EL_MOLE_DOWN:
1805       InitMovDir(x, y);
1806       break;
1807
1808     case EL_AMOEBA_FULL:
1809     case EL_BD_AMOEBA:
1810       InitAmoebaNr(x, y);
1811       break;
1812
1813     case EL_AMOEBA_DROP:
1814       if (y == lev_fieldy - 1)
1815       {
1816         Feld[x][y] = EL_AMOEBA_GROWING;
1817         Store[x][y] = EL_AMOEBA_WET;
1818       }
1819       break;
1820
1821     case EL_DYNAMITE_ACTIVE:
1822     case EL_SP_DISK_RED_ACTIVE:
1823     case EL_DYNABOMB_PLAYER_1_ACTIVE:
1824     case EL_DYNABOMB_PLAYER_2_ACTIVE:
1825     case EL_DYNABOMB_PLAYER_3_ACTIVE:
1826     case EL_DYNABOMB_PLAYER_4_ACTIVE:
1827       MovDelay[x][y] = 96;
1828       break;
1829
1830     case EL_EM_DYNAMITE_ACTIVE:
1831       MovDelay[x][y] = 32;
1832       break;
1833
1834     case EL_LAMP:
1835       local_player->lights_still_needed++;
1836       break;
1837
1838     case EL_PENGUIN:
1839       local_player->friends_still_needed++;
1840       break;
1841
1842     case EL_PIG:
1843     case EL_DRAGON:
1844       GfxDir[x][y] = MovDir[x][y] = 1 << RND(4);
1845       break;
1846
1847     case EL_CONVEYOR_BELT_1_SWITCH_LEFT:
1848     case EL_CONVEYOR_BELT_1_SWITCH_MIDDLE:
1849     case EL_CONVEYOR_BELT_1_SWITCH_RIGHT:
1850     case EL_CONVEYOR_BELT_2_SWITCH_LEFT:
1851     case EL_CONVEYOR_BELT_2_SWITCH_MIDDLE:
1852     case EL_CONVEYOR_BELT_2_SWITCH_RIGHT:
1853     case EL_CONVEYOR_BELT_3_SWITCH_LEFT:
1854     case EL_CONVEYOR_BELT_3_SWITCH_MIDDLE:
1855     case EL_CONVEYOR_BELT_3_SWITCH_RIGHT:
1856     case EL_CONVEYOR_BELT_4_SWITCH_LEFT:
1857     case EL_CONVEYOR_BELT_4_SWITCH_MIDDLE:
1858     case EL_CONVEYOR_BELT_4_SWITCH_RIGHT:
1859       if (init_game)
1860       {
1861         int belt_nr = getBeltNrFromBeltSwitchElement(Feld[x][y]);
1862         int belt_dir = getBeltDirFromBeltSwitchElement(Feld[x][y]);
1863         int belt_dir_nr = getBeltDirNrFromBeltSwitchElement(Feld[x][y]);
1864
1865         if (game.belt_dir_nr[belt_nr] == 3)     /* initial value */
1866         {
1867           game.belt_dir[belt_nr] = belt_dir;
1868           game.belt_dir_nr[belt_nr] = belt_dir_nr;
1869         }
1870         else    /* more than one switch -- set it like the first switch */
1871         {
1872           Feld[x][y] = Feld[x][y] - belt_dir_nr + game.belt_dir_nr[belt_nr];
1873         }
1874       }
1875       break;
1876
1877     case EL_LIGHT_SWITCH_ACTIVE:
1878       if (init_game)
1879         game.light_time_left = level.time_light * FRAMES_PER_SECOND;
1880       break;
1881
1882     case EL_INVISIBLE_STEELWALL:
1883     case EL_INVISIBLE_WALL:
1884     case EL_INVISIBLE_SAND:
1885       if (game.light_time_left > 0 ||
1886           game.lenses_time_left > 0)
1887         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
1888       break;
1889
1890     case EL_EMC_MAGIC_BALL:
1891       if (game.ball_state)
1892         Feld[x][y] = EL_EMC_MAGIC_BALL_ACTIVE;
1893       break;
1894
1895     case EL_EMC_MAGIC_BALL_SWITCH:
1896       if (game.ball_state)
1897         Feld[x][y] = EL_EMC_MAGIC_BALL_SWITCH_ACTIVE;
1898       break;
1899
1900     case EL_TRIGGER_PLAYER:
1901     case EL_TRIGGER_ELEMENT:
1902     case EL_TRIGGER_CE_VALUE:
1903     case EL_TRIGGER_CE_SCORE:
1904     case EL_SELF:
1905     case EL_ANY_ELEMENT:
1906     case EL_CURRENT_CE_VALUE:
1907     case EL_CURRENT_CE_SCORE:
1908     case EL_PREV_CE_1:
1909     case EL_PREV_CE_2:
1910     case EL_PREV_CE_3:
1911     case EL_PREV_CE_4:
1912     case EL_PREV_CE_5:
1913     case EL_PREV_CE_6:
1914     case EL_PREV_CE_7:
1915     case EL_PREV_CE_8:
1916     case EL_NEXT_CE_1:
1917     case EL_NEXT_CE_2:
1918     case EL_NEXT_CE_3:
1919     case EL_NEXT_CE_4:
1920     case EL_NEXT_CE_5:
1921     case EL_NEXT_CE_6:
1922     case EL_NEXT_CE_7:
1923     case EL_NEXT_CE_8:
1924       /* reference elements should not be used on the playfield */
1925       Feld[x][y] = EL_EMPTY;
1926       break;
1927
1928     default:
1929       if (IS_CUSTOM_ELEMENT(element))
1930       {
1931         if (CAN_MOVE(element))
1932           InitMovDir(x, y);
1933
1934         if (!element_info[element].use_last_ce_value || init_game)
1935           CustomValue[x][y] = GET_NEW_CE_VALUE(Feld[x][y]);
1936       }
1937       else if (IS_GROUP_ELEMENT(element))
1938       {
1939         Feld[x][y] = GetElementFromGroupElement(element);
1940
1941         InitField(x, y, init_game);
1942       }
1943
1944       break;
1945   }
1946
1947   if (!init_game)
1948     CheckTriggeredElementChange(x, y, element, CE_CREATION_OF_X);
1949 }
1950
1951 static inline void InitField_WithBug1(int x, int y, boolean init_game)
1952 {
1953   InitField(x, y, init_game);
1954
1955   /* not needed to call InitMovDir() -- already done by InitField()! */
1956   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1957       CAN_MOVE(Feld[x][y]))
1958     InitMovDir(x, y);
1959 }
1960
1961 static inline void InitField_WithBug2(int x, int y, boolean init_game)
1962 {
1963   int old_element = Feld[x][y];
1964
1965   InitField(x, y, init_game);
1966
1967   /* not needed to call InitMovDir() -- already done by InitField()! */
1968   if (game.engine_version < VERSION_IDENT(3,1,0,0) &&
1969       CAN_MOVE(old_element) &&
1970       (old_element < EL_MOLE_LEFT || old_element > EL_MOLE_DOWN))
1971     InitMovDir(x, y);
1972
1973   /* this case is in fact a combination of not less than three bugs:
1974      first, it calls InitMovDir() for elements that can move, although this is
1975      already done by InitField(); then, it checks the element that was at this
1976      field _before_ the call to InitField() (which can change it); lastly, it
1977      was not called for "mole with direction" elements, which were treated as
1978      "cannot move" due to (fixed) wrong element initialization in "src/init.c"
1979   */
1980 }
1981
1982 static int get_key_element_from_nr(int key_nr)
1983 {
1984   int key_base_element = (key_nr >= STD_NUM_KEYS ? EL_EMC_KEY_5 - STD_NUM_KEYS :
1985                           level.game_engine_type == GAME_ENGINE_TYPE_EM ?
1986                           EL_EM_KEY_1 : EL_KEY_1);
1987
1988   return key_base_element + key_nr;
1989 }
1990
1991 static int get_next_dropped_element(struct PlayerInfo *player)
1992 {
1993   return (player->inventory_size > 0 ?
1994           player->inventory_element[player->inventory_size - 1] :
1995           player->inventory_infinite_element != EL_UNDEFINED ?
1996           player->inventory_infinite_element :
1997           player->dynabombs_left > 0 ?
1998           EL_DYNABOMB_PLAYER_1_ACTIVE + player->index_nr :
1999           EL_UNDEFINED);
2000 }
2001
2002 static int get_inventory_element_from_pos(struct PlayerInfo *player, int pos)
2003 {
2004   /* pos >= 0: get element from bottom of the stack;
2005      pos <  0: get element from top of the stack */
2006
2007   if (pos < 0)
2008   {
2009     int min_inventory_size = -pos;
2010     int inventory_pos = player->inventory_size - min_inventory_size;
2011     int min_dynabombs_left = min_inventory_size - player->inventory_size;
2012
2013     return (player->inventory_size >= min_inventory_size ?
2014             player->inventory_element[inventory_pos] :
2015             player->inventory_infinite_element != EL_UNDEFINED ?
2016             player->inventory_infinite_element :
2017             player->dynabombs_left >= min_dynabombs_left ?
2018             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2019             EL_UNDEFINED);
2020   }
2021   else
2022   {
2023     int min_dynabombs_left = pos + 1;
2024     int min_inventory_size = pos + 1 - player->dynabombs_left;
2025     int inventory_pos = pos - player->dynabombs_left;
2026
2027     return (player->inventory_infinite_element != EL_UNDEFINED ?
2028             player->inventory_infinite_element :
2029             player->dynabombs_left >= min_dynabombs_left ?
2030             EL_DYNABOMB_PLAYER_1 + player->index_nr :
2031             player->inventory_size >= min_inventory_size ?
2032             player->inventory_element[inventory_pos] :
2033             EL_UNDEFINED);
2034   }
2035 }
2036
2037 static int compareGamePanelOrderInfo(const void *object1, const void *object2)
2038 {
2039   const struct GamePanelOrderInfo *gpo1 = (struct GamePanelOrderInfo *)object1;
2040   const struct GamePanelOrderInfo *gpo2 = (struct GamePanelOrderInfo *)object2;
2041   int compare_result;
2042
2043   if (gpo1->sort_priority != gpo2->sort_priority)
2044     compare_result = gpo1->sort_priority - gpo2->sort_priority;
2045   else
2046     compare_result = gpo1->nr - gpo2->nr;
2047
2048   return compare_result;
2049 }
2050
2051 void InitGameControlValues()
2052 {
2053   int i;
2054
2055   for (i = 0; game_panel_controls[i].nr != -1; i++)
2056   {
2057     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2058     struct GamePanelOrderInfo *gpo = &game_panel_order[i];
2059     struct TextPosInfo *pos = gpc->pos;
2060     int nr = gpc->nr;
2061     int type = gpc->type;
2062
2063     if (nr != i)
2064     {
2065       Error(ERR_INFO, "'game_panel_controls' structure corrupted at %d", i);
2066       Error(ERR_EXIT, "this should not happen -- please debug");
2067     }
2068
2069     /* force update of game controls after initialization */
2070     gpc->value = gpc->last_value = -1;
2071     gpc->frame = gpc->last_frame = -1;
2072     gpc->gfx_frame = -1;
2073
2074     /* determine panel value width for later calculation of alignment */
2075     if (type == TYPE_INTEGER || type == TYPE_STRING)
2076     {
2077       pos->width = pos->size * getFontWidth(pos->font);
2078       pos->height = getFontHeight(pos->font);
2079     }
2080     else if (type == TYPE_ELEMENT)
2081     {
2082       pos->width = pos->size;
2083       pos->height = pos->size;
2084     }
2085
2086     /* fill structure for game panel draw order */
2087     gpo->nr = gpc->nr;
2088     gpo->sort_priority = pos->sort_priority;
2089   }
2090
2091   /* sort game panel controls according to sort_priority and control number */
2092   qsort(game_panel_order, NUM_GAME_PANEL_CONTROLS,
2093         sizeof(struct GamePanelOrderInfo), compareGamePanelOrderInfo);
2094 }
2095
2096 void UpdatePlayfieldElementCount()
2097 {
2098   boolean use_element_count = FALSE;
2099   int i, j, x, y;
2100
2101   /* first check if it is needed at all to calculate playfield element count */
2102   for (i = GAME_PANEL_ELEMENT_COUNT_1; i <= GAME_PANEL_ELEMENT_COUNT_8; i++)
2103     if (!PANEL_DEACTIVATED(game_panel_controls[i].pos))
2104       use_element_count = TRUE;
2105
2106   if (!use_element_count)
2107     return;
2108
2109   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2110     element_info[i].element_count = 0;
2111
2112   SCAN_PLAYFIELD(x, y)
2113   {
2114     element_info[Feld[x][y]].element_count++;
2115   }
2116
2117   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
2118     for (j = 0; j < MAX_NUM_ELEMENTS; j++)
2119       if (IS_IN_GROUP(j, i))
2120         element_info[EL_GROUP_START + i].element_count +=
2121           element_info[j].element_count;
2122 }
2123
2124 void UpdateGameControlValues()
2125 {
2126   int i, k;
2127   int time = (local_player->LevelSolved ?
2128               local_player->LevelSolved_CountingTime :
2129               level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2130               level.native_em_level->lev->time :
2131               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2132               level.native_sp_level->game_sp->time_played :
2133               game.no_time_limit ? TimePlayed : TimeLeft);
2134   int score = (local_player->LevelSolved ?
2135                local_player->LevelSolved_CountingScore :
2136                level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2137                level.native_em_level->lev->score :
2138                level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2139                level.native_sp_level->game_sp->score :
2140                local_player->score);
2141   int gems = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2142               level.native_em_level->lev->required :
2143               level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2144               level.native_sp_level->game_sp->infotrons_still_needed :
2145               local_player->gems_still_needed);
2146   int exit_closed = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
2147                      level.native_em_level->lev->required > 0 :
2148                      level.game_engine_type == GAME_ENGINE_TYPE_SP ?
2149                      level.native_sp_level->game_sp->infotrons_still_needed > 0 :
2150                      local_player->gems_still_needed > 0 ||
2151                      local_player->sokobanfields_still_needed > 0 ||
2152                      local_player->lights_still_needed > 0);
2153
2154   UpdatePlayfieldElementCount();
2155
2156   /* update game panel control values */
2157
2158   game_panel_controls[GAME_PANEL_LEVEL_NUMBER].value = level_nr;
2159   game_panel_controls[GAME_PANEL_GEMS].value = gems;
2160
2161   game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value = 0;
2162   for (i = 0; i < MAX_NUM_KEYS; i++)
2163     game_panel_controls[GAME_PANEL_KEY_1 + i].value = EL_EMPTY;
2164   game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_EMPTY;
2165   game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value = 0;
2166
2167   if (game.centered_player_nr == -1)
2168   {
2169     for (i = 0; i < MAX_PLAYERS; i++)
2170     {
2171       /* only one player in Supaplex game engine */
2172       if (level.game_engine_type == GAME_ENGINE_TYPE_SP && i > 0)
2173         break;
2174
2175       for (k = 0; k < MAX_NUM_KEYS; k++)
2176       {
2177         if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2178         {
2179           if (level.native_em_level->ply[i]->keys & (1 << k))
2180             game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2181               get_key_element_from_nr(k);
2182         }
2183         else if (stored_player[i].key[k])
2184           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2185             get_key_element_from_nr(k);
2186       }
2187
2188       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2189         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2190           level.native_em_level->ply[i]->dynamite;
2191       else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2192         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2193           level.native_sp_level->game_sp->red_disk_count;
2194       else
2195         game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2196           stored_player[i].inventory_size;
2197
2198       if (stored_player[i].num_white_keys > 0)
2199         game_panel_controls[GAME_PANEL_KEY_WHITE].value =
2200           EL_DC_KEY_WHITE;
2201
2202       game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2203         stored_player[i].num_white_keys;
2204     }
2205   }
2206   else
2207   {
2208     int player_nr = game.centered_player_nr;
2209
2210     for (k = 0; k < MAX_NUM_KEYS; k++)
2211     {
2212       if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2213       {
2214         if (level.native_em_level->ply[player_nr]->keys & (1 << k))
2215           game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2216             get_key_element_from_nr(k);
2217       }
2218       else if (stored_player[player_nr].key[k])
2219         game_panel_controls[GAME_PANEL_KEY_1 + k].value =
2220           get_key_element_from_nr(k);
2221     }
2222
2223     if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
2224       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2225         level.native_em_level->ply[player_nr]->dynamite;
2226     else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
2227       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2228         level.native_sp_level->game_sp->red_disk_count;
2229     else
2230       game_panel_controls[GAME_PANEL_INVENTORY_COUNT].value +=
2231         stored_player[player_nr].inventory_size;
2232
2233     if (stored_player[player_nr].num_white_keys > 0)
2234       game_panel_controls[GAME_PANEL_KEY_WHITE].value = EL_DC_KEY_WHITE;
2235
2236     game_panel_controls[GAME_PANEL_KEY_WHITE_COUNT].value +=
2237       stored_player[player_nr].num_white_keys;
2238   }
2239
2240   for (i = 0; i < NUM_PANEL_INVENTORY; i++)
2241   {
2242     game_panel_controls[GAME_PANEL_INVENTORY_FIRST_1 + i].value =
2243       get_inventory_element_from_pos(local_player, i);
2244     game_panel_controls[GAME_PANEL_INVENTORY_LAST_1 + i].value =
2245       get_inventory_element_from_pos(local_player, -i - 1);
2246   }
2247
2248   game_panel_controls[GAME_PANEL_SCORE].value = score;
2249   game_panel_controls[GAME_PANEL_HIGHSCORE].value = highscore[0].Score;
2250
2251   game_panel_controls[GAME_PANEL_TIME].value = time;
2252
2253   game_panel_controls[GAME_PANEL_TIME_HH].value = time / 3600;
2254   game_panel_controls[GAME_PANEL_TIME_MM].value = (time / 60) % 60;
2255   game_panel_controls[GAME_PANEL_TIME_SS].value = time % 60;
2256
2257   game_panel_controls[GAME_PANEL_FRAME].value = FrameCounter;
2258
2259   game_panel_controls[GAME_PANEL_SHIELD_NORMAL].value =
2260     (local_player->shield_normal_time_left > 0 ? EL_SHIELD_NORMAL_ACTIVE :
2261      EL_EMPTY);
2262   game_panel_controls[GAME_PANEL_SHIELD_NORMAL_TIME].value =
2263     local_player->shield_normal_time_left;
2264   game_panel_controls[GAME_PANEL_SHIELD_DEADLY].value =
2265     (local_player->shield_deadly_time_left > 0 ? EL_SHIELD_DEADLY_ACTIVE :
2266      EL_EMPTY);
2267   game_panel_controls[GAME_PANEL_SHIELD_DEADLY_TIME].value =
2268     local_player->shield_deadly_time_left;
2269
2270   game_panel_controls[GAME_PANEL_EXIT].value =
2271     (exit_closed ? EL_EXIT_CLOSED : EL_EXIT_OPEN);
2272
2273   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL].value =
2274     (game.ball_state ? EL_EMC_MAGIC_BALL_ACTIVE : EL_EMC_MAGIC_BALL);
2275   game_panel_controls[GAME_PANEL_EMC_MAGIC_BALL_SWITCH].value =
2276     (game.ball_state ? EL_EMC_MAGIC_BALL_SWITCH_ACTIVE :
2277      EL_EMC_MAGIC_BALL_SWITCH);
2278
2279   game_panel_controls[GAME_PANEL_LIGHT_SWITCH].value =
2280     (game.light_time_left > 0 ? EL_LIGHT_SWITCH_ACTIVE : EL_LIGHT_SWITCH);
2281   game_panel_controls[GAME_PANEL_LIGHT_SWITCH_TIME].value =
2282     game.light_time_left;
2283
2284   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH].value =
2285     (game.timegate_time_left > 0 ? EL_TIMEGATE_OPEN : EL_TIMEGATE_CLOSED);
2286   game_panel_controls[GAME_PANEL_TIMEGATE_SWITCH_TIME].value =
2287     game.timegate_time_left;
2288
2289   game_panel_controls[GAME_PANEL_SWITCHGATE_SWITCH].value =
2290     EL_SWITCHGATE_SWITCH_UP + game.switchgate_pos;
2291
2292   game_panel_controls[GAME_PANEL_EMC_LENSES].value =
2293     (game.lenses_time_left > 0 ? EL_EMC_LENSES : EL_EMPTY);
2294   game_panel_controls[GAME_PANEL_EMC_LENSES_TIME].value =
2295     game.lenses_time_left;
2296
2297   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER].value =
2298     (game.magnify_time_left > 0 ? EL_EMC_MAGNIFIER : EL_EMPTY);
2299   game_panel_controls[GAME_PANEL_EMC_MAGNIFIER_TIME].value =
2300     game.magnify_time_left;
2301
2302   game_panel_controls[GAME_PANEL_BALLOON_SWITCH].value =
2303     (game.wind_direction == MV_LEFT  ? EL_BALLOON_SWITCH_LEFT  :
2304      game.wind_direction == MV_RIGHT ? EL_BALLOON_SWITCH_RIGHT :
2305      game.wind_direction == MV_UP    ? EL_BALLOON_SWITCH_UP    :
2306      game.wind_direction == MV_DOWN  ? EL_BALLOON_SWITCH_DOWN  :
2307      EL_BALLOON_SWITCH_NONE);
2308
2309   game_panel_controls[GAME_PANEL_DYNABOMB_NUMBER].value =
2310     local_player->dynabomb_count;
2311   game_panel_controls[GAME_PANEL_DYNABOMB_SIZE].value =
2312     local_player->dynabomb_size;
2313   game_panel_controls[GAME_PANEL_DYNABOMB_POWER].value =
2314     (local_player->dynabomb_xl ? EL_DYNABOMB_INCREASE_POWER : EL_EMPTY);
2315
2316   game_panel_controls[GAME_PANEL_PENGUINS].value =
2317     local_player->friends_still_needed;
2318
2319   game_panel_controls[GAME_PANEL_SOKOBAN_OBJECTS].value =
2320     local_player->sokobanfields_still_needed;
2321   game_panel_controls[GAME_PANEL_SOKOBAN_FIELDS].value =
2322     local_player->sokobanfields_still_needed;
2323
2324   game_panel_controls[GAME_PANEL_ROBOT_WHEEL].value =
2325     (game.robot_wheel_active ? EL_ROBOT_WHEEL_ACTIVE : EL_ROBOT_WHEEL);
2326
2327   for (i = 0; i < NUM_BELTS; i++)
2328   {
2329     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1 + i].value =
2330       (game.belt_dir[i] != MV_NONE ? EL_CONVEYOR_BELT_1_MIDDLE_ACTIVE :
2331        EL_CONVEYOR_BELT_1_MIDDLE) + i;
2332     game_panel_controls[GAME_PANEL_CONVEYOR_BELT_1_SWITCH + i].value =
2333       getBeltSwitchElementFromBeltNrAndBeltDir(i, game.belt_dir[i]);
2334   }
2335
2336   game_panel_controls[GAME_PANEL_MAGIC_WALL].value =
2337     (game.magic_wall_active ? EL_MAGIC_WALL_ACTIVE : EL_MAGIC_WALL);
2338   game_panel_controls[GAME_PANEL_MAGIC_WALL_TIME].value =
2339     game.magic_wall_time_left;
2340
2341   game_panel_controls[GAME_PANEL_GRAVITY_STATE].value =
2342     local_player->gravity;
2343
2344   for (i = 0; i < NUM_PANEL_GRAPHICS; i++)
2345     game_panel_controls[GAME_PANEL_GRAPHIC_1 + i].value = EL_GRAPHIC_1 + i;
2346
2347   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2348     game_panel_controls[GAME_PANEL_ELEMENT_1 + i].value =
2349       (IS_DRAWABLE_ELEMENT(game.panel.element[i].id) ?
2350        game.panel.element[i].id : EL_UNDEFINED);
2351
2352   for (i = 0; i < NUM_PANEL_ELEMENTS; i++)
2353     game_panel_controls[GAME_PANEL_ELEMENT_COUNT_1 + i].value =
2354       (IS_VALID_ELEMENT(game.panel.element_count[i].id) ?
2355        element_info[game.panel.element_count[i].id].element_count : 0);
2356
2357   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2358     game_panel_controls[GAME_PANEL_CE_SCORE_1 + i].value =
2359       (IS_CUSTOM_ELEMENT(game.panel.ce_score[i].id) ?
2360        element_info[game.panel.ce_score[i].id].collect_score : 0);
2361
2362   for (i = 0; i < NUM_PANEL_CE_SCORE; i++)
2363     game_panel_controls[GAME_PANEL_CE_SCORE_1_ELEMENT + i].value =
2364       (IS_CUSTOM_ELEMENT(game.panel.ce_score_element[i].id) ?
2365        element_info[game.panel.ce_score_element[i].id].collect_score :
2366        EL_UNDEFINED);
2367
2368   game_panel_controls[GAME_PANEL_PLAYER_NAME].value = 0;
2369   game_panel_controls[GAME_PANEL_LEVEL_NAME].value = 0;
2370   game_panel_controls[GAME_PANEL_LEVEL_AUTHOR].value = 0;
2371
2372   /* update game panel control frames */
2373
2374   for (i = 0; game_panel_controls[i].nr != -1; i++)
2375   {
2376     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2377
2378     if (gpc->type == TYPE_ELEMENT)
2379     {
2380       if (gpc->value != EL_UNDEFINED && gpc->value != EL_EMPTY)
2381       {
2382         int last_anim_random_frame = gfx.anim_random_frame;
2383         int element = gpc->value;
2384         int graphic = el2panelimg(element);
2385
2386         if (gpc->value != gpc->last_value)
2387         {
2388           gpc->gfx_frame = 0;
2389           gpc->gfx_random = INIT_GFX_RANDOM();
2390         }
2391         else
2392         {
2393           gpc->gfx_frame++;
2394
2395           if (ANIM_MODE(graphic) == ANIM_RANDOM &&
2396               IS_NEXT_FRAME(gpc->gfx_frame, graphic))
2397             gpc->gfx_random = INIT_GFX_RANDOM();
2398         }
2399
2400         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2401           gfx.anim_random_frame = gpc->gfx_random;
2402
2403         if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
2404           gpc->gfx_frame = element_info[element].collect_score;
2405
2406         gpc->frame = getGraphicAnimationFrame(el2panelimg(gpc->value),
2407                                               gpc->gfx_frame);
2408
2409         if (ANIM_MODE(graphic) == ANIM_RANDOM)
2410           gfx.anim_random_frame = last_anim_random_frame;
2411       }
2412     }
2413   }
2414 }
2415
2416 void DisplayGameControlValues()
2417 {
2418   boolean redraw_panel = FALSE;
2419   int i;
2420
2421   for (i = 0; game_panel_controls[i].nr != -1; i++)
2422   {
2423     struct GamePanelControlInfo *gpc = &game_panel_controls[i];
2424
2425     if (PANEL_DEACTIVATED(gpc->pos))
2426       continue;
2427
2428     if (gpc->value == gpc->last_value &&
2429         gpc->frame == gpc->last_frame)
2430       continue;
2431
2432     redraw_panel = TRUE;
2433   }
2434
2435   if (!redraw_panel)
2436     return;
2437
2438   /* copy default game door content to main double buffer */
2439
2440   /* !!! CHECK AGAIN !!! */
2441   SetPanelBackground();
2442   // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
2443   DrawBackground(DX, DY, DXSIZE, DYSIZE);
2444
2445   /* redraw game control buttons */
2446   RedrawGameButtons();
2447
2448   game_status = GAME_MODE_PSEUDO_PANEL;
2449
2450   for (i = 0; i < NUM_GAME_PANEL_CONTROLS; i++)
2451   {
2452     int nr = game_panel_order[i].nr;
2453     struct GamePanelControlInfo *gpc = &game_panel_controls[nr];
2454     struct TextPosInfo *pos = gpc->pos;
2455     int type = gpc->type;
2456     int value = gpc->value;
2457     int frame = gpc->frame;
2458     int size = pos->size;
2459     int font = pos->font;
2460     boolean draw_masked = pos->draw_masked;
2461     int mask_mode = (draw_masked ? BLIT_MASKED : BLIT_OPAQUE);
2462
2463     if (PANEL_DEACTIVATED(pos))
2464       continue;
2465
2466     gpc->last_value = value;
2467     gpc->last_frame = frame;
2468
2469     if (type == TYPE_INTEGER)
2470     {
2471       if (nr == GAME_PANEL_LEVEL_NUMBER ||
2472           nr == GAME_PANEL_TIME)
2473       {
2474         boolean use_dynamic_size = (size == -1 ? TRUE : FALSE);
2475
2476         if (use_dynamic_size)           /* use dynamic number of digits */
2477         {
2478           int value_change = (nr == GAME_PANEL_LEVEL_NUMBER ? 100 : 1000);
2479           int size1 = (nr == GAME_PANEL_LEVEL_NUMBER ? 2 : 3);
2480           int size2 = size1 + 1;
2481           int font1 = pos->font;
2482           int font2 = pos->font_alt;
2483
2484           size = (value < value_change ? size1 : size2);
2485           font = (value < value_change ? font1 : font2);
2486         }
2487       }
2488
2489       /* correct text size if "digits" is zero or less */
2490       if (size <= 0)
2491         size = strlen(int2str(value, size));
2492
2493       /* dynamically correct text alignment */
2494       pos->width = size * getFontWidth(font);
2495
2496       DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2497                   int2str(value, size), font, mask_mode);
2498     }
2499     else if (type == TYPE_ELEMENT)
2500     {
2501       int element, graphic;
2502       Bitmap *src_bitmap;
2503       int src_x, src_y;
2504       int width, height;
2505       int dst_x = PANEL_XPOS(pos);
2506       int dst_y = PANEL_YPOS(pos);
2507
2508       if (value != EL_UNDEFINED && value != EL_EMPTY)
2509       {
2510         element = value;
2511         graphic = el2panelimg(value);
2512
2513         // printf("::: %d, '%s' [%d]\n", element, EL_NAME(element), size);
2514
2515         if (element >= EL_GRAPHIC_1 && element <= EL_GRAPHIC_8 && size == 0)
2516           size = TILESIZE;
2517
2518         getSizedGraphicSource(graphic, frame, size, &src_bitmap,
2519                               &src_x, &src_y);
2520
2521         width  = graphic_info[graphic].width  * size / TILESIZE;
2522         height = graphic_info[graphic].height * size / TILESIZE;
2523
2524         if (draw_masked)
2525           BlitBitmapMasked(src_bitmap, drawto, src_x, src_y, width, height,
2526                            dst_x, dst_y);
2527         else
2528           BlitBitmap(src_bitmap, drawto, src_x, src_y, width, height,
2529                      dst_x, dst_y);
2530       }
2531     }
2532     else if (type == TYPE_STRING)
2533     {
2534       boolean active = (value != 0);
2535       char *state_normal = "off";
2536       char *state_active = "on";
2537       char *state = (active ? state_active : state_normal);
2538       char *s = (nr == GAME_PANEL_GRAVITY_STATE ? state :
2539                  nr == GAME_PANEL_PLAYER_NAME   ? setup.player_name :
2540                  nr == GAME_PANEL_LEVEL_NAME    ? level.name :
2541                  nr == GAME_PANEL_LEVEL_AUTHOR  ? level.author : NULL);
2542
2543       if (nr == GAME_PANEL_GRAVITY_STATE)
2544       {
2545         int font1 = pos->font;          /* (used for normal state) */
2546         int font2 = pos->font_alt;      /* (used for active state) */
2547
2548         font = (active ? font2 : font1);
2549       }
2550
2551       if (s != NULL)
2552       {
2553         char *s_cut;
2554
2555         if (size <= 0)
2556         {
2557           /* don't truncate output if "chars" is zero or less */
2558           size = strlen(s);
2559
2560           /* dynamically correct text alignment */
2561           pos->width = size * getFontWidth(font);
2562         }
2563
2564         s_cut = getStringCopyN(s, size);
2565
2566         DrawTextExt(drawto, PANEL_XPOS(pos), PANEL_YPOS(pos),
2567                     s_cut, font, mask_mode);
2568
2569         free(s_cut);
2570       }
2571     }
2572
2573     redraw_mask |= REDRAW_DOOR_1;
2574   }
2575
2576   game_status = GAME_MODE_PLAYING;
2577 }
2578
2579 void UpdateAndDisplayGameControlValues()
2580 {
2581   if (tape.deactivate_display)
2582     return;
2583
2584   UpdateGameControlValues();
2585   DisplayGameControlValues();
2586 }
2587
2588 void UpdateGameDoorValues()
2589 {
2590   UpdateGameControlValues();
2591 }
2592
2593 void DrawGameDoorValues()
2594 {
2595   DisplayGameControlValues();
2596 }
2597
2598
2599 /*
2600   =============================================================================
2601   InitGameEngine()
2602   -----------------------------------------------------------------------------
2603   initialize game engine due to level / tape version number
2604   =============================================================================
2605 */
2606
2607 static void InitGameEngine()
2608 {
2609   int i, j, k, l, x, y;
2610
2611   /* set game engine from tape file when re-playing, else from level file */
2612   game.engine_version = (tape.playing ? tape.engine_version :
2613                          level.game_version);
2614
2615   /* set single or multi-player game mode (needed for re-playing tapes) */
2616   game.team_mode = setup.team_mode;
2617
2618   if (tape.playing)
2619   {
2620     int num_players = 0;
2621
2622     for (i = 0; i < MAX_PLAYERS; i++)
2623       if (tape.player_participates[i])
2624         num_players++;
2625
2626     /* multi-player tapes contain input data for more than one player */
2627     game.team_mode = (num_players > 1);
2628   }
2629
2630   /* ---------------------------------------------------------------------- */
2631   /* set flags for bugs and changes according to active game engine version */
2632   /* ---------------------------------------------------------------------- */
2633
2634   /*
2635     Summary of bugfix/change:
2636     Fixed handling for custom elements that change when pushed by the player.
2637
2638     Fixed/changed in version:
2639     3.1.0
2640
2641     Description:
2642     Before 3.1.0, custom elements that "change when pushing" changed directly
2643     after the player started pushing them (until then handled in "DigField()").
2644     Since 3.1.0, these custom elements are not changed until the "pushing"
2645     move of the element is finished (now handled in "ContinueMoving()").
2646
2647     Affected levels/tapes:
2648     The first condition is generally needed for all levels/tapes before version
2649     3.1.0, which might use the old behaviour before it was changed; known tapes
2650     that are affected are some tapes from the level set "Walpurgis Gardens" by
2651     Jamie Cullen.
2652     The second condition is an exception from the above case and is needed for
2653     the special case of tapes recorded with game (not engine!) version 3.1.0 or
2654     above (including some development versions of 3.1.0), but before it was
2655     known that this change would break tapes like the above and was fixed in
2656     3.1.1, so that the changed behaviour was active although the engine version
2657     while recording maybe was before 3.1.0. There is at least one tape that is
2658     affected by this exception, which is the tape for the one-level set "Bug
2659     Machine" by Juergen Bonhagen.
2660   */
2661
2662   game.use_change_when_pushing_bug =
2663     (game.engine_version < VERSION_IDENT(3,1,0,0) &&
2664      !(tape.playing &&
2665        tape.game_version >= VERSION_IDENT(3,1,0,0) &&
2666        tape.game_version <  VERSION_IDENT(3,1,1,0)));
2667
2668   /*
2669     Summary of bugfix/change:
2670     Fixed handling for blocking the field the player leaves when moving.
2671
2672     Fixed/changed in version:
2673     3.1.1
2674
2675     Description:
2676     Before 3.1.1, when "block last field when moving" was enabled, the field
2677     the player is leaving when moving was blocked for the time of the move,
2678     and was directly unblocked afterwards. This resulted in the last field
2679     being blocked for exactly one less than the number of frames of one player
2680     move. Additionally, even when blocking was disabled, the last field was
2681     blocked for exactly one frame.
2682     Since 3.1.1, due to changes in player movement handling, the last field
2683     is not blocked at all when blocking is disabled. When blocking is enabled,
2684     the last field is blocked for exactly the number of frames of one player
2685     move. Additionally, if the player is Murphy, the hero of Supaplex, the
2686     last field is blocked for exactly one more than the number of frames of
2687     one player move.
2688
2689     Affected levels/tapes:
2690     (!!! yet to be determined -- probably many !!!)
2691   */
2692
2693   game.use_block_last_field_bug =
2694     (game.engine_version < VERSION_IDENT(3,1,1,0));
2695
2696   /* ---------------------------------------------------------------------- */
2697
2698   /* set maximal allowed number of custom element changes per game frame */
2699   game.max_num_changes_per_frame = 1;
2700
2701   /* default scan direction: scan playfield from top/left to bottom/right */
2702   InitPlayfieldScanMode(CA_ARG_SCAN_MODE_NORMAL);
2703
2704   /* dynamically adjust element properties according to game engine version */
2705   InitElementPropertiesEngine(game.engine_version);
2706
2707 #if 0
2708   printf("level %d: level version == %06d\n", level_nr, level.game_version);
2709   printf("          tape version == %06d [%s] [file: %06d]\n",
2710          tape.engine_version, (tape.playing ? "PLAYING" : "RECORDING"),
2711          tape.file_version);
2712   printf("       => game.engine_version == %06d\n", game.engine_version);
2713 #endif
2714
2715   /* ---------- initialize player's initial move delay --------------------- */
2716
2717   /* dynamically adjust player properties according to level information */
2718   for (i = 0; i < MAX_PLAYERS; i++)
2719     game.initial_move_delay_value[i] =
2720       get_move_delay_from_stepsize(level.initial_player_stepsize[i]);
2721
2722   /* dynamically adjust player properties according to game engine version */
2723   for (i = 0; i < MAX_PLAYERS; i++)
2724     game.initial_move_delay[i] =
2725       (game.engine_version <= VERSION_IDENT(2,0,1,0) ?
2726        game.initial_move_delay_value[i] : 0);
2727
2728   /* ---------- initialize player's initial push delay --------------------- */
2729
2730   /* dynamically adjust player properties according to game engine version */
2731   game.initial_push_delay_value =
2732     (game.engine_version < VERSION_IDENT(3,0,7,1) ? 5 : -1);
2733
2734   /* ---------- initialize changing elements ------------------------------- */
2735
2736   /* initialize changing elements information */
2737   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2738   {
2739     struct ElementInfo *ei = &element_info[i];
2740
2741     /* this pointer might have been changed in the level editor */
2742     ei->change = &ei->change_page[0];
2743
2744     if (!IS_CUSTOM_ELEMENT(i))
2745     {
2746       ei->change->target_element = EL_EMPTY_SPACE;
2747       ei->change->delay_fixed = 0;
2748       ei->change->delay_random = 0;
2749       ei->change->delay_frames = 1;
2750     }
2751
2752     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2753     {
2754       ei->has_change_event[j] = FALSE;
2755
2756       ei->event_page_nr[j] = 0;
2757       ei->event_page[j] = &ei->change_page[0];
2758     }
2759   }
2760
2761   /* add changing elements from pre-defined list */
2762   for (i = 0; change_delay_list[i].element != EL_UNDEFINED; i++)
2763   {
2764     struct ChangingElementInfo *ch_delay = &change_delay_list[i];
2765     struct ElementInfo *ei = &element_info[ch_delay->element];
2766
2767     ei->change->target_element       = ch_delay->target_element;
2768     ei->change->delay_fixed          = ch_delay->change_delay;
2769
2770     ei->change->pre_change_function  = ch_delay->pre_change_function;
2771     ei->change->change_function      = ch_delay->change_function;
2772     ei->change->post_change_function = ch_delay->post_change_function;
2773
2774     ei->change->can_change = TRUE;
2775     ei->change->can_change_or_has_action = TRUE;
2776
2777     ei->has_change_event[CE_DELAY] = TRUE;
2778
2779     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE, TRUE);
2780     SET_PROPERTY(ch_delay->element, EP_CAN_CHANGE_OR_HAS_ACTION, TRUE);
2781   }
2782
2783   /* ---------- initialize internal run-time variables --------------------- */
2784
2785   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2786   {
2787     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2788
2789     for (j = 0; j < ei->num_change_pages; j++)
2790     {
2791       ei->change_page[j].can_change_or_has_action =
2792         (ei->change_page[j].can_change |
2793          ei->change_page[j].has_action);
2794     }
2795   }
2796
2797   /* add change events from custom element configuration */
2798   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2799   {
2800     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2801
2802     for (j = 0; j < ei->num_change_pages; j++)
2803     {
2804       if (!ei->change_page[j].can_change_or_has_action)
2805         continue;
2806
2807       for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2808       {
2809         /* only add event page for the first page found with this event */
2810         if (ei->change_page[j].has_event[k] && !(ei->has_change_event[k]))
2811         {
2812           ei->has_change_event[k] = TRUE;
2813
2814           ei->event_page_nr[k] = j;
2815           ei->event_page[k] = &ei->change_page[j];
2816         }
2817       }
2818     }
2819   }
2820
2821   /* ---------- initialize reference elements in change conditions --------- */
2822
2823   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2824   {
2825     int element = EL_CUSTOM_START + i;
2826     struct ElementInfo *ei = &element_info[element];
2827
2828     for (j = 0; j < ei->num_change_pages; j++)
2829     {
2830       int trigger_element = ei->change_page[j].initial_trigger_element;
2831
2832       if (trigger_element >= EL_PREV_CE_8 &&
2833           trigger_element <= EL_NEXT_CE_8)
2834         trigger_element = RESOLVED_REFERENCE_ELEMENT(element, trigger_element);
2835
2836       ei->change_page[j].trigger_element = trigger_element;
2837     }
2838   }
2839
2840   /* ---------- initialize run-time trigger player and element ------------- */
2841
2842   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
2843   {
2844     struct ElementInfo *ei = &element_info[EL_CUSTOM_START + i];
2845
2846     for (j = 0; j < ei->num_change_pages; j++)
2847     {
2848       ei->change_page[j].actual_trigger_element = EL_EMPTY;
2849       ei->change_page[j].actual_trigger_player = EL_EMPTY;
2850       ei->change_page[j].actual_trigger_player_bits = CH_PLAYER_NONE;
2851       ei->change_page[j].actual_trigger_side = CH_SIDE_NONE;
2852       ei->change_page[j].actual_trigger_ce_value = 0;
2853       ei->change_page[j].actual_trigger_ce_score = 0;
2854     }
2855   }
2856
2857   /* ---------- initialize trigger events ---------------------------------- */
2858
2859   /* initialize trigger events information */
2860   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2861     for (j = 0; j < NUM_CHANGE_EVENTS; j++)
2862       trigger_events[i][j] = FALSE;
2863
2864   /* add trigger events from element change event properties */
2865   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2866   {
2867     struct ElementInfo *ei = &element_info[i];
2868
2869     for (j = 0; j < ei->num_change_pages; j++)
2870     {
2871       if (!ei->change_page[j].can_change_or_has_action)
2872         continue;
2873
2874       if (ei->change_page[j].has_event[CE_BY_OTHER_ACTION])
2875       {
2876         int trigger_element = ei->change_page[j].trigger_element;
2877
2878         for (k = 0; k < NUM_CHANGE_EVENTS; k++)
2879         {
2880           if (ei->change_page[j].has_event[k])
2881           {
2882             if (IS_GROUP_ELEMENT(trigger_element))
2883             {
2884               struct ElementGroupInfo *group =
2885                 element_info[trigger_element].group;
2886
2887               for (l = 0; l < group->num_elements_resolved; l++)
2888                 trigger_events[group->element_resolved[l]][k] = TRUE;
2889             }
2890             else if (trigger_element == EL_ANY_ELEMENT)
2891               for (l = 0; l < MAX_NUM_ELEMENTS; l++)
2892                 trigger_events[l][k] = TRUE;
2893             else
2894               trigger_events[trigger_element][k] = TRUE;
2895           }
2896         }
2897       }
2898     }
2899   }
2900
2901   /* ---------- initialize push delay -------------------------------------- */
2902
2903   /* initialize push delay values to default */
2904   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2905   {
2906     if (!IS_CUSTOM_ELEMENT(i))
2907     {
2908       /* set default push delay values (corrected since version 3.0.7-1) */
2909       if (game.engine_version < VERSION_IDENT(3,0,7,1))
2910       {
2911         element_info[i].push_delay_fixed = 2;
2912         element_info[i].push_delay_random = 8;
2913       }
2914       else
2915       {
2916         element_info[i].push_delay_fixed = 8;
2917         element_info[i].push_delay_random = 8;
2918       }
2919     }
2920   }
2921
2922   /* set push delay value for certain elements from pre-defined list */
2923   for (i = 0; push_delay_list[i].element != EL_UNDEFINED; i++)
2924   {
2925     int e = push_delay_list[i].element;
2926
2927     element_info[e].push_delay_fixed  = push_delay_list[i].push_delay_fixed;
2928     element_info[e].push_delay_random = push_delay_list[i].push_delay_random;
2929   }
2930
2931   /* set push delay value for Supaplex elements for newer engine versions */
2932   if (game.engine_version >= VERSION_IDENT(3,1,0,0))
2933   {
2934     for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2935     {
2936       if (IS_SP_ELEMENT(i))
2937       {
2938         /* set SP push delay to just enough to push under a falling zonk */
2939         int delay = (game.engine_version >= VERSION_IDENT(3,1,1,0) ? 8 : 6);
2940
2941         element_info[i].push_delay_fixed  = delay;
2942         element_info[i].push_delay_random = 0;
2943       }
2944     }
2945   }
2946
2947   /* ---------- initialize move stepsize ----------------------------------- */
2948
2949   /* initialize move stepsize values to default */
2950   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2951     if (!IS_CUSTOM_ELEMENT(i))
2952       element_info[i].move_stepsize = MOVE_STEPSIZE_NORMAL;
2953
2954   /* set move stepsize value for certain elements from pre-defined list */
2955   for (i = 0; move_stepsize_list[i].element != EL_UNDEFINED; i++)
2956   {
2957     int e = move_stepsize_list[i].element;
2958
2959     element_info[e].move_stepsize = move_stepsize_list[i].move_stepsize;
2960   }
2961
2962   /* ---------- initialize collect score ----------------------------------- */
2963
2964   /* initialize collect score values for custom elements from initial value */
2965   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2966     if (IS_CUSTOM_ELEMENT(i))
2967       element_info[i].collect_score = element_info[i].collect_score_initial;
2968
2969   /* ---------- initialize collect count ----------------------------------- */
2970
2971   /* initialize collect count values for non-custom elements */
2972   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2973     if (!IS_CUSTOM_ELEMENT(i))
2974       element_info[i].collect_count_initial = 0;
2975
2976   /* add collect count values for all elements from pre-defined list */
2977   for (i = 0; collect_count_list[i].element != EL_UNDEFINED; i++)
2978     element_info[collect_count_list[i].element].collect_count_initial =
2979       collect_count_list[i].count;
2980
2981   /* ---------- initialize access direction -------------------------------- */
2982
2983   /* initialize access direction values to default (access from every side) */
2984   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2985     if (!IS_CUSTOM_ELEMENT(i))
2986       element_info[i].access_direction = MV_ALL_DIRECTIONS;
2987
2988   /* set access direction value for certain elements from pre-defined list */
2989   for (i = 0; access_direction_list[i].element != EL_UNDEFINED; i++)
2990     element_info[access_direction_list[i].element].access_direction =
2991       access_direction_list[i].direction;
2992
2993   /* ---------- initialize explosion content ------------------------------- */
2994   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
2995   {
2996     if (IS_CUSTOM_ELEMENT(i))
2997       continue;
2998
2999     for (y = 0; y < 3; y++) for (x = 0; x < 3; x++)
3000     {
3001       /* (content for EL_YAMYAM set at run-time with game.yamyam_content_nr) */
3002
3003       element_info[i].content.e[x][y] =
3004         (i == EL_PLAYER_1 ? EL_EMERALD_YELLOW :
3005          i == EL_PLAYER_2 ? EL_EMERALD_RED :
3006          i == EL_PLAYER_3 ? EL_EMERALD :
3007          i == EL_PLAYER_4 ? EL_EMERALD_PURPLE :
3008          i == EL_MOLE ? EL_EMERALD_RED :
3009          i == EL_PENGUIN ? EL_EMERALD_PURPLE :
3010          i == EL_BUG ? (x == 1 && y == 1 ? EL_DIAMOND : EL_EMERALD) :
3011          i == EL_BD_BUTTERFLY ? EL_BD_DIAMOND :
3012          i == EL_SP_ELECTRON ? EL_SP_INFOTRON :
3013          i == EL_AMOEBA_TO_DIAMOND ? level.amoeba_content :
3014          i == EL_WALL_EMERALD ? EL_EMERALD :
3015          i == EL_WALL_DIAMOND ? EL_DIAMOND :
3016          i == EL_WALL_BD_DIAMOND ? EL_BD_DIAMOND :
3017          i == EL_WALL_EMERALD_YELLOW ? EL_EMERALD_YELLOW :
3018          i == EL_WALL_EMERALD_RED ? EL_EMERALD_RED :
3019          i == EL_WALL_EMERALD_PURPLE ? EL_EMERALD_PURPLE :
3020          i == EL_WALL_PEARL ? EL_PEARL :
3021          i == EL_WALL_CRYSTAL ? EL_CRYSTAL :
3022          EL_EMPTY);
3023     }
3024   }
3025
3026   /* ---------- initialize recursion detection ------------------------------ */
3027   recursion_loop_depth = 0;
3028   recursion_loop_detected = FALSE;
3029   recursion_loop_element = EL_UNDEFINED;
3030
3031   /* ---------- initialize graphics engine ---------------------------------- */
3032   game.scroll_delay_value =
3033     (game.forced_scroll_delay_value != -1 ? game.forced_scroll_delay_value :
3034      setup.scroll_delay                   ? setup.scroll_delay_value       : 0);
3035   game.scroll_delay_value =
3036     MIN(MAX(MIN_SCROLL_DELAY, game.scroll_delay_value), MAX_SCROLL_DELAY);
3037
3038   /* ---------- initialize game engine snapshots ---------------------------- */
3039   for (i = 0; i < MAX_PLAYERS; i++)
3040     game.snapshot.last_action[i] = 0;
3041   game.snapshot.changed_action = FALSE;
3042   game.snapshot.mode = SNAPSHOT_MODE_MOVE;
3043
3044   FreeEngineSnapshotList();
3045 }
3046
3047 int get_num_special_action(int element, int action_first, int action_last)
3048 {
3049   int num_special_action = 0;
3050   int i, j;
3051
3052   for (i = action_first; i <= action_last; i++)
3053   {
3054     boolean found = FALSE;
3055
3056     for (j = 0; j < NUM_DIRECTIONS; j++)
3057       if (el_act_dir2img(element, i, j) !=
3058           el_act_dir2img(element, ACTION_DEFAULT, j))
3059         found = TRUE;
3060
3061     if (found)
3062       num_special_action++;
3063     else
3064       break;
3065   }
3066
3067   return num_special_action;
3068 }
3069
3070
3071 /*
3072   =============================================================================
3073   InitGame()
3074   -----------------------------------------------------------------------------
3075   initialize and start new game
3076   =============================================================================
3077 */
3078
3079 void InitGame()
3080 {
3081   int full_lev_fieldx = lev_fieldx + (BorderElement != EL_EMPTY ? 2 : 0);
3082   int full_lev_fieldy = lev_fieldy + (BorderElement != EL_EMPTY ? 2 : 0);
3083
3084   boolean emulate_bd = TRUE;    /* unless non-BOULDERDASH elements found */
3085   boolean emulate_sb = TRUE;    /* unless non-SOKOBAN     elements found */
3086   boolean emulate_sp = TRUE;    /* unless non-SUPAPLEX    elements found */
3087   int initial_move_dir = MV_DOWN;
3088   int i, j, x, y;
3089
3090   game_status = GAME_MODE_PLAYING;
3091
3092   StopAnimation();
3093
3094   if (!game.restart_level)
3095     CloseDoor(DOOR_CLOSE_1);
3096
3097   if (level_editor_test_game)
3098     FadeSkipNextFadeIn();
3099   else
3100     FadeSetEnterScreen();
3101
3102   FadeOut(REDRAW_FIELD);
3103
3104   /* needed if different viewport properties defined for playing */
3105   ChangeViewportPropertiesIfNeeded();
3106
3107   DrawCompleteVideoDisplay();
3108
3109   InitGameEngine();
3110   InitGameControlValues();
3111
3112   /* don't play tapes over network */
3113   network_playing = (options.network && !tape.playing);
3114
3115   for (i = 0; i < MAX_PLAYERS; i++)
3116   {
3117     struct PlayerInfo *player = &stored_player[i];
3118
3119     player->index_nr = i;
3120     player->index_bit = (1 << i);
3121     player->element_nr = EL_PLAYER_1 + i;
3122
3123     player->present = FALSE;
3124     player->active = FALSE;
3125     player->mapped = FALSE;
3126
3127     player->killed = FALSE;
3128     player->reanimated = FALSE;
3129
3130     player->action = 0;
3131     player->effective_action = 0;
3132     player->programmed_action = 0;
3133
3134     player->score = 0;
3135     player->score_final = 0;
3136
3137     player->gems_still_needed = level.gems_needed;
3138     player->sokobanfields_still_needed = 0;
3139     player->lights_still_needed = 0;
3140     player->friends_still_needed = 0;
3141
3142     for (j = 0; j < MAX_NUM_KEYS; j++)
3143       player->key[j] = FALSE;
3144
3145     player->num_white_keys = 0;
3146
3147     player->dynabomb_count = 0;
3148     player->dynabomb_size = 1;
3149     player->dynabombs_left = 0;
3150     player->dynabomb_xl = FALSE;
3151
3152     player->MovDir = initial_move_dir;
3153     player->MovPos = 0;
3154     player->GfxPos = 0;
3155     player->GfxDir = initial_move_dir;
3156     player->GfxAction = ACTION_DEFAULT;
3157     player->Frame = 0;
3158     player->StepFrame = 0;
3159
3160     player->initial_element = player->element_nr;
3161     player->artwork_element =
3162       (level.use_artwork_element[i] ? level.artwork_element[i] :
3163        player->element_nr);
3164     player->use_murphy = FALSE;
3165
3166     player->block_last_field = FALSE;   /* initialized in InitPlayerField() */
3167     player->block_delay_adjustment = 0; /* initialized in InitPlayerField() */
3168
3169     player->gravity = level.initial_player_gravity[i];
3170
3171     player->can_fall_into_acid = CAN_MOVE_INTO_ACID(player->element_nr);
3172
3173     player->actual_frame_counter = 0;
3174
3175     player->step_counter = 0;
3176
3177     player->last_move_dir = initial_move_dir;
3178
3179     player->is_active = FALSE;
3180
3181     player->is_waiting = FALSE;
3182     player->is_moving = FALSE;
3183     player->is_auto_moving = FALSE;
3184     player->is_digging = FALSE;
3185     player->is_snapping = FALSE;
3186     player->is_collecting = FALSE;
3187     player->is_pushing = FALSE;
3188     player->is_switching = FALSE;
3189     player->is_dropping = FALSE;
3190     player->is_dropping_pressed = FALSE;
3191
3192     player->is_bored = FALSE;
3193     player->is_sleeping = FALSE;
3194
3195     player->frame_counter_bored = -1;
3196     player->frame_counter_sleeping = -1;
3197
3198     player->anim_delay_counter = 0;
3199     player->post_delay_counter = 0;
3200
3201     player->dir_waiting = initial_move_dir;
3202     player->action_waiting = ACTION_DEFAULT;
3203     player->last_action_waiting = ACTION_DEFAULT;
3204     player->special_action_bored = ACTION_DEFAULT;
3205     player->special_action_sleeping = ACTION_DEFAULT;
3206
3207     player->switch_x = -1;
3208     player->switch_y = -1;
3209
3210     player->drop_x = -1;
3211     player->drop_y = -1;
3212
3213     player->show_envelope = 0;
3214
3215     SetPlayerMoveSpeed(player, level.initial_player_stepsize[i], TRUE);
3216
3217     player->push_delay       = -1;      /* initialized when pushing starts */
3218     player->push_delay_value = game.initial_push_delay_value;
3219
3220     player->drop_delay = 0;
3221     player->drop_pressed_delay = 0;
3222
3223     player->last_jx = -1;
3224     player->last_jy = -1;
3225     player->jx = -1;
3226     player->jy = -1;
3227
3228     player->shield_normal_time_left = 0;
3229     player->shield_deadly_time_left = 0;
3230
3231     player->inventory_infinite_element = EL_UNDEFINED;
3232     player->inventory_size = 0;
3233
3234     if (level.use_initial_inventory[i])
3235     {
3236       for (j = 0; j < level.initial_inventory_size[i]; j++)
3237       {
3238         int element = level.initial_inventory_content[i][j];
3239         int collect_count = element_info[element].collect_count_initial;
3240         int k;
3241
3242         if (!IS_CUSTOM_ELEMENT(element))
3243           collect_count = 1;
3244
3245         if (collect_count == 0)
3246           player->inventory_infinite_element = element;
3247         else
3248           for (k = 0; k < collect_count; k++)
3249             if (player->inventory_size < MAX_INVENTORY_SIZE)
3250               player->inventory_element[player->inventory_size++] = element;
3251       }
3252     }
3253
3254     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
3255     SnapField(player, 0, 0);
3256
3257     player->LevelSolved = FALSE;
3258     player->GameOver = FALSE;
3259
3260     player->LevelSolved_GameWon = FALSE;
3261     player->LevelSolved_GameEnd = FALSE;
3262     player->LevelSolved_PanelOff = FALSE;
3263     player->LevelSolved_SaveTape = FALSE;
3264     player->LevelSolved_SaveScore = FALSE;
3265     player->LevelSolved_CountingTime = 0;
3266     player->LevelSolved_CountingScore = 0;
3267
3268     map_player_action[i] = i;
3269   }
3270
3271   network_player_action_received = FALSE;
3272
3273 #if defined(NETWORK_AVALIABLE)
3274   /* initial null action */
3275   if (network_playing)
3276     SendToServer_MovePlayer(MV_NONE);
3277 #endif
3278
3279   ZX = ZY = -1;
3280   ExitX = ExitY = -1;
3281
3282   FrameCounter = 0;
3283   TimeFrames = 0;
3284   TimePlayed = 0;
3285   TimeLeft = level.time;
3286   TapeTime = 0;
3287
3288   ScreenMovDir = MV_NONE;
3289   ScreenMovPos = 0;
3290   ScreenGfxPos = 0;
3291
3292   ScrollStepSize = 0;   /* will be correctly initialized by ScrollScreen() */
3293
3294   AllPlayersGone = FALSE;
3295
3296   game.no_time_limit = (level.time == 0);
3297
3298   game.yamyam_content_nr = 0;
3299   game.robot_wheel_active = FALSE;
3300   game.magic_wall_active = FALSE;
3301   game.magic_wall_time_left = 0;
3302   game.light_time_left = 0;
3303   game.timegate_time_left = 0;
3304   game.switchgate_pos = 0;
3305   game.wind_direction = level.wind_direction_initial;
3306
3307   game.lenses_time_left = 0;
3308   game.magnify_time_left = 0;
3309
3310   game.ball_state = level.ball_state_initial;
3311   game.ball_content_nr = 0;
3312
3313   game.envelope_active = FALSE;
3314
3315   /* set focus to local player for network games, else to all players */
3316   game.centered_player_nr = (network_playing ? local_player->index_nr : -1);
3317   game.centered_player_nr_next = game.centered_player_nr;
3318   game.set_centered_player = FALSE;
3319
3320   if (network_playing && tape.recording)
3321   {
3322     /* store client dependent player focus when recording network games */
3323     tape.centered_player_nr_next = game.centered_player_nr_next;
3324     tape.set_centered_player = TRUE;
3325   }
3326
3327   for (i = 0; i < NUM_BELTS; i++)
3328   {
3329     game.belt_dir[i] = MV_NONE;
3330     game.belt_dir_nr[i] = 3;            /* not moving, next moving left */
3331   }
3332
3333   for (i = 0; i < MAX_NUM_AMOEBA; i++)
3334     AmoebaCnt[i] = AmoebaCnt2[i] = 0;
3335
3336 #if DEBUG_INIT_PLAYER
3337   if (options.debug)
3338   {
3339     printf("Player status at level initialization:\n");
3340   }
3341 #endif
3342
3343   SCAN_PLAYFIELD(x, y)
3344   {
3345     Feld[x][y] = level.field[x][y];
3346     MovPos[x][y] = MovDir[x][y] = MovDelay[x][y] = 0;
3347     ChangeDelay[x][y] = 0;
3348     ChangePage[x][y] = -1;
3349     CustomValue[x][y] = 0;              /* initialized in InitField() */
3350     Store[x][y] = Store2[x][y] = StorePlayer[x][y] = Back[x][y] = 0;
3351     AmoebaNr[x][y] = 0;
3352     WasJustMoving[x][y] = 0;
3353     WasJustFalling[x][y] = 0;
3354     CheckCollision[x][y] = 0;
3355     CheckImpact[x][y] = 0;
3356     Stop[x][y] = FALSE;
3357     Pushed[x][y] = FALSE;
3358
3359     ChangeCount[x][y] = 0;
3360     ChangeEvent[x][y] = -1;
3361
3362     ExplodePhase[x][y] = 0;
3363     ExplodeDelay[x][y] = 0;
3364     ExplodeField[x][y] = EX_TYPE_NONE;
3365
3366     RunnerVisit[x][y] = 0;
3367     PlayerVisit[x][y] = 0;
3368
3369     GfxFrame[x][y] = 0;
3370     GfxRandom[x][y] = INIT_GFX_RANDOM();
3371     GfxElement[x][y] = EL_UNDEFINED;
3372     GfxAction[x][y] = ACTION_DEFAULT;
3373     GfxDir[x][y] = MV_NONE;
3374     GfxRedraw[x][y] = GFX_REDRAW_NONE;
3375   }
3376
3377   SCAN_PLAYFIELD(x, y)
3378   {
3379     if (emulate_bd && !IS_BD_ELEMENT(Feld[x][y]))
3380       emulate_bd = FALSE;
3381     if (emulate_sb && !IS_SB_ELEMENT(Feld[x][y]))
3382       emulate_sb = FALSE;
3383     if (emulate_sp && !IS_SP_ELEMENT(Feld[x][y]))
3384       emulate_sp = FALSE;
3385
3386     InitField(x, y, TRUE);
3387
3388     ResetGfxAnimation(x, y);
3389   }
3390
3391   InitBeltMovement();
3392
3393   for (i = 0; i < MAX_PLAYERS; i++)
3394   {
3395     struct PlayerInfo *player = &stored_player[i];
3396
3397     /* set number of special actions for bored and sleeping animation */
3398     player->num_special_action_bored =
3399       get_num_special_action(player->artwork_element,
3400                              ACTION_BORING_1, ACTION_BORING_LAST);
3401     player->num_special_action_sleeping =
3402       get_num_special_action(player->artwork_element,
3403                              ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
3404   }
3405
3406   game.emulation = (emulate_bd ? EMU_BOULDERDASH :
3407                     emulate_sb ? EMU_SOKOBAN :
3408                     emulate_sp ? EMU_SUPAPLEX : EMU_NONE);
3409
3410   /* initialize type of slippery elements */
3411   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3412   {
3413     if (!IS_CUSTOM_ELEMENT(i))
3414     {
3415       /* default: elements slip down either to the left or right randomly */
3416       element_info[i].slippery_type = SLIPPERY_ANY_RANDOM;
3417
3418       /* SP style elements prefer to slip down on the left side */
3419       if (game.engine_version >= VERSION_IDENT(3,1,1,0) && IS_SP_ELEMENT(i))
3420         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3421
3422       /* BD style elements prefer to slip down on the left side */
3423       if (game.emulation == EMU_BOULDERDASH)
3424         element_info[i].slippery_type = SLIPPERY_ANY_LEFT_RIGHT;
3425     }
3426   }
3427
3428   /* initialize explosion and ignition delay */
3429   for (i = 0; i < MAX_NUM_ELEMENTS; i++)
3430   {
3431     if (!IS_CUSTOM_ELEMENT(i))
3432     {
3433       int num_phase = 8;
3434       int delay = (((IS_SP_ELEMENT(i) && i != EL_EMPTY_SPACE) &&
3435                     game.engine_version >= VERSION_IDENT(3,1,0,0)) ||
3436                    game.emulation == EMU_SUPAPLEX ? 3 : 2);
3437       int last_phase = (num_phase + 1) * delay;
3438       int half_phase = (num_phase / 2) * delay;
3439
3440       element_info[i].explosion_delay = last_phase - 1;
3441       element_info[i].ignition_delay = half_phase;
3442
3443       if (i == EL_BLACK_ORB)
3444         element_info[i].ignition_delay = 1;
3445     }
3446   }
3447
3448   /* correct non-moving belts to start moving left */
3449   for (i = 0; i < NUM_BELTS; i++)
3450     if (game.belt_dir[i] == MV_NONE)
3451       game.belt_dir_nr[i] = 3;          /* not moving, next moving left */
3452
3453 #if USE_NEW_PLAYER_ASSIGNMENTS
3454   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3455   /* choose default local player */
3456   local_player = &stored_player[0];
3457
3458   for (i = 0; i < MAX_PLAYERS; i++)
3459     stored_player[i].connected = FALSE;
3460
3461   local_player->connected = TRUE;
3462   /* !!! SAME AS init.c:InitPlayerInfo() -- FIX THIS !!! */
3463
3464   if (tape.playing)
3465   {
3466     for (i = 0; i < MAX_PLAYERS; i++)
3467       stored_player[i].connected = tape.player_participates[i];
3468   }
3469   else if (game.team_mode && !options.network)
3470   {
3471     /* try to guess locally connected team mode players (needed for correct
3472        assignment of player figures from level to locally playing players) */
3473
3474     for (i = 0; i < MAX_PLAYERS; i++)
3475       if (setup.input[i].use_joystick ||
3476           setup.input[i].key.left != KSYM_UNDEFINED)
3477         stored_player[i].connected = TRUE;
3478   }
3479
3480 #if DEBUG_INIT_PLAYER
3481   if (options.debug)
3482   {
3483     printf("Player status after level initialization:\n");
3484
3485     for (i = 0; i < MAX_PLAYERS; i++)
3486     {
3487       struct PlayerInfo *player = &stored_player[i];
3488
3489       printf("- player %d: present == %d, connected == %d, active == %d",
3490              i + 1,
3491              player->present,
3492              player->connected,
3493              player->active);
3494
3495       if (local_player == player)
3496         printf(" (local player)");
3497
3498       printf("\n");
3499     }
3500   }
3501 #endif
3502
3503 #if DEBUG_INIT_PLAYER
3504   if (options.debug)
3505     printf("Reassigning players ...\n");
3506 #endif
3507
3508   /* check if any connected player was not found in playfield */
3509   for (i = 0; i < MAX_PLAYERS; i++)
3510   {
3511     struct PlayerInfo *player = &stored_player[i];
3512
3513     if (player->connected && !player->present)
3514     {
3515       struct PlayerInfo *field_player = NULL;
3516
3517 #if DEBUG_INIT_PLAYER
3518       if (options.debug)
3519         printf("- looking for field player for player %d ...\n", i + 1);
3520 #endif
3521
3522       /* assign first free player found that is present in the playfield */
3523
3524       /* first try: look for unmapped playfield player that is not connected */
3525       for (j = 0; j < MAX_PLAYERS; j++)
3526         if (field_player == NULL &&
3527             stored_player[j].present &&
3528             !stored_player[j].mapped &&
3529             !stored_player[j].connected)
3530           field_player = &stored_player[j];
3531
3532       /* second try: look for *any* unmapped playfield player */
3533       for (j = 0; j < MAX_PLAYERS; j++)
3534         if (field_player == NULL &&
3535             stored_player[j].present &&
3536             !stored_player[j].mapped)
3537           field_player = &stored_player[j];
3538
3539       if (field_player != NULL)
3540       {
3541         int jx = field_player->jx, jy = field_player->jy;
3542
3543 #if DEBUG_INIT_PLAYER
3544         if (options.debug)
3545           printf("- found player %d\n", field_player->index_nr + 1);
3546 #endif
3547
3548         player->present = FALSE;
3549         player->active = FALSE;
3550
3551         field_player->present = TRUE;
3552         field_player->active = TRUE;
3553
3554         /*
3555         player->initial_element = field_player->initial_element;
3556         player->artwork_element = field_player->artwork_element;
3557
3558         player->block_last_field       = field_player->block_last_field;
3559         player->block_delay_adjustment = field_player->block_delay_adjustment;
3560         */
3561
3562         StorePlayer[jx][jy] = field_player->element_nr;
3563
3564         field_player->jx = field_player->last_jx = jx;
3565         field_player->jy = field_player->last_jy = jy;
3566
3567         if (local_player == player)
3568           local_player = field_player;
3569
3570         map_player_action[field_player->index_nr] = i;
3571
3572         field_player->mapped = TRUE;
3573
3574 #if DEBUG_INIT_PLAYER
3575         if (options.debug)
3576           printf("- map_player_action[%d] == %d\n",
3577                  field_player->index_nr + 1, i + 1);
3578 #endif
3579       }
3580     }
3581
3582     if (player->connected && player->present)
3583       player->mapped = TRUE;
3584   }
3585
3586 #if DEBUG_INIT_PLAYER
3587   if (options.debug)
3588   {
3589     printf("Player status after player assignment (first stage):\n");
3590
3591     for (i = 0; i < MAX_PLAYERS; i++)
3592     {
3593       struct PlayerInfo *player = &stored_player[i];
3594
3595       printf("- player %d: present == %d, connected == %d, active == %d",
3596              i + 1,
3597              player->present,
3598              player->connected,
3599              player->active);
3600
3601       if (local_player == player)
3602         printf(" (local player)");
3603
3604       printf("\n");
3605     }
3606   }
3607 #endif
3608
3609 #else
3610
3611   /* check if any connected player was not found in playfield */
3612   for (i = 0; i < MAX_PLAYERS; i++)
3613   {
3614     struct PlayerInfo *player = &stored_player[i];
3615
3616     if (player->connected && !player->present)
3617     {
3618       for (j = 0; j < MAX_PLAYERS; j++)
3619       {
3620         struct PlayerInfo *field_player = &stored_player[j];
3621         int jx = field_player->jx, jy = field_player->jy;
3622
3623         /* assign first free player found that is present in the playfield */
3624         if (field_player->present && !field_player->connected)
3625         {
3626           player->present = TRUE;
3627           player->active = TRUE;
3628
3629           field_player->present = FALSE;
3630           field_player->active = FALSE;
3631
3632           player->initial_element = field_player->initial_element;
3633           player->artwork_element = field_player->artwork_element;
3634
3635           player->block_last_field       = field_player->block_last_field;
3636           player->block_delay_adjustment = field_player->block_delay_adjustment;
3637
3638           StorePlayer[jx][jy] = player->element_nr;
3639
3640           player->jx = player->last_jx = jx;
3641           player->jy = player->last_jy = jy;
3642
3643           break;
3644         }
3645       }
3646     }
3647   }
3648 #endif
3649
3650 #if 0
3651   printf("::: local_player->present == %d\n", local_player->present);
3652 #endif
3653
3654   if (tape.playing)
3655   {
3656     /* when playing a tape, eliminate all players who do not participate */
3657
3658 #if USE_NEW_PLAYER_ASSIGNMENTS
3659
3660     if (!game.team_mode)
3661     {
3662       for (i = 0; i < MAX_PLAYERS; i++)
3663       {
3664         if (stored_player[i].active &&
3665             !tape.player_participates[map_player_action[i]])
3666         {
3667           struct PlayerInfo *player = &stored_player[i];
3668           int jx = player->jx, jy = player->jy;
3669
3670 #if DEBUG_INIT_PLAYER
3671           if (options.debug)
3672             printf("Removing player %d at (%d, %d)\n", i + 1, jx, jy);
3673 #endif
3674
3675           player->active = FALSE;
3676           StorePlayer[jx][jy] = 0;
3677           Feld[jx][jy] = EL_EMPTY;
3678         }
3679       }
3680     }
3681
3682 #else
3683
3684     for (i = 0; i < MAX_PLAYERS; i++)
3685     {
3686       if (stored_player[i].active &&
3687           !tape.player_participates[i])
3688       {
3689         struct PlayerInfo *player = &stored_player[i];
3690         int jx = player->jx, jy = player->jy;
3691
3692         player->active = FALSE;
3693         StorePlayer[jx][jy] = 0;
3694         Feld[jx][jy] = EL_EMPTY;
3695       }
3696     }
3697 #endif
3698   }
3699   else if (!options.network && !game.team_mode)         /* && !tape.playing */
3700   {
3701     /* when in single player mode, eliminate all but the first active player */
3702
3703     for (i = 0; i < MAX_PLAYERS; i++)
3704     {
3705       if (stored_player[i].active)
3706       {
3707         for (j = i + 1; j < MAX_PLAYERS; j++)
3708         {
3709           if (stored_player[j].active)
3710           {
3711             struct PlayerInfo *player = &stored_player[j];
3712             int jx = player->jx, jy = player->jy;
3713
3714             player->active = FALSE;
3715             player->present = FALSE;
3716
3717             StorePlayer[jx][jy] = 0;
3718             Feld[jx][jy] = EL_EMPTY;
3719           }
3720         }
3721       }
3722     }
3723   }
3724
3725   /* when recording the game, store which players take part in the game */
3726   if (tape.recording)
3727   {
3728 #if USE_NEW_PLAYER_ASSIGNMENTS
3729     for (i = 0; i < MAX_PLAYERS; i++)
3730       if (stored_player[i].connected)
3731         tape.player_participates[i] = TRUE;
3732 #else
3733     for (i = 0; i < MAX_PLAYERS; i++)
3734       if (stored_player[i].active)
3735         tape.player_participates[i] = TRUE;
3736 #endif
3737   }
3738
3739 #if DEBUG_INIT_PLAYER
3740   if (options.debug)
3741   {
3742     printf("Player status after player assignment (final stage):\n");
3743
3744     for (i = 0; i < MAX_PLAYERS; i++)
3745     {
3746       struct PlayerInfo *player = &stored_player[i];
3747
3748       printf("- player %d: present == %d, connected == %d, active == %d",
3749              i + 1,
3750              player->present,
3751              player->connected,
3752              player->active);
3753
3754       if (local_player == player)
3755         printf(" (local player)");
3756
3757       printf("\n");
3758     }
3759   }
3760 #endif
3761
3762   if (BorderElement == EL_EMPTY)
3763   {
3764     SBX_Left = 0;
3765     SBX_Right = lev_fieldx - SCR_FIELDX;
3766     SBY_Upper = 0;
3767     SBY_Lower = lev_fieldy - SCR_FIELDY;
3768   }
3769   else
3770   {
3771     SBX_Left = -1;
3772     SBX_Right = lev_fieldx - SCR_FIELDX + 1;
3773     SBY_Upper = -1;
3774     SBY_Lower = lev_fieldy - SCR_FIELDY + 1;
3775   }
3776
3777   if (full_lev_fieldx <= SCR_FIELDX)
3778     SBX_Left = SBX_Right = -1 * (SCR_FIELDX - lev_fieldx) / 2;
3779   if (full_lev_fieldy <= SCR_FIELDY)
3780     SBY_Upper = SBY_Lower = -1 * (SCR_FIELDY - lev_fieldy) / 2;
3781
3782   if (EVEN(SCR_FIELDX) && full_lev_fieldx > SCR_FIELDX)
3783     SBX_Left--;
3784   if (EVEN(SCR_FIELDY) && full_lev_fieldy > SCR_FIELDY)
3785     SBY_Upper--;
3786
3787   /* if local player not found, look for custom element that might create
3788      the player (make some assumptions about the right custom element) */
3789   if (!local_player->present)
3790   {
3791     int start_x = 0, start_y = 0;
3792     int found_rating = 0;
3793     int found_element = EL_UNDEFINED;
3794     int player_nr = local_player->index_nr;
3795
3796     SCAN_PLAYFIELD(x, y)
3797     {
3798       int element = Feld[x][y];
3799       int content;
3800       int xx, yy;
3801       boolean is_player;
3802
3803       if (level.use_start_element[player_nr] &&
3804           level.start_element[player_nr] == element &&
3805           found_rating < 4)
3806       {
3807         start_x = x;
3808         start_y = y;
3809
3810         found_rating = 4;
3811         found_element = element;
3812       }
3813
3814       if (!IS_CUSTOM_ELEMENT(element))
3815         continue;
3816
3817       if (CAN_CHANGE(element))
3818       {
3819         for (i = 0; i < element_info[element].num_change_pages; i++)
3820         {
3821           /* check for player created from custom element as single target */
3822           content = element_info[element].change_page[i].target_element;
3823           is_player = ELEM_IS_PLAYER(content);
3824
3825           if (is_player && (found_rating < 3 ||
3826                             (found_rating == 3 && element < found_element)))
3827           {
3828             start_x = x;
3829             start_y = y;
3830
3831             found_rating = 3;
3832             found_element = element;
3833           }
3834         }
3835       }
3836
3837       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3; xx++)
3838       {
3839         /* check for player created from custom element as explosion content */
3840         content = element_info[element].content.e[xx][yy];
3841         is_player = ELEM_IS_PLAYER(content);
3842
3843         if (is_player && (found_rating < 2 ||
3844                           (found_rating == 2 && element < found_element)))
3845         {
3846           start_x = x + xx - 1;
3847           start_y = y + yy - 1;
3848
3849           found_rating = 2;
3850           found_element = element;
3851         }
3852
3853         if (!CAN_CHANGE(element))
3854           continue;
3855
3856         for (i = 0; i < element_info[element].num_change_pages; i++)
3857         {
3858           /* check for player created from custom element as extended target */
3859           content =
3860             element_info[element].change_page[i].target_content.e[xx][yy];
3861
3862           is_player = ELEM_IS_PLAYER(content);
3863
3864           if (is_player && (found_rating < 1 ||
3865                             (found_rating == 1 && element < found_element)))
3866           {
3867             start_x = x + xx - 1;
3868             start_y = y + yy - 1;
3869
3870             found_rating = 1;
3871             found_element = element;
3872           }
3873         }
3874       }
3875     }
3876
3877     scroll_x = (start_x < SBX_Left  + MIDPOSX ? SBX_Left :
3878                 start_x > SBX_Right + MIDPOSX ? SBX_Right :
3879                 start_x - MIDPOSX);
3880
3881     scroll_y = (start_y < SBY_Upper + MIDPOSY ? SBY_Upper :
3882                 start_y > SBY_Lower + MIDPOSY ? SBY_Lower :
3883                 start_y - MIDPOSY);
3884   }
3885   else
3886   {
3887     scroll_x = (local_player->jx < SBX_Left  + MIDPOSX ? SBX_Left :
3888                 local_player->jx > SBX_Right + MIDPOSX ? SBX_Right :
3889                 local_player->jx - MIDPOSX);
3890
3891     scroll_y = (local_player->jy < SBY_Upper + MIDPOSY ? SBY_Upper :
3892                 local_player->jy > SBY_Lower + MIDPOSY ? SBY_Lower :
3893                 local_player->jy - MIDPOSY);
3894   }
3895
3896   /* !!! FIX THIS (START) !!! */
3897   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
3898   {
3899     InitGameEngine_EM();
3900   }
3901   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
3902   {
3903     InitGameEngine_SP();
3904   }
3905   else
3906   {
3907     DrawLevel(REDRAW_FIELD);
3908     DrawAllPlayers();
3909
3910     /* after drawing the level, correct some elements */
3911     if (game.timegate_time_left == 0)
3912       CloseAllOpenTimegates();
3913   }
3914
3915   /* blit playfield from scroll buffer to normal back buffer for fading in */
3916   BlitScreenToBitmap(backbuffer);
3917
3918   redraw_mask |= REDRAW_FROM_BACKBUFFER;
3919   /* !!! FIX THIS (END) !!! */
3920
3921   FadeIn(REDRAW_FIELD);
3922
3923 #if 1
3924   // full screen redraw is required at this point in the following cases:
3925   // - special editor door undrawn when game was started from level editor
3926   // - drawing area (playfield) was changed and has to be removed completely
3927   redraw_mask = REDRAW_ALL;
3928   BackToFront();
3929 #endif
3930
3931   if (!game.restart_level)
3932   {
3933     /* copy default game door content to main double buffer */
3934
3935     /* !!! CHECK AGAIN !!! */
3936     SetPanelBackground();
3937     // SetDoorBackgroundImage(IMG_BACKGROUND_PANEL);
3938     DrawBackground(DX, DY, DXSIZE, DYSIZE);
3939   }
3940
3941   SetPanelBackground();
3942   SetDrawBackgroundMask(REDRAW_DOOR_1);
3943
3944   UpdateAndDisplayGameControlValues();
3945
3946   if (!game.restart_level)
3947   {
3948     UnmapGameButtons();
3949     UnmapTapeButtons();
3950     game_gadget[SOUND_CTRL_ID_MUSIC]->checked = setup.sound_music;
3951     game_gadget[SOUND_CTRL_ID_LOOPS]->checked = setup.sound_loops;
3952     game_gadget[SOUND_CTRL_ID_SIMPLE]->checked = setup.sound_simple;
3953     MapGameButtons();
3954     MapTapeButtons();
3955
3956     /* copy actual game door content to door double buffer for OpenDoor() */
3957     BlitBitmap(drawto, bitmap_db_door_1, DX, DY, DXSIZE, DYSIZE, 0, 0);
3958
3959     OpenDoor(DOOR_OPEN_ALL);
3960
3961     PlaySound(SND_GAME_STARTING);
3962
3963     if (setup.sound_music)
3964       PlayLevelMusic();
3965
3966     KeyboardAutoRepeatOffUnlessAutoplay();
3967
3968 #if DEBUG_INIT_PLAYER
3969     if (options.debug)
3970     {
3971       printf("Player status (final):\n");
3972
3973       for (i = 0; i < MAX_PLAYERS; i++)
3974       {
3975         struct PlayerInfo *player = &stored_player[i];
3976
3977         printf("- player %d: present == %d, connected == %d, active == %d",
3978                i + 1,
3979                player->present,
3980                player->connected,
3981                player->active);
3982
3983         if (local_player == player)
3984           printf(" (local player)");
3985
3986         printf("\n");
3987       }
3988     }
3989 #endif
3990   }
3991
3992   UnmapAllGadgets();
3993
3994   MapGameButtons();
3995   MapTapeButtons();
3996
3997   if (!game.restart_level && !tape.playing)
3998   {
3999     LevelStats_incPlayed(level_nr);
4000
4001     SaveLevelSetup_SeriesInfo();
4002   }
4003
4004   game.restart_level = FALSE;
4005
4006   SaveEngineSnapshotToList();
4007 }
4008
4009 void UpdateEngineValues(int actual_scroll_x, int actual_scroll_y)
4010 {
4011   /* this is used for non-R'n'D game engines to update certain engine values */
4012
4013   /* needed to determine if sounds are played within the visible screen area */
4014   scroll_x = actual_scroll_x;
4015   scroll_y = actual_scroll_y;
4016 }
4017
4018 void InitMovDir(int x, int y)
4019 {
4020   int i, element = Feld[x][y];
4021   static int xy[4][2] =
4022   {
4023     {  0, +1 },
4024     { +1,  0 },
4025     {  0, -1 },
4026     { -1,  0 }
4027   };
4028   static int direction[3][4] =
4029   {
4030     { MV_RIGHT, MV_UP,   MV_LEFT,  MV_DOWN },
4031     { MV_LEFT,  MV_DOWN, MV_RIGHT, MV_UP },
4032     { MV_LEFT,  MV_RIGHT, MV_UP, MV_DOWN }
4033   };
4034
4035   switch (element)
4036   {
4037     case EL_BUG_RIGHT:
4038     case EL_BUG_UP:
4039     case EL_BUG_LEFT:
4040     case EL_BUG_DOWN:
4041       Feld[x][y] = EL_BUG;
4042       MovDir[x][y] = direction[0][element - EL_BUG_RIGHT];
4043       break;
4044
4045     case EL_SPACESHIP_RIGHT:
4046     case EL_SPACESHIP_UP:
4047     case EL_SPACESHIP_LEFT:
4048     case EL_SPACESHIP_DOWN:
4049       Feld[x][y] = EL_SPACESHIP;
4050       MovDir[x][y] = direction[0][element - EL_SPACESHIP_RIGHT];
4051       break;
4052
4053     case EL_BD_BUTTERFLY_RIGHT:
4054     case EL_BD_BUTTERFLY_UP:
4055     case EL_BD_BUTTERFLY_LEFT:
4056     case EL_BD_BUTTERFLY_DOWN:
4057       Feld[x][y] = EL_BD_BUTTERFLY;
4058       MovDir[x][y] = direction[0][element - EL_BD_BUTTERFLY_RIGHT];
4059       break;
4060
4061     case EL_BD_FIREFLY_RIGHT:
4062     case EL_BD_FIREFLY_UP:
4063     case EL_BD_FIREFLY_LEFT:
4064     case EL_BD_FIREFLY_DOWN:
4065       Feld[x][y] = EL_BD_FIREFLY;
4066       MovDir[x][y] = direction[0][element - EL_BD_FIREFLY_RIGHT];
4067       break;
4068
4069     case EL_PACMAN_RIGHT:
4070     case EL_PACMAN_UP:
4071     case EL_PACMAN_LEFT:
4072     case EL_PACMAN_DOWN:
4073       Feld[x][y] = EL_PACMAN;
4074       MovDir[x][y] = direction[0][element - EL_PACMAN_RIGHT];
4075       break;
4076
4077     case EL_YAMYAM_LEFT:
4078     case EL_YAMYAM_RIGHT:
4079     case EL_YAMYAM_UP:
4080     case EL_YAMYAM_DOWN:
4081       Feld[x][y] = EL_YAMYAM;
4082       MovDir[x][y] = direction[2][element - EL_YAMYAM_LEFT];
4083       break;
4084
4085     case EL_SP_SNIKSNAK:
4086       MovDir[x][y] = MV_UP;
4087       break;
4088
4089     case EL_SP_ELECTRON:
4090       MovDir[x][y] = MV_LEFT;
4091       break;
4092
4093     case EL_MOLE_LEFT:
4094     case EL_MOLE_RIGHT:
4095     case EL_MOLE_UP:
4096     case EL_MOLE_DOWN:
4097       Feld[x][y] = EL_MOLE;
4098       MovDir[x][y] = direction[2][element - EL_MOLE_LEFT];
4099       break;
4100
4101     default:
4102       if (IS_CUSTOM_ELEMENT(element))
4103       {
4104         struct ElementInfo *ei = &element_info[element];
4105         int move_direction_initial = ei->move_direction_initial;
4106         int move_pattern = ei->move_pattern;
4107
4108         if (move_direction_initial == MV_START_PREVIOUS)
4109         {
4110           if (MovDir[x][y] != MV_NONE)
4111             return;
4112
4113           move_direction_initial = MV_START_AUTOMATIC;
4114         }
4115
4116         if (move_direction_initial == MV_START_RANDOM)
4117           MovDir[x][y] = 1 << RND(4);
4118         else if (move_direction_initial & MV_ANY_DIRECTION)
4119           MovDir[x][y] = move_direction_initial;
4120         else if (move_pattern == MV_ALL_DIRECTIONS ||
4121                  move_pattern == MV_TURNING_LEFT ||
4122                  move_pattern == MV_TURNING_RIGHT ||
4123                  move_pattern == MV_TURNING_LEFT_RIGHT ||
4124                  move_pattern == MV_TURNING_RIGHT_LEFT ||
4125                  move_pattern == MV_TURNING_RANDOM)
4126           MovDir[x][y] = 1 << RND(4);
4127         else if (move_pattern == MV_HORIZONTAL)
4128           MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
4129         else if (move_pattern == MV_VERTICAL)
4130           MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
4131         else if (move_pattern & MV_ANY_DIRECTION)
4132           MovDir[x][y] = element_info[element].move_pattern;
4133         else if (move_pattern == MV_ALONG_LEFT_SIDE ||
4134                  move_pattern == MV_ALONG_RIGHT_SIDE)
4135         {
4136           /* use random direction as default start direction */
4137           if (game.engine_version >= VERSION_IDENT(3,1,0,0))
4138             MovDir[x][y] = 1 << RND(4);
4139
4140           for (i = 0; i < NUM_DIRECTIONS; i++)
4141           {
4142             int x1 = x + xy[i][0];
4143             int y1 = y + xy[i][1];
4144
4145             if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4146             {
4147               if (move_pattern == MV_ALONG_RIGHT_SIDE)
4148                 MovDir[x][y] = direction[0][i];
4149               else
4150                 MovDir[x][y] = direction[1][i];
4151
4152               break;
4153             }
4154           }
4155         }                
4156       }
4157       else
4158       {
4159         MovDir[x][y] = 1 << RND(4);
4160
4161         if (element != EL_BUG &&
4162             element != EL_SPACESHIP &&
4163             element != EL_BD_BUTTERFLY &&
4164             element != EL_BD_FIREFLY)
4165           break;
4166
4167         for (i = 0; i < NUM_DIRECTIONS; i++)
4168         {
4169           int x1 = x + xy[i][0];
4170           int y1 = y + xy[i][1];
4171
4172           if (!IN_LEV_FIELD(x1, y1) || !IS_FREE(x1, y1))
4173           {
4174             if (element == EL_BUG || element == EL_BD_BUTTERFLY)
4175             {
4176               MovDir[x][y] = direction[0][i];
4177               break;
4178             }
4179             else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY ||
4180                      element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
4181             {
4182               MovDir[x][y] = direction[1][i];
4183               break;
4184             }
4185           }
4186         }
4187       }
4188       break;
4189   }
4190
4191   GfxDir[x][y] = MovDir[x][y];
4192 }
4193
4194 void InitAmoebaNr(int x, int y)
4195 {
4196   int i;
4197   int group_nr = AmoebeNachbarNr(x, y);
4198
4199   if (group_nr == 0)
4200   {
4201     for (i = 1; i < MAX_NUM_AMOEBA; i++)
4202     {
4203       if (AmoebaCnt[i] == 0)
4204       {
4205         group_nr = i;
4206         break;
4207       }
4208     }
4209   }
4210
4211   AmoebaNr[x][y] = group_nr;
4212   AmoebaCnt[group_nr]++;
4213   AmoebaCnt2[group_nr]++;
4214 }
4215
4216 static void PlayerWins(struct PlayerInfo *player)
4217 {
4218   player->LevelSolved = TRUE;
4219   player->GameOver = TRUE;
4220
4221   player->score_final = (level.game_engine_type == GAME_ENGINE_TYPE_EM ?
4222                          level.native_em_level->lev->score : player->score);
4223
4224   player->LevelSolved_CountingTime = (game.no_time_limit ? TimePlayed :
4225                                       TimeLeft);
4226   player->LevelSolved_CountingScore = player->score_final;
4227 }
4228
4229 void GameWon()
4230 {
4231   static int time, time_final;
4232   static int score, score_final;
4233   static int game_over_delay_1 = 0;
4234   static int game_over_delay_2 = 0;
4235   int game_over_delay_value_1 = 50;
4236   int game_over_delay_value_2 = 50;
4237
4238   if (!local_player->LevelSolved_GameWon)
4239   {
4240     int i;
4241
4242     /* do not start end game actions before the player stops moving (to exit) */
4243     if (local_player->MovPos)
4244       return;
4245
4246     local_player->LevelSolved_GameWon = TRUE;
4247     local_player->LevelSolved_SaveTape = tape.recording;
4248     local_player->LevelSolved_SaveScore = !tape.playing;
4249
4250     if (!tape.playing)
4251     {
4252       LevelStats_incSolved(level_nr);
4253
4254       SaveLevelSetup_SeriesInfo();
4255     }
4256
4257     if (tape.auto_play)         /* tape might already be stopped here */
4258       tape.auto_play_level_solved = TRUE;
4259
4260     TapeStop();
4261
4262     game_over_delay_1 = game_over_delay_value_1;
4263     game_over_delay_2 = game_over_delay_value_2;
4264
4265     time = time_final = (game.no_time_limit ? TimePlayed : TimeLeft);
4266     score = score_final = local_player->score_final;
4267
4268     if (TimeLeft > 0)
4269     {
4270       time_final = 0;
4271       score_final += TimeLeft * level.score[SC_TIME_BONUS];
4272     }
4273     else if (game.no_time_limit && TimePlayed < 999)
4274     {
4275       time_final = 999;
4276       score_final += (999 - TimePlayed) * level.score[SC_TIME_BONUS];
4277     }
4278
4279     local_player->score_final = score_final;
4280
4281     if (level_editor_test_game)
4282     {
4283       time = time_final;
4284       score = score_final;
4285
4286       local_player->LevelSolved_CountingTime = time;
4287       local_player->LevelSolved_CountingScore = score;
4288
4289       game_panel_controls[GAME_PANEL_TIME].value = time;
4290       game_panel_controls[GAME_PANEL_SCORE].value = score;
4291
4292       DisplayGameControlValues();
4293     }
4294
4295     if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
4296     {
4297       if (ExitX >= 0 && ExitY >= 0)     /* local player has left the level */
4298       {
4299         /* close exit door after last player */
4300         if ((AllPlayersGone &&
4301              (Feld[ExitX][ExitY] == EL_EXIT_OPEN ||
4302               Feld[ExitX][ExitY] == EL_SP_EXIT_OPEN ||
4303               Feld[ExitX][ExitY] == EL_STEEL_EXIT_OPEN)) ||
4304             Feld[ExitX][ExitY] == EL_EM_EXIT_OPEN ||
4305             Feld[ExitX][ExitY] == EL_EM_STEEL_EXIT_OPEN)
4306         {
4307           int element = Feld[ExitX][ExitY];
4308
4309           Feld[ExitX][ExitY] =
4310             (element == EL_EXIT_OPEN            ? EL_EXIT_CLOSING :
4311              element == EL_EM_EXIT_OPEN ? EL_EM_EXIT_CLOSING :
4312              element == EL_SP_EXIT_OPEN ? EL_SP_EXIT_CLOSING:
4313              element == EL_STEEL_EXIT_OPEN      ? EL_STEEL_EXIT_CLOSING:
4314              EL_EM_STEEL_EXIT_CLOSING);
4315
4316           PlayLevelSoundElementAction(ExitX, ExitY, element, ACTION_CLOSING);
4317         }
4318
4319         /* player disappears */
4320         DrawLevelField(ExitX, ExitY);
4321       }
4322
4323       for (i = 0; i < MAX_PLAYERS; i++)
4324       {
4325         struct PlayerInfo *player = &stored_player[i];
4326
4327         if (player->present)
4328         {
4329           RemovePlayer(player);
4330
4331           /* player disappears */
4332           DrawLevelField(player->jx, player->jy);
4333         }
4334       }
4335     }
4336
4337     PlaySound(SND_GAME_WINNING);
4338   }
4339
4340   if (game_over_delay_1 > 0)
4341   {
4342     game_over_delay_1--;
4343
4344     return;
4345   }
4346
4347   if (time != time_final)
4348   {
4349     int time_to_go = ABS(time_final - time);
4350     int time_count_dir = (time < time_final ? +1 : -1);
4351     int time_count_steps = (time_to_go > 100 && time_to_go % 10 == 0 ? 10 : 1);
4352
4353     time  += time_count_steps * time_count_dir;
4354     score += time_count_steps * level.score[SC_TIME_BONUS];
4355
4356     local_player->LevelSolved_CountingTime = time;
4357     local_player->LevelSolved_CountingScore = score;
4358
4359     game_panel_controls[GAME_PANEL_TIME].value = time;
4360     game_panel_controls[GAME_PANEL_SCORE].value = score;
4361
4362     DisplayGameControlValues();
4363
4364     if (time == time_final)
4365       StopSound(SND_GAME_LEVELTIME_BONUS);
4366     else if (setup.sound_loops)
4367       PlaySoundLoop(SND_GAME_LEVELTIME_BONUS);
4368     else
4369       PlaySound(SND_GAME_LEVELTIME_BONUS);
4370
4371     return;
4372   }
4373
4374   local_player->LevelSolved_PanelOff = TRUE;
4375
4376   if (game_over_delay_2 > 0)
4377   {
4378     game_over_delay_2--;
4379
4380     return;
4381   }
4382
4383   GameEnd();
4384 }
4385
4386 void GameEnd()
4387 {
4388   int hi_pos;
4389   boolean raise_level = FALSE;
4390
4391   local_player->LevelSolved_GameEnd = TRUE;
4392
4393   CloseDoor(DOOR_CLOSE_1);
4394
4395   if (local_player->LevelSolved_SaveTape)
4396   {
4397     SaveTapeChecked(tape.level_nr);     /* ask to save tape */
4398   }
4399
4400   if (level_editor_test_game)
4401   {
4402     game_status = GAME_MODE_MAIN;
4403
4404     DrawAndFadeInMainMenu(REDRAW_FIELD);
4405
4406     return;
4407   }
4408
4409   if (!local_player->LevelSolved_SaveScore)
4410   {
4411     FadeOut(REDRAW_FIELD);
4412
4413     game_status = GAME_MODE_MAIN;
4414
4415     DrawAndFadeInMainMenu(REDRAW_FIELD);
4416
4417     return;
4418   }
4419
4420   if (level_nr == leveldir_current->handicap_level)
4421   {
4422     leveldir_current->handicap_level++;
4423
4424     SaveLevelSetup_SeriesInfo();
4425   }
4426
4427   if (level_nr < leveldir_current->last_level)
4428     raise_level = TRUE;                 /* advance to next level */
4429
4430   if ((hi_pos = NewHiScore()) >= 0) 
4431   {
4432     game_status = GAME_MODE_SCORES;
4433
4434     DrawHallOfFame(hi_pos);
4435
4436     if (raise_level)
4437     {
4438       level_nr++;
4439       TapeErase();
4440     }
4441   }
4442   else
4443   {
4444     FadeOut(REDRAW_FIELD);
4445
4446     game_status = GAME_MODE_MAIN;
4447
4448     if (raise_level)
4449     {
4450       level_nr++;
4451       TapeErase();
4452     }
4453
4454     DrawAndFadeInMainMenu(REDRAW_FIELD);
4455   }
4456 }
4457
4458 int NewHiScore()
4459 {
4460   int k, l;
4461   int position = -1;
4462
4463   LoadScore(level_nr);
4464
4465   if (strEqual(setup.player_name, EMPTY_PLAYER_NAME) ||
4466       local_player->score_final < highscore[MAX_SCORE_ENTRIES - 1].Score) 
4467     return -1;
4468
4469   for (k = 0; k < MAX_SCORE_ENTRIES; k++) 
4470   {
4471     if (local_player->score_final > highscore[k].Score)
4472     {
4473       /* player has made it to the hall of fame */
4474
4475       if (k < MAX_SCORE_ENTRIES - 1)
4476       {
4477         int m = MAX_SCORE_ENTRIES - 1;
4478
4479 #ifdef ONE_PER_NAME
4480         for (l = k; l < MAX_SCORE_ENTRIES; l++)
4481           if (strEqual(setup.player_name, highscore[l].Name))
4482             m = l;
4483         if (m == k)     /* player's new highscore overwrites his old one */
4484           goto put_into_list;
4485 #endif
4486
4487         for (l = m; l > k; l--)
4488         {
4489           strcpy(highscore[l].Name, highscore[l - 1].Name);
4490           highscore[l].Score = highscore[l - 1].Score;
4491         }
4492       }
4493
4494 #ifdef ONE_PER_NAME
4495       put_into_list:
4496 #endif
4497       strncpy(highscore[k].Name, setup.player_name, MAX_PLAYER_NAME_LEN);
4498       highscore[k].Name[MAX_PLAYER_NAME_LEN] = '\0';
4499       highscore[k].Score = local_player->score_final; 
4500       position = k;
4501       break;
4502     }
4503
4504 #ifdef ONE_PER_NAME
4505     else if (!strncmp(setup.player_name, highscore[k].Name,
4506                       MAX_PLAYER_NAME_LEN))
4507       break;    /* player already there with a higher score */
4508 #endif
4509
4510   }
4511
4512   if (position >= 0) 
4513     SaveScore(level_nr);
4514
4515   return position;
4516 }
4517
4518 inline static int getElementMoveStepsizeExt(int x, int y, int direction)
4519 {
4520   int element = Feld[x][y];
4521   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4522   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4523   int horiz_move = (dx != 0);
4524   int sign = (horiz_move ? dx : dy);
4525   int step = sign * element_info[element].move_stepsize;
4526
4527   /* special values for move stepsize for spring and things on conveyor belt */
4528   if (horiz_move)
4529   {
4530     if (CAN_FALL(element) &&
4531         y < lev_fieldy - 1 && IS_BELT_ACTIVE(Feld[x][y + 1]))
4532       step = sign * MOVE_STEPSIZE_NORMAL / 2;
4533     else if (element == EL_SPRING)
4534       step = sign * MOVE_STEPSIZE_NORMAL * 2;
4535   }
4536
4537   return step;
4538 }
4539
4540 inline static int getElementMoveStepsize(int x, int y)
4541 {
4542   return getElementMoveStepsizeExt(x, y, MovDir[x][y]);
4543 }
4544
4545 void InitPlayerGfxAnimation(struct PlayerInfo *player, int action, int dir)
4546 {
4547   if (player->GfxAction != action || player->GfxDir != dir)
4548   {
4549     player->GfxAction = action;
4550     player->GfxDir = dir;
4551     player->Frame = 0;
4552     player->StepFrame = 0;
4553   }
4554 }
4555
4556 static void ResetGfxFrame(int x, int y, boolean redraw)
4557 {
4558   int element = Feld[x][y];
4559   int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
4560   int last_gfx_frame = GfxFrame[x][y];
4561
4562   if (graphic_info[graphic].anim_global_sync)
4563     GfxFrame[x][y] = FrameCounter;
4564   else if (ANIM_MODE(graphic) == ANIM_CE_VALUE)
4565     GfxFrame[x][y] = CustomValue[x][y];
4566   else if (ANIM_MODE(graphic) == ANIM_CE_SCORE)
4567     GfxFrame[x][y] = element_info[element].collect_score;
4568   else if (ANIM_MODE(graphic) == ANIM_CE_DELAY)
4569     GfxFrame[x][y] = ChangeDelay[x][y];
4570
4571   if (redraw && GfxFrame[x][y] != last_gfx_frame)
4572     DrawLevelGraphicAnimation(x, y, graphic);
4573 }
4574
4575 static void ResetGfxAnimation(int x, int y)
4576 {
4577   GfxAction[x][y] = ACTION_DEFAULT;
4578   GfxDir[x][y] = MovDir[x][y];
4579   GfxFrame[x][y] = 0;
4580
4581   ResetGfxFrame(x, y, FALSE);
4582 }
4583
4584 static void ResetRandomAnimationValue(int x, int y)
4585 {
4586   GfxRandom[x][y] = INIT_GFX_RANDOM();
4587 }
4588
4589 void InitMovingField(int x, int y, int direction)
4590 {
4591   int element = Feld[x][y];
4592   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
4593   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
4594   int newx = x + dx;
4595   int newy = y + dy;
4596   boolean is_moving_before, is_moving_after;
4597
4598   /* check if element was/is moving or being moved before/after mode change */
4599   is_moving_before = (WasJustMoving[x][y] != 0);
4600   is_moving_after  = (getElementMoveStepsizeExt(x, y, direction)    != 0);
4601
4602   /* reset animation only for moving elements which change direction of moving
4603      or which just started or stopped moving
4604      (else CEs with property "can move" / "not moving" are reset each frame) */
4605   if (is_moving_before != is_moving_after ||
4606       direction != MovDir[x][y])
4607     ResetGfxAnimation(x, y);
4608
4609   MovDir[x][y] = direction;
4610   GfxDir[x][y] = direction;
4611
4612   GfxAction[x][y] = (!is_moving_after ? ACTION_WAITING :
4613                      direction == MV_DOWN && CAN_FALL(element) ?
4614                      ACTION_FALLING : ACTION_MOVING);
4615
4616   /* this is needed for CEs with property "can move" / "not moving" */
4617
4618   if (is_moving_after)
4619   {
4620     if (Feld[newx][newy] == EL_EMPTY)
4621       Feld[newx][newy] = EL_BLOCKED;
4622
4623     MovDir[newx][newy] = MovDir[x][y];
4624
4625     CustomValue[newx][newy] = CustomValue[x][y];
4626
4627     GfxFrame[newx][newy] = GfxFrame[x][y];
4628     GfxRandom[newx][newy] = GfxRandom[x][y];
4629     GfxAction[newx][newy] = GfxAction[x][y];
4630     GfxDir[newx][newy] = GfxDir[x][y];
4631   }
4632 }
4633
4634 void Moving2Blocked(int x, int y, int *goes_to_x, int *goes_to_y)
4635 {
4636   int direction = MovDir[x][y];
4637   int newx = x + (direction & MV_LEFT ? -1 : direction & MV_RIGHT ? +1 : 0);
4638   int newy = y + (direction & MV_UP   ? -1 : direction & MV_DOWN  ? +1 : 0);
4639
4640   *goes_to_x = newx;
4641   *goes_to_y = newy;
4642 }
4643
4644 void Blocked2Moving(int x, int y, int *comes_from_x, int *comes_from_y)
4645 {
4646   int oldx = x, oldy = y;
4647   int direction = MovDir[x][y];
4648
4649   if (direction == MV_LEFT)
4650     oldx++;
4651   else if (direction == MV_RIGHT)
4652     oldx--;
4653   else if (direction == MV_UP)
4654     oldy++;
4655   else if (direction == MV_DOWN)
4656     oldy--;
4657
4658   *comes_from_x = oldx;
4659   *comes_from_y = oldy;
4660 }
4661
4662 int MovingOrBlocked2Element(int x, int y)
4663 {
4664   int element = Feld[x][y];
4665
4666   if (element == EL_BLOCKED)
4667   {
4668     int oldx, oldy;
4669
4670     Blocked2Moving(x, y, &oldx, &oldy);
4671     return Feld[oldx][oldy];
4672   }
4673   else
4674     return element;
4675 }
4676
4677 static int MovingOrBlocked2ElementIfNotLeaving(int x, int y)
4678 {
4679   /* like MovingOrBlocked2Element(), but if element is moving
4680      and (x,y) is the field the moving element is just leaving,
4681      return EL_BLOCKED instead of the element value */
4682   int element = Feld[x][y];
4683
4684   if (IS_MOVING(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 EL_BLOCKED;
4695   }
4696   else
4697     return element;
4698 }
4699
4700 static void RemoveField(int x, int y)
4701 {
4702   Feld[x][y] = EL_EMPTY;
4703
4704   MovPos[x][y] = 0;
4705   MovDir[x][y] = 0;
4706   MovDelay[x][y] = 0;
4707
4708   CustomValue[x][y] = 0;
4709
4710   AmoebaNr[x][y] = 0;
4711   ChangeDelay[x][y] = 0;
4712   ChangePage[x][y] = -1;
4713   Pushed[x][y] = FALSE;
4714
4715   GfxElement[x][y] = EL_UNDEFINED;
4716   GfxAction[x][y] = ACTION_DEFAULT;
4717   GfxDir[x][y] = MV_NONE;
4718 }
4719
4720 void RemoveMovingField(int x, int y)
4721 {
4722   int oldx = x, oldy = y, newx = x, newy = y;
4723   int element = Feld[x][y];
4724   int next_element = EL_UNDEFINED;
4725
4726   if (element != EL_BLOCKED && !IS_MOVING(x, y))
4727     return;
4728
4729   if (IS_MOVING(x, y))
4730   {
4731     Moving2Blocked(x, y, &newx, &newy);
4732
4733     if (Feld[newx][newy] != EL_BLOCKED)
4734     {
4735       /* element is moving, but target field is not free (blocked), but
4736          already occupied by something different (example: acid pool);
4737          in this case, only remove the moving field, but not the target */
4738
4739       RemoveField(oldx, oldy);
4740
4741       Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4742
4743       TEST_DrawLevelField(oldx, oldy);
4744
4745       return;
4746     }
4747   }
4748   else if (element == EL_BLOCKED)
4749   {
4750     Blocked2Moving(x, y, &oldx, &oldy);
4751     if (!IS_MOVING(oldx, oldy))
4752       return;
4753   }
4754
4755   if (element == EL_BLOCKED &&
4756       (Feld[oldx][oldy] == EL_QUICKSAND_EMPTYING ||
4757        Feld[oldx][oldy] == EL_QUICKSAND_FAST_EMPTYING ||
4758        Feld[oldx][oldy] == EL_MAGIC_WALL_EMPTYING ||
4759        Feld[oldx][oldy] == EL_BD_MAGIC_WALL_EMPTYING ||
4760        Feld[oldx][oldy] == EL_DC_MAGIC_WALL_EMPTYING ||
4761        Feld[oldx][oldy] == EL_AMOEBA_DROPPING))
4762     next_element = get_next_element(Feld[oldx][oldy]);
4763
4764   RemoveField(oldx, oldy);
4765   RemoveField(newx, newy);
4766
4767   Store[oldx][oldy] = Store2[oldx][oldy] = 0;
4768
4769   if (next_element != EL_UNDEFINED)
4770     Feld[oldx][oldy] = next_element;
4771
4772   TEST_DrawLevelField(oldx, oldy);
4773   TEST_DrawLevelField(newx, newy);
4774 }
4775
4776 void DrawDynamite(int x, int y)
4777 {
4778   int sx = SCREENX(x), sy = SCREENY(y);
4779   int graphic = el2img(Feld[x][y]);
4780   int frame;
4781
4782   if (!IN_SCR_FIELD(sx, sy) || IS_PLAYER(x, y))
4783     return;
4784
4785   if (IS_WALKABLE_INSIDE(Back[x][y]))
4786     return;
4787
4788   if (Back[x][y])
4789     DrawGraphic(sx, sy, el2img(Back[x][y]), 0);
4790   else if (Store[x][y])
4791     DrawGraphic(sx, sy, el2img(Store[x][y]), 0);
4792
4793   frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
4794
4795   if (Back[x][y] || Store[x][y])
4796     DrawGraphicThruMask(sx, sy, graphic, frame);
4797   else
4798     DrawGraphic(sx, sy, graphic, frame);
4799 }
4800
4801 void CheckDynamite(int x, int y)
4802 {
4803   if (MovDelay[x][y] != 0)      /* dynamite is still waiting to explode */
4804   {
4805     MovDelay[x][y]--;
4806
4807     if (MovDelay[x][y] != 0)
4808     {
4809       DrawDynamite(x, y);
4810       PlayLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4811
4812       return;
4813     }
4814   }
4815
4816   StopLevelSoundActionIfLoop(x, y, ACTION_ACTIVE);
4817
4818   Bang(x, y);
4819 }
4820
4821 static void setMinimalPlayerBoundaries(int *sx1, int *sy1, int *sx2, int *sy2)
4822 {
4823   boolean num_checked_players = 0;
4824   int i;
4825
4826   for (i = 0; i < MAX_PLAYERS; i++)
4827   {
4828     if (stored_player[i].active)
4829     {
4830       int sx = stored_player[i].jx;
4831       int sy = stored_player[i].jy;
4832
4833       if (num_checked_players == 0)
4834       {
4835         *sx1 = *sx2 = sx;
4836         *sy1 = *sy2 = sy;
4837       }
4838       else
4839       {
4840         *sx1 = MIN(*sx1, sx);
4841         *sy1 = MIN(*sy1, sy);
4842         *sx2 = MAX(*sx2, sx);
4843         *sy2 = MAX(*sy2, sy);
4844       }
4845
4846       num_checked_players++;
4847     }
4848   }
4849 }
4850
4851 static boolean checkIfAllPlayersFitToScreen_RND()
4852 {
4853   int sx1 = 0, sy1 = 0, sx2 = 0, sy2 = 0;
4854
4855   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4856
4857   return (sx2 - sx1 < SCR_FIELDX &&
4858           sy2 - sy1 < SCR_FIELDY);
4859 }
4860
4861 static void setScreenCenteredToAllPlayers(int *sx, int *sy)
4862 {
4863   int sx1 = scroll_x, sy1 = scroll_y, sx2 = scroll_x, sy2 = scroll_y;
4864
4865   setMinimalPlayerBoundaries(&sx1, &sy1, &sx2, &sy2);
4866
4867   *sx = (sx1 + sx2) / 2;
4868   *sy = (sy1 + sy2) / 2;
4869 }
4870
4871 void DrawRelocateScreen(int old_x, int old_y, int x, int y, int move_dir,
4872                         boolean center_screen, boolean quick_relocation)
4873 {
4874   boolean ffwd_delay = (tape.playing && tape.fast_forward);
4875   boolean no_delay = (tape.warp_forward);
4876   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
4877   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
4878
4879   if (quick_relocation)
4880   {
4881     if (!IN_VIS_FIELD(SCREENX(x), SCREENY(y)) || center_screen)
4882     {
4883       if (!level.shifted_relocation || center_screen)
4884       {
4885         /* quick relocation (without scrolling), with centering of screen */
4886
4887         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4888                     x > SBX_Right + MIDPOSX ? SBX_Right :
4889                     x - MIDPOSX);
4890
4891         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4892                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4893                     y - MIDPOSY);
4894       }
4895       else
4896       {
4897         /* quick relocation (without scrolling), but do not center screen */
4898
4899         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4900                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4901                                old_x - MIDPOSX);
4902
4903         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4904                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4905                                old_y - MIDPOSY);
4906
4907         int offset_x = x + (scroll_x - center_scroll_x);
4908         int offset_y = y + (scroll_y - center_scroll_y);
4909
4910         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4911                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4912                     offset_x - MIDPOSX);
4913
4914         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4915                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4916                     offset_y - MIDPOSY);
4917       }
4918     }
4919     else
4920     {
4921       if (!level.shifted_relocation || center_screen)
4922       {
4923         /* quick relocation (without scrolling), with centering of screen */
4924
4925         scroll_x = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4926                     x > SBX_Right + MIDPOSX ? SBX_Right :
4927                     x - MIDPOSX);
4928
4929         scroll_y = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4930                     y > SBY_Lower + MIDPOSY ? SBY_Lower :
4931                     y - MIDPOSY);
4932       }
4933       else
4934       {
4935         /* quick relocation (without scrolling), but do not center screen */
4936
4937         int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4938                                old_x > SBX_Right + MIDPOSX ? SBX_Right :
4939                                old_x - MIDPOSX);
4940
4941         int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4942                                old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4943                                old_y - MIDPOSY);
4944
4945         int offset_x = x + (scroll_x - center_scroll_x);
4946         int offset_y = y + (scroll_y - center_scroll_y);
4947
4948         scroll_x = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4949                     offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4950                     offset_x - MIDPOSX);
4951
4952         scroll_y = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4953                     offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4954                     offset_y - MIDPOSY);
4955       }
4956     }
4957
4958     RedrawPlayfield(TRUE, 0,0,0,0);
4959   }
4960   else
4961   {
4962     int scroll_xx, scroll_yy;
4963
4964     if (!level.shifted_relocation || center_screen)
4965     {
4966       /* visible relocation (with scrolling), with centering of screen */
4967
4968       scroll_xx = (x < SBX_Left  + MIDPOSX ? SBX_Left :
4969                    x > SBX_Right + MIDPOSX ? SBX_Right :
4970                    x - MIDPOSX);
4971
4972       scroll_yy = (y < SBY_Upper + MIDPOSY ? SBY_Upper :
4973                    y > SBY_Lower + MIDPOSY ? SBY_Lower :
4974                    y - MIDPOSY);
4975     }
4976     else
4977     {
4978       /* visible relocation (with scrolling), but do not center screen */
4979
4980       int center_scroll_x = (old_x < SBX_Left  + MIDPOSX ? SBX_Left :
4981                              old_x > SBX_Right + MIDPOSX ? SBX_Right :
4982                              old_x - MIDPOSX);
4983
4984       int center_scroll_y = (old_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4985                              old_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4986                              old_y - MIDPOSY);
4987
4988       int offset_x = x + (scroll_x - center_scroll_x);
4989       int offset_y = y + (scroll_y - center_scroll_y);
4990
4991       scroll_xx = (offset_x < SBX_Left  + MIDPOSX ? SBX_Left :
4992                    offset_x > SBX_Right + MIDPOSX ? SBX_Right :
4993                    offset_x - MIDPOSX);
4994
4995       scroll_yy = (offset_y < SBY_Upper + MIDPOSY ? SBY_Upper :
4996                    offset_y > SBY_Lower + MIDPOSY ? SBY_Lower :
4997                    offset_y - MIDPOSY);
4998     }
4999
5000
5001     ScrollScreen(NULL, SCROLL_GO_ON);   /* scroll last frame to full tile */
5002
5003     while (scroll_x != scroll_xx || scroll_y != scroll_yy)
5004     {
5005       int dx = 0, dy = 0;
5006       int fx = FX, fy = FY;
5007
5008       dx = (scroll_xx < scroll_x ? +1 : scroll_xx > scroll_x ? -1 : 0);
5009       dy = (scroll_yy < scroll_y ? +1 : scroll_yy > scroll_y ? -1 : 0);
5010
5011       if (dx == 0 && dy == 0)           /* no scrolling needed at all */
5012         break;
5013
5014       scroll_x -= dx;
5015       scroll_y -= dy;
5016
5017       fx += dx * TILEX / 2;
5018       fy += dy * TILEY / 2;
5019
5020       ScrollLevel(dx, dy);
5021       DrawAllPlayers();
5022
5023       /* scroll in two steps of half tile size to make things smoother */
5024       BlitBitmap(drawto_field, window, fx, fy, SXSIZE, SYSIZE, SX, SY);
5025       Delay(wait_delay_value);
5026
5027       /* scroll second step to align at full tile size */
5028       BackToFront();
5029       Delay(wait_delay_value);
5030     }
5031
5032     DrawAllPlayers();
5033     BackToFront();
5034     Delay(wait_delay_value);
5035   }
5036 }
5037
5038 void RelocatePlayer(int jx, int jy, int el_player_raw)
5039 {
5040   int el_player = GET_PLAYER_ELEMENT(el_player_raw);
5041   int player_nr = GET_PLAYER_NR(el_player);
5042   struct PlayerInfo *player = &stored_player[player_nr];
5043   boolean ffwd_delay = (tape.playing && tape.fast_forward);
5044   boolean no_delay = (tape.warp_forward);
5045   int frame_delay_value = (ffwd_delay ? FfwdFrameDelay : GameFrameDelay);
5046   int wait_delay_value = (no_delay ? 0 : frame_delay_value);
5047   int old_jx = player->jx;
5048   int old_jy = player->jy;
5049   int old_element = Feld[old_jx][old_jy];
5050   int element = Feld[jx][jy];
5051   boolean player_relocated = (old_jx != jx || old_jy != jy);
5052
5053   int move_dir_horiz = (jx < old_jx ? MV_LEFT : jx > old_jx ? MV_RIGHT : 0);
5054   int move_dir_vert  = (jy < old_jy ? MV_UP   : jy > old_jy ? MV_DOWN  : 0);
5055   int enter_side_horiz = MV_DIR_OPPOSITE(move_dir_horiz);
5056   int enter_side_vert  = MV_DIR_OPPOSITE(move_dir_vert);
5057   int leave_side_horiz = move_dir_horiz;
5058   int leave_side_vert  = move_dir_vert;
5059   int enter_side = enter_side_horiz | enter_side_vert;
5060   int leave_side = leave_side_horiz | leave_side_vert;
5061
5062   if (player->GameOver)         /* do not reanimate dead player */
5063     return;
5064
5065   if (!player_relocated)        /* no need to relocate the player */
5066     return;
5067
5068   if (IS_PLAYER(jx, jy))        /* player already placed at new position */
5069   {
5070     RemoveField(jx, jy);        /* temporarily remove newly placed player */
5071     DrawLevelField(jx, jy);
5072   }
5073
5074   if (player->present)
5075   {
5076     while (player->MovPos)
5077     {
5078       ScrollPlayer(player, SCROLL_GO_ON);
5079       ScrollScreen(NULL, SCROLL_GO_ON);
5080
5081       AdvanceFrameAndPlayerCounters(player->index_nr);
5082
5083       DrawPlayer(player);
5084
5085       BackToFront();
5086       Delay(wait_delay_value);
5087     }
5088
5089     DrawPlayer(player);         /* needed here only to cleanup last field */
5090     DrawLevelField(player->jx, player->jy);     /* remove player graphic */
5091
5092     player->is_moving = FALSE;
5093   }
5094
5095   if (IS_CUSTOM_ELEMENT(old_element))
5096     CheckElementChangeByPlayer(old_jx, old_jy, old_element,
5097                                CE_LEFT_BY_PLAYER,
5098                                player->index_bit, leave_side);
5099
5100   CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
5101                                       CE_PLAYER_LEAVES_X,
5102                                       player->index_bit, leave_side);
5103
5104   Feld[jx][jy] = el_player;
5105   InitPlayerField(jx, jy, el_player, TRUE);
5106
5107   /* "InitPlayerField()" above sets Feld[jx][jy] to EL_EMPTY, but it may be
5108      possible that the relocation target field did not contain a player element,
5109      but a walkable element, to which the new player was relocated -- in this
5110      case, restore that (already initialized!) element on the player field */
5111   if (!ELEM_IS_PLAYER(element)) /* player may be set on walkable element */
5112   {
5113     Feld[jx][jy] = element;     /* restore previously existing element */
5114   }
5115
5116   /* only visually relocate centered player */
5117   DrawRelocateScreen(old_jx, old_jy, player->jx, player->jy, player->MovDir,
5118                      FALSE, level.instant_relocation);
5119
5120   TestIfPlayerTouchesBadThing(jx, jy);
5121   TestIfPlayerTouchesCustomElement(jx, jy);
5122
5123   if (IS_CUSTOM_ELEMENT(element))
5124     CheckElementChangeByPlayer(jx, jy, element, CE_ENTERED_BY_PLAYER,
5125                                player->index_bit, enter_side);
5126
5127   CheckTriggeredElementChangeByPlayer(jx, jy, element, CE_PLAYER_ENTERS_X,
5128                                       player->index_bit, enter_side);
5129
5130   if (player->is_switching)
5131   {
5132     /* ensure that relocation while still switching an element does not cause
5133        a new element to be treated as also switched directly after relocation
5134        (this is important for teleporter switches that teleport the player to
5135        a place where another teleporter switch is in the same direction, which
5136        would then incorrectly be treated as immediately switched before the
5137        direction key that caused the switch was released) */
5138
5139     player->switch_x += jx - old_jx;
5140     player->switch_y += jy - old_jy;
5141   }
5142 }
5143
5144 void Explode(int ex, int ey, int phase, int mode)
5145 {
5146   int x, y;
5147   int last_phase;
5148   int border_element;
5149
5150   /* !!! eliminate this variable !!! */
5151   int delay = (game.emulation == EMU_SUPAPLEX ? 3 : 2);
5152
5153   if (game.explosions_delayed)
5154   {
5155     ExplodeField[ex][ey] = mode;
5156     return;
5157   }
5158
5159   if (phase == EX_PHASE_START)          /* initialize 'Store[][]' field */
5160   {
5161     int center_element = Feld[ex][ey];
5162     int artwork_element, explosion_element;     /* set these values later */
5163
5164     /* remove things displayed in background while burning dynamite */
5165     if (Back[ex][ey] != EL_EMPTY && !IS_INDESTRUCTIBLE(Back[ex][ey]))
5166       Back[ex][ey] = 0;
5167
5168     if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
5169     {
5170       /* put moving element to center field (and let it explode there) */
5171       center_element = MovingOrBlocked2Element(ex, ey);
5172       RemoveMovingField(ex, ey);
5173       Feld[ex][ey] = center_element;
5174     }
5175
5176     /* now "center_element" is finally determined -- set related values now */
5177     artwork_element = center_element;           /* for custom player artwork */
5178     explosion_element = center_element;         /* for custom player artwork */
5179
5180     if (IS_PLAYER(ex, ey))
5181     {
5182       int player_nr = GET_PLAYER_NR(StorePlayer[ex][ey]);
5183
5184       artwork_element = stored_player[player_nr].artwork_element;
5185
5186       if (level.use_explosion_element[player_nr])
5187       {
5188         explosion_element = level.explosion_element[player_nr];
5189         artwork_element = explosion_element;
5190       }
5191     }
5192
5193     if (mode == EX_TYPE_NORMAL ||
5194         mode == EX_TYPE_CENTER ||
5195         mode == EX_TYPE_CROSS)
5196       PlayLevelSoundElementAction(ex, ey, artwork_element, ACTION_EXPLODING);
5197
5198     last_phase = element_info[explosion_element].explosion_delay + 1;
5199
5200     for (y = ey - 1; y <= ey + 1; y++) for (x = ex - 1; x <= ex + 1; x++)
5201     {
5202       int xx = x - ex + 1;
5203       int yy = y - ey + 1;
5204       int element;
5205
5206       if (!IN_LEV_FIELD(x, y) ||
5207           (mode & EX_TYPE_SINGLE_TILE && (x != ex || y != ey)) ||
5208           (mode == EX_TYPE_CROSS      && (x != ex && y != ey)))
5209         continue;
5210
5211       element = Feld[x][y];
5212
5213       if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
5214       {
5215         element = MovingOrBlocked2Element(x, y);
5216
5217         if (!IS_EXPLOSION_PROOF(element))
5218           RemoveMovingField(x, y);
5219       }
5220
5221       /* indestructible elements can only explode in center (but not flames) */
5222       if ((IS_EXPLOSION_PROOF(element) && (x != ex || y != ey ||
5223                                            mode == EX_TYPE_BORDER)) ||
5224           element == EL_FLAMES)
5225         continue;
5226
5227       /* no idea why this was changed from 3.0.8 to 3.1.0 -- this causes buggy
5228          behaviour, for example when touching a yamyam that explodes to rocks
5229          with active deadly shield, a rock is created under the player !!! */
5230       /* (case 1 (surely buggy): >= 3.1.0, case 2 (maybe buggy): <= 3.0.8) */
5231 #if 0
5232       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)) &&
5233           (game.engine_version < VERSION_IDENT(3,1,0,0) ||
5234            (x == ex && y == ey && mode != EX_TYPE_BORDER)))
5235 #else
5236       if (IS_PLAYER(x, y) && SHIELD_ON(PLAYERINFO(x, y)))
5237 #endif
5238       {
5239         if (IS_ACTIVE_BOMB(element))
5240         {
5241           /* re-activate things under the bomb like gate or penguin */
5242           Feld[x][y] = (Back[x][y] ? Back[x][y] : EL_EMPTY);
5243           Back[x][y] = 0;
5244         }
5245
5246         continue;
5247       }
5248
5249       /* save walkable background elements while explosion on same tile */
5250       if (IS_WALKABLE(element) && IS_INDESTRUCTIBLE(element) &&
5251           (x != ex || y != ey || mode == EX_TYPE_BORDER))
5252         Back[x][y] = element;
5253
5254       /* ignite explodable elements reached by other explosion */
5255       if (element == EL_EXPLOSION)
5256         element = Store2[x][y];
5257
5258       if (AmoebaNr[x][y] &&
5259           (element == EL_AMOEBA_FULL ||
5260            element == EL_BD_AMOEBA ||
5261            element == EL_AMOEBA_GROWING))
5262       {
5263         AmoebaCnt[AmoebaNr[x][y]]--;
5264         AmoebaCnt2[AmoebaNr[x][y]]--;
5265       }
5266
5267       RemoveField(x, y);
5268
5269       if (IS_PLAYER(ex, ey) && !PLAYER_EXPLOSION_PROTECTED(ex, ey))
5270       {
5271         int player_nr = StorePlayer[ex][ey] - EL_PLAYER_1;
5272
5273         Store[x][y] = EL_PLAYER_IS_EXPLODING_1 + player_nr;
5274
5275         if (PLAYERINFO(ex, ey)->use_murphy)
5276           Store[x][y] = EL_EMPTY;
5277       }
5278
5279       /* !!! check this case -- currently needed for rnd_rado_negundo_v,
5280          !!! levels 015 018 019 020 021 022 023 026 027 028 !!! */
5281       else if (ELEM_IS_PLAYER(center_element))
5282         Store[x][y] = EL_EMPTY;
5283       else if (center_element == EL_YAMYAM)
5284         Store[x][y] = level.yamyam_content[game.yamyam_content_nr].e[xx][yy];
5285       else if (element_info[center_element].content.e[xx][yy] != EL_EMPTY)
5286         Store[x][y] = element_info[center_element].content.e[xx][yy];
5287 #if 1
5288       /* needed because EL_BD_BUTTERFLY is not defined as "CAN_EXPLODE"
5289          (killing EL_BD_BUTTERFLY with dynamite would result in BD diamond
5290          otherwise) -- FIX THIS !!! */
5291       else if (!CAN_EXPLODE(element) && element != EL_BD_BUTTERFLY)
5292         Store[x][y] = element_info[element].content.e[1][1];
5293 #else
5294       else if (!CAN_EXPLODE(element))
5295         Store[x][y] = element_info[element].content.e[1][1];
5296 #endif
5297       else
5298         Store[x][y] = EL_EMPTY;
5299
5300       if (x != ex || y != ey || mode == EX_TYPE_BORDER ||
5301           center_element == EL_AMOEBA_TO_DIAMOND)
5302         Store2[x][y] = element;
5303
5304       Feld[x][y] = EL_EXPLOSION;
5305       GfxElement[x][y] = artwork_element;
5306
5307       ExplodePhase[x][y] = 1;
5308       ExplodeDelay[x][y] = last_phase;
5309
5310       Stop[x][y] = TRUE;
5311     }
5312
5313     if (center_element == EL_YAMYAM)
5314       game.yamyam_content_nr =
5315         (game.yamyam_content_nr + 1) % level.num_yamyam_contents;
5316
5317     return;
5318   }
5319
5320   if (Stop[ex][ey])
5321     return;
5322
5323   x = ex;
5324   y = ey;
5325
5326   if (phase == 1)
5327     GfxFrame[x][y] = 0;         /* restart explosion animation */
5328
5329   last_phase = ExplodeDelay[x][y];
5330
5331   ExplodePhase[x][y] = (phase < last_phase ? phase + 1 : 0);
5332
5333   /* this can happen if the player leaves an explosion just in time */
5334   if (GfxElement[x][y] == EL_UNDEFINED)
5335     GfxElement[x][y] = EL_EMPTY;
5336
5337   border_element = Store2[x][y];
5338   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5339     border_element = StorePlayer[x][y];
5340
5341   if (phase == element_info[border_element].ignition_delay ||
5342       phase == last_phase)
5343   {
5344     boolean border_explosion = FALSE;
5345
5346     if (IS_PLAYER(x, y) && PLAYERINFO(x, y)->present &&
5347         !PLAYER_EXPLOSION_PROTECTED(x, y))
5348     {
5349       KillPlayerUnlessExplosionProtected(x, y);
5350       border_explosion = TRUE;
5351     }
5352     else if (CAN_EXPLODE_BY_EXPLOSION(border_element))
5353     {
5354       Feld[x][y] = Store2[x][y];
5355       Store2[x][y] = 0;
5356       Bang(x, y);
5357       border_explosion = TRUE;
5358     }
5359     else if (border_element == EL_AMOEBA_TO_DIAMOND)
5360     {
5361       AmoebeUmwandeln(x, y);
5362       Store2[x][y] = 0;
5363       border_explosion = TRUE;
5364     }
5365
5366     /* if an element just explodes due to another explosion (chain-reaction),
5367        do not immediately end the new explosion when it was the last frame of
5368        the explosion (as it would be done in the following "if"-statement!) */
5369     if (border_explosion && phase == last_phase)
5370       return;
5371   }
5372
5373   if (phase == last_phase)
5374   {
5375     int element;
5376
5377     element = Feld[x][y] = Store[x][y];
5378     Store[x][y] = Store2[x][y] = 0;
5379     GfxElement[x][y] = EL_UNDEFINED;
5380
5381     /* player can escape from explosions and might therefore be still alive */
5382     if (element >= EL_PLAYER_IS_EXPLODING_1 &&
5383         element <= EL_PLAYER_IS_EXPLODING_4)
5384     {
5385       int player_nr = element - EL_PLAYER_IS_EXPLODING_1;
5386       int explosion_element = EL_PLAYER_1 + player_nr;
5387       int xx = MIN(MAX(0, x - stored_player[player_nr].jx + 1), 2);
5388       int yy = MIN(MAX(0, y - stored_player[player_nr].jy + 1), 2);
5389
5390       if (level.use_explosion_element[player_nr])
5391         explosion_element = level.explosion_element[player_nr];
5392
5393       Feld[x][y] = (stored_player[player_nr].active ? EL_EMPTY :
5394                     element_info[explosion_element].content.e[xx][yy]);
5395     }
5396
5397     /* restore probably existing indestructible background element */
5398     if (Back[x][y] && IS_INDESTRUCTIBLE(Back[x][y]))
5399       element = Feld[x][y] = Back[x][y];
5400     Back[x][y] = 0;
5401
5402     MovDir[x][y] = MovPos[x][y] = MovDelay[x][y] = 0;
5403     GfxDir[x][y] = MV_NONE;
5404     ChangeDelay[x][y] = 0;
5405     ChangePage[x][y] = -1;
5406
5407     CustomValue[x][y] = 0;
5408
5409     InitField_WithBug2(x, y, FALSE);
5410
5411     TEST_DrawLevelField(x, y);
5412
5413     TestIfElementTouchesCustomElement(x, y);
5414
5415     if (GFX_CRUMBLED(element))
5416       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5417
5418     if (IS_PLAYER(x, y) && !PLAYERINFO(x, y)->present)
5419       StorePlayer[x][y] = 0;
5420
5421     if (ELEM_IS_PLAYER(element))
5422       RelocatePlayer(x, y, element);
5423   }
5424   else if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
5425   {
5426     int graphic = el_act2img(GfxElement[x][y], ACTION_EXPLODING);
5427     int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
5428
5429     if (phase == delay)
5430       TEST_DrawLevelFieldCrumbled(x, y);
5431
5432     if (IS_WALKABLE_OVER(Back[x][y]) && Back[x][y] != EL_EMPTY)
5433     {
5434       DrawLevelElement(x, y, Back[x][y]);
5435       DrawGraphicThruMask(SCREENX(x), SCREENY(y), graphic, frame);
5436     }
5437     else if (IS_WALKABLE_UNDER(Back[x][y]))
5438     {
5439       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5440       DrawLevelElementThruMask(x, y, Back[x][y]);
5441     }
5442     else if (!IS_WALKABLE_INSIDE(Back[x][y]))
5443       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
5444   }
5445 }
5446
5447 void DynaExplode(int ex, int ey)
5448 {
5449   int i, j;
5450   int dynabomb_element = Feld[ex][ey];
5451   int dynabomb_size = 1;
5452   boolean dynabomb_xl = FALSE;
5453   struct PlayerInfo *player;
5454   static int xy[4][2] =
5455   {
5456     { 0, -1 },
5457     { -1, 0 },
5458     { +1, 0 },
5459     { 0, +1 }
5460   };
5461
5462   if (IS_ACTIVE_BOMB(dynabomb_element))
5463   {
5464     player = &stored_player[dynabomb_element - EL_DYNABOMB_PLAYER_1_ACTIVE];
5465     dynabomb_size = player->dynabomb_size;
5466     dynabomb_xl = player->dynabomb_xl;
5467     player->dynabombs_left++;
5468   }
5469
5470   Explode(ex, ey, EX_PHASE_START, EX_TYPE_CENTER);
5471
5472   for (i = 0; i < NUM_DIRECTIONS; i++)
5473   {
5474     for (j = 1; j <= dynabomb_size; j++)
5475     {
5476       int x = ex + j * xy[i][0];
5477       int y = ey + j * xy[i][1];
5478       int element;
5479
5480       if (!IN_LEV_FIELD(x, y) || IS_INDESTRUCTIBLE(Feld[x][y]))
5481         break;
5482
5483       element = Feld[x][y];
5484
5485       /* do not restart explosions of fields with active bombs */
5486       if (element == EL_EXPLOSION && IS_ACTIVE_BOMB(Store2[x][y]))
5487         continue;
5488
5489       Explode(x, y, EX_PHASE_START, EX_TYPE_BORDER);
5490
5491       if (element != EL_EMPTY && element != EL_EXPLOSION &&
5492           !IS_DIGGABLE(element) && !dynabomb_xl)
5493         break;
5494     }
5495   }
5496 }
5497
5498 void Bang(int x, int y)
5499 {
5500   int element = MovingOrBlocked2Element(x, y);
5501   int explosion_type = EX_TYPE_NORMAL;
5502
5503   if (IS_PLAYER(x, y) && !PLAYER_EXPLOSION_PROTECTED(x, y))
5504   {
5505     struct PlayerInfo *player = PLAYERINFO(x, y);
5506
5507     element = Feld[x][y] = player->initial_element;
5508
5509     if (level.use_explosion_element[player->index_nr])
5510     {
5511       int explosion_element = level.explosion_element[player->index_nr];
5512
5513       if (element_info[explosion_element].explosion_type == EXPLODES_CROSS)
5514         explosion_type = EX_TYPE_CROSS;
5515       else if (element_info[explosion_element].explosion_type == EXPLODES_1X1)
5516         explosion_type = EX_TYPE_CENTER;
5517     }
5518   }
5519
5520   switch (element)
5521   {
5522     case EL_BUG:
5523     case EL_SPACESHIP:
5524     case EL_BD_BUTTERFLY:
5525     case EL_BD_FIREFLY:
5526     case EL_YAMYAM:
5527     case EL_DARK_YAMYAM:
5528     case EL_ROBOT:
5529     case EL_PACMAN:
5530     case EL_MOLE:
5531       RaiseScoreElement(element);
5532       break;
5533
5534     case EL_DYNABOMB_PLAYER_1_ACTIVE:
5535     case EL_DYNABOMB_PLAYER_2_ACTIVE:
5536     case EL_DYNABOMB_PLAYER_3_ACTIVE:
5537     case EL_DYNABOMB_PLAYER_4_ACTIVE:
5538     case EL_DYNABOMB_INCREASE_NUMBER:
5539     case EL_DYNABOMB_INCREASE_SIZE:
5540     case EL_DYNABOMB_INCREASE_POWER:
5541       explosion_type = EX_TYPE_DYNA;
5542       break;
5543
5544     case EL_DC_LANDMINE:
5545       explosion_type = EX_TYPE_CENTER;
5546       break;
5547
5548     case EL_PENGUIN:
5549     case EL_LAMP:
5550     case EL_LAMP_ACTIVE:
5551     case EL_AMOEBA_TO_DIAMOND:
5552       if (!IS_PLAYER(x, y))     /* penguin and player may be at same field */
5553         explosion_type = EX_TYPE_CENTER;
5554       break;
5555
5556     default:
5557       if (element_info[element].explosion_type == EXPLODES_CROSS)
5558         explosion_type = EX_TYPE_CROSS;
5559       else if (element_info[element].explosion_type == EXPLODES_1X1)
5560         explosion_type = EX_TYPE_CENTER;
5561       break;
5562   }
5563
5564   if (explosion_type == EX_TYPE_DYNA)
5565     DynaExplode(x, y);
5566   else
5567     Explode(x, y, EX_PHASE_START, explosion_type);
5568
5569   CheckTriggeredElementChange(x, y, element, CE_EXPLOSION_OF_X);
5570 }
5571
5572 void SplashAcid(int x, int y)
5573 {
5574   if (IN_LEV_FIELD(x - 1, y - 1) && IS_FREE(x - 1, y - 1) &&
5575       (!IN_LEV_FIELD(x - 1, y - 2) ||
5576        !CAN_FALL(MovingOrBlocked2Element(x - 1, y - 2))))
5577     Feld[x - 1][y - 1] = EL_ACID_SPLASH_LEFT;
5578
5579   if (IN_LEV_FIELD(x + 1, y - 1) && IS_FREE(x + 1, y - 1) &&
5580       (!IN_LEV_FIELD(x + 1, y - 2) ||
5581        !CAN_FALL(MovingOrBlocked2Element(x + 1, y - 2))))
5582     Feld[x + 1][y - 1] = EL_ACID_SPLASH_RIGHT;
5583
5584   PlayLevelSound(x, y, SND_ACID_SPLASHING);
5585 }
5586
5587 static void InitBeltMovement()
5588 {
5589   static int belt_base_element[4] =
5590   {
5591     EL_CONVEYOR_BELT_1_LEFT,
5592     EL_CONVEYOR_BELT_2_LEFT,
5593     EL_CONVEYOR_BELT_3_LEFT,
5594     EL_CONVEYOR_BELT_4_LEFT
5595   };
5596   static int belt_base_active_element[4] =
5597   {
5598     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5599     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5600     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5601     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5602   };
5603
5604   int x, y, i, j;
5605
5606   /* set frame order for belt animation graphic according to belt direction */
5607   for (i = 0; i < NUM_BELTS; i++)
5608   {
5609     int belt_nr = i;
5610
5611     for (j = 0; j < NUM_BELT_PARTS; j++)
5612     {
5613       int element = belt_base_active_element[belt_nr] + j;
5614       int graphic_1 = el2img(element);
5615       int graphic_2 = el2panelimg(element);
5616
5617       if (game.belt_dir[i] == MV_LEFT)
5618       {
5619         graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5620         graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5621       }
5622       else
5623       {
5624         graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5625         graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5626       }
5627     }
5628   }
5629
5630   SCAN_PLAYFIELD(x, y)
5631   {
5632     int element = Feld[x][y];
5633
5634     for (i = 0; i < NUM_BELTS; i++)
5635     {
5636       if (IS_BELT(element) && game.belt_dir[i] != MV_NONE)
5637       {
5638         int e_belt_nr = getBeltNrFromBeltElement(element);
5639         int belt_nr = i;
5640
5641         if (e_belt_nr == belt_nr)
5642         {
5643           int belt_part = Feld[x][y] - belt_base_element[belt_nr];
5644
5645           Feld[x][y] = belt_base_active_element[belt_nr] + belt_part;
5646         }
5647       }
5648     }
5649   }
5650 }
5651
5652 static void ToggleBeltSwitch(int x, int y)
5653 {
5654   static int belt_base_element[4] =
5655   {
5656     EL_CONVEYOR_BELT_1_LEFT,
5657     EL_CONVEYOR_BELT_2_LEFT,
5658     EL_CONVEYOR_BELT_3_LEFT,
5659     EL_CONVEYOR_BELT_4_LEFT
5660   };
5661   static int belt_base_active_element[4] =
5662   {
5663     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
5664     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
5665     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
5666     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
5667   };
5668   static int belt_base_switch_element[4] =
5669   {
5670     EL_CONVEYOR_BELT_1_SWITCH_LEFT,
5671     EL_CONVEYOR_BELT_2_SWITCH_LEFT,
5672     EL_CONVEYOR_BELT_3_SWITCH_LEFT,
5673     EL_CONVEYOR_BELT_4_SWITCH_LEFT
5674   };
5675   static int belt_move_dir[4] =
5676   {
5677     MV_LEFT,
5678     MV_NONE,
5679     MV_RIGHT,
5680     MV_NONE,
5681   };
5682
5683   int element = Feld[x][y];
5684   int belt_nr = getBeltNrFromBeltSwitchElement(element);
5685   int belt_dir_nr = (game.belt_dir_nr[belt_nr] + 1) % 4;
5686   int belt_dir = belt_move_dir[belt_dir_nr];
5687   int xx, yy, i;
5688
5689   if (!IS_BELT_SWITCH(element))
5690     return;
5691
5692   game.belt_dir_nr[belt_nr] = belt_dir_nr;
5693   game.belt_dir[belt_nr] = belt_dir;
5694
5695   if (belt_dir_nr == 3)
5696     belt_dir_nr = 1;
5697
5698   /* set frame order for belt animation graphic according to belt direction */
5699   for (i = 0; i < NUM_BELT_PARTS; i++)
5700   {
5701     int element = belt_base_active_element[belt_nr] + i;
5702     int graphic_1 = el2img(element);
5703     int graphic_2 = el2panelimg(element);
5704
5705     if (belt_dir == MV_LEFT)
5706     {
5707       graphic_info[graphic_1].anim_mode &= ~ANIM_REVERSE;
5708       graphic_info[graphic_2].anim_mode &= ~ANIM_REVERSE;
5709     }
5710     else
5711     {
5712       graphic_info[graphic_1].anim_mode |=  ANIM_REVERSE;
5713       graphic_info[graphic_2].anim_mode |=  ANIM_REVERSE;
5714     }
5715   }
5716
5717   SCAN_PLAYFIELD(xx, yy)
5718   {
5719     int element = Feld[xx][yy];
5720
5721     if (IS_BELT_SWITCH(element))
5722     {
5723       int e_belt_nr = getBeltNrFromBeltSwitchElement(element);
5724
5725       if (e_belt_nr == belt_nr)
5726       {
5727         Feld[xx][yy] = belt_base_switch_element[belt_nr] + belt_dir_nr;
5728         TEST_DrawLevelField(xx, yy);
5729       }
5730     }
5731     else if (IS_BELT(element) && belt_dir != MV_NONE)
5732     {
5733       int e_belt_nr = getBeltNrFromBeltElement(element);
5734
5735       if (e_belt_nr == belt_nr)
5736       {
5737         int belt_part = Feld[xx][yy] - belt_base_element[belt_nr];
5738
5739         Feld[xx][yy] = belt_base_active_element[belt_nr] + belt_part;
5740         TEST_DrawLevelField(xx, yy);
5741       }
5742     }
5743     else if (IS_BELT_ACTIVE(element) && belt_dir == MV_NONE)
5744     {
5745       int e_belt_nr = getBeltNrFromBeltActiveElement(element);
5746
5747       if (e_belt_nr == belt_nr)
5748       {
5749         int belt_part = Feld[xx][yy] - belt_base_active_element[belt_nr];
5750
5751         Feld[xx][yy] = belt_base_element[belt_nr] + belt_part;
5752         TEST_DrawLevelField(xx, yy);
5753       }
5754     }
5755   }
5756 }
5757
5758 static void ToggleSwitchgateSwitch(int x, int y)
5759 {
5760   int xx, yy;
5761
5762   game.switchgate_pos = !game.switchgate_pos;
5763
5764   SCAN_PLAYFIELD(xx, yy)
5765   {
5766     int element = Feld[xx][yy];
5767
5768     if (element == EL_SWITCHGATE_SWITCH_UP)
5769     {
5770       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_DOWN;
5771       TEST_DrawLevelField(xx, yy);
5772     }
5773     else if (element == EL_SWITCHGATE_SWITCH_DOWN)
5774     {
5775       Feld[xx][yy] = EL_SWITCHGATE_SWITCH_UP;
5776       TEST_DrawLevelField(xx, yy);
5777     }
5778     else if (element == EL_DC_SWITCHGATE_SWITCH_UP)
5779     {
5780       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_DOWN;
5781       TEST_DrawLevelField(xx, yy);
5782     }
5783     else if (element == EL_DC_SWITCHGATE_SWITCH_DOWN)
5784     {
5785       Feld[xx][yy] = EL_DC_SWITCHGATE_SWITCH_UP;
5786       TEST_DrawLevelField(xx, yy);
5787     }
5788     else if (element == EL_SWITCHGATE_OPEN ||
5789              element == EL_SWITCHGATE_OPENING)
5790     {
5791       Feld[xx][yy] = EL_SWITCHGATE_CLOSING;
5792
5793       PlayLevelSoundAction(xx, yy, ACTION_CLOSING);
5794     }
5795     else if (element == EL_SWITCHGATE_CLOSED ||
5796              element == EL_SWITCHGATE_CLOSING)
5797     {
5798       Feld[xx][yy] = EL_SWITCHGATE_OPENING;
5799
5800       PlayLevelSoundAction(xx, yy, ACTION_OPENING);
5801     }
5802   }
5803 }
5804
5805 static int getInvisibleActiveFromInvisibleElement(int element)
5806 {
5807   return (element == EL_INVISIBLE_STEELWALL ? EL_INVISIBLE_STEELWALL_ACTIVE :
5808           element == EL_INVISIBLE_WALL      ? EL_INVISIBLE_WALL_ACTIVE :
5809           element == EL_INVISIBLE_SAND      ? EL_INVISIBLE_SAND_ACTIVE :
5810           element);
5811 }
5812
5813 static int getInvisibleFromInvisibleActiveElement(int element)
5814 {
5815   return (element == EL_INVISIBLE_STEELWALL_ACTIVE ? EL_INVISIBLE_STEELWALL :
5816           element == EL_INVISIBLE_WALL_ACTIVE      ? EL_INVISIBLE_WALL :
5817           element == EL_INVISIBLE_SAND_ACTIVE      ? EL_INVISIBLE_SAND :
5818           element);
5819 }
5820
5821 static void RedrawAllLightSwitchesAndInvisibleElements()
5822 {
5823   int x, y;
5824
5825   SCAN_PLAYFIELD(x, y)
5826   {
5827     int element = Feld[x][y];
5828
5829     if (element == EL_LIGHT_SWITCH &&
5830         game.light_time_left > 0)
5831     {
5832       Feld[x][y] = EL_LIGHT_SWITCH_ACTIVE;
5833       TEST_DrawLevelField(x, y);
5834     }
5835     else if (element == EL_LIGHT_SWITCH_ACTIVE &&
5836              game.light_time_left == 0)
5837     {
5838       Feld[x][y] = EL_LIGHT_SWITCH;
5839       TEST_DrawLevelField(x, y);
5840     }
5841     else if (element == EL_EMC_DRIPPER &&
5842              game.light_time_left > 0)
5843     {
5844       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5845       TEST_DrawLevelField(x, y);
5846     }
5847     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5848              game.light_time_left == 0)
5849     {
5850       Feld[x][y] = EL_EMC_DRIPPER;
5851       TEST_DrawLevelField(x, y);
5852     }
5853     else if (element == EL_INVISIBLE_STEELWALL ||
5854              element == EL_INVISIBLE_WALL ||
5855              element == EL_INVISIBLE_SAND)
5856     {
5857       if (game.light_time_left > 0)
5858         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5859
5860       TEST_DrawLevelField(x, y);
5861
5862       /* uncrumble neighbour fields, if needed */
5863       if (element == EL_INVISIBLE_SAND)
5864         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5865     }
5866     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5867              element == EL_INVISIBLE_WALL_ACTIVE ||
5868              element == EL_INVISIBLE_SAND_ACTIVE)
5869     {
5870       if (game.light_time_left == 0)
5871         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5872
5873       TEST_DrawLevelField(x, y);
5874
5875       /* re-crumble neighbour fields, if needed */
5876       if (element == EL_INVISIBLE_SAND)
5877         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5878     }
5879   }
5880 }
5881
5882 static void RedrawAllInvisibleElementsForLenses()
5883 {
5884   int x, y;
5885
5886   SCAN_PLAYFIELD(x, y)
5887   {
5888     int element = Feld[x][y];
5889
5890     if (element == EL_EMC_DRIPPER &&
5891         game.lenses_time_left > 0)
5892     {
5893       Feld[x][y] = EL_EMC_DRIPPER_ACTIVE;
5894       TEST_DrawLevelField(x, y);
5895     }
5896     else if (element == EL_EMC_DRIPPER_ACTIVE &&
5897              game.lenses_time_left == 0)
5898     {
5899       Feld[x][y] = EL_EMC_DRIPPER;
5900       TEST_DrawLevelField(x, y);
5901     }
5902     else if (element == EL_INVISIBLE_STEELWALL ||
5903              element == EL_INVISIBLE_WALL ||
5904              element == EL_INVISIBLE_SAND)
5905     {
5906       if (game.lenses_time_left > 0)
5907         Feld[x][y] = getInvisibleActiveFromInvisibleElement(element);
5908
5909       TEST_DrawLevelField(x, y);
5910
5911       /* uncrumble neighbour fields, if needed */
5912       if (element == EL_INVISIBLE_SAND)
5913         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5914     }
5915     else if (element == EL_INVISIBLE_STEELWALL_ACTIVE ||
5916              element == EL_INVISIBLE_WALL_ACTIVE ||
5917              element == EL_INVISIBLE_SAND_ACTIVE)
5918     {
5919       if (game.lenses_time_left == 0)
5920         Feld[x][y] = getInvisibleFromInvisibleActiveElement(element);
5921
5922       TEST_DrawLevelField(x, y);
5923
5924       /* re-crumble neighbour fields, if needed */
5925       if (element == EL_INVISIBLE_SAND)
5926         TEST_DrawLevelFieldCrumbledNeighbours(x, y);
5927     }
5928   }
5929 }
5930
5931 static void RedrawAllInvisibleElementsForMagnifier()
5932 {
5933   int x, y;
5934
5935   SCAN_PLAYFIELD(x, y)
5936   {
5937     int element = Feld[x][y];
5938
5939     if (element == EL_EMC_FAKE_GRASS &&
5940         game.magnify_time_left > 0)
5941     {
5942       Feld[x][y] = EL_EMC_FAKE_GRASS_ACTIVE;
5943       TEST_DrawLevelField(x, y);
5944     }
5945     else if (element == EL_EMC_FAKE_GRASS_ACTIVE &&
5946              game.magnify_time_left == 0)
5947     {
5948       Feld[x][y] = EL_EMC_FAKE_GRASS;
5949       TEST_DrawLevelField(x, y);
5950     }
5951     else if (IS_GATE_GRAY(element) &&
5952              game.magnify_time_left > 0)
5953     {
5954       Feld[x][y] = (IS_RND_GATE_GRAY(element) ?
5955                     element - EL_GATE_1_GRAY + EL_GATE_1_GRAY_ACTIVE :
5956                     IS_EM_GATE_GRAY(element) ?
5957                     element - EL_EM_GATE_1_GRAY + EL_EM_GATE_1_GRAY_ACTIVE :
5958                     IS_EMC_GATE_GRAY(element) ?
5959                     element - EL_EMC_GATE_5_GRAY + EL_EMC_GATE_5_GRAY_ACTIVE :
5960                     IS_DC_GATE_GRAY(element) ?
5961                     EL_DC_GATE_WHITE_GRAY_ACTIVE :
5962                     element);
5963       TEST_DrawLevelField(x, y);
5964     }
5965     else if (IS_GATE_GRAY_ACTIVE(element) &&
5966              game.magnify_time_left == 0)
5967     {
5968       Feld[x][y] = (IS_RND_GATE_GRAY_ACTIVE(element) ?
5969                     element - EL_GATE_1_GRAY_ACTIVE + EL_GATE_1_GRAY :
5970                     IS_EM_GATE_GRAY_ACTIVE(element) ?
5971                     element - EL_EM_GATE_1_GRAY_ACTIVE + EL_EM_GATE_1_GRAY :
5972                     IS_EMC_GATE_GRAY_ACTIVE(element) ?
5973                     element - EL_EMC_GATE_5_GRAY_ACTIVE + EL_EMC_GATE_5_GRAY :
5974                     IS_DC_GATE_GRAY_ACTIVE(element) ?
5975                     EL_DC_GATE_WHITE_GRAY :
5976                     element);
5977       TEST_DrawLevelField(x, y);
5978     }
5979   }
5980 }
5981
5982 static void ToggleLightSwitch(int x, int y)
5983 {
5984   int element = Feld[x][y];
5985
5986   game.light_time_left =
5987     (element == EL_LIGHT_SWITCH ?
5988      level.time_light * FRAMES_PER_SECOND : 0);
5989
5990   RedrawAllLightSwitchesAndInvisibleElements();
5991 }
5992
5993 static void ActivateTimegateSwitch(int x, int y)
5994 {
5995   int xx, yy;
5996
5997   game.timegate_time_left = level.time_timegate * FRAMES_PER_SECOND;
5998
5999   SCAN_PLAYFIELD(xx, yy)
6000   {
6001     int element = Feld[xx][yy];
6002
6003     if (element == EL_TIMEGATE_CLOSED ||
6004         element == EL_TIMEGATE_CLOSING)
6005     {
6006       Feld[xx][yy] = EL_TIMEGATE_OPENING;
6007       PlayLevelSound(xx, yy, SND_CLASS_TIMEGATE_OPENING);
6008     }
6009
6010     /*
6011     else if (element == EL_TIMEGATE_SWITCH_ACTIVE)
6012     {
6013       Feld[xx][yy] = EL_TIMEGATE_SWITCH;
6014       TEST_DrawLevelField(xx, yy);
6015     }
6016     */
6017
6018   }
6019
6020   Feld[x][y] = (Feld[x][y] == EL_TIMEGATE_SWITCH ? EL_TIMEGATE_SWITCH_ACTIVE :
6021                 EL_DC_TIMEGATE_SWITCH_ACTIVE);
6022 }
6023
6024 void Impact(int x, int y)
6025 {
6026   boolean last_line = (y == lev_fieldy - 1);
6027   boolean object_hit = FALSE;
6028   boolean impact = (last_line || object_hit);
6029   int element = Feld[x][y];
6030   int smashed = EL_STEELWALL;
6031
6032   if (!last_line)       /* check if element below was hit */
6033   {
6034     if (Feld[x][y + 1] == EL_PLAYER_IS_LEAVING)
6035       return;
6036
6037     object_hit = (!IS_FREE(x, y + 1) && (!IS_MOVING(x, y + 1) ||
6038                                          MovDir[x][y + 1] != MV_DOWN ||
6039                                          MovPos[x][y + 1] <= TILEY / 2));
6040
6041     /* do not smash moving elements that left the smashed field in time */
6042     if (game.engine_version >= VERSION_IDENT(2,2,0,7) && IS_MOVING(x, y + 1) &&
6043         ABS(MovPos[x][y + 1] + getElementMoveStepsize(x, y + 1)) >= TILEX)
6044       object_hit = FALSE;
6045
6046 #if USE_QUICKSAND_IMPACT_BUGFIX
6047     if (Feld[x][y + 1] == EL_QUICKSAND_EMPTYING && object_hit == FALSE)
6048     {
6049       RemoveMovingField(x, y + 1);
6050       Feld[x][y + 1] = EL_QUICKSAND_EMPTY;
6051       Feld[x][y + 2] = EL_ROCK;
6052       TEST_DrawLevelField(x, y + 2);
6053
6054       object_hit = TRUE;
6055     }
6056
6057     if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTYING && object_hit == FALSE)
6058     {
6059       RemoveMovingField(x, y + 1);
6060       Feld[x][y + 1] = EL_QUICKSAND_FAST_EMPTY;
6061       Feld[x][y + 2] = EL_ROCK;
6062       TEST_DrawLevelField(x, y + 2);
6063
6064       object_hit = TRUE;
6065     }
6066 #endif
6067
6068     if (object_hit)
6069       smashed = MovingOrBlocked2Element(x, y + 1);
6070
6071     impact = (last_line || object_hit);
6072   }
6073
6074   if (!last_line && smashed == EL_ACID) /* element falls into acid */
6075   {
6076     SplashAcid(x, y + 1);
6077     return;
6078   }
6079
6080   /* !!! not sufficient for all cases -- see EL_PEARL below !!! */
6081   /* only reset graphic animation if graphic really changes after impact */
6082   if (impact &&
6083       el_act_dir2img(element, GfxAction[x][y], MV_DOWN) != el2img(element))
6084   {
6085     ResetGfxAnimation(x, y);
6086     TEST_DrawLevelField(x, y);
6087   }
6088
6089   if (impact && CAN_EXPLODE_IMPACT(element))
6090   {
6091     Bang(x, y);
6092     return;
6093   }
6094   else if (impact && element == EL_PEARL &&
6095            smashed != EL_DC_MAGIC_WALL && smashed != EL_DC_MAGIC_WALL_ACTIVE)
6096   {
6097     ResetGfxAnimation(x, y);
6098
6099     Feld[x][y] = EL_PEARL_BREAKING;
6100     PlayLevelSound(x, y, SND_PEARL_BREAKING);
6101     return;
6102   }
6103   else if (impact && CheckElementChange(x, y, element, smashed, CE_IMPACT))
6104   {
6105     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6106
6107     return;
6108   }
6109
6110   if (impact && element == EL_AMOEBA_DROP)
6111   {
6112     if (object_hit && IS_PLAYER(x, y + 1))
6113       KillPlayerUnlessEnemyProtected(x, y + 1);
6114     else if (object_hit && smashed == EL_PENGUIN)
6115       Bang(x, y + 1);
6116     else
6117     {
6118       Feld[x][y] = EL_AMOEBA_GROWING;
6119       Store[x][y] = EL_AMOEBA_WET;
6120
6121       ResetRandomAnimationValue(x, y);
6122     }
6123     return;
6124   }
6125
6126   if (object_hit)               /* check which object was hit */
6127   {
6128     if ((CAN_PASS_MAGIC_WALL(element) && 
6129          (smashed == EL_MAGIC_WALL ||
6130           smashed == EL_BD_MAGIC_WALL)) ||
6131         (CAN_PASS_DC_MAGIC_WALL(element) &&
6132          smashed == EL_DC_MAGIC_WALL))
6133     {
6134       int xx, yy;
6135       int activated_magic_wall =
6136         (smashed == EL_MAGIC_WALL ? EL_MAGIC_WALL_ACTIVE :
6137          smashed == EL_BD_MAGIC_WALL ? EL_BD_MAGIC_WALL_ACTIVE :
6138          EL_DC_MAGIC_WALL_ACTIVE);
6139
6140       /* activate magic wall / mill */
6141       SCAN_PLAYFIELD(xx, yy)
6142       {
6143         if (Feld[xx][yy] == smashed)
6144           Feld[xx][yy] = activated_magic_wall;
6145       }
6146
6147       game.magic_wall_time_left = level.time_magic_wall * FRAMES_PER_SECOND;
6148       game.magic_wall_active = TRUE;
6149
6150       PlayLevelSound(x, y, (smashed == EL_MAGIC_WALL ?
6151                             SND_MAGIC_WALL_ACTIVATING :
6152                             smashed == EL_BD_MAGIC_WALL ?
6153                             SND_BD_MAGIC_WALL_ACTIVATING :
6154                             SND_DC_MAGIC_WALL_ACTIVATING));
6155     }
6156
6157     if (IS_PLAYER(x, y + 1))
6158     {
6159       if (CAN_SMASH_PLAYER(element))
6160       {
6161         KillPlayerUnlessEnemyProtected(x, y + 1);
6162         return;
6163       }
6164     }
6165     else if (smashed == EL_PENGUIN)
6166     {
6167       if (CAN_SMASH_PLAYER(element))
6168       {
6169         Bang(x, y + 1);
6170         return;
6171       }
6172     }
6173     else if (element == EL_BD_DIAMOND)
6174     {
6175       if (IS_CLASSIC_ENEMY(smashed) && IS_BD_ELEMENT(smashed))
6176       {
6177         Bang(x, y + 1);
6178         return;
6179       }
6180     }
6181     else if (((element == EL_SP_INFOTRON ||
6182                element == EL_SP_ZONK) &&
6183               (smashed == EL_SP_SNIKSNAK ||
6184                smashed == EL_SP_ELECTRON ||
6185                smashed == EL_SP_DISK_ORANGE)) ||
6186              (element == EL_SP_INFOTRON &&
6187               smashed == EL_SP_DISK_YELLOW))
6188     {
6189       Bang(x, y + 1);
6190       return;
6191     }
6192     else if (CAN_SMASH_EVERYTHING(element))
6193     {
6194       if (IS_CLASSIC_ENEMY(smashed) ||
6195           CAN_EXPLODE_SMASHED(smashed))
6196       {
6197         Bang(x, y + 1);
6198         return;
6199       }
6200       else if (!IS_MOVING(x, y + 1) && !IS_BLOCKED(x, y + 1))
6201       {
6202         if (smashed == EL_LAMP ||
6203             smashed == EL_LAMP_ACTIVE)
6204         {
6205           Bang(x, y + 1);
6206           return;
6207         }
6208         else if (smashed == EL_NUT)
6209         {
6210           Feld[x][y + 1] = EL_NUT_BREAKING;
6211           PlayLevelSound(x, y, SND_NUT_BREAKING);
6212           RaiseScoreElement(EL_NUT);
6213           return;
6214         }
6215         else if (smashed == EL_PEARL)
6216         {
6217           ResetGfxAnimation(x, y);
6218
6219           Feld[x][y + 1] = EL_PEARL_BREAKING;
6220           PlayLevelSound(x, y, SND_PEARL_BREAKING);
6221           return;
6222         }
6223         else if (smashed == EL_DIAMOND)
6224         {
6225           Feld[x][y + 1] = EL_DIAMOND_BREAKING;
6226           PlayLevelSound(x, y, SND_DIAMOND_BREAKING);
6227           return;
6228         }
6229         else if (IS_BELT_SWITCH(smashed))
6230         {
6231           ToggleBeltSwitch(x, y + 1);
6232         }
6233         else if (smashed == EL_SWITCHGATE_SWITCH_UP ||
6234                  smashed == EL_SWITCHGATE_SWITCH_DOWN ||
6235                  smashed == EL_DC_SWITCHGATE_SWITCH_UP ||
6236                  smashed == EL_DC_SWITCHGATE_SWITCH_DOWN)
6237         {
6238           ToggleSwitchgateSwitch(x, y + 1);
6239         }
6240         else if (smashed == EL_LIGHT_SWITCH ||
6241                  smashed == EL_LIGHT_SWITCH_ACTIVE)
6242         {
6243           ToggleLightSwitch(x, y + 1);
6244         }
6245         else
6246         {
6247           CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6248
6249           CheckElementChangeBySide(x, y + 1, smashed, element,
6250                                    CE_SWITCHED, CH_SIDE_TOP);
6251           CheckTriggeredElementChangeBySide(x, y + 1, smashed, CE_SWITCH_OF_X,
6252                                             CH_SIDE_TOP);
6253         }
6254       }
6255       else
6256       {
6257         CheckElementChange(x, y + 1, smashed, element, CE_SMASHED);
6258       }
6259     }
6260   }
6261
6262   /* play sound of magic wall / mill */
6263   if (!last_line &&
6264       (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
6265        Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ||
6266        Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE))
6267   {
6268     if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
6269       PlayLevelSound(x, y, SND_MAGIC_WALL_FILLING);
6270     else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
6271       PlayLevelSound(x, y, SND_BD_MAGIC_WALL_FILLING);
6272     else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
6273       PlayLevelSound(x, y, SND_DC_MAGIC_WALL_FILLING);
6274
6275     return;
6276   }
6277
6278   /* play sound of object that hits the ground */
6279   if (last_line || object_hit)
6280     PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
6281 }
6282
6283 inline static void TurnRoundExt(int x, int y)
6284 {
6285   static struct
6286   {
6287     int dx, dy;
6288   } move_xy[] =
6289   {
6290     {  0,  0 },
6291     { -1,  0 },
6292     { +1,  0 },
6293     {  0,  0 },
6294     {  0, -1 },
6295     {  0,  0 }, { 0, 0 }, { 0, 0 },
6296     {  0, +1 }
6297   };
6298   static struct
6299   {
6300     int left, right, back;
6301   } turn[] =
6302   {
6303     { 0,        0,              0        },
6304     { MV_DOWN,  MV_UP,          MV_RIGHT },
6305     { MV_UP,    MV_DOWN,        MV_LEFT  },
6306     { 0,        0,              0        },
6307     { MV_LEFT,  MV_RIGHT,       MV_DOWN  },
6308     { 0,        0,              0        },
6309     { 0,        0,              0        },
6310     { 0,        0,              0        },
6311     { MV_RIGHT, MV_LEFT,        MV_UP    }
6312   };
6313
6314   int element = Feld[x][y];
6315   int move_pattern = element_info[element].move_pattern;
6316
6317   int old_move_dir = MovDir[x][y];
6318   int left_dir  = turn[old_move_dir].left;
6319   int right_dir = turn[old_move_dir].right;
6320   int back_dir  = turn[old_move_dir].back;
6321
6322   int left_dx  = move_xy[left_dir].dx,     left_dy  = move_xy[left_dir].dy;
6323   int right_dx = move_xy[right_dir].dx,    right_dy = move_xy[right_dir].dy;
6324   int move_dx  = move_xy[old_move_dir].dx, move_dy  = move_xy[old_move_dir].dy;
6325   int back_dx  = move_xy[back_dir].dx,     back_dy  = move_xy[back_dir].dy;
6326
6327   int left_x  = x + left_dx,  left_y  = y + left_dy;
6328   int right_x = x + right_dx, right_y = y + right_dy;
6329   int move_x  = x + move_dx,  move_y  = y + move_dy;
6330
6331   int xx, yy;
6332
6333   if (element == EL_BUG || element == EL_BD_BUTTERFLY)
6334   {
6335     TestIfBadThingTouchesOtherBadThing(x, y);
6336
6337     if (ENEMY_CAN_ENTER_FIELD(element, right_x, right_y))
6338       MovDir[x][y] = right_dir;
6339     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6340       MovDir[x][y] = left_dir;
6341
6342     if (element == EL_BUG && MovDir[x][y] != old_move_dir)
6343       MovDelay[x][y] = 9;
6344     else if (element == EL_BD_BUTTERFLY)     /* && MovDir[x][y] == left_dir) */
6345       MovDelay[x][y] = 1;
6346   }
6347   else if (element == EL_SPACESHIP || element == EL_BD_FIREFLY)
6348   {
6349     TestIfBadThingTouchesOtherBadThing(x, y);
6350
6351     if (ENEMY_CAN_ENTER_FIELD(element, left_x, left_y))
6352       MovDir[x][y] = left_dir;
6353     else if (!ENEMY_CAN_ENTER_FIELD(element, move_x, move_y))
6354       MovDir[x][y] = right_dir;
6355
6356     if (element == EL_SPACESHIP && MovDir[x][y] != old_move_dir)
6357       MovDelay[x][y] = 9;
6358     else if (element == EL_BD_FIREFLY)      /* && MovDir[x][y] == right_dir) */
6359       MovDelay[x][y] = 1;
6360   }
6361   else if (element == EL_SP_SNIKSNAK || element == EL_SP_ELECTRON)
6362   {
6363     TestIfBadThingTouchesOtherBadThing(x, y);
6364
6365     if (ELEMENT_CAN_ENTER_FIELD_BASE_4(element, left_x, left_y, 0))
6366       MovDir[x][y] = left_dir;
6367     else if (!ELEMENT_CAN_ENTER_FIELD_BASE_4(element, move_x, move_y, 0))
6368       MovDir[x][y] = right_dir;
6369
6370     if (MovDir[x][y] != old_move_dir)
6371       MovDelay[x][y] = 9;
6372   }
6373   else if (element == EL_YAMYAM)
6374   {
6375     boolean can_turn_left  = YAMYAM_CAN_ENTER_FIELD(element, left_x, left_y);
6376     boolean can_turn_right = YAMYAM_CAN_ENTER_FIELD(element, right_x, right_y);
6377
6378     if (can_turn_left && can_turn_right)
6379       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6380     else if (can_turn_left)
6381       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6382     else if (can_turn_right)
6383       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6384     else
6385       MovDir[x][y] = back_dir;
6386
6387     MovDelay[x][y] = 16 + 16 * RND(3);
6388   }
6389   else if (element == EL_DARK_YAMYAM)
6390   {
6391     boolean can_turn_left  = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6392                                                          left_x, left_y);
6393     boolean can_turn_right = DARK_YAMYAM_CAN_ENTER_FIELD(element,
6394                                                          right_x, right_y);
6395
6396     if (can_turn_left && can_turn_right)
6397       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6398     else if (can_turn_left)
6399       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6400     else if (can_turn_right)
6401       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6402     else
6403       MovDir[x][y] = back_dir;
6404
6405     MovDelay[x][y] = 16 + 16 * RND(3);
6406   }
6407   else if (element == EL_PACMAN)
6408   {
6409     boolean can_turn_left  = PACMAN_CAN_ENTER_FIELD(element, left_x, left_y);
6410     boolean can_turn_right = PACMAN_CAN_ENTER_FIELD(element, right_x, right_y);
6411
6412     if (can_turn_left && can_turn_right)
6413       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6414     else if (can_turn_left)
6415       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6416     else if (can_turn_right)
6417       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6418     else
6419       MovDir[x][y] = back_dir;
6420
6421     MovDelay[x][y] = 6 + RND(40);
6422   }
6423   else if (element == EL_PIG)
6424   {
6425     boolean can_turn_left  = PIG_CAN_ENTER_FIELD(element, left_x, left_y);
6426     boolean can_turn_right = PIG_CAN_ENTER_FIELD(element, right_x, right_y);
6427     boolean can_move_on    = PIG_CAN_ENTER_FIELD(element, move_x, move_y);
6428     boolean should_turn_left, should_turn_right, should_move_on;
6429     int rnd_value = 24;
6430     int rnd = RND(rnd_value);
6431
6432     should_turn_left = (can_turn_left &&
6433                         (!can_move_on ||
6434                          IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + left_dx,
6435                                                    y + back_dy + left_dy)));
6436     should_turn_right = (can_turn_right &&
6437                          (!can_move_on ||
6438                           IN_LEV_FIELD_AND_NOT_FREE(x + back_dx + right_dx,
6439                                                     y + back_dy + right_dy)));
6440     should_move_on = (can_move_on &&
6441                       (!can_turn_left ||
6442                        !can_turn_right ||
6443                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + left_dx,
6444                                                  y + move_dy + left_dy) ||
6445                        IN_LEV_FIELD_AND_NOT_FREE(x + move_dx + right_dx,
6446                                                  y + move_dy + right_dy)));
6447
6448     if (should_turn_left || should_turn_right || should_move_on)
6449     {
6450       if (should_turn_left && should_turn_right && should_move_on)
6451         MovDir[x][y] = (rnd < rnd_value / 3     ? left_dir :
6452                         rnd < 2 * rnd_value / 3 ? right_dir :
6453                         old_move_dir);
6454       else if (should_turn_left && should_turn_right)
6455         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6456       else if (should_turn_left && should_move_on)
6457         MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : old_move_dir);
6458       else if (should_turn_right && should_move_on)
6459         MovDir[x][y] = (rnd < rnd_value / 2 ? right_dir : old_move_dir);
6460       else if (should_turn_left)
6461         MovDir[x][y] = left_dir;
6462       else if (should_turn_right)
6463         MovDir[x][y] = right_dir;
6464       else if (should_move_on)
6465         MovDir[x][y] = old_move_dir;
6466     }
6467     else if (can_move_on && rnd > rnd_value / 8)
6468       MovDir[x][y] = old_move_dir;
6469     else if (can_turn_left && can_turn_right)
6470       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6471     else if (can_turn_left && rnd > rnd_value / 8)
6472       MovDir[x][y] = left_dir;
6473     else if (can_turn_right && rnd > rnd_value/8)
6474       MovDir[x][y] = right_dir;
6475     else
6476       MovDir[x][y] = back_dir;
6477
6478     xx = x + move_xy[MovDir[x][y]].dx;
6479     yy = y + move_xy[MovDir[x][y]].dy;
6480
6481     if (!IN_LEV_FIELD(xx, yy) ||
6482         (!IS_FREE(xx, yy) && !IS_FOOD_PIG(Feld[xx][yy])))
6483       MovDir[x][y] = old_move_dir;
6484
6485     MovDelay[x][y] = 0;
6486   }
6487   else if (element == EL_DRAGON)
6488   {
6489     boolean can_turn_left  = DRAGON_CAN_ENTER_FIELD(element, left_x, left_y);
6490     boolean can_turn_right = DRAGON_CAN_ENTER_FIELD(element, right_x, right_y);
6491     boolean can_move_on    = DRAGON_CAN_ENTER_FIELD(element, move_x, move_y);
6492     int rnd_value = 24;
6493     int rnd = RND(rnd_value);
6494
6495     if (can_move_on && rnd > rnd_value / 8)
6496       MovDir[x][y] = old_move_dir;
6497     else if (can_turn_left && can_turn_right)
6498       MovDir[x][y] = (rnd < rnd_value / 2 ? left_dir : right_dir);
6499     else if (can_turn_left && rnd > rnd_value / 8)
6500       MovDir[x][y] = left_dir;
6501     else if (can_turn_right && rnd > rnd_value / 8)
6502       MovDir[x][y] = right_dir;
6503     else
6504       MovDir[x][y] = back_dir;
6505
6506     xx = x + move_xy[MovDir[x][y]].dx;
6507     yy = y + move_xy[MovDir[x][y]].dy;
6508
6509     if (!IN_LEV_FIELD_AND_IS_FREE(xx, yy))
6510       MovDir[x][y] = old_move_dir;
6511
6512     MovDelay[x][y] = 0;
6513   }
6514   else if (element == EL_MOLE)
6515   {
6516     boolean can_move_on =
6517       (MOLE_CAN_ENTER_FIELD(element, move_x, move_y,
6518                             IS_AMOEBOID(Feld[move_x][move_y]) ||
6519                             Feld[move_x][move_y] == EL_AMOEBA_SHRINKING));
6520     if (!can_move_on)
6521     {
6522       boolean can_turn_left =
6523         (MOLE_CAN_ENTER_FIELD(element, left_x, left_y,
6524                               IS_AMOEBOID(Feld[left_x][left_y])));
6525
6526       boolean can_turn_right =
6527         (MOLE_CAN_ENTER_FIELD(element, right_x, right_y,
6528                               IS_AMOEBOID(Feld[right_x][right_y])));
6529
6530       if (can_turn_left && can_turn_right)
6531         MovDir[x][y] = (RND(2) ? left_dir : right_dir);
6532       else if (can_turn_left)
6533         MovDir[x][y] = left_dir;
6534       else
6535         MovDir[x][y] = right_dir;
6536     }
6537
6538     if (MovDir[x][y] != old_move_dir)
6539       MovDelay[x][y] = 9;
6540   }
6541   else if (element == EL_BALLOON)
6542   {
6543     MovDir[x][y] = game.wind_direction;
6544     MovDelay[x][y] = 0;
6545   }
6546   else if (element == EL_SPRING)
6547   {
6548     if (MovDir[x][y] & MV_HORIZONTAL)
6549     {
6550       if (SPRING_CAN_BUMP_FROM_FIELD(move_x, move_y) &&
6551           !SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6552       {
6553         Feld[move_x][move_y] = EL_EMC_SPRING_BUMPER_ACTIVE;
6554         ResetGfxAnimation(move_x, move_y);
6555         TEST_DrawLevelField(move_x, move_y);
6556
6557         MovDir[x][y] = back_dir;
6558       }
6559       else if (!SPRING_CAN_ENTER_FIELD(element, move_x, move_y) ||
6560                SPRING_CAN_ENTER_FIELD(element, x, y + 1))
6561         MovDir[x][y] = MV_NONE;
6562     }
6563
6564     MovDelay[x][y] = 0;
6565   }
6566   else if (element == EL_ROBOT ||
6567            element == EL_SATELLITE ||
6568            element == EL_PENGUIN ||
6569            element == EL_EMC_ANDROID)
6570   {
6571     int attr_x = -1, attr_y = -1;
6572
6573     if (AllPlayersGone)
6574     {
6575       attr_x = ExitX;
6576       attr_y = ExitY;
6577     }
6578     else
6579     {
6580       int i;
6581
6582       for (i = 0; i < MAX_PLAYERS; i++)
6583       {
6584         struct PlayerInfo *player = &stored_player[i];
6585         int jx = player->jx, jy = player->jy;
6586
6587         if (!player->active)
6588           continue;
6589
6590         if (attr_x == -1 ||
6591             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6592         {
6593           attr_x = jx;
6594           attr_y = jy;
6595         }
6596       }
6597     }
6598
6599     if (element == EL_ROBOT && ZX >= 0 && ZY >= 0 &&
6600         (Feld[ZX][ZY] == EL_ROBOT_WHEEL_ACTIVE ||
6601          game.engine_version < VERSION_IDENT(3,1,0,0)))
6602     {
6603       attr_x = ZX;
6604       attr_y = ZY;
6605     }
6606
6607     if (element == EL_PENGUIN)
6608     {
6609       int i;
6610       static int xy[4][2] =
6611       {
6612         { 0, -1 },
6613         { -1, 0 },
6614         { +1, 0 },
6615         { 0, +1 }
6616       };
6617
6618       for (i = 0; i < NUM_DIRECTIONS; i++)
6619       {
6620         int ex = x + xy[i][0];
6621         int ey = y + xy[i][1];
6622
6623         if (IN_LEV_FIELD(ex, ey) && (Feld[ex][ey] == EL_EXIT_OPEN ||
6624                                      Feld[ex][ey] == EL_EM_EXIT_OPEN ||
6625                                      Feld[ex][ey] == EL_STEEL_EXIT_OPEN ||
6626                                      Feld[ex][ey] == EL_EM_STEEL_EXIT_OPEN))
6627         {
6628           attr_x = ex;
6629           attr_y = ey;
6630           break;
6631         }
6632       }
6633     }
6634
6635     MovDir[x][y] = MV_NONE;
6636     if (attr_x < x)
6637       MovDir[x][y] |= (AllPlayersGone ? MV_RIGHT : MV_LEFT);
6638     else if (attr_x > x)
6639       MovDir[x][y] |= (AllPlayersGone ? MV_LEFT : MV_RIGHT);
6640     if (attr_y < y)
6641       MovDir[x][y] |= (AllPlayersGone ? MV_DOWN : MV_UP);
6642     else if (attr_y > y)
6643       MovDir[x][y] |= (AllPlayersGone ? MV_UP : MV_DOWN);
6644
6645     if (element == EL_ROBOT)
6646     {
6647       int newx, newy;
6648
6649       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6650         MovDir[x][y] &= (RND(2) ? MV_HORIZONTAL : MV_VERTICAL);
6651       Moving2Blocked(x, y, &newx, &newy);
6652
6653       if (IN_LEV_FIELD(newx, newy) && IS_FREE_OR_PLAYER(newx, newy))
6654         MovDelay[x][y] = 8 + 8 * !RND(3);
6655       else
6656         MovDelay[x][y] = 16;
6657     }
6658     else if (element == EL_PENGUIN)
6659     {
6660       int newx, newy;
6661
6662       MovDelay[x][y] = 1;
6663
6664       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6665       {
6666         boolean first_horiz = RND(2);
6667         int new_move_dir = MovDir[x][y];
6668
6669         MovDir[x][y] =
6670           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6671         Moving2Blocked(x, y, &newx, &newy);
6672
6673         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6674           return;
6675
6676         MovDir[x][y] =
6677           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6678         Moving2Blocked(x, y, &newx, &newy);
6679
6680         if (PENGUIN_CAN_ENTER_FIELD(element, newx, newy))
6681           return;
6682
6683         MovDir[x][y] = old_move_dir;
6684         return;
6685       }
6686     }
6687     else if (element == EL_SATELLITE)
6688     {
6689       int newx, newy;
6690
6691       MovDelay[x][y] = 1;
6692
6693       if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6694       {
6695         boolean first_horiz = RND(2);
6696         int new_move_dir = MovDir[x][y];
6697
6698         MovDir[x][y] =
6699           new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6700         Moving2Blocked(x, y, &newx, &newy);
6701
6702         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6703           return;
6704
6705         MovDir[x][y] =
6706           new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6707         Moving2Blocked(x, y, &newx, &newy);
6708
6709         if (SATELLITE_CAN_ENTER_FIELD(newx, newy))
6710           return;
6711
6712         MovDir[x][y] = old_move_dir;
6713         return;
6714       }
6715     }
6716     else if (element == EL_EMC_ANDROID)
6717     {
6718       static int check_pos[16] =
6719       {
6720         -1,             /*  0 => (invalid)          */
6721         7,              /*  1 => MV_LEFT            */
6722         3,              /*  2 => MV_RIGHT           */
6723         -1,             /*  3 => (invalid)          */
6724         1,              /*  4 =>            MV_UP   */
6725         0,              /*  5 => MV_LEFT  | MV_UP   */
6726         2,              /*  6 => MV_RIGHT | MV_UP   */
6727         -1,             /*  7 => (invalid)          */
6728         5,              /*  8 =>            MV_DOWN */
6729         6,              /*  9 => MV_LEFT  | MV_DOWN */
6730         4,              /* 10 => MV_RIGHT | MV_DOWN */
6731         -1,             /* 11 => (invalid)          */
6732         -1,             /* 12 => (invalid)          */
6733         -1,             /* 13 => (invalid)          */
6734         -1,             /* 14 => (invalid)          */
6735         -1,             /* 15 => (invalid)          */
6736       };
6737       static struct
6738       {
6739         int dx, dy;
6740         int dir;
6741       } check_xy[8] =
6742       {
6743         { -1, -1,       MV_LEFT  | MV_UP   },
6744         {  0, -1,                  MV_UP   },
6745         { +1, -1,       MV_RIGHT | MV_UP   },
6746         { +1,  0,       MV_RIGHT           },
6747         { +1, +1,       MV_RIGHT | MV_DOWN },
6748         {  0, +1,                  MV_DOWN },
6749         { -1, +1,       MV_LEFT  | MV_DOWN },
6750         { -1,  0,       MV_LEFT            },
6751       };
6752       int start_pos, check_order;
6753       boolean can_clone = FALSE;
6754       int i;
6755
6756       /* check if there is any free field around current position */
6757       for (i = 0; i < 8; i++)
6758       {
6759         int newx = x + check_xy[i].dx;
6760         int newy = y + check_xy[i].dy;
6761
6762         if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6763         {
6764           can_clone = TRUE;
6765
6766           break;
6767         }
6768       }
6769
6770       if (can_clone)            /* randomly find an element to clone */
6771       {
6772         can_clone = FALSE;
6773
6774         start_pos = check_pos[RND(8)];
6775         check_order = (RND(2) ? -1 : +1);
6776
6777         for (i = 0; i < 8; i++)
6778         {
6779           int pos_raw = start_pos + i * check_order;
6780           int pos = (pos_raw + 8) % 8;
6781           int newx = x + check_xy[pos].dx;
6782           int newy = y + check_xy[pos].dy;
6783
6784           if (ANDROID_CAN_CLONE_FIELD(newx, newy))
6785           {
6786             element_info[element].move_leave_type = LEAVE_TYPE_LIMITED;
6787             element_info[element].move_leave_element = EL_TRIGGER_ELEMENT;
6788
6789             Store[x][y] = Feld[newx][newy];
6790
6791             can_clone = TRUE;
6792
6793             break;
6794           }
6795         }
6796       }
6797
6798       if (can_clone)            /* randomly find a direction to move */
6799       {
6800         can_clone = FALSE;
6801
6802         start_pos = check_pos[RND(8)];
6803         check_order = (RND(2) ? -1 : +1);
6804
6805         for (i = 0; i < 8; i++)
6806         {
6807           int pos_raw = start_pos + i * check_order;
6808           int pos = (pos_raw + 8) % 8;
6809           int newx = x + check_xy[pos].dx;
6810           int newy = y + check_xy[pos].dy;
6811           int new_move_dir = check_xy[pos].dir;
6812
6813           if (IN_LEV_FIELD_AND_IS_FREE(newx, newy))
6814           {
6815             MovDir[x][y] = new_move_dir;
6816             MovDelay[x][y] = level.android_clone_time * 8 + 1;
6817
6818             can_clone = TRUE;
6819
6820             break;
6821           }
6822         }
6823       }
6824
6825       if (can_clone)            /* cloning and moving successful */
6826         return;
6827
6828       /* cannot clone -- try to move towards player */
6829
6830       start_pos = check_pos[MovDir[x][y] & 0x0f];
6831       check_order = (RND(2) ? -1 : +1);
6832
6833       for (i = 0; i < 3; i++)
6834       {
6835         /* first check start_pos, then previous/next or (next/previous) pos */
6836         int pos_raw = start_pos + (i < 2 ? i : -1) * check_order;
6837         int pos = (pos_raw + 8) % 8;
6838         int newx = x + check_xy[pos].dx;
6839         int newy = y + check_xy[pos].dy;
6840         int new_move_dir = check_xy[pos].dir;
6841
6842         if (IS_PLAYER(newx, newy))
6843           break;
6844
6845         if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
6846         {
6847           MovDir[x][y] = new_move_dir;
6848           MovDelay[x][y] = level.android_move_time * 8 + 1;
6849
6850           break;
6851         }
6852       }
6853     }
6854   }
6855   else if (move_pattern == MV_TURNING_LEFT ||
6856            move_pattern == MV_TURNING_RIGHT ||
6857            move_pattern == MV_TURNING_LEFT_RIGHT ||
6858            move_pattern == MV_TURNING_RIGHT_LEFT ||
6859            move_pattern == MV_TURNING_RANDOM ||
6860            move_pattern == MV_ALL_DIRECTIONS)
6861   {
6862     boolean can_turn_left =
6863       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y);
6864     boolean can_turn_right =
6865       CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x,right_y);
6866
6867     if (element_info[element].move_stepsize == 0)       /* "not moving" */
6868       return;
6869
6870     if (move_pattern == MV_TURNING_LEFT)
6871       MovDir[x][y] = left_dir;
6872     else if (move_pattern == MV_TURNING_RIGHT)
6873       MovDir[x][y] = right_dir;
6874     else if (move_pattern == MV_TURNING_LEFT_RIGHT)
6875       MovDir[x][y] = (can_turn_left || !can_turn_right ? left_dir : right_dir);
6876     else if (move_pattern == MV_TURNING_RIGHT_LEFT)
6877       MovDir[x][y] = (can_turn_right || !can_turn_left ? right_dir : left_dir);
6878     else if (move_pattern == MV_TURNING_RANDOM)
6879       MovDir[x][y] = (can_turn_left && !can_turn_right ? left_dir :
6880                       can_turn_right && !can_turn_left ? right_dir :
6881                       RND(2) ? left_dir : right_dir);
6882     else if (can_turn_left && can_turn_right)
6883       MovDir[x][y] = (RND(3) ? (RND(2) ? left_dir : right_dir) : back_dir);
6884     else if (can_turn_left)
6885       MovDir[x][y] = (RND(2) ? left_dir : back_dir);
6886     else if (can_turn_right)
6887       MovDir[x][y] = (RND(2) ? right_dir : back_dir);
6888     else
6889       MovDir[x][y] = back_dir;
6890
6891     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6892   }
6893   else if (move_pattern == MV_HORIZONTAL ||
6894            move_pattern == MV_VERTICAL)
6895   {
6896     if (move_pattern & old_move_dir)
6897       MovDir[x][y] = back_dir;
6898     else if (move_pattern == MV_HORIZONTAL)
6899       MovDir[x][y] = (RND(2) ? MV_LEFT : MV_RIGHT);
6900     else if (move_pattern == MV_VERTICAL)
6901       MovDir[x][y] = (RND(2) ? MV_UP : MV_DOWN);
6902
6903     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6904   }
6905   else if (move_pattern & MV_ANY_DIRECTION)
6906   {
6907     MovDir[x][y] = move_pattern;
6908     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6909   }
6910   else if (move_pattern & MV_WIND_DIRECTION)
6911   {
6912     MovDir[x][y] = game.wind_direction;
6913     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6914   }
6915   else if (move_pattern == MV_ALONG_LEFT_SIDE)
6916   {
6917     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, left_x, left_y))
6918       MovDir[x][y] = left_dir;
6919     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6920       MovDir[x][y] = right_dir;
6921
6922     if (MovDir[x][y] != old_move_dir)
6923       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6924   }
6925   else if (move_pattern == MV_ALONG_RIGHT_SIDE)
6926   {
6927     if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, right_x, right_y))
6928       MovDir[x][y] = right_dir;
6929     else if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
6930       MovDir[x][y] = left_dir;
6931
6932     if (MovDir[x][y] != old_move_dir)
6933       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6934   }
6935   else if (move_pattern == MV_TOWARDS_PLAYER ||
6936            move_pattern == MV_AWAY_FROM_PLAYER)
6937   {
6938     int attr_x = -1, attr_y = -1;
6939     int newx, newy;
6940     boolean move_away = (move_pattern == MV_AWAY_FROM_PLAYER);
6941
6942     if (AllPlayersGone)
6943     {
6944       attr_x = ExitX;
6945       attr_y = ExitY;
6946     }
6947     else
6948     {
6949       int i;
6950
6951       for (i = 0; i < MAX_PLAYERS; i++)
6952       {
6953         struct PlayerInfo *player = &stored_player[i];
6954         int jx = player->jx, jy = player->jy;
6955
6956         if (!player->active)
6957           continue;
6958
6959         if (attr_x == -1 ||
6960             ABS(jx - x) + ABS(jy - y) < ABS(attr_x - x) + ABS(attr_y - y))
6961         {
6962           attr_x = jx;
6963           attr_y = jy;
6964         }
6965       }
6966     }
6967
6968     MovDir[x][y] = MV_NONE;
6969     if (attr_x < x)
6970       MovDir[x][y] |= (move_away ? MV_RIGHT : MV_LEFT);
6971     else if (attr_x > x)
6972       MovDir[x][y] |= (move_away ? MV_LEFT : MV_RIGHT);
6973     if (attr_y < y)
6974       MovDir[x][y] |= (move_away ? MV_DOWN : MV_UP);
6975     else if (attr_y > y)
6976       MovDir[x][y] |= (move_away ? MV_UP : MV_DOWN);
6977
6978     MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
6979
6980     if (MovDir[x][y] & MV_HORIZONTAL && MovDir[x][y] & MV_VERTICAL)
6981     {
6982       boolean first_horiz = RND(2);
6983       int new_move_dir = MovDir[x][y];
6984
6985       if (element_info[element].move_stepsize == 0)     /* "not moving" */
6986       {
6987         first_horiz = (ABS(attr_x - x) >= ABS(attr_y - y));
6988         MovDir[x][y] &= (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6989
6990         return;
6991       }
6992
6993       MovDir[x][y] =
6994         new_move_dir & (first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
6995       Moving2Blocked(x, y, &newx, &newy);
6996
6997       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
6998         return;
6999
7000       MovDir[x][y] =
7001         new_move_dir & (!first_horiz ? MV_HORIZONTAL : MV_VERTICAL);
7002       Moving2Blocked(x, y, &newx, &newy);
7003
7004       if (CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7005         return;
7006
7007       MovDir[x][y] = old_move_dir;
7008     }
7009   }
7010   else if (move_pattern == MV_WHEN_PUSHED ||
7011            move_pattern == MV_WHEN_DROPPED)
7012   {
7013     if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, move_x, move_y))
7014       MovDir[x][y] = MV_NONE;
7015
7016     MovDelay[x][y] = 0;
7017   }
7018   else if (move_pattern & MV_MAZE_RUNNER_STYLE)
7019   {
7020     static int test_xy[7][2] =
7021     {
7022       { 0, -1 },
7023       { -1, 0 },
7024       { +1, 0 },
7025       { 0, +1 },
7026       { 0, -1 },
7027       { -1, 0 },
7028       { +1, 0 },
7029     };
7030     static int test_dir[7] =
7031     {
7032       MV_UP,
7033       MV_LEFT,
7034       MV_RIGHT,
7035       MV_DOWN,
7036       MV_UP,
7037       MV_LEFT,
7038       MV_RIGHT,
7039     };
7040     boolean hunter_mode = (move_pattern == MV_MAZE_HUNTER);
7041     int move_preference = -1000000;     /* start with very low preference */
7042     int new_move_dir = MV_NONE;
7043     int start_test = RND(4);
7044     int i;
7045
7046     for (i = 0; i < NUM_DIRECTIONS; i++)
7047     {
7048       int move_dir = test_dir[start_test + i];
7049       int move_dir_preference;
7050
7051       xx = x + test_xy[start_test + i][0];
7052       yy = y + test_xy[start_test + i][1];
7053
7054       if (hunter_mode && IN_LEV_FIELD(xx, yy) &&
7055           (IS_PLAYER(xx, yy) || Feld[xx][yy] == EL_PLAYER_IS_LEAVING))
7056       {
7057         new_move_dir = move_dir;
7058
7059         break;
7060       }
7061
7062       if (!CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, xx, yy))
7063         continue;
7064
7065       move_dir_preference = -1 * RunnerVisit[xx][yy];
7066       if (hunter_mode && PlayerVisit[xx][yy] > 0)
7067         move_dir_preference = PlayerVisit[xx][yy];
7068
7069       if (move_dir_preference > move_preference)
7070       {
7071         /* prefer field that has not been visited for the longest time */
7072         move_preference = move_dir_preference;
7073         new_move_dir = move_dir;
7074       }
7075       else if (move_dir_preference == move_preference &&
7076                move_dir == old_move_dir)
7077       {
7078         /* prefer last direction when all directions are preferred equally */
7079         move_preference = move_dir_preference;
7080         new_move_dir = move_dir;
7081       }
7082     }
7083
7084     MovDir[x][y] = new_move_dir;
7085     if (old_move_dir != new_move_dir)
7086       MovDelay[x][y] = GET_NEW_MOVE_DELAY(element);
7087   }
7088 }
7089
7090 static void TurnRound(int x, int y)
7091 {
7092   int direction = MovDir[x][y];
7093
7094   TurnRoundExt(x, y);
7095
7096   GfxDir[x][y] = MovDir[x][y];
7097
7098   if (direction != MovDir[x][y])
7099     GfxFrame[x][y] = 0;
7100
7101   if (MovDelay[x][y])
7102     GfxAction[x][y] = ACTION_TURNING_FROM_LEFT + MV_DIR_TO_BIT(direction);
7103
7104   ResetGfxFrame(x, y, FALSE);
7105 }
7106
7107 static boolean JustBeingPushed(int x, int y)
7108 {
7109   int i;
7110
7111   for (i = 0; i < MAX_PLAYERS; i++)
7112   {
7113     struct PlayerInfo *player = &stored_player[i];
7114
7115     if (player->active && player->is_pushing && player->MovPos)
7116     {
7117       int next_jx = player->jx + (player->jx - player->last_jx);
7118       int next_jy = player->jy + (player->jy - player->last_jy);
7119
7120       if (x == next_jx && y == next_jy)
7121         return TRUE;
7122     }
7123   }
7124
7125   return FALSE;
7126 }
7127
7128 void StartMoving(int x, int y)
7129 {
7130   boolean started_moving = FALSE;       /* some elements can fall _and_ move */
7131   int element = Feld[x][y];
7132
7133   if (Stop[x][y])
7134     return;
7135
7136   if (MovDelay[x][y] == 0)
7137     GfxAction[x][y] = ACTION_DEFAULT;
7138
7139   if (CAN_FALL(element) && y < lev_fieldy - 1)
7140   {
7141     if ((x > 0              && IS_PLAYER(x - 1, y)) ||
7142         (x < lev_fieldx - 1 && IS_PLAYER(x + 1, y)))
7143       if (JustBeingPushed(x, y))
7144         return;
7145
7146     if (element == EL_QUICKSAND_FULL)
7147     {
7148       if (IS_FREE(x, y + 1))
7149       {
7150         InitMovingField(x, y, MV_DOWN);
7151         started_moving = TRUE;
7152
7153         Feld[x][y] = EL_QUICKSAND_EMPTYING;
7154 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7155         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7156           Store[x][y] = EL_ROCK;
7157 #else
7158         Store[x][y] = EL_ROCK;
7159 #endif
7160
7161         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7162       }
7163       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7164       {
7165         if (!MovDelay[x][y])
7166         {
7167           MovDelay[x][y] = TILEY + 1;
7168
7169           ResetGfxAnimation(x, y);
7170           ResetGfxAnimation(x, y + 1);
7171         }
7172
7173         if (MovDelay[x][y])
7174         {
7175           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7176           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7177
7178           MovDelay[x][y]--;
7179           if (MovDelay[x][y])
7180             return;
7181         }
7182
7183         Feld[x][y] = EL_QUICKSAND_EMPTY;
7184         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7185         Store[x][y + 1] = Store[x][y];
7186         Store[x][y] = 0;
7187
7188         PlayLevelSoundAction(x, y, ACTION_FILLING);
7189       }
7190       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7191       {
7192         if (!MovDelay[x][y])
7193         {
7194           MovDelay[x][y] = TILEY + 1;
7195
7196           ResetGfxAnimation(x, y);
7197           ResetGfxAnimation(x, y + 1);
7198         }
7199
7200         if (MovDelay[x][y])
7201         {
7202           DrawLevelElement(x, y, EL_QUICKSAND_EMPTYING);
7203           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7204
7205           MovDelay[x][y]--;
7206           if (MovDelay[x][y])
7207             return;
7208         }
7209
7210         Feld[x][y] = EL_QUICKSAND_EMPTY;
7211         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7212         Store[x][y + 1] = Store[x][y];
7213         Store[x][y] = 0;
7214
7215         PlayLevelSoundAction(x, y, ACTION_FILLING);
7216       }
7217     }
7218     else if (element == EL_QUICKSAND_FAST_FULL)
7219     {
7220       if (IS_FREE(x, y + 1))
7221       {
7222         InitMovingField(x, y, MV_DOWN);
7223         started_moving = TRUE;
7224
7225         Feld[x][y] = EL_QUICKSAND_FAST_EMPTYING;
7226 #if USE_QUICKSAND_BD_ROCK_BUGFIX
7227         if (Store[x][y] != EL_ROCK && Store[x][y] != EL_BD_ROCK)
7228           Store[x][y] = EL_ROCK;
7229 #else
7230         Store[x][y] = EL_ROCK;
7231 #endif
7232
7233         PlayLevelSoundAction(x, y, ACTION_EMPTYING);
7234       }
7235       else if (Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7236       {
7237         if (!MovDelay[x][y])
7238         {
7239           MovDelay[x][y] = TILEY + 1;
7240
7241           ResetGfxAnimation(x, y);
7242           ResetGfxAnimation(x, y + 1);
7243         }
7244
7245         if (MovDelay[x][y])
7246         {
7247           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7248           DrawLevelElement(x, y + 1, EL_QUICKSAND_FAST_FILLING);
7249
7250           MovDelay[x][y]--;
7251           if (MovDelay[x][y])
7252             return;
7253         }
7254
7255         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7256         Feld[x][y + 1] = EL_QUICKSAND_FAST_FULL;
7257         Store[x][y + 1] = Store[x][y];
7258         Store[x][y] = 0;
7259
7260         PlayLevelSoundAction(x, y, ACTION_FILLING);
7261       }
7262       else if (Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7263       {
7264         if (!MovDelay[x][y])
7265         {
7266           MovDelay[x][y] = TILEY + 1;
7267
7268           ResetGfxAnimation(x, y);
7269           ResetGfxAnimation(x, y + 1);
7270         }
7271
7272         if (MovDelay[x][y])
7273         {
7274           DrawLevelElement(x, y, EL_QUICKSAND_FAST_EMPTYING);
7275           DrawLevelElement(x, y + 1, EL_QUICKSAND_FILLING);
7276
7277           MovDelay[x][y]--;
7278           if (MovDelay[x][y])
7279             return;
7280         }
7281
7282         Feld[x][y] = EL_QUICKSAND_FAST_EMPTY;
7283         Feld[x][y + 1] = EL_QUICKSAND_FULL;
7284         Store[x][y + 1] = Store[x][y];
7285         Store[x][y] = 0;
7286
7287         PlayLevelSoundAction(x, y, ACTION_FILLING);
7288       }
7289     }
7290     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7291              Feld[x][y + 1] == EL_QUICKSAND_EMPTY)
7292     {
7293       InitMovingField(x, y, MV_DOWN);
7294       started_moving = TRUE;
7295
7296       Feld[x][y] = EL_QUICKSAND_FILLING;
7297       Store[x][y] = element;
7298
7299       PlayLevelSoundAction(x, y, ACTION_FILLING);
7300     }
7301     else if ((element == EL_ROCK || element == EL_BD_ROCK) &&
7302              Feld[x][y + 1] == EL_QUICKSAND_FAST_EMPTY)
7303     {
7304       InitMovingField(x, y, MV_DOWN);
7305       started_moving = TRUE;
7306
7307       Feld[x][y] = EL_QUICKSAND_FAST_FILLING;
7308       Store[x][y] = element;
7309
7310       PlayLevelSoundAction(x, y, ACTION_FILLING);
7311     }
7312     else if (element == EL_MAGIC_WALL_FULL)
7313     {
7314       if (IS_FREE(x, y + 1))
7315       {
7316         InitMovingField(x, y, MV_DOWN);
7317         started_moving = TRUE;
7318
7319         Feld[x][y] = EL_MAGIC_WALL_EMPTYING;
7320         Store[x][y] = EL_CHANGED(Store[x][y]);
7321       }
7322       else if (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE)
7323       {
7324         if (!MovDelay[x][y])
7325           MovDelay[x][y] = TILEY / 4 + 1;
7326
7327         if (MovDelay[x][y])
7328         {
7329           MovDelay[x][y]--;
7330           if (MovDelay[x][y])
7331             return;
7332         }
7333
7334         Feld[x][y] = EL_MAGIC_WALL_ACTIVE;
7335         Feld[x][y + 1] = EL_MAGIC_WALL_FULL;
7336         Store[x][y + 1] = EL_CHANGED(Store[x][y]);
7337         Store[x][y] = 0;
7338       }
7339     }
7340     else if (element == EL_BD_MAGIC_WALL_FULL)
7341     {
7342       if (IS_FREE(x, y + 1))
7343       {
7344         InitMovingField(x, y, MV_DOWN);
7345         started_moving = TRUE;
7346
7347         Feld[x][y] = EL_BD_MAGIC_WALL_EMPTYING;
7348         Store[x][y] = EL_CHANGED_BD(Store[x][y]);
7349       }
7350       else if (Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)
7351       {
7352         if (!MovDelay[x][y])
7353           MovDelay[x][y] = TILEY / 4 + 1;
7354
7355         if (MovDelay[x][y])
7356         {
7357           MovDelay[x][y]--;
7358           if (MovDelay[x][y])
7359             return;
7360         }
7361
7362         Feld[x][y] = EL_BD_MAGIC_WALL_ACTIVE;
7363         Feld[x][y + 1] = EL_BD_MAGIC_WALL_FULL;
7364         Store[x][y + 1] = EL_CHANGED_BD(Store[x][y]);
7365         Store[x][y] = 0;
7366       }
7367     }
7368     else if (element == EL_DC_MAGIC_WALL_FULL)
7369     {
7370       if (IS_FREE(x, y + 1))
7371       {
7372         InitMovingField(x, y, MV_DOWN);
7373         started_moving = TRUE;
7374
7375         Feld[x][y] = EL_DC_MAGIC_WALL_EMPTYING;
7376         Store[x][y] = EL_CHANGED_DC(Store[x][y]);
7377       }
7378       else if (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)
7379       {
7380         if (!MovDelay[x][y])
7381           MovDelay[x][y] = TILEY / 4 + 1;
7382
7383         if (MovDelay[x][y])
7384         {
7385           MovDelay[x][y]--;
7386           if (MovDelay[x][y])
7387             return;
7388         }
7389
7390         Feld[x][y] = EL_DC_MAGIC_WALL_ACTIVE;
7391         Feld[x][y + 1] = EL_DC_MAGIC_WALL_FULL;
7392         Store[x][y + 1] = EL_CHANGED_DC(Store[x][y]);
7393         Store[x][y] = 0;
7394       }
7395     }
7396     else if ((CAN_PASS_MAGIC_WALL(element) &&
7397               (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ||
7398                Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE)) ||
7399              (CAN_PASS_DC_MAGIC_WALL(element) &&
7400               (Feld[x][y + 1] == EL_DC_MAGIC_WALL_ACTIVE)))
7401
7402     {
7403       InitMovingField(x, y, MV_DOWN);
7404       started_moving = TRUE;
7405
7406       Feld[x][y] =
7407         (Feld[x][y + 1] == EL_MAGIC_WALL_ACTIVE ? EL_MAGIC_WALL_FILLING :
7408          Feld[x][y + 1] == EL_BD_MAGIC_WALL_ACTIVE ? EL_BD_MAGIC_WALL_FILLING :
7409          EL_DC_MAGIC_WALL_FILLING);
7410       Store[x][y] = element;
7411     }
7412     else if (CAN_FALL(element) && Feld[x][y + 1] == EL_ACID)
7413     {
7414       SplashAcid(x, y + 1);
7415
7416       InitMovingField(x, y, MV_DOWN);
7417       started_moving = TRUE;
7418
7419       Store[x][y] = EL_ACID;
7420     }
7421     else if (
7422              (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7423               CheckImpact[x][y] && !IS_FREE(x, y + 1)) ||
7424              (game.engine_version >= VERSION_IDENT(3,0,7,0) &&
7425               CAN_FALL(element) && WasJustFalling[x][y] &&
7426               (Feld[x][y + 1] == EL_BLOCKED || IS_PLAYER(x, y + 1))) ||
7427
7428              (game.engine_version < VERSION_IDENT(2,2,0,7) &&
7429               CAN_FALL(element) && WasJustMoving[x][y] && !Pushed[x][y + 1] &&
7430               (Feld[x][y + 1] == EL_BLOCKED)))
7431     {
7432       /* this is needed for a special case not covered by calling "Impact()"
7433          from "ContinueMoving()": if an element moves to a tile directly below
7434          another element which was just falling on that tile (which was empty
7435          in the previous frame), the falling element above would just stop
7436          instead of smashing the element below (in previous version, the above
7437          element was just checked for "moving" instead of "falling", resulting
7438          in incorrect smashes caused by horizontal movement of the above
7439          element; also, the case of the player being the element to smash was
7440          simply not covered here... :-/ ) */
7441
7442       CheckCollision[x][y] = 0;
7443       CheckImpact[x][y] = 0;
7444
7445       Impact(x, y);
7446     }
7447     else if (IS_FREE(x, y + 1) && element == EL_SPRING && level.use_spring_bug)
7448     {
7449       if (MovDir[x][y] == MV_NONE)
7450       {
7451         InitMovingField(x, y, MV_DOWN);
7452         started_moving = TRUE;
7453       }
7454     }
7455     else if (IS_FREE(x, y + 1) || Feld[x][y + 1] == EL_DIAMOND_BREAKING)
7456     {
7457       if (WasJustFalling[x][y]) /* prevent animation from being restarted */
7458         MovDir[x][y] = MV_DOWN;
7459
7460       InitMovingField(x, y, MV_DOWN);
7461       started_moving = TRUE;
7462     }
7463     else if (element == EL_AMOEBA_DROP)
7464     {
7465       Feld[x][y] = EL_AMOEBA_GROWING;
7466       Store[x][y] = EL_AMOEBA_WET;
7467     }
7468     else if (((IS_SLIPPERY(Feld[x][y + 1]) && !IS_PLAYER(x, y + 1)) ||
7469               (IS_EM_SLIPPERY_WALL(Feld[x][y + 1]) && IS_GEM(element))) &&
7470              !IS_FALLING(x, y + 1) && !WasJustMoving[x][y + 1] &&
7471              element != EL_DX_SUPABOMB && element != EL_SP_DISK_ORANGE)
7472     {
7473       boolean can_fall_left  = (x > 0 && IS_FREE(x - 1, y) &&
7474                                 (IS_FREE(x - 1, y + 1) ||
7475                                  Feld[x - 1][y + 1] == EL_ACID));
7476       boolean can_fall_right = (x < lev_fieldx - 1 && IS_FREE(x + 1, y) &&
7477                                 (IS_FREE(x + 1, y + 1) ||
7478                                  Feld[x + 1][y + 1] == EL_ACID));
7479       boolean can_fall_any  = (can_fall_left || can_fall_right);
7480       boolean can_fall_both = (can_fall_left && can_fall_right);
7481       int slippery_type = element_info[Feld[x][y + 1]].slippery_type;
7482
7483       if (can_fall_any && slippery_type != SLIPPERY_ANY_RANDOM)
7484       {
7485         if (slippery_type == SLIPPERY_ANY_LEFT_RIGHT && can_fall_both)
7486           can_fall_right = FALSE;
7487         else if (slippery_type == SLIPPERY_ANY_RIGHT_LEFT && can_fall_both)
7488           can_fall_left = FALSE;
7489         else if (slippery_type == SLIPPERY_ONLY_LEFT)
7490           can_fall_right = FALSE;
7491         else if (slippery_type == SLIPPERY_ONLY_RIGHT)
7492           can_fall_left = FALSE;
7493
7494         can_fall_any  = (can_fall_left || can_fall_right);
7495         can_fall_both = FALSE;
7496       }
7497
7498       if (can_fall_both)
7499       {
7500         if (element == EL_BD_ROCK || element == EL_BD_DIAMOND)
7501           can_fall_right = FALSE;       /* slip down on left side */
7502         else
7503           can_fall_left = !(can_fall_right = RND(2));
7504
7505         can_fall_both = FALSE;
7506       }
7507
7508       if (can_fall_any)
7509       {
7510         /* if not determined otherwise, prefer left side for slipping down */
7511         InitMovingField(x, y, can_fall_left ? MV_LEFT : MV_RIGHT);
7512         started_moving = TRUE;
7513       }
7514     }
7515     else if (IS_BELT_ACTIVE(Feld[x][y + 1]))
7516     {
7517       boolean left_is_free  = (x > 0 && IS_FREE(x - 1, y));
7518       boolean right_is_free = (x < lev_fieldx - 1 && IS_FREE(x + 1, y));
7519       int belt_nr = getBeltNrFromBeltActiveElement(Feld[x][y + 1]);
7520       int belt_dir = game.belt_dir[belt_nr];
7521
7522       if ((belt_dir == MV_LEFT  && left_is_free) ||
7523           (belt_dir == MV_RIGHT && right_is_free))
7524       {
7525         int nextx = (belt_dir == MV_LEFT ? x - 1 : x + 1);
7526
7527         InitMovingField(x, y, belt_dir);
7528         started_moving = TRUE;
7529
7530         Pushed[x][y] = TRUE;
7531         Pushed[nextx][y] = TRUE;
7532
7533         GfxAction[x][y] = ACTION_DEFAULT;
7534       }
7535       else
7536       {
7537         MovDir[x][y] = 0;       /* if element was moving, stop it */
7538       }
7539     }
7540   }
7541
7542   /* not "else if" because of elements that can fall and move (EL_SPRING) */
7543   if (CAN_MOVE(element) && !started_moving)
7544   {
7545     int move_pattern = element_info[element].move_pattern;
7546     int newx, newy;
7547
7548     Moving2Blocked(x, y, &newx, &newy);
7549
7550     if (IS_PUSHABLE(element) && JustBeingPushed(x, y))
7551       return;
7552
7553     if (game.engine_version >= VERSION_IDENT(3,1,0,0) &&
7554         CheckCollision[x][y] && !IN_LEV_FIELD_AND_IS_FREE(newx, newy))
7555     {
7556       WasJustMoving[x][y] = 0;
7557       CheckCollision[x][y] = 0;
7558
7559       TestIfElementHitsCustomElement(x, y, MovDir[x][y]);
7560
7561       if (Feld[x][y] != element)        /* element has changed */
7562         return;
7563     }
7564
7565     if (!MovDelay[x][y])        /* start new movement phase */
7566     {
7567       /* all objects that can change their move direction after each step
7568          (YAMYAM, DARK_YAMYAM and PACMAN go straight until they hit a wall */
7569
7570       if (element != EL_YAMYAM &&
7571           element != EL_DARK_YAMYAM &&
7572           element != EL_PACMAN &&
7573           !(move_pattern & MV_ANY_DIRECTION) &&
7574           move_pattern != MV_TURNING_LEFT &&
7575           move_pattern != MV_TURNING_RIGHT &&
7576           move_pattern != MV_TURNING_LEFT_RIGHT &&
7577           move_pattern != MV_TURNING_RIGHT_LEFT &&
7578           move_pattern != MV_TURNING_RANDOM)
7579       {
7580         TurnRound(x, y);
7581
7582         if (MovDelay[x][y] && (element == EL_BUG ||
7583                                element == EL_SPACESHIP ||
7584                                element == EL_SP_SNIKSNAK ||
7585                                element == EL_SP_ELECTRON ||
7586                                element == EL_MOLE))
7587           TEST_DrawLevelField(x, y);
7588       }
7589     }
7590
7591     if (MovDelay[x][y])         /* wait some time before next movement */
7592     {
7593       MovDelay[x][y]--;
7594
7595       if (element == EL_ROBOT ||
7596           element == EL_YAMYAM ||
7597           element == EL_DARK_YAMYAM)
7598       {
7599         DrawLevelElementAnimationIfNeeded(x, y, element);
7600         PlayLevelSoundAction(x, y, ACTION_WAITING);
7601       }
7602       else if (element == EL_SP_ELECTRON)
7603         DrawLevelElementAnimationIfNeeded(x, y, element);
7604       else if (element == EL_DRAGON)
7605       {
7606         int i;
7607         int dir = MovDir[x][y];
7608         int dx = (dir == MV_LEFT ? -1 : dir == MV_RIGHT ? +1 : 0);
7609         int dy = (dir == MV_UP   ? -1 : dir == MV_DOWN  ? +1 : 0);
7610         int graphic = (dir == MV_LEFT   ? IMG_FLAMES_1_LEFT :
7611                        dir == MV_RIGHT  ? IMG_FLAMES_1_RIGHT :
7612                        dir == MV_UP     ? IMG_FLAMES_1_UP :
7613                        dir == MV_DOWN   ? IMG_FLAMES_1_DOWN : IMG_EMPTY);
7614         int frame = getGraphicAnimationFrame(graphic, GfxFrame[x][y]);
7615
7616         GfxAction[x][y] = ACTION_ATTACKING;
7617
7618         if (IS_PLAYER(x, y))
7619           DrawPlayerField(x, y);
7620         else
7621           TEST_DrawLevelField(x, y);
7622
7623         PlayLevelSoundActionIfLoop(x, y, ACTION_ATTACKING);
7624
7625         for (i = 1; i <= 3; i++)
7626         {
7627           int xx = x + i * dx;
7628           int yy = y + i * dy;
7629           int sx = SCREENX(xx);
7630           int sy = SCREENY(yy);
7631           int flame_graphic = graphic + (i - 1);
7632
7633           if (!IN_LEV_FIELD(xx, yy) || IS_DRAGONFIRE_PROOF(Feld[xx][yy]))
7634             break;
7635
7636           if (MovDelay[x][y])
7637           {
7638             int flamed = MovingOrBlocked2Element(xx, yy);
7639
7640             if (IS_CLASSIC_ENEMY(flamed) || CAN_EXPLODE_BY_DRAGONFIRE(flamed))
7641               Bang(xx, yy);
7642             else
7643               RemoveMovingField(xx, yy);
7644
7645             ChangeDelay[xx][yy] = 0;
7646
7647             Feld[xx][yy] = EL_FLAMES;
7648
7649             if (IN_SCR_FIELD(sx, sy))
7650             {
7651               TEST_DrawLevelFieldCrumbled(xx, yy);
7652               DrawGraphic(sx, sy, flame_graphic, frame);
7653             }
7654           }
7655           else
7656           {
7657             if (Feld[xx][yy] == EL_FLAMES)
7658               Feld[xx][yy] = EL_EMPTY;
7659             TEST_DrawLevelField(xx, yy);
7660           }
7661         }
7662       }
7663
7664       if (MovDelay[x][y])       /* element still has to wait some time */
7665       {
7666         PlayLevelSoundAction(x, y, ACTION_WAITING);
7667
7668         return;
7669       }
7670     }
7671
7672     /* now make next step */
7673
7674     Moving2Blocked(x, y, &newx, &newy); /* get next screen position */
7675
7676     if (DONT_COLLIDE_WITH(element) &&
7677         IN_LEV_FIELD(newx, newy) && IS_PLAYER(newx, newy) &&
7678         !PLAYER_ENEMY_PROTECTED(newx, newy))
7679     {
7680       TestIfBadThingRunsIntoPlayer(x, y, MovDir[x][y]);
7681
7682       return;
7683     }
7684
7685     else if (CAN_MOVE_INTO_ACID(element) &&
7686              IN_LEV_FIELD(newx, newy) && Feld[newx][newy] == EL_ACID &&
7687              !IS_MV_DIAGONAL(MovDir[x][y]) &&
7688              (MovDir[x][y] == MV_DOWN ||
7689               game.engine_version >= VERSION_IDENT(3,1,0,0)))
7690     {
7691       SplashAcid(newx, newy);
7692       Store[x][y] = EL_ACID;
7693     }
7694     else if (element == EL_PENGUIN && IN_LEV_FIELD(newx, newy))
7695     {
7696       if (Feld[newx][newy] == EL_EXIT_OPEN ||
7697           Feld[newx][newy] == EL_EM_EXIT_OPEN ||
7698           Feld[newx][newy] == EL_STEEL_EXIT_OPEN ||
7699           Feld[newx][newy] == EL_EM_STEEL_EXIT_OPEN)
7700       {
7701         RemoveField(x, y);
7702         TEST_DrawLevelField(x, y);
7703
7704         PlayLevelSound(newx, newy, SND_PENGUIN_PASSING);
7705         if (IN_SCR_FIELD(SCREENX(newx), SCREENY(newy)))
7706           DrawGraphicThruMask(SCREENX(newx),SCREENY(newy), el2img(element), 0);
7707
7708         local_player->friends_still_needed--;
7709         if (!local_player->friends_still_needed &&
7710             !local_player->GameOver && AllPlayersGone)
7711           PlayerWins(local_player);
7712
7713         return;
7714       }
7715       else if (IS_FOOD_PENGUIN(Feld[newx][newy]))
7716       {
7717         if (DigField(local_player, x, y, newx, newy, 0,0, DF_DIG) == MP_MOVING)
7718           TEST_DrawLevelField(newx, newy);
7719         else
7720           GfxDir[x][y] = MovDir[x][y] = MV_NONE;
7721       }
7722       else if (!IS_FREE(newx, newy))
7723       {
7724         GfxAction[x][y] = ACTION_WAITING;
7725
7726         if (IS_PLAYER(x, y))
7727           DrawPlayerField(x, y);
7728         else
7729           TEST_DrawLevelField(x, y);
7730
7731         return;
7732       }
7733     }
7734     else if (element == EL_PIG && IN_LEV_FIELD(newx, newy))
7735     {
7736       if (IS_FOOD_PIG(Feld[newx][newy]))
7737       {
7738         if (IS_MOVING(newx, newy))
7739           RemoveMovingField(newx, newy);
7740         else
7741         {
7742           Feld[newx][newy] = EL_EMPTY;
7743           TEST_DrawLevelField(newx, newy);
7744         }
7745
7746         PlayLevelSound(x, y, SND_PIG_DIGGING);
7747       }
7748       else if (!IS_FREE(newx, newy))
7749       {
7750         if (IS_PLAYER(x, y))
7751           DrawPlayerField(x, y);
7752         else
7753           TEST_DrawLevelField(x, y);
7754
7755         return;
7756       }
7757     }
7758     else if (element == EL_EMC_ANDROID && IN_LEV_FIELD(newx, newy))
7759     {
7760       if (Store[x][y] != EL_EMPTY)
7761       {
7762         boolean can_clone = FALSE;
7763         int xx, yy;
7764
7765         /* check if element to clone is still there */
7766         for (yy = y - 1; yy <= y + 1; yy++) for (xx = x - 1; xx <= x + 1; xx++)
7767         {
7768           if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == Store[x][y])
7769           {
7770             can_clone = TRUE;
7771
7772             break;
7773           }
7774         }
7775
7776         /* cannot clone or target field not free anymore -- do not clone */
7777         if (!can_clone || !ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7778           Store[x][y] = EL_EMPTY;
7779       }
7780
7781       if (ANDROID_CAN_ENTER_FIELD(element, newx, newy))
7782       {
7783         if (IS_MV_DIAGONAL(MovDir[x][y]))
7784         {
7785           int diagonal_move_dir = MovDir[x][y];
7786           int stored = Store[x][y];
7787           int change_delay = 8;
7788           int graphic;
7789
7790           /* android is moving diagonally */
7791
7792           CreateField(x, y, EL_DIAGONAL_SHRINKING);
7793
7794           Store[x][y] = (stored == EL_ACID ? EL_EMPTY : stored);
7795           GfxElement[x][y] = EL_EMC_ANDROID;
7796           GfxAction[x][y] = ACTION_SHRINKING;
7797           GfxDir[x][y] = diagonal_move_dir;
7798           ChangeDelay[x][y] = change_delay;
7799
7800           graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],
7801                                    GfxDir[x][y]);
7802
7803           DrawLevelGraphicAnimation(x, y, graphic);
7804           PlayLevelSoundAction(x, y, ACTION_SHRINKING);
7805
7806           if (Feld[newx][newy] == EL_ACID)
7807           {
7808             SplashAcid(newx, newy);
7809
7810             return;
7811           }
7812
7813           CreateField(newx, newy, EL_DIAGONAL_GROWING);
7814
7815           Store[newx][newy] = EL_EMC_ANDROID;
7816           GfxElement[newx][newy] = EL_EMC_ANDROID;
7817           GfxAction[newx][newy] = ACTION_GROWING;
7818           GfxDir[newx][newy] = diagonal_move_dir;
7819           ChangeDelay[newx][newy] = change_delay;
7820
7821           graphic = el_act_dir2img(GfxElement[newx][newy],
7822                                    GfxAction[newx][newy], GfxDir[newx][newy]);
7823
7824           DrawLevelGraphicAnimation(newx, newy, graphic);
7825           PlayLevelSoundAction(newx, newy, ACTION_GROWING);
7826
7827           return;
7828         }
7829         else
7830         {
7831           Feld[newx][newy] = EL_EMPTY;
7832           TEST_DrawLevelField(newx, newy);
7833
7834           PlayLevelSoundAction(x, y, ACTION_DIGGING);
7835         }
7836       }
7837       else if (!IS_FREE(newx, newy))
7838       {
7839         return;
7840       }
7841     }
7842     else if (IS_CUSTOM_ELEMENT(element) &&
7843              CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, newx, newy))
7844     {
7845       if (!DigFieldByCE(newx, newy, element))
7846         return;
7847
7848       if (move_pattern & MV_MAZE_RUNNER_STYLE)
7849       {
7850         RunnerVisit[x][y] = FrameCounter;
7851         PlayerVisit[x][y] /= 8;         /* expire player visit path */
7852       }
7853     }
7854     else if (element == EL_DRAGON && IN_LEV_FIELD(newx, newy))
7855     {
7856       if (!IS_FREE(newx, newy))
7857       {
7858         if (IS_PLAYER(x, y))
7859           DrawPlayerField(x, y);
7860         else
7861           TEST_DrawLevelField(x, y);
7862
7863         return;
7864       }
7865       else
7866       {
7867         boolean wanna_flame = !RND(10);
7868         int dx = newx - x, dy = newy - y;
7869         int newx1 = newx + 1 * dx, newy1 = newy + 1 * dy;
7870         int newx2 = newx + 2 * dx, newy2 = newy + 2 * dy;
7871         int element1 = (IN_LEV_FIELD(newx1, newy1) ?
7872                         MovingOrBlocked2Element(newx1, newy1) : EL_STEELWALL);
7873         int element2 = (IN_LEV_FIELD(newx2, newy2) ?
7874                         MovingOrBlocked2Element(newx2, newy2) : EL_STEELWALL);
7875
7876         if ((wanna_flame ||
7877              IS_CLASSIC_ENEMY(element1) ||
7878              IS_CLASSIC_ENEMY(element2)) &&
7879             element1 != EL_DRAGON && element2 != EL_DRAGON &&
7880             element1 != EL_FLAMES && element2 != EL_FLAMES)
7881         {
7882           ResetGfxAnimation(x, y);
7883           GfxAction[x][y] = ACTION_ATTACKING;
7884
7885           if (IS_PLAYER(x, y))
7886             DrawPlayerField(x, y);
7887           else
7888             TEST_DrawLevelField(x, y);
7889
7890           PlayLevelSound(x, y, SND_DRAGON_ATTACKING);
7891
7892           MovDelay[x][y] = 50;
7893
7894           Feld[newx][newy] = EL_FLAMES;
7895           if (IN_LEV_FIELD(newx1, newy1) && Feld[newx1][newy1] == EL_EMPTY)
7896             Feld[newx1][newy1] = EL_FLAMES;
7897           if (IN_LEV_FIELD(newx2, newy2) && Feld[newx2][newy2] == EL_EMPTY)
7898             Feld[newx2][newy2] = EL_FLAMES;
7899
7900           return;
7901         }
7902       }
7903     }
7904     else if (element == EL_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7905              Feld[newx][newy] == EL_DIAMOND)
7906     {
7907       if (IS_MOVING(newx, newy))
7908         RemoveMovingField(newx, newy);
7909       else
7910       {
7911         Feld[newx][newy] = EL_EMPTY;
7912         TEST_DrawLevelField(newx, newy);
7913       }
7914
7915       PlayLevelSound(x, y, SND_YAMYAM_DIGGING);
7916     }
7917     else if (element == EL_DARK_YAMYAM && IN_LEV_FIELD(newx, newy) &&
7918              IS_FOOD_DARK_YAMYAM(Feld[newx][newy]))
7919     {
7920       if (AmoebaNr[newx][newy])
7921       {
7922         AmoebaCnt2[AmoebaNr[newx][newy]]--;
7923         if (Feld[newx][newy] == EL_AMOEBA_FULL ||
7924             Feld[newx][newy] == EL_BD_AMOEBA)
7925           AmoebaCnt[AmoebaNr[newx][newy]]--;
7926       }
7927
7928       if (IS_MOVING(newx, newy))
7929       {
7930         RemoveMovingField(newx, newy);
7931       }
7932       else
7933       {
7934         Feld[newx][newy] = EL_EMPTY;
7935         TEST_DrawLevelField(newx, newy);
7936       }
7937
7938       PlayLevelSound(x, y, SND_DARK_YAMYAM_DIGGING);
7939     }
7940     else if ((element == EL_PACMAN || element == EL_MOLE)
7941              && IN_LEV_FIELD(newx, newy) && IS_AMOEBOID(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 (element == EL_MOLE)
7952       {
7953         Feld[newx][newy] = EL_AMOEBA_SHRINKING;
7954         PlayLevelSound(x, y, SND_MOLE_DIGGING);
7955
7956         ResetGfxAnimation(x, y);
7957         GfxAction[x][y] = ACTION_DIGGING;
7958         TEST_DrawLevelField(x, y);
7959
7960         MovDelay[newx][newy] = 0;       /* start amoeba shrinking delay */
7961
7962         return;                         /* wait for shrinking amoeba */
7963       }
7964       else      /* element == EL_PACMAN */
7965       {
7966         Feld[newx][newy] = EL_EMPTY;
7967         TEST_DrawLevelField(newx, newy);
7968         PlayLevelSound(x, y, SND_PACMAN_DIGGING);
7969       }
7970     }
7971     else if (element == EL_MOLE && IN_LEV_FIELD(newx, newy) &&
7972              (Feld[newx][newy] == EL_AMOEBA_SHRINKING ||
7973               (Feld[newx][newy] == EL_EMPTY && Stop[newx][newy])))
7974     {
7975       /* wait for shrinking amoeba to completely disappear */
7976       return;
7977     }
7978     else if (!IN_LEV_FIELD(newx, newy) || !IS_FREE(newx, newy))
7979     {
7980       /* object was running against a wall */
7981
7982       TurnRound(x, y);
7983
7984       if (GFX_ELEMENT(element) != EL_SAND)     /* !!! FIX THIS (crumble) !!! */
7985         DrawLevelElementAnimation(x, y, element);
7986
7987       if (DONT_TOUCH(element))
7988         TestIfBadThingTouchesPlayer(x, y);
7989
7990       return;
7991     }
7992
7993     InitMovingField(x, y, MovDir[x][y]);
7994
7995     PlayLevelSoundAction(x, y, ACTION_MOVING);
7996   }
7997
7998   if (MovDir[x][y])
7999     ContinueMoving(x, y);
8000 }
8001
8002 void ContinueMoving(int x, int y)
8003 {
8004   int element = Feld[x][y];
8005   struct ElementInfo *ei = &element_info[element];
8006   int direction = MovDir[x][y];
8007   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
8008   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
8009   int newx = x + dx, newy = y + dy;
8010   int stored = Store[x][y];
8011   int stored_new = Store[newx][newy];
8012   boolean pushed_by_player   = (Pushed[x][y] && IS_PLAYER(x, y));
8013   boolean pushed_by_conveyor = (Pushed[x][y] && !IS_PLAYER(x, y));
8014   boolean last_line = (newy == lev_fieldy - 1);
8015
8016   MovPos[x][y] += getElementMoveStepsize(x, y);
8017
8018   if (pushed_by_player) /* special case: moving object pushed by player */
8019     MovPos[x][y] = SIGN(MovPos[x][y]) * (TILEX - ABS(PLAYERINFO(x,y)->MovPos));
8020
8021   if (ABS(MovPos[x][y]) < TILEX)
8022   {
8023     TEST_DrawLevelField(x, y);
8024
8025     return;     /* element is still moving */
8026   }
8027
8028   /* element reached destination field */
8029
8030   Feld[x][y] = EL_EMPTY;
8031   Feld[newx][newy] = element;
8032   MovPos[x][y] = 0;     /* force "not moving" for "crumbled sand" */
8033
8034   if (Store[x][y] == EL_ACID)   /* element is moving into acid pool */
8035   {
8036     element = Feld[newx][newy] = EL_ACID;
8037   }
8038   else if (element == EL_MOLE)
8039   {
8040     Feld[x][y] = EL_SAND;
8041
8042     TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8043   }
8044   else if (element == EL_QUICKSAND_FILLING)
8045   {
8046     element = Feld[newx][newy] = get_next_element(element);
8047     Store[newx][newy] = Store[x][y];
8048   }
8049   else if (element == EL_QUICKSAND_EMPTYING)
8050   {
8051     Feld[x][y] = get_next_element(element);
8052     element = Feld[newx][newy] = Store[x][y];
8053   }
8054   else if (element == EL_QUICKSAND_FAST_FILLING)
8055   {
8056     element = Feld[newx][newy] = get_next_element(element);
8057     Store[newx][newy] = Store[x][y];
8058   }
8059   else if (element == EL_QUICKSAND_FAST_EMPTYING)
8060   {
8061     Feld[x][y] = get_next_element(element);
8062     element = Feld[newx][newy] = Store[x][y];
8063   }
8064   else if (element == EL_MAGIC_WALL_FILLING)
8065   {
8066     element = Feld[newx][newy] = get_next_element(element);
8067     if (!game.magic_wall_active)
8068       element = Feld[newx][newy] = EL_MAGIC_WALL_DEAD;
8069     Store[newx][newy] = Store[x][y];
8070   }
8071   else if (element == EL_MAGIC_WALL_EMPTYING)
8072   {
8073     Feld[x][y] = get_next_element(element);
8074     if (!game.magic_wall_active)
8075       Feld[x][y] = EL_MAGIC_WALL_DEAD;
8076     element = Feld[newx][newy] = Store[x][y];
8077
8078     InitField(newx, newy, FALSE);
8079   }
8080   else if (element == EL_BD_MAGIC_WALL_FILLING)
8081   {
8082     element = Feld[newx][newy] = get_next_element(element);
8083     if (!game.magic_wall_active)
8084       element = Feld[newx][newy] = EL_BD_MAGIC_WALL_DEAD;
8085     Store[newx][newy] = Store[x][y];
8086   }
8087   else if (element == EL_BD_MAGIC_WALL_EMPTYING)
8088   {
8089     Feld[x][y] = get_next_element(element);
8090     if (!game.magic_wall_active)
8091       Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
8092     element = Feld[newx][newy] = Store[x][y];
8093
8094     InitField(newx, newy, FALSE);
8095   }
8096   else if (element == EL_DC_MAGIC_WALL_FILLING)
8097   {
8098     element = Feld[newx][newy] = get_next_element(element);
8099     if (!game.magic_wall_active)
8100       element = Feld[newx][newy] = EL_DC_MAGIC_WALL_DEAD;
8101     Store[newx][newy] = Store[x][y];
8102   }
8103   else if (element == EL_DC_MAGIC_WALL_EMPTYING)
8104   {
8105     Feld[x][y] = get_next_element(element);
8106     if (!game.magic_wall_active)
8107       Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
8108     element = Feld[newx][newy] = Store[x][y];
8109
8110     InitField(newx, newy, FALSE);
8111   }
8112   else if (element == EL_AMOEBA_DROPPING)
8113   {
8114     Feld[x][y] = get_next_element(element);
8115     element = Feld[newx][newy] = Store[x][y];
8116   }
8117   else if (element == EL_SOKOBAN_OBJECT)
8118   {
8119     if (Back[x][y])
8120       Feld[x][y] = Back[x][y];
8121
8122     if (Back[newx][newy])
8123       Feld[newx][newy] = EL_SOKOBAN_FIELD_FULL;
8124
8125     Back[x][y] = Back[newx][newy] = 0;
8126   }
8127
8128   Store[x][y] = EL_EMPTY;
8129   MovPos[x][y] = 0;
8130   MovDir[x][y] = 0;
8131   MovDelay[x][y] = 0;
8132
8133   MovDelay[newx][newy] = 0;
8134
8135   if (CAN_CHANGE_OR_HAS_ACTION(element))
8136   {
8137     /* copy element change control values to new field */
8138     ChangeDelay[newx][newy] = ChangeDelay[x][y];
8139     ChangePage[newx][newy]  = ChangePage[x][y];
8140     ChangeCount[newx][newy] = ChangeCount[x][y];
8141     ChangeEvent[newx][newy] = ChangeEvent[x][y];
8142   }
8143
8144   CustomValue[newx][newy] = CustomValue[x][y];
8145
8146   ChangeDelay[x][y] = 0;
8147   ChangePage[x][y] = -1;
8148   ChangeCount[x][y] = 0;
8149   ChangeEvent[x][y] = -1;
8150
8151   CustomValue[x][y] = 0;
8152
8153   /* copy animation control values to new field */
8154   GfxFrame[newx][newy]  = GfxFrame[x][y];
8155   GfxRandom[newx][newy] = GfxRandom[x][y];      /* keep same random value */
8156   GfxAction[newx][newy] = GfxAction[x][y];      /* keep action one frame  */
8157   GfxDir[newx][newy]    = GfxDir[x][y];         /* keep element direction */
8158
8159   Pushed[x][y] = Pushed[newx][newy] = FALSE;
8160
8161   /* some elements can leave other elements behind after moving */
8162   if (ei->move_leave_element != EL_EMPTY &&
8163       (ei->move_leave_type == LEAVE_TYPE_UNLIMITED || stored != EL_EMPTY) &&
8164       (!IS_PLAYER(x, y) || IS_WALKABLE(ei->move_leave_element)))
8165   {
8166     int move_leave_element = ei->move_leave_element;
8167
8168     /* this makes it possible to leave the removed element again */
8169     if (ei->move_leave_element == EL_TRIGGER_ELEMENT)
8170       move_leave_element = (stored == EL_ACID ? EL_EMPTY : stored);
8171
8172     Feld[x][y] = move_leave_element;
8173
8174     if (element_info[Feld[x][y]].move_direction_initial == MV_START_PREVIOUS)
8175       MovDir[x][y] = direction;
8176
8177     InitField(x, y, FALSE);
8178
8179     if (GFX_CRUMBLED(Feld[x][y]))
8180       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
8181
8182     if (ELEM_IS_PLAYER(move_leave_element))
8183       RelocatePlayer(x, y, move_leave_element);
8184   }
8185
8186   /* do this after checking for left-behind element */
8187   ResetGfxAnimation(x, y);      /* reset animation values for old field */
8188
8189   if (!CAN_MOVE(element) ||
8190       (CAN_FALL(element) && direction == MV_DOWN &&
8191        (element == EL_SPRING ||
8192         element_info[element].move_pattern == MV_WHEN_PUSHED ||
8193         element_info[element].move_pattern == MV_WHEN_DROPPED)))
8194     GfxDir[x][y] = MovDir[newx][newy] = 0;
8195
8196   TEST_DrawLevelField(x, y);
8197   TEST_DrawLevelField(newx, newy);
8198
8199   Stop[newx][newy] = TRUE;      /* ignore this element until the next frame */
8200
8201   /* prevent pushed element from moving on in pushed direction */
8202   if (pushed_by_player && CAN_MOVE(element) &&
8203       element_info[element].move_pattern & MV_ANY_DIRECTION &&
8204       !(element_info[element].move_pattern & direction))
8205     TurnRound(newx, newy);
8206
8207   /* prevent elements on conveyor belt from moving on in last direction */
8208   if (pushed_by_conveyor && CAN_FALL(element) &&
8209       direction & MV_HORIZONTAL)
8210     MovDir[newx][newy] = 0;
8211
8212   if (!pushed_by_player)
8213   {
8214     int nextx = newx + dx, nexty = newy + dy;
8215     boolean check_collision_again = IN_LEV_FIELD_AND_IS_FREE(nextx, nexty);
8216
8217     WasJustMoving[newx][newy] = CHECK_DELAY_MOVING;
8218
8219     if (CAN_FALL(element) && direction == MV_DOWN)
8220       WasJustFalling[newx][newy] = CHECK_DELAY_FALLING;
8221
8222     if ((!CAN_FALL(element) || direction == MV_DOWN) && check_collision_again)
8223       CheckCollision[newx][newy] = CHECK_DELAY_COLLISION;
8224
8225     if (CAN_FALL(element) && direction == MV_DOWN && check_collision_again)
8226       CheckImpact[newx][newy] = CHECK_DELAY_IMPACT;
8227   }
8228
8229   if (DONT_TOUCH(element))      /* object may be nasty to player or others */
8230   {
8231     TestIfBadThingTouchesPlayer(newx, newy);
8232     TestIfBadThingTouchesFriend(newx, newy);
8233
8234     if (!IS_CUSTOM_ELEMENT(element))
8235       TestIfBadThingTouchesOtherBadThing(newx, newy);
8236   }
8237   else if (element == EL_PENGUIN)
8238     TestIfFriendTouchesBadThing(newx, newy);
8239
8240   if (DONT_GET_HIT_BY(element))
8241   {
8242     TestIfGoodThingGetsHitByBadThing(newx, newy, direction);
8243   }
8244
8245   /* give the player one last chance (one more frame) to move away */
8246   if (CAN_FALL(element) && direction == MV_DOWN &&
8247       (last_line || (!IS_FREE(x, newy + 1) &&
8248                      (!IS_PLAYER(x, newy + 1) ||
8249                       game.engine_version < VERSION_IDENT(3,1,1,0)))))
8250     Impact(x, newy);
8251
8252   if (pushed_by_player && !game.use_change_when_pushing_bug)
8253   {
8254     int push_side = MV_DIR_OPPOSITE(direction);
8255     struct PlayerInfo *player = PLAYERINFO(x, y);
8256
8257     CheckElementChangeByPlayer(newx, newy, element, CE_PUSHED_BY_PLAYER,
8258                                player->index_bit, push_side);
8259     CheckTriggeredElementChangeByPlayer(newx,newy, element, CE_PLAYER_PUSHES_X,
8260                                         player->index_bit, push_side);
8261   }
8262
8263   if (element == EL_EMC_ANDROID && pushed_by_player)    /* make another move */
8264     MovDelay[newx][newy] = 1;
8265
8266   CheckTriggeredElementChangeBySide(x, y, element, CE_MOVE_OF_X, direction);
8267
8268   TestIfElementTouchesCustomElement(x, y);      /* empty or new element */
8269   TestIfElementHitsCustomElement(newx, newy, direction);
8270   TestIfPlayerTouchesCustomElement(newx, newy);
8271   TestIfElementTouchesCustomElement(newx, newy);
8272
8273   if (IS_CUSTOM_ELEMENT(element) && ei->move_enter_element != EL_EMPTY &&
8274       IS_EQUAL_OR_IN_GROUP(stored_new, ei->move_enter_element))
8275     CheckElementChangeBySide(newx, newy, element, stored_new, CE_DIGGING_X,
8276                              MV_DIR_OPPOSITE(direction));
8277 }
8278
8279 int AmoebeNachbarNr(int ax, int ay)
8280 {
8281   int i;
8282   int element = Feld[ax][ay];
8283   int group_nr = 0;
8284   static int xy[4][2] =
8285   {
8286     { 0, -1 },
8287     { -1, 0 },
8288     { +1, 0 },
8289     { 0, +1 }
8290   };
8291
8292   for (i = 0; i < NUM_DIRECTIONS; i++)
8293   {
8294     int x = ax + xy[i][0];
8295     int y = ay + xy[i][1];
8296
8297     if (!IN_LEV_FIELD(x, y))
8298       continue;
8299
8300     if (Feld[x][y] == element && AmoebaNr[x][y] > 0)
8301       group_nr = AmoebaNr[x][y];
8302   }
8303
8304   return group_nr;
8305 }
8306
8307 void AmoebenVereinigen(int ax, int ay)
8308 {
8309   int i, x, y, xx, yy;
8310   int new_group_nr = AmoebaNr[ax][ay];
8311   static int xy[4][2] =
8312   {
8313     { 0, -1 },
8314     { -1, 0 },
8315     { +1, 0 },
8316     { 0, +1 }
8317   };
8318
8319   if (new_group_nr == 0)
8320     return;
8321
8322   for (i = 0; i < NUM_DIRECTIONS; i++)
8323   {
8324     x = ax + xy[i][0];
8325     y = ay + xy[i][1];
8326
8327     if (!IN_LEV_FIELD(x, y))
8328       continue;
8329
8330     if ((Feld[x][y] == EL_AMOEBA_FULL ||
8331          Feld[x][y] == EL_BD_AMOEBA ||
8332          Feld[x][y] == EL_AMOEBA_DEAD) &&
8333         AmoebaNr[x][y] != new_group_nr)
8334     {
8335       int old_group_nr = AmoebaNr[x][y];
8336
8337       if (old_group_nr == 0)
8338         return;
8339
8340       AmoebaCnt[new_group_nr] += AmoebaCnt[old_group_nr];
8341       AmoebaCnt[old_group_nr] = 0;
8342       AmoebaCnt2[new_group_nr] += AmoebaCnt2[old_group_nr];
8343       AmoebaCnt2[old_group_nr] = 0;
8344
8345       SCAN_PLAYFIELD(xx, yy)
8346       {
8347         if (AmoebaNr[xx][yy] == old_group_nr)
8348           AmoebaNr[xx][yy] = new_group_nr;
8349       }
8350     }
8351   }
8352 }
8353
8354 void AmoebeUmwandeln(int ax, int ay)
8355 {
8356   int i, x, y;
8357
8358   if (Feld[ax][ay] == EL_AMOEBA_DEAD)
8359   {
8360     int group_nr = AmoebaNr[ax][ay];
8361
8362 #ifdef DEBUG
8363     if (group_nr == 0)
8364     {
8365       printf("AmoebeUmwandeln(): ax = %d, ay = %d\n", ax, ay);
8366       printf("AmoebeUmwandeln(): This should never happen!\n");
8367       return;
8368     }
8369 #endif
8370
8371     SCAN_PLAYFIELD(x, y)
8372     {
8373       if (Feld[x][y] == EL_AMOEBA_DEAD && AmoebaNr[x][y] == group_nr)
8374       {
8375         AmoebaNr[x][y] = 0;
8376         Feld[x][y] = EL_AMOEBA_TO_DIAMOND;
8377       }
8378     }
8379
8380     PlayLevelSound(ax, ay, (IS_GEM(level.amoeba_content) ?
8381                             SND_AMOEBA_TURNING_TO_GEM :
8382                             SND_AMOEBA_TURNING_TO_ROCK));
8383     Bang(ax, ay);
8384   }
8385   else
8386   {
8387     static int xy[4][2] =
8388     {
8389       { 0, -1 },
8390       { -1, 0 },
8391       { +1, 0 },
8392       { 0, +1 }
8393     };
8394
8395     for (i = 0; i < NUM_DIRECTIONS; i++)
8396     {
8397       x = ax + xy[i][0];
8398       y = ay + xy[i][1];
8399
8400       if (!IN_LEV_FIELD(x, y))
8401         continue;
8402
8403       if (Feld[x][y] == EL_AMOEBA_TO_DIAMOND)
8404       {
8405         PlayLevelSound(x, y, (IS_GEM(level.amoeba_content) ?
8406                               SND_AMOEBA_TURNING_TO_GEM :
8407                               SND_AMOEBA_TURNING_TO_ROCK));
8408         Bang(x, y);
8409       }
8410     }
8411   }
8412 }
8413
8414 void AmoebeUmwandelnBD(int ax, int ay, int new_element)
8415 {
8416   int x, y;
8417   int group_nr = AmoebaNr[ax][ay];
8418   boolean done = FALSE;
8419
8420 #ifdef DEBUG
8421   if (group_nr == 0)
8422   {
8423     printf("AmoebeUmwandelnBD(): ax = %d, ay = %d\n", ax, ay);
8424     printf("AmoebeUmwandelnBD(): This should never happen!\n");
8425     return;
8426   }
8427 #endif
8428
8429   SCAN_PLAYFIELD(x, y)
8430   {
8431     if (AmoebaNr[x][y] == group_nr &&
8432         (Feld[x][y] == EL_AMOEBA_DEAD ||
8433          Feld[x][y] == EL_BD_AMOEBA ||
8434          Feld[x][y] == EL_AMOEBA_GROWING))
8435     {
8436       AmoebaNr[x][y] = 0;
8437       Feld[x][y] = new_element;
8438       InitField(x, y, FALSE);
8439       TEST_DrawLevelField(x, y);
8440       done = TRUE;
8441     }
8442   }
8443
8444   if (done)
8445     PlayLevelSound(ax, ay, (new_element == EL_BD_ROCK ?
8446                             SND_BD_AMOEBA_TURNING_TO_ROCK :
8447                             SND_BD_AMOEBA_TURNING_TO_GEM));
8448 }
8449
8450 void AmoebeWaechst(int x, int y)
8451 {
8452   static unsigned int sound_delay = 0;
8453   static unsigned int sound_delay_value = 0;
8454
8455   if (!MovDelay[x][y])          /* start new growing cycle */
8456   {
8457     MovDelay[x][y] = 7;
8458
8459     if (DelayReached(&sound_delay, sound_delay_value))
8460     {
8461       PlayLevelSoundElementAction(x, y, Store[x][y], ACTION_GROWING);
8462       sound_delay_value = 30;
8463     }
8464   }
8465
8466   if (MovDelay[x][y])           /* wait some time before growing bigger */
8467   {
8468     MovDelay[x][y]--;
8469     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8470     {
8471       int frame = getGraphicAnimationFrame(IMG_AMOEBA_GROWING,
8472                                            6 - MovDelay[x][y]);
8473
8474       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_GROWING, frame);
8475     }
8476
8477     if (!MovDelay[x][y])
8478     {
8479       Feld[x][y] = Store[x][y];
8480       Store[x][y] = 0;
8481       TEST_DrawLevelField(x, y);
8482     }
8483   }
8484 }
8485
8486 void AmoebaDisappearing(int x, int y)
8487 {
8488   static unsigned int sound_delay = 0;
8489   static unsigned int sound_delay_value = 0;
8490
8491   if (!MovDelay[x][y])          /* start new shrinking cycle */
8492   {
8493     MovDelay[x][y] = 7;
8494
8495     if (DelayReached(&sound_delay, sound_delay_value))
8496       sound_delay_value = 30;
8497   }
8498
8499   if (MovDelay[x][y])           /* wait some time before shrinking */
8500   {
8501     MovDelay[x][y]--;
8502     if (MovDelay[x][y]/2 && IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8503     {
8504       int frame = getGraphicAnimationFrame(IMG_AMOEBA_SHRINKING,
8505                                            6 - MovDelay[x][y]);
8506
8507       DrawGraphic(SCREENX(x), SCREENY(y), IMG_AMOEBA_SHRINKING, frame);
8508     }
8509
8510     if (!MovDelay[x][y])
8511     {
8512       Feld[x][y] = EL_EMPTY;
8513       TEST_DrawLevelField(x, y);
8514
8515       /* don't let mole enter this field in this cycle;
8516          (give priority to objects falling to this field from above) */
8517       Stop[x][y] = TRUE;
8518     }
8519   }
8520 }
8521
8522 void AmoebeAbleger(int ax, int ay)
8523 {
8524   int i;
8525   int element = Feld[ax][ay];
8526   int graphic = el2img(element);
8527   int newax = ax, neway = ay;
8528   boolean can_drop = (element == EL_AMOEBA_WET || element == EL_EMC_DRIPPER);
8529   static int xy[4][2] =
8530   {
8531     { 0, -1 },
8532     { -1, 0 },
8533     { +1, 0 },
8534     { 0, +1 }
8535   };
8536
8537   if (!level.amoeba_speed && element != EL_EMC_DRIPPER)
8538   {
8539     Feld[ax][ay] = EL_AMOEBA_DEAD;
8540     TEST_DrawLevelField(ax, ay);
8541     return;
8542   }
8543
8544   if (IS_ANIMATED(graphic))
8545     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8546
8547   if (!MovDelay[ax][ay])        /* start making new amoeba field */
8548     MovDelay[ax][ay] = RND(FRAMES_PER_SECOND * 25 / (1 + level.amoeba_speed));
8549
8550   if (MovDelay[ax][ay])         /* wait some time before making new amoeba */
8551   {
8552     MovDelay[ax][ay]--;
8553     if (MovDelay[ax][ay])
8554       return;
8555   }
8556
8557   if (can_drop)                 /* EL_AMOEBA_WET or EL_EMC_DRIPPER */
8558   {
8559     int start = RND(4);
8560     int x = ax + xy[start][0];
8561     int y = ay + xy[start][1];
8562
8563     if (!IN_LEV_FIELD(x, y))
8564       return;
8565
8566     if (IS_FREE(x, y) ||
8567         CAN_GROW_INTO(Feld[x][y]) ||
8568         Feld[x][y] == EL_QUICKSAND_EMPTY ||
8569         Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8570     {
8571       newax = x;
8572       neway = y;
8573     }
8574
8575     if (newax == ax && neway == ay)
8576       return;
8577   }
8578   else                          /* normal or "filled" (BD style) amoeba */
8579   {
8580     int start = RND(4);
8581     boolean waiting_for_player = FALSE;
8582
8583     for (i = 0; i < NUM_DIRECTIONS; i++)
8584     {
8585       int j = (start + i) % 4;
8586       int x = ax + xy[j][0];
8587       int y = ay + xy[j][1];
8588
8589       if (!IN_LEV_FIELD(x, y))
8590         continue;
8591
8592       if (IS_FREE(x, y) ||
8593           CAN_GROW_INTO(Feld[x][y]) ||
8594           Feld[x][y] == EL_QUICKSAND_EMPTY ||
8595           Feld[x][y] == EL_QUICKSAND_FAST_EMPTY)
8596       {
8597         newax = x;
8598         neway = y;
8599         break;
8600       }
8601       else if (IS_PLAYER(x, y))
8602         waiting_for_player = TRUE;
8603     }
8604
8605     if (newax == ax && neway == ay)             /* amoeba cannot grow */
8606     {
8607       if (i == 4 && (!waiting_for_player || element == EL_BD_AMOEBA))
8608       {
8609         Feld[ax][ay] = EL_AMOEBA_DEAD;
8610         TEST_DrawLevelField(ax, ay);
8611         AmoebaCnt[AmoebaNr[ax][ay]]--;
8612
8613         if (AmoebaCnt[AmoebaNr[ax][ay]] <= 0)   /* amoeba is completely dead */
8614         {
8615           if (element == EL_AMOEBA_FULL)
8616             AmoebeUmwandeln(ax, ay);
8617           else if (element == EL_BD_AMOEBA)
8618             AmoebeUmwandelnBD(ax, ay, level.amoeba_content);
8619         }
8620       }
8621       return;
8622     }
8623     else if (element == EL_AMOEBA_FULL || element == EL_BD_AMOEBA)
8624     {
8625       /* amoeba gets larger by growing in some direction */
8626
8627       int new_group_nr = AmoebaNr[ax][ay];
8628
8629 #ifdef DEBUG
8630   if (new_group_nr == 0)
8631   {
8632     printf("AmoebeAbleger(): newax = %d, neway = %d\n", newax, neway);
8633     printf("AmoebeAbleger(): This should never happen!\n");
8634     return;
8635   }
8636 #endif
8637
8638       AmoebaNr[newax][neway] = new_group_nr;
8639       AmoebaCnt[new_group_nr]++;
8640       AmoebaCnt2[new_group_nr]++;
8641
8642       /* if amoeba touches other amoeba(s) after growing, unify them */
8643       AmoebenVereinigen(newax, neway);
8644
8645       if (element == EL_BD_AMOEBA && AmoebaCnt2[new_group_nr] >= 200)
8646       {
8647         AmoebeUmwandelnBD(newax, neway, EL_BD_ROCK);
8648         return;
8649       }
8650     }
8651   }
8652
8653   if (!can_drop || neway < ay || !IS_FREE(newax, neway) ||
8654       (neway == lev_fieldy - 1 && newax != ax))
8655   {
8656     Feld[newax][neway] = EL_AMOEBA_GROWING;     /* creation of new amoeba */
8657     Store[newax][neway] = element;
8658   }
8659   else if (neway == ay || element == EL_EMC_DRIPPER)
8660   {
8661     Feld[newax][neway] = EL_AMOEBA_DROP;        /* drop left/right of amoeba */
8662
8663     PlayLevelSoundAction(newax, neway, ACTION_GROWING);
8664   }
8665   else
8666   {
8667     InitMovingField(ax, ay, MV_DOWN);           /* drop dripping from amoeba */
8668     Feld[ax][ay] = EL_AMOEBA_DROPPING;
8669     Store[ax][ay] = EL_AMOEBA_DROP;
8670     ContinueMoving(ax, ay);
8671     return;
8672   }
8673
8674   TEST_DrawLevelField(newax, neway);
8675 }
8676
8677 void Life(int ax, int ay)
8678 {
8679   int x1, y1, x2, y2;
8680   int life_time = 40;
8681   int element = Feld[ax][ay];
8682   int graphic = el2img(element);
8683   int *life_parameter = (element == EL_GAME_OF_LIFE ? level.game_of_life :
8684                          level.biomaze);
8685   boolean changed = FALSE;
8686
8687   if (IS_ANIMATED(graphic))
8688     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
8689
8690   if (Stop[ax][ay])
8691     return;
8692
8693   if (!MovDelay[ax][ay])        /* start new "game of life" cycle */
8694     MovDelay[ax][ay] = life_time;
8695
8696   if (MovDelay[ax][ay])         /* wait some time before next cycle */
8697   {
8698     MovDelay[ax][ay]--;
8699     if (MovDelay[ax][ay])
8700       return;
8701   }
8702
8703   for (y1 = -1; y1 < 2; y1++) for (x1 = -1; x1 < 2; x1++)
8704   {
8705     int xx = ax+x1, yy = ay+y1;
8706     int nachbarn = 0;
8707
8708     if (!IN_LEV_FIELD(xx, yy))
8709       continue;
8710
8711     for (y2 = -1; y2 < 2; y2++) for (x2 = -1; x2 < 2; x2++)
8712     {
8713       int x = xx+x2, y = yy+y2;
8714
8715       if (!IN_LEV_FIELD(x, y) || (x == xx && y == yy))
8716         continue;
8717
8718       if (((Feld[x][y] == element ||
8719             (element == EL_GAME_OF_LIFE && IS_PLAYER(x, y))) &&
8720            !Stop[x][y]) ||
8721           (IS_FREE(x, y) && Stop[x][y]))
8722         nachbarn++;
8723     }
8724
8725     if (xx == ax && yy == ay)           /* field in the middle */
8726     {
8727       if (nachbarn < life_parameter[0] ||
8728           nachbarn > life_parameter[1])
8729       {
8730         Feld[xx][yy] = EL_EMPTY;
8731         if (!Stop[xx][yy])
8732           TEST_DrawLevelField(xx, yy);
8733         Stop[xx][yy] = TRUE;
8734         changed = TRUE;
8735       }
8736     }
8737     else if (IS_FREE(xx, yy) || CAN_GROW_INTO(Feld[xx][yy]))
8738     {                                   /* free border field */
8739       if (nachbarn >= life_parameter[2] &&
8740           nachbarn <= life_parameter[3])
8741       {
8742         Feld[xx][yy] = element;
8743         MovDelay[xx][yy] = (element == EL_GAME_OF_LIFE ? 0 : life_time-1);
8744         if (!Stop[xx][yy])
8745           TEST_DrawLevelField(xx, yy);
8746         Stop[xx][yy] = TRUE;
8747         changed = TRUE;
8748       }
8749     }
8750   }
8751
8752   if (changed)
8753     PlayLevelSound(ax, ay, element == EL_BIOMAZE ? SND_BIOMAZE_GROWING :
8754                    SND_GAME_OF_LIFE_GROWING);
8755 }
8756
8757 static void InitRobotWheel(int x, int y)
8758 {
8759   ChangeDelay[x][y] = level.time_wheel * FRAMES_PER_SECOND;
8760 }
8761
8762 static void RunRobotWheel(int x, int y)
8763 {
8764   PlayLevelSound(x, y, SND_ROBOT_WHEEL_ACTIVE);
8765 }
8766
8767 static void StopRobotWheel(int x, int y)
8768 {
8769   if (ZX == x && ZY == y)
8770   {
8771     ZX = ZY = -1;
8772
8773     game.robot_wheel_active = FALSE;
8774   }
8775 }
8776
8777 static void InitTimegateWheel(int x, int y)
8778 {
8779   ChangeDelay[x][y] = level.time_timegate * FRAMES_PER_SECOND;
8780 }
8781
8782 static void RunTimegateWheel(int x, int y)
8783 {
8784   PlayLevelSound(x, y, SND_CLASS_TIMEGATE_SWITCH_ACTIVE);
8785 }
8786
8787 static void InitMagicBallDelay(int x, int y)
8788 {
8789   ChangeDelay[x][y] = (level.ball_time + 1) * 8 + 1;
8790 }
8791
8792 static void ActivateMagicBall(int bx, int by)
8793 {
8794   int x, y;
8795
8796   if (level.ball_random)
8797   {
8798     int pos_border = RND(8);    /* select one of the eight border elements */
8799     int pos_content = (pos_border > 3 ? pos_border + 1 : pos_border);
8800     int xx = pos_content % 3;
8801     int yy = pos_content / 3;
8802
8803     x = bx - 1 + xx;
8804     y = by - 1 + yy;
8805
8806     if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8807       CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8808   }
8809   else
8810   {
8811     for (y = by - 1; y <= by + 1; y++) for (x = bx - 1; x <= bx + 1; x++)
8812     {
8813       int xx = x - bx + 1;
8814       int yy = y - by + 1;
8815
8816       if (IN_LEV_FIELD(x, y) && Feld[x][y] == EL_EMPTY)
8817         CreateField(x, y, level.ball_content[game.ball_content_nr].e[xx][yy]);
8818     }
8819   }
8820
8821   game.ball_content_nr = (game.ball_content_nr + 1) % level.num_ball_contents;
8822 }
8823
8824 void CheckExit(int x, int y)
8825 {
8826   if (local_player->gems_still_needed > 0 ||
8827       local_player->sokobanfields_still_needed > 0 ||
8828       local_player->lights_still_needed > 0)
8829   {
8830     int element = Feld[x][y];
8831     int graphic = el2img(element);
8832
8833     if (IS_ANIMATED(graphic))
8834       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8835
8836     return;
8837   }
8838
8839   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8840     return;
8841
8842   Feld[x][y] = EL_EXIT_OPENING;
8843
8844   PlayLevelSoundNearest(x, y, SND_CLASS_EXIT_OPENING);
8845 }
8846
8847 void CheckExitEM(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_EM_EXIT_OPENING;
8866
8867   PlayLevelSoundNearest(x, y, SND_CLASS_EM_EXIT_OPENING);
8868 }
8869
8870 void CheckExitSteel(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_STEEL_EXIT_OPENING;
8889
8890   PlayLevelSoundNearest(x, y, SND_CLASS_STEEL_EXIT_OPENING);
8891 }
8892
8893 void CheckExitSteelEM(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_EM_STEEL_EXIT_OPENING;
8912
8913   PlayLevelSoundNearest(x, y, SND_CLASS_EM_STEEL_EXIT_OPENING);
8914 }
8915
8916 void CheckExitSP(int x, int y)
8917 {
8918   if (local_player->gems_still_needed > 0)
8919   {
8920     int element = Feld[x][y];
8921     int graphic = el2img(element);
8922
8923     if (IS_ANIMATED(graphic))
8924       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
8925
8926     return;
8927   }
8928
8929   if (AllPlayersGone)   /* do not re-open exit door closed after last player */
8930     return;
8931
8932   Feld[x][y] = EL_SP_EXIT_OPENING;
8933
8934   PlayLevelSoundNearest(x, y, SND_CLASS_SP_EXIT_OPENING);
8935 }
8936
8937 static void CloseAllOpenTimegates()
8938 {
8939   int x, y;
8940
8941   SCAN_PLAYFIELD(x, y)
8942   {
8943     int element = Feld[x][y];
8944
8945     if (element == EL_TIMEGATE_OPEN || element == EL_TIMEGATE_OPENING)
8946     {
8947       Feld[x][y] = EL_TIMEGATE_CLOSING;
8948
8949       PlayLevelSoundAction(x, y, ACTION_CLOSING);
8950     }
8951   }
8952 }
8953
8954 void DrawTwinkleOnField(int x, int y)
8955 {
8956   if (!IN_SCR_FIELD(SCREENX(x), SCREENY(y)) || IS_MOVING(x, y))
8957     return;
8958
8959   if (Feld[x][y] == EL_BD_DIAMOND)
8960     return;
8961
8962   if (MovDelay[x][y] == 0)      /* next animation frame */
8963     MovDelay[x][y] = 11 * !GetSimpleRandom(500);
8964
8965   if (MovDelay[x][y] != 0)      /* wait some time before next frame */
8966   {
8967     MovDelay[x][y]--;
8968
8969     DrawLevelElementAnimation(x, y, Feld[x][y]);
8970
8971     if (MovDelay[x][y] != 0)
8972     {
8973       int frame = getGraphicAnimationFrame(IMG_TWINKLE_WHITE,
8974                                            10 - MovDelay[x][y]);
8975
8976       DrawGraphicThruMask(SCREENX(x), SCREENY(y), IMG_TWINKLE_WHITE, frame);
8977     }
8978   }
8979 }
8980
8981 void MauerWaechst(int x, int y)
8982 {
8983   int delay = 6;
8984
8985   if (!MovDelay[x][y])          /* next animation frame */
8986     MovDelay[x][y] = 3 * delay;
8987
8988   if (MovDelay[x][y])           /* wait some time before next frame */
8989   {
8990     MovDelay[x][y]--;
8991
8992     if (IN_SCR_FIELD(SCREENX(x), SCREENY(y)))
8993     {
8994       int graphic = el_dir2img(Feld[x][y], GfxDir[x][y]);
8995       int frame = getGraphicAnimationFrame(graphic, 17 - MovDelay[x][y]);
8996
8997       DrawGraphic(SCREENX(x), SCREENY(y), graphic, frame);
8998     }
8999
9000     if (!MovDelay[x][y])
9001     {
9002       if (MovDir[x][y] == MV_LEFT)
9003       {
9004         if (IN_LEV_FIELD(x - 1, y) && IS_WALL(Feld[x - 1][y]))
9005           TEST_DrawLevelField(x - 1, y);
9006       }
9007       else if (MovDir[x][y] == MV_RIGHT)
9008       {
9009         if (IN_LEV_FIELD(x + 1, y) && IS_WALL(Feld[x + 1][y]))
9010           TEST_DrawLevelField(x + 1, y);
9011       }
9012       else if (MovDir[x][y] == MV_UP)
9013       {
9014         if (IN_LEV_FIELD(x, y - 1) && IS_WALL(Feld[x][y - 1]))
9015           TEST_DrawLevelField(x, y - 1);
9016       }
9017       else
9018       {
9019         if (IN_LEV_FIELD(x, y + 1) && IS_WALL(Feld[x][y + 1]))
9020           TEST_DrawLevelField(x, y + 1);
9021       }
9022
9023       Feld[x][y] = Store[x][y];
9024       Store[x][y] = 0;
9025       GfxDir[x][y] = MovDir[x][y] = MV_NONE;
9026       TEST_DrawLevelField(x, y);
9027     }
9028   }
9029 }
9030
9031 void MauerAbleger(int ax, int ay)
9032 {
9033   int element = Feld[ax][ay];
9034   int graphic = el2img(element);
9035   boolean oben_frei = FALSE, unten_frei = FALSE;
9036   boolean links_frei = FALSE, rechts_frei = FALSE;
9037   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9038   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9039   boolean new_wall = FALSE;
9040
9041   if (IS_ANIMATED(graphic))
9042     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9043
9044   if (!MovDelay[ax][ay])        /* start building new wall */
9045     MovDelay[ax][ay] = 6;
9046
9047   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9048   {
9049     MovDelay[ax][ay]--;
9050     if (MovDelay[ax][ay])
9051       return;
9052   }
9053
9054   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9055     oben_frei = TRUE;
9056   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9057     unten_frei = TRUE;
9058   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9059     links_frei = TRUE;
9060   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9061     rechts_frei = TRUE;
9062
9063   if (element == EL_EXPANDABLE_WALL_VERTICAL ||
9064       element == EL_EXPANDABLE_WALL_ANY)
9065   {
9066     if (oben_frei)
9067     {
9068       Feld[ax][ay-1] = EL_EXPANDABLE_WALL_GROWING;
9069       Store[ax][ay-1] = element;
9070       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9071       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9072         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9073                     IMG_EXPANDABLE_WALL_GROWING_UP, 0);
9074       new_wall = TRUE;
9075     }
9076     if (unten_frei)
9077     {
9078       Feld[ax][ay+1] = EL_EXPANDABLE_WALL_GROWING;
9079       Store[ax][ay+1] = element;
9080       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9081       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9082         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9083                     IMG_EXPANDABLE_WALL_GROWING_DOWN, 0);
9084       new_wall = TRUE;
9085     }
9086   }
9087
9088   if (element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9089       element == EL_EXPANDABLE_WALL_ANY ||
9090       element == EL_EXPANDABLE_WALL ||
9091       element == EL_BD_EXPANDABLE_WALL)
9092   {
9093     if (links_frei)
9094     {
9095       Feld[ax-1][ay] = EL_EXPANDABLE_WALL_GROWING;
9096       Store[ax-1][ay] = element;
9097       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9098       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9099         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9100                     IMG_EXPANDABLE_WALL_GROWING_LEFT, 0);
9101       new_wall = TRUE;
9102     }
9103
9104     if (rechts_frei)
9105     {
9106       Feld[ax+1][ay] = EL_EXPANDABLE_WALL_GROWING;
9107       Store[ax+1][ay] = element;
9108       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9109       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9110         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9111                     IMG_EXPANDABLE_WALL_GROWING_RIGHT, 0);
9112       new_wall = TRUE;
9113     }
9114   }
9115
9116   if (element == EL_EXPANDABLE_WALL && (links_frei || rechts_frei))
9117     TEST_DrawLevelField(ax, ay);
9118
9119   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9120     oben_massiv = TRUE;
9121   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9122     unten_massiv = TRUE;
9123   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9124     links_massiv = TRUE;
9125   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9126     rechts_massiv = TRUE;
9127
9128   if (((oben_massiv && unten_massiv) ||
9129        element == EL_EXPANDABLE_WALL_HORIZONTAL ||
9130        element == EL_EXPANDABLE_WALL) &&
9131       ((links_massiv && rechts_massiv) ||
9132        element == EL_EXPANDABLE_WALL_VERTICAL))
9133     Feld[ax][ay] = EL_WALL;
9134
9135   if (new_wall)
9136     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9137 }
9138
9139 void MauerAblegerStahl(int ax, int ay)
9140 {
9141   int element = Feld[ax][ay];
9142   int graphic = el2img(element);
9143   boolean oben_frei = FALSE, unten_frei = FALSE;
9144   boolean links_frei = FALSE, rechts_frei = FALSE;
9145   boolean oben_massiv = FALSE, unten_massiv = FALSE;
9146   boolean links_massiv = FALSE, rechts_massiv = FALSE;
9147   boolean new_wall = FALSE;
9148
9149   if (IS_ANIMATED(graphic))
9150     DrawLevelGraphicAnimationIfNeeded(ax, ay, graphic);
9151
9152   if (!MovDelay[ax][ay])        /* start building new wall */
9153     MovDelay[ax][ay] = 6;
9154
9155   if (MovDelay[ax][ay])         /* wait some time before building new wall */
9156   {
9157     MovDelay[ax][ay]--;
9158     if (MovDelay[ax][ay])
9159       return;
9160   }
9161
9162   if (IN_LEV_FIELD(ax, ay-1) && IS_FREE(ax, ay-1))
9163     oben_frei = TRUE;
9164   if (IN_LEV_FIELD(ax, ay+1) && IS_FREE(ax, ay+1))
9165     unten_frei = TRUE;
9166   if (IN_LEV_FIELD(ax-1, ay) && IS_FREE(ax-1, ay))
9167     links_frei = TRUE;
9168   if (IN_LEV_FIELD(ax+1, ay) && IS_FREE(ax+1, ay))
9169     rechts_frei = TRUE;
9170
9171   if (element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
9172       element == EL_EXPANDABLE_STEELWALL_ANY)
9173   {
9174     if (oben_frei)
9175     {
9176       Feld[ax][ay-1] = EL_EXPANDABLE_STEELWALL_GROWING;
9177       Store[ax][ay-1] = element;
9178       GfxDir[ax][ay-1] = MovDir[ax][ay-1] = MV_UP;
9179       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay-1)))
9180         DrawGraphic(SCREENX(ax), SCREENY(ay - 1),
9181                     IMG_EXPANDABLE_STEELWALL_GROWING_UP, 0);
9182       new_wall = TRUE;
9183     }
9184     if (unten_frei)
9185     {
9186       Feld[ax][ay+1] = EL_EXPANDABLE_STEELWALL_GROWING;
9187       Store[ax][ay+1] = element;
9188       GfxDir[ax][ay+1] = MovDir[ax][ay+1] = MV_DOWN;
9189       if (IN_SCR_FIELD(SCREENX(ax), SCREENY(ay+1)))
9190         DrawGraphic(SCREENX(ax), SCREENY(ay + 1),
9191                     IMG_EXPANDABLE_STEELWALL_GROWING_DOWN, 0);
9192       new_wall = TRUE;
9193     }
9194   }
9195
9196   if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
9197       element == EL_EXPANDABLE_STEELWALL_ANY)
9198   {
9199     if (links_frei)
9200     {
9201       Feld[ax-1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9202       Store[ax-1][ay] = element;
9203       GfxDir[ax-1][ay] = MovDir[ax-1][ay] = MV_LEFT;
9204       if (IN_SCR_FIELD(SCREENX(ax-1), SCREENY(ay)))
9205         DrawGraphic(SCREENX(ax - 1), SCREENY(ay),
9206                     IMG_EXPANDABLE_STEELWALL_GROWING_LEFT, 0);
9207       new_wall = TRUE;
9208     }
9209
9210     if (rechts_frei)
9211     {
9212       Feld[ax+1][ay] = EL_EXPANDABLE_STEELWALL_GROWING;
9213       Store[ax+1][ay] = element;
9214       GfxDir[ax+1][ay] = MovDir[ax+1][ay] = MV_RIGHT;
9215       if (IN_SCR_FIELD(SCREENX(ax+1), SCREENY(ay)))
9216         DrawGraphic(SCREENX(ax + 1), SCREENY(ay),
9217                     IMG_EXPANDABLE_STEELWALL_GROWING_RIGHT, 0);
9218       new_wall = TRUE;
9219     }
9220   }
9221
9222   if (!IN_LEV_FIELD(ax, ay-1) || IS_WALL(Feld[ax][ay-1]))
9223     oben_massiv = TRUE;
9224   if (!IN_LEV_FIELD(ax, ay+1) || IS_WALL(Feld[ax][ay+1]))
9225     unten_massiv = TRUE;
9226   if (!IN_LEV_FIELD(ax-1, ay) || IS_WALL(Feld[ax-1][ay]))
9227     links_massiv = TRUE;
9228   if (!IN_LEV_FIELD(ax+1, ay) || IS_WALL(Feld[ax+1][ay]))
9229     rechts_massiv = TRUE;
9230
9231   if (((oben_massiv && unten_massiv) ||
9232        element == EL_EXPANDABLE_STEELWALL_HORIZONTAL) &&
9233       ((links_massiv && rechts_massiv) ||
9234        element == EL_EXPANDABLE_STEELWALL_VERTICAL))
9235     Feld[ax][ay] = EL_STEELWALL;
9236
9237   if (new_wall)
9238     PlayLevelSoundAction(ax, ay, ACTION_GROWING);
9239 }
9240
9241 void CheckForDragon(int x, int y)
9242 {
9243   int i, j;
9244   boolean dragon_found = FALSE;
9245   static int xy[4][2] =
9246   {
9247     { 0, -1 },
9248     { -1, 0 },
9249     { +1, 0 },
9250     { 0, +1 }
9251   };
9252
9253   for (i = 0; i < NUM_DIRECTIONS; i++)
9254   {
9255     for (j = 0; j < 4; j++)
9256     {
9257       int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9258
9259       if (IN_LEV_FIELD(xx, yy) &&
9260           (Feld[xx][yy] == EL_FLAMES || Feld[xx][yy] == EL_DRAGON))
9261       {
9262         if (Feld[xx][yy] == EL_DRAGON)
9263           dragon_found = TRUE;
9264       }
9265       else
9266         break;
9267     }
9268   }
9269
9270   if (!dragon_found)
9271   {
9272     for (i = 0; i < NUM_DIRECTIONS; i++)
9273     {
9274       for (j = 0; j < 3; j++)
9275       {
9276         int xx = x + j * xy[i][0], yy = y + j * xy[i][1];
9277   
9278         if (IN_LEV_FIELD(xx, yy) && Feld[xx][yy] == EL_FLAMES)
9279         {
9280           Feld[xx][yy] = EL_EMPTY;
9281           TEST_DrawLevelField(xx, yy);
9282         }
9283         else
9284           break;
9285       }
9286     }
9287   }
9288 }
9289
9290 static void InitBuggyBase(int x, int y)
9291 {
9292   int element = Feld[x][y];
9293   int activating_delay = FRAMES_PER_SECOND / 4;
9294
9295   ChangeDelay[x][y] =
9296     (element == EL_SP_BUGGY_BASE ?
9297      2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND) - activating_delay :
9298      element == EL_SP_BUGGY_BASE_ACTIVATING ?
9299      activating_delay :
9300      element == EL_SP_BUGGY_BASE_ACTIVE ?
9301      1 * FRAMES_PER_SECOND + RND(1 * FRAMES_PER_SECOND) : 1);
9302 }
9303
9304 static void WarnBuggyBase(int x, int y)
9305 {
9306   int i;
9307   static int xy[4][2] =
9308   {
9309     { 0, -1 },
9310     { -1, 0 },
9311     { +1, 0 },
9312     { 0, +1 }
9313   };
9314
9315   for (i = 0; i < NUM_DIRECTIONS; i++)
9316   {
9317     int xx = x + xy[i][0];
9318     int yy = y + xy[i][1];
9319
9320     if (IN_LEV_FIELD(xx, yy) && IS_PLAYER(xx, yy))
9321     {
9322       PlayLevelSound(x, y, SND_SP_BUGGY_BASE_ACTIVE);
9323
9324       break;
9325     }
9326   }
9327 }
9328
9329 static void InitTrap(int x, int y)
9330 {
9331   ChangeDelay[x][y] = 2 * FRAMES_PER_SECOND + RND(5 * FRAMES_PER_SECOND);
9332 }
9333
9334 static void ActivateTrap(int x, int y)
9335 {
9336   PlayLevelSound(x, y, SND_TRAP_ACTIVATING);
9337 }
9338
9339 static void ChangeActiveTrap(int x, int y)
9340 {
9341   int graphic = IMG_TRAP_ACTIVE;
9342
9343   /* if new animation frame was drawn, correct crumbled sand border */
9344   if (IS_NEW_FRAME(GfxFrame[x][y], graphic))
9345     TEST_DrawLevelFieldCrumbled(x, y);
9346 }
9347
9348 static int getSpecialActionElement(int element, int number, int base_element)
9349 {
9350   return (element != EL_EMPTY ? element :
9351           number != -1 ? base_element + number - 1 :
9352           EL_EMPTY);
9353 }
9354
9355 static int getModifiedActionNumber(int value_old, int operator, int operand,
9356                                    int value_min, int value_max)
9357 {
9358   int value_new = (operator == CA_MODE_SET      ? operand :
9359                    operator == CA_MODE_ADD      ? value_old + operand :
9360                    operator == CA_MODE_SUBTRACT ? value_old - operand :
9361                    operator == CA_MODE_MULTIPLY ? value_old * operand :
9362                    operator == CA_MODE_DIVIDE   ? value_old / MAX(1, operand) :
9363                    operator == CA_MODE_MODULO   ? value_old % MAX(1, operand) :
9364                    value_old);
9365
9366   return (value_new < value_min ? value_min :
9367           value_new > value_max ? value_max :
9368           value_new);
9369 }
9370
9371 static void ExecuteCustomElementAction(int x, int y, int element, int page)
9372 {
9373   struct ElementInfo *ei = &element_info[element];
9374   struct ElementChangeInfo *change = &ei->change_page[page];
9375   int target_element = change->target_element;
9376   int action_type = change->action_type;
9377   int action_mode = change->action_mode;
9378   int action_arg = change->action_arg;
9379   int action_element = change->action_element;
9380   int i;
9381
9382   if (!change->has_action)
9383     return;
9384
9385   /* ---------- determine action paramater values -------------------------- */
9386
9387   int level_time_value =
9388     (level.time > 0 ? TimeLeft :
9389      TimePlayed);
9390
9391   int action_arg_element_raw =
9392     (action_arg == CA_ARG_PLAYER_TRIGGER  ? change->actual_trigger_player :
9393      action_arg == CA_ARG_ELEMENT_TRIGGER ? change->actual_trigger_element :
9394      action_arg == CA_ARG_ELEMENT_TARGET  ? change->target_element :
9395      action_arg == CA_ARG_ELEMENT_ACTION  ? change->action_element :
9396      action_arg == CA_ARG_INVENTORY_RM_TRIGGER ? change->actual_trigger_element:
9397      action_arg == CA_ARG_INVENTORY_RM_TARGET  ? change->target_element :
9398      action_arg == CA_ARG_INVENTORY_RM_ACTION  ? change->action_element :
9399      EL_EMPTY);
9400   int action_arg_element = GetElementFromGroupElement(action_arg_element_raw);
9401
9402   int action_arg_direction =
9403     (action_arg >= CA_ARG_DIRECTION_LEFT &&
9404      action_arg <= CA_ARG_DIRECTION_DOWN ? action_arg - CA_ARG_DIRECTION :
9405      action_arg == CA_ARG_DIRECTION_TRIGGER ?
9406      change->actual_trigger_side :
9407      action_arg == CA_ARG_DIRECTION_TRIGGER_BACK ?
9408      MV_DIR_OPPOSITE(change->actual_trigger_side) :
9409      MV_NONE);
9410
9411   int action_arg_number_min =
9412     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_NOT_MOVING :
9413      CA_ARG_MIN);
9414
9415   int action_arg_number_max =
9416     (action_type == CA_SET_PLAYER_SPEED ? STEPSIZE_EVEN_FASTER :
9417      action_type == CA_SET_LEVEL_GEMS ? 999 :
9418      action_type == CA_SET_LEVEL_TIME ? 9999 :
9419      action_type == CA_SET_LEVEL_SCORE ? 99999 :
9420      action_type == CA_SET_CE_VALUE ? 9999 :
9421      action_type == CA_SET_CE_SCORE ? 9999 :
9422      CA_ARG_MAX);
9423
9424   int action_arg_number_reset =
9425     (action_type == CA_SET_PLAYER_SPEED ? level.initial_player_stepsize[0] :
9426      action_type == CA_SET_LEVEL_GEMS ? level.gems_needed :
9427      action_type == CA_SET_LEVEL_TIME ? level.time :
9428      action_type == CA_SET_LEVEL_SCORE ? 0 :
9429      action_type == CA_SET_CE_VALUE ? GET_NEW_CE_VALUE(element) :
9430      action_type == CA_SET_CE_SCORE ? 0 :
9431      0);
9432
9433   int action_arg_number =
9434     (action_arg <= CA_ARG_MAX ? action_arg :
9435      action_arg >= CA_ARG_SPEED_NOT_MOVING &&
9436      action_arg <= CA_ARG_SPEED_EVEN_FASTER ? (action_arg - CA_ARG_SPEED) :
9437      action_arg == CA_ARG_SPEED_RESET ? action_arg_number_reset :
9438      action_arg == CA_ARG_NUMBER_MIN ? action_arg_number_min :
9439      action_arg == CA_ARG_NUMBER_MAX ? action_arg_number_max :
9440      action_arg == CA_ARG_NUMBER_RESET ? action_arg_number_reset :
9441      action_arg == CA_ARG_NUMBER_CE_VALUE ? CustomValue[x][y] :
9442      action_arg == CA_ARG_NUMBER_CE_SCORE ? ei->collect_score :
9443      action_arg == CA_ARG_NUMBER_CE_DELAY ? GET_CE_DELAY_VALUE(change) :
9444      action_arg == CA_ARG_NUMBER_LEVEL_TIME ? level_time_value :
9445      action_arg == CA_ARG_NUMBER_LEVEL_GEMS ? local_player->gems_still_needed :
9446      action_arg == CA_ARG_NUMBER_LEVEL_SCORE ? local_player->score :
9447      action_arg == CA_ARG_ELEMENT_CV_TARGET ? GET_NEW_CE_VALUE(target_element):
9448      action_arg == CA_ARG_ELEMENT_CV_TRIGGER ? change->actual_trigger_ce_value:
9449      action_arg == CA_ARG_ELEMENT_CV_ACTION ? GET_NEW_CE_VALUE(action_element):
9450      action_arg == CA_ARG_ELEMENT_CS_TARGET ? GET_CE_SCORE(target_element) :
9451      action_arg == CA_ARG_ELEMENT_CS_TRIGGER ? change->actual_trigger_ce_score:
9452      action_arg == CA_ARG_ELEMENT_CS_ACTION ? GET_CE_SCORE(action_element) :
9453      action_arg == CA_ARG_ELEMENT_NR_TARGET  ? change->target_element :
9454      action_arg == CA_ARG_ELEMENT_NR_TRIGGER ? change->actual_trigger_element :
9455      action_arg == CA_ARG_ELEMENT_NR_ACTION  ? change->action_element :
9456      -1);
9457
9458   int action_arg_number_old =
9459     (action_type == CA_SET_LEVEL_GEMS ? local_player->gems_still_needed :
9460      action_type == CA_SET_LEVEL_TIME ? TimeLeft :
9461      action_type == CA_SET_LEVEL_SCORE ? local_player->score :
9462      action_type == CA_SET_CE_VALUE ? CustomValue[x][y] :
9463      action_type == CA_SET_CE_SCORE ? ei->collect_score :
9464      0);
9465
9466   int action_arg_number_new =
9467     getModifiedActionNumber(action_arg_number_old,
9468                             action_mode, action_arg_number,
9469                             action_arg_number_min, action_arg_number_max);
9470
9471   int trigger_player_bits =
9472     (change->actual_trigger_player_bits != CH_PLAYER_NONE ?
9473      change->actual_trigger_player_bits : change->trigger_player);
9474
9475   int action_arg_player_bits =
9476     (action_arg >= CA_ARG_PLAYER_1 &&
9477      action_arg <= CA_ARG_PLAYER_4 ? action_arg - CA_ARG_PLAYER :
9478      action_arg == CA_ARG_PLAYER_TRIGGER ? trigger_player_bits :
9479      action_arg == CA_ARG_PLAYER_ACTION ? 1 << GET_PLAYER_NR(action_element) :
9480      PLAYER_BITS_ANY);
9481
9482   /* ---------- execute action  -------------------------------------------- */
9483
9484   switch (action_type)
9485   {
9486     case CA_NO_ACTION:
9487     {
9488       return;
9489     }
9490
9491     /* ---------- level actions  ------------------------------------------- */
9492
9493     case CA_RESTART_LEVEL:
9494     {
9495       game.restart_level = TRUE;
9496
9497       break;
9498     }
9499
9500     case CA_SHOW_ENVELOPE:
9501     {
9502       int element = getSpecialActionElement(action_arg_element,
9503                                             action_arg_number, EL_ENVELOPE_1);
9504
9505       if (IS_ENVELOPE(element))
9506         local_player->show_envelope = element;
9507
9508       break;
9509     }
9510
9511     case CA_SET_LEVEL_TIME:
9512     {
9513       if (level.time > 0)       /* only modify limited time value */
9514       {
9515         TimeLeft = action_arg_number_new;
9516
9517         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
9518
9519         DisplayGameControlValues();
9520
9521         if (!TimeLeft && setup.time_limit)
9522           for (i = 0; i < MAX_PLAYERS; i++)
9523             KillPlayer(&stored_player[i]);
9524       }
9525
9526       break;
9527     }
9528
9529     case CA_SET_LEVEL_SCORE:
9530     {
9531       local_player->score = action_arg_number_new;
9532
9533       game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
9534
9535       DisplayGameControlValues();
9536
9537       break;
9538     }
9539
9540     case CA_SET_LEVEL_GEMS:
9541     {
9542       local_player->gems_still_needed = action_arg_number_new;
9543
9544       game_panel_controls[GAME_PANEL_GEMS].value =
9545         local_player->gems_still_needed;
9546
9547       DisplayGameControlValues();
9548
9549       break;
9550     }
9551
9552     case CA_SET_LEVEL_WIND:
9553     {
9554       game.wind_direction = action_arg_direction;
9555
9556       break;
9557     }
9558
9559     case CA_SET_LEVEL_RANDOM_SEED:
9560     {
9561       /* ensure that setting a new random seed while playing is predictable */
9562       InitRND(action_arg_number_new ? action_arg_number_new : RND(1000000) + 1);
9563
9564       break;
9565     }
9566
9567     /* ---------- player actions  ------------------------------------------ */
9568
9569     case CA_MOVE_PLAYER:
9570     {
9571       /* automatically move to the next field in specified direction */
9572       for (i = 0; i < MAX_PLAYERS; i++)
9573         if (trigger_player_bits & (1 << i))
9574           stored_player[i].programmed_action = action_arg_direction;
9575
9576       break;
9577     }
9578
9579     case CA_EXIT_PLAYER:
9580     {
9581       for (i = 0; i < MAX_PLAYERS; i++)
9582         if (action_arg_player_bits & (1 << i))
9583           PlayerWins(&stored_player[i]);
9584
9585       break;
9586     }
9587
9588     case CA_KILL_PLAYER:
9589     {
9590       for (i = 0; i < MAX_PLAYERS; i++)
9591         if (action_arg_player_bits & (1 << i))
9592           KillPlayer(&stored_player[i]);
9593
9594       break;
9595     }
9596
9597     case CA_SET_PLAYER_KEYS:
9598     {
9599       int key_state = (action_mode == CA_MODE_ADD ? TRUE : FALSE);
9600       int element = getSpecialActionElement(action_arg_element,
9601                                             action_arg_number, EL_KEY_1);
9602
9603       if (IS_KEY(element))
9604       {
9605         for (i = 0; i < MAX_PLAYERS; i++)
9606         {
9607           if (trigger_player_bits & (1 << i))
9608           {
9609             stored_player[i].key[KEY_NR(element)] = key_state;
9610
9611             DrawGameDoorValues();
9612           }
9613         }
9614       }
9615
9616       break;
9617     }
9618
9619     case CA_SET_PLAYER_SPEED:
9620     {
9621       for (i = 0; i < MAX_PLAYERS; i++)
9622       {
9623         if (trigger_player_bits & (1 << i))
9624         {
9625           int move_stepsize = TILEX / stored_player[i].move_delay_value;
9626
9627           if (action_arg == CA_ARG_SPEED_FASTER &&
9628               stored_player[i].cannot_move)
9629           {
9630             action_arg_number = STEPSIZE_VERY_SLOW;
9631           }
9632           else if (action_arg == CA_ARG_SPEED_SLOWER ||
9633                    action_arg == CA_ARG_SPEED_FASTER)
9634           {
9635             action_arg_number = 2;
9636             action_mode = (action_arg == CA_ARG_SPEED_SLOWER ? CA_MODE_DIVIDE :
9637                            CA_MODE_MULTIPLY);
9638           }
9639           else if (action_arg == CA_ARG_NUMBER_RESET)
9640           {
9641             action_arg_number = level.initial_player_stepsize[i];
9642           }
9643
9644           move_stepsize =
9645             getModifiedActionNumber(move_stepsize,
9646                                     action_mode,
9647                                     action_arg_number,
9648                                     action_arg_number_min,
9649                                     action_arg_number_max);
9650
9651           SetPlayerMoveSpeed(&stored_player[i], move_stepsize, FALSE);
9652         }
9653       }
9654
9655       break;
9656     }
9657
9658     case CA_SET_PLAYER_SHIELD:
9659     {
9660       for (i = 0; i < MAX_PLAYERS; i++)
9661       {
9662         if (trigger_player_bits & (1 << i))
9663         {
9664           if (action_arg == CA_ARG_SHIELD_OFF)
9665           {
9666             stored_player[i].shield_normal_time_left = 0;
9667             stored_player[i].shield_deadly_time_left = 0;
9668           }
9669           else if (action_arg == CA_ARG_SHIELD_NORMAL)
9670           {
9671             stored_player[i].shield_normal_time_left = 999999;
9672           }
9673           else if (action_arg == CA_ARG_SHIELD_DEADLY)
9674           {
9675             stored_player[i].shield_normal_time_left = 999999;
9676             stored_player[i].shield_deadly_time_left = 999999;
9677           }
9678         }
9679       }
9680
9681       break;
9682     }
9683
9684     case CA_SET_PLAYER_GRAVITY:
9685     {
9686       for (i = 0; i < MAX_PLAYERS; i++)
9687       {
9688         if (trigger_player_bits & (1 << i))
9689         {
9690           stored_player[i].gravity =
9691             (action_arg == CA_ARG_GRAVITY_OFF    ? FALSE                     :
9692              action_arg == CA_ARG_GRAVITY_ON     ? TRUE                      :
9693              action_arg == CA_ARG_GRAVITY_TOGGLE ? !stored_player[i].gravity :
9694              stored_player[i].gravity);
9695         }
9696       }
9697
9698       break;
9699     }
9700
9701     case CA_SET_PLAYER_ARTWORK:
9702     {
9703       for (i = 0; i < MAX_PLAYERS; i++)
9704       {
9705         if (trigger_player_bits & (1 << i))
9706         {
9707           int artwork_element = action_arg_element;
9708
9709           if (action_arg == CA_ARG_ELEMENT_RESET)
9710             artwork_element =
9711               (level.use_artwork_element[i] ? level.artwork_element[i] :
9712                stored_player[i].element_nr);
9713
9714           if (stored_player[i].artwork_element != artwork_element)
9715             stored_player[i].Frame = 0;
9716
9717           stored_player[i].artwork_element = artwork_element;
9718
9719           SetPlayerWaiting(&stored_player[i], FALSE);
9720
9721           /* set number of special actions for bored and sleeping animation */
9722           stored_player[i].num_special_action_bored =
9723             get_num_special_action(artwork_element,
9724                                    ACTION_BORING_1, ACTION_BORING_LAST);
9725           stored_player[i].num_special_action_sleeping =
9726             get_num_special_action(artwork_element,
9727                                    ACTION_SLEEPING_1, ACTION_SLEEPING_LAST);
9728         }
9729       }
9730
9731       break;
9732     }
9733
9734     case CA_SET_PLAYER_INVENTORY:
9735     {
9736       for (i = 0; i < MAX_PLAYERS; i++)
9737       {
9738         struct PlayerInfo *player = &stored_player[i];
9739         int j, k;
9740
9741         if (trigger_player_bits & (1 << i))
9742         {
9743           int inventory_element = action_arg_element;
9744
9745           if (action_arg == CA_ARG_ELEMENT_TARGET ||
9746               action_arg == CA_ARG_ELEMENT_TRIGGER ||
9747               action_arg == CA_ARG_ELEMENT_ACTION)
9748           {
9749             int element = inventory_element;
9750             int collect_count = element_info[element].collect_count_initial;
9751
9752             if (!IS_CUSTOM_ELEMENT(element))
9753               collect_count = 1;
9754
9755             if (collect_count == 0)
9756               player->inventory_infinite_element = element;
9757             else
9758               for (k = 0; k < collect_count; k++)
9759                 if (player->inventory_size < MAX_INVENTORY_SIZE)
9760                   player->inventory_element[player->inventory_size++] =
9761                     element;
9762           }
9763           else if (action_arg == CA_ARG_INVENTORY_RM_TARGET ||
9764                    action_arg == CA_ARG_INVENTORY_RM_TRIGGER ||
9765                    action_arg == CA_ARG_INVENTORY_RM_ACTION)
9766           {
9767             if (player->inventory_infinite_element != EL_UNDEFINED &&
9768                 IS_EQUAL_OR_IN_GROUP(player->inventory_infinite_element,
9769                                      action_arg_element_raw))
9770               player->inventory_infinite_element = EL_UNDEFINED;
9771
9772             for (k = 0, j = 0; j < player->inventory_size; j++)
9773             {
9774               if (!IS_EQUAL_OR_IN_GROUP(player->inventory_element[j],
9775                                         action_arg_element_raw))
9776                 player->inventory_element[k++] = player->inventory_element[j];
9777             }
9778
9779             player->inventory_size = k;
9780           }
9781           else if (action_arg == CA_ARG_INVENTORY_RM_FIRST)
9782           {
9783             if (player->inventory_size > 0)
9784             {
9785               for (j = 0; j < player->inventory_size - 1; j++)
9786                 player->inventory_element[j] = player->inventory_element[j + 1];
9787
9788               player->inventory_size--;
9789             }
9790           }
9791           else if (action_arg == CA_ARG_INVENTORY_RM_LAST)
9792           {
9793             if (player->inventory_size > 0)
9794               player->inventory_size--;
9795           }
9796           else if (action_arg == CA_ARG_INVENTORY_RM_ALL)
9797           {
9798             player->inventory_infinite_element = EL_UNDEFINED;
9799             player->inventory_size = 0;
9800           }
9801           else if (action_arg == CA_ARG_INVENTORY_RESET)
9802           {
9803             player->inventory_infinite_element = EL_UNDEFINED;
9804             player->inventory_size = 0;
9805
9806             if (level.use_initial_inventory[i])
9807             {
9808               for (j = 0; j < level.initial_inventory_size[i]; j++)
9809               {
9810                 int element = level.initial_inventory_content[i][j];
9811                 int collect_count = element_info[element].collect_count_initial;
9812
9813                 if (!IS_CUSTOM_ELEMENT(element))
9814                   collect_count = 1;
9815
9816                 if (collect_count == 0)
9817                   player->inventory_infinite_element = element;
9818                 else
9819                   for (k = 0; k < collect_count; k++)
9820                     if (player->inventory_size < MAX_INVENTORY_SIZE)
9821                       player->inventory_element[player->inventory_size++] =
9822                         element;
9823               }
9824             }
9825           }
9826         }
9827       }
9828
9829       break;
9830     }
9831
9832     /* ---------- CE actions  ---------------------------------------------- */
9833
9834     case CA_SET_CE_VALUE:
9835     {
9836       int last_ce_value = CustomValue[x][y];
9837
9838       CustomValue[x][y] = action_arg_number_new;
9839
9840       if (CustomValue[x][y] != last_ce_value)
9841       {
9842         CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_CHANGES);
9843         CheckTriggeredElementChange(x, y, element, CE_VALUE_CHANGES_OF_X);
9844
9845         if (CustomValue[x][y] == 0)
9846         {
9847           CheckElementChange(x, y, element, EL_UNDEFINED, CE_VALUE_GETS_ZERO);
9848           CheckTriggeredElementChange(x, y, element, CE_VALUE_GETS_ZERO_OF_X);
9849         }
9850       }
9851
9852       break;
9853     }
9854
9855     case CA_SET_CE_SCORE:
9856     {
9857       int last_ce_score = ei->collect_score;
9858
9859       ei->collect_score = action_arg_number_new;
9860
9861       if (ei->collect_score != last_ce_score)
9862       {
9863         CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_CHANGES);
9864         CheckTriggeredElementChange(x, y, element, CE_SCORE_CHANGES_OF_X);
9865
9866         if (ei->collect_score == 0)
9867         {
9868           int xx, yy;
9869
9870           CheckElementChange(x, y, element, EL_UNDEFINED, CE_SCORE_GETS_ZERO);
9871           CheckTriggeredElementChange(x, y, element, CE_SCORE_GETS_ZERO_OF_X);
9872
9873           /*
9874             This is a very special case that seems to be a mixture between
9875             CheckElementChange() and CheckTriggeredElementChange(): while
9876             the first one only affects single elements that are triggered
9877             directly, the second one affects multiple elements in the playfield
9878             that are triggered indirectly by another element. This is a third
9879             case: Changing the CE score always affects multiple identical CEs,
9880             so every affected CE must be checked, not only the single CE for
9881             which the CE score was changed in the first place (as every instance
9882             of that CE shares the same CE score, and therefore also can change)!
9883           */
9884           SCAN_PLAYFIELD(xx, yy)
9885           {
9886             if (Feld[xx][yy] == element)
9887               CheckElementChange(xx, yy, element, EL_UNDEFINED,
9888                                  CE_SCORE_GETS_ZERO);
9889           }
9890         }
9891       }
9892
9893       break;
9894     }
9895
9896     case CA_SET_CE_ARTWORK:
9897     {
9898       int artwork_element = action_arg_element;
9899       boolean reset_frame = FALSE;
9900       int xx, yy;
9901
9902       if (action_arg == CA_ARG_ELEMENT_RESET)
9903         artwork_element = (ei->use_gfx_element ? ei->gfx_element_initial :
9904                            element);
9905
9906       if (ei->gfx_element != artwork_element)
9907         reset_frame = TRUE;
9908
9909       ei->gfx_element = artwork_element;
9910
9911       SCAN_PLAYFIELD(xx, yy)
9912       {
9913         if (Feld[xx][yy] == element)
9914         {
9915           if (reset_frame)
9916           {
9917             ResetGfxAnimation(xx, yy);
9918             ResetRandomAnimationValue(xx, yy);
9919           }
9920
9921           TEST_DrawLevelField(xx, yy);
9922         }
9923       }
9924
9925       break;
9926     }
9927
9928     /* ---------- engine actions  ------------------------------------------ */
9929
9930     case CA_SET_ENGINE_SCAN_MODE:
9931     {
9932       InitPlayfieldScanMode(action_arg);
9933
9934       break;
9935     }
9936
9937     default:
9938       break;
9939   }
9940 }
9941
9942 static void CreateFieldExt(int x, int y, int element, boolean is_change)
9943 {
9944   int old_element = Feld[x][y];
9945   int new_element = GetElementFromGroupElement(element);
9946   int previous_move_direction = MovDir[x][y];
9947   int last_ce_value = CustomValue[x][y];
9948   boolean player_explosion_protected = PLAYER_EXPLOSION_PROTECTED(x, y);
9949   boolean new_element_is_player = ELEM_IS_PLAYER(new_element);
9950   boolean add_player_onto_element = (new_element_is_player &&
9951                                      new_element != EL_SOKOBAN_FIELD_PLAYER &&
9952                                      IS_WALKABLE(old_element));
9953
9954   if (!add_player_onto_element)
9955   {
9956     if (IS_MOVING(x, y) || IS_BLOCKED(x, y))
9957       RemoveMovingField(x, y);
9958     else
9959       RemoveField(x, y);
9960
9961     Feld[x][y] = new_element;
9962
9963     if (element_info[new_element].move_direction_initial == MV_START_PREVIOUS)
9964       MovDir[x][y] = previous_move_direction;
9965
9966     if (element_info[new_element].use_last_ce_value)
9967       CustomValue[x][y] = last_ce_value;
9968
9969     InitField_WithBug1(x, y, FALSE);
9970
9971     new_element = Feld[x][y];   /* element may have changed */
9972
9973     ResetGfxAnimation(x, y);
9974     ResetRandomAnimationValue(x, y);
9975
9976     TEST_DrawLevelField(x, y);
9977
9978     if (GFX_CRUMBLED(new_element))
9979       TEST_DrawLevelFieldCrumbledNeighbours(x, y);
9980   }
9981
9982   /* check if element under the player changes from accessible to unaccessible
9983      (needed for special case of dropping element which then changes) */
9984   /* (must be checked after creating new element for walkable group elements) */
9985   if (IS_PLAYER(x, y) && !player_explosion_protected &&
9986       IS_ACCESSIBLE(old_element) && !IS_ACCESSIBLE(new_element))
9987   {
9988     Bang(x, y);
9989
9990     return;
9991   }
9992
9993   /* "ChangeCount" not set yet to allow "entered by player" change one time */
9994   if (new_element_is_player)
9995     RelocatePlayer(x, y, new_element);
9996
9997   if (is_change)
9998     ChangeCount[x][y]++;        /* count number of changes in the same frame */
9999
10000   TestIfBadThingTouchesPlayer(x, y);
10001   TestIfPlayerTouchesCustomElement(x, y);
10002   TestIfElementTouchesCustomElement(x, y);
10003 }
10004
10005 static void CreateField(int x, int y, int element)
10006 {
10007   CreateFieldExt(x, y, element, FALSE);
10008 }
10009
10010 static void CreateElementFromChange(int x, int y, int element)
10011 {
10012   element = GET_VALID_RUNTIME_ELEMENT(element);
10013
10014   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
10015   {
10016     int old_element = Feld[x][y];
10017
10018     /* prevent changed element from moving in same engine frame
10019        unless both old and new element can either fall or move */
10020     if ((!CAN_FALL(old_element) || !CAN_FALL(element)) &&
10021         (!CAN_MOVE(old_element) || !CAN_MOVE(element)))
10022       Stop[x][y] = TRUE;
10023   }
10024
10025   CreateFieldExt(x, y, element, TRUE);
10026 }
10027
10028 static boolean ChangeElement(int x, int y, int element, int page)
10029 {
10030   struct ElementInfo *ei = &element_info[element];
10031   struct ElementChangeInfo *change = &ei->change_page[page];
10032   int ce_value = CustomValue[x][y];
10033   int ce_score = ei->collect_score;
10034   int target_element;
10035   int old_element = Feld[x][y];
10036
10037   /* always use default change event to prevent running into a loop */
10038   if (ChangeEvent[x][y] == -1)
10039     ChangeEvent[x][y] = CE_DELAY;
10040
10041   if (ChangeEvent[x][y] == CE_DELAY)
10042   {
10043     /* reset actual trigger element, trigger player and action element */
10044     change->actual_trigger_element = EL_EMPTY;
10045     change->actual_trigger_player = EL_EMPTY;
10046     change->actual_trigger_player_bits = CH_PLAYER_NONE;
10047     change->actual_trigger_side = CH_SIDE_NONE;
10048     change->actual_trigger_ce_value = 0;
10049     change->actual_trigger_ce_score = 0;
10050   }
10051
10052   /* do not change elements more than a specified maximum number of changes */
10053   if (ChangeCount[x][y] >= game.max_num_changes_per_frame)
10054     return FALSE;
10055
10056   ChangeCount[x][y]++;          /* count number of changes in the same frame */
10057
10058   if (change->explode)
10059   {
10060     Bang(x, y);
10061
10062     return TRUE;
10063   }
10064
10065   if (change->use_target_content)
10066   {
10067     boolean complete_replace = TRUE;
10068     boolean can_replace[3][3];
10069     int xx, yy;
10070
10071     for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10072     {
10073       boolean is_empty;
10074       boolean is_walkable;
10075       boolean is_diggable;
10076       boolean is_collectible;
10077       boolean is_removable;
10078       boolean is_destructible;
10079       int ex = x + xx - 1;
10080       int ey = y + yy - 1;
10081       int content_element = change->target_content.e[xx][yy];
10082       int e;
10083
10084       can_replace[xx][yy] = TRUE;
10085
10086       if (ex == x && ey == y)   /* do not check changing element itself */
10087         continue;
10088
10089       if (content_element == EL_EMPTY_SPACE)
10090       {
10091         can_replace[xx][yy] = FALSE;    /* do not replace border with space */
10092
10093         continue;
10094       }
10095
10096       if (!IN_LEV_FIELD(ex, ey))
10097       {
10098         can_replace[xx][yy] = FALSE;
10099         complete_replace = FALSE;
10100
10101         continue;
10102       }
10103
10104       e = Feld[ex][ey];
10105
10106       if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10107         e = MovingOrBlocked2Element(ex, ey);
10108
10109       is_empty = (IS_FREE(ex, ey) ||
10110                   (IS_FREE_OR_PLAYER(ex, ey) && IS_WALKABLE(content_element)));
10111
10112       is_walkable     = (is_empty || IS_WALKABLE(e));
10113       is_diggable     = (is_empty || IS_DIGGABLE(e));
10114       is_collectible  = (is_empty || IS_COLLECTIBLE(e));
10115       is_destructible = (is_empty || !IS_INDESTRUCTIBLE(e));
10116       is_removable    = (is_diggable || is_collectible);
10117
10118       can_replace[xx][yy] =
10119         (((change->replace_when == CP_WHEN_EMPTY        && is_empty) ||
10120           (change->replace_when == CP_WHEN_WALKABLE     && is_walkable) ||
10121           (change->replace_when == CP_WHEN_DIGGABLE     && is_diggable) ||
10122           (change->replace_when == CP_WHEN_COLLECTIBLE  && is_collectible) ||
10123           (change->replace_when == CP_WHEN_REMOVABLE    && is_removable) ||
10124           (change->replace_when == CP_WHEN_DESTRUCTIBLE && is_destructible)) &&
10125          !(IS_PLAYER(ex, ey) && ELEM_IS_PLAYER(content_element)));
10126
10127       if (!can_replace[xx][yy])
10128         complete_replace = FALSE;
10129     }
10130
10131     if (!change->only_if_complete || complete_replace)
10132     {
10133       boolean something_has_changed = FALSE;
10134
10135       if (change->only_if_complete && change->use_random_replace &&
10136           RND(100) < change->random_percentage)
10137         return FALSE;
10138
10139       for (yy = 0; yy < 3; yy++) for (xx = 0; xx < 3 ; xx++)
10140       {
10141         int ex = x + xx - 1;
10142         int ey = y + yy - 1;
10143         int content_element;
10144
10145         if (can_replace[xx][yy] && (!change->use_random_replace ||
10146                                     RND(100) < change->random_percentage))
10147         {
10148           if (IS_MOVING(ex, ey) || IS_BLOCKED(ex, ey))
10149             RemoveMovingField(ex, ey);
10150
10151           ChangeEvent[ex][ey] = ChangeEvent[x][y];
10152
10153           content_element = change->target_content.e[xx][yy];
10154           target_element = GET_TARGET_ELEMENT(element, content_element, change,
10155                                               ce_value, ce_score);
10156
10157           CreateElementFromChange(ex, ey, target_element);
10158
10159           something_has_changed = TRUE;
10160
10161           /* for symmetry reasons, freeze newly created border elements */
10162           if (ex != x || ey != y)
10163             Stop[ex][ey] = TRUE;        /* no more moving in this frame */
10164         }
10165       }
10166
10167       if (something_has_changed)
10168       {
10169         PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10170         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10171       }
10172     }
10173   }
10174   else
10175   {
10176     target_element = GET_TARGET_ELEMENT(element, change->target_element, change,
10177                                         ce_value, ce_score);
10178
10179     if (element == EL_DIAGONAL_GROWING ||
10180         element == EL_DIAGONAL_SHRINKING)
10181     {
10182       target_element = Store[x][y];
10183
10184       Store[x][y] = EL_EMPTY;
10185     }
10186
10187     CreateElementFromChange(x, y, target_element);
10188
10189     PlayLevelSoundElementAction(x, y, element, ACTION_CHANGING);
10190     PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + page);
10191   }
10192
10193   /* this uses direct change before indirect change */
10194   CheckTriggeredElementChangeByPage(x, y, old_element, CE_CHANGE_OF_X, page);
10195
10196   return TRUE;
10197 }
10198
10199 static void HandleElementChange(int x, int y, int page)
10200 {
10201   int element = MovingOrBlocked2Element(x, y);
10202   struct ElementInfo *ei = &element_info[element];
10203   struct ElementChangeInfo *change = &ei->change_page[page];
10204   boolean handle_action_before_change = FALSE;
10205
10206 #ifdef DEBUG
10207   if (!CAN_CHANGE_OR_HAS_ACTION(element) &&
10208       !CAN_CHANGE_OR_HAS_ACTION(Back[x][y]))
10209   {
10210     printf("\n\n");
10211     printf("HandleElementChange(): %d,%d: element = %d ('%s')\n",
10212            x, y, element, element_info[element].token_name);
10213     printf("HandleElementChange(): This should never happen!\n");
10214     printf("\n\n");
10215   }
10216 #endif
10217
10218   /* this can happen with classic bombs on walkable, changing elements */
10219   if (!CAN_CHANGE_OR_HAS_ACTION(element))
10220   {
10221     return;
10222   }
10223
10224   if (ChangeDelay[x][y] == 0)           /* initialize element change */
10225   {
10226     ChangeDelay[x][y] = GET_CHANGE_DELAY(change) + 1;
10227
10228     if (change->can_change)
10229     {
10230       /* !!! not clear why graphic animation should be reset at all here !!! */
10231       /* !!! UPDATE: but is needed for correct Snake Bite tail animation !!! */
10232       /* when a custom element is about to change (for example by change delay),
10233          do not reset graphic animation when the custom element is moving */
10234       if (!IS_MOVING(x, y))
10235       {
10236         ResetGfxAnimation(x, y);
10237         ResetRandomAnimationValue(x, y);
10238       }
10239
10240       if (change->pre_change_function)
10241         change->pre_change_function(x, y);
10242     }
10243   }
10244
10245   ChangeDelay[x][y]--;
10246
10247   if (ChangeDelay[x][y] != 0)           /* continue element change */
10248   {
10249     if (change->can_change)
10250     {
10251       int graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
10252
10253       if (IS_ANIMATED(graphic))
10254         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
10255
10256       if (change->change_function)
10257         change->change_function(x, y);
10258     }
10259   }
10260   else                                  /* finish element change */
10261   {
10262     if (ChangePage[x][y] != -1)         /* remember page from delayed change */
10263     {
10264       page = ChangePage[x][y];
10265       ChangePage[x][y] = -1;
10266
10267       change = &ei->change_page[page];
10268     }
10269
10270     if (IS_MOVING(x, y))                /* never change a running system ;-) */
10271     {
10272       ChangeDelay[x][y] = 1;            /* try change after next move step */
10273       ChangePage[x][y] = page;          /* remember page to use for change */
10274
10275       return;
10276     }
10277
10278     /* special case: set new level random seed before changing element */
10279     if (change->has_action && change->action_type == CA_SET_LEVEL_RANDOM_SEED)
10280       handle_action_before_change = TRUE;
10281
10282     if (change->has_action && handle_action_before_change)
10283       ExecuteCustomElementAction(x, y, element, page);
10284
10285     if (change->can_change)
10286     {
10287       if (ChangeElement(x, y, element, page))
10288       {
10289         if (change->post_change_function)
10290           change->post_change_function(x, y);
10291       }
10292     }
10293
10294     if (change->has_action && !handle_action_before_change)
10295       ExecuteCustomElementAction(x, y, element, page);
10296   }
10297 }
10298
10299 static boolean CheckTriggeredElementChangeExt(int trigger_x, int trigger_y,
10300                                               int trigger_element,
10301                                               int trigger_event,
10302                                               int trigger_player,
10303                                               int trigger_side,
10304                                               int trigger_page)
10305 {
10306   boolean change_done_any = FALSE;
10307   int trigger_page_bits = (trigger_page < 0 ? CH_PAGE_ANY : 1 << trigger_page);
10308   int i;
10309
10310   if (!(trigger_events[trigger_element][trigger_event]))
10311     return FALSE;
10312
10313   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10314
10315   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
10316   {
10317     int element = EL_CUSTOM_START + i;
10318     boolean change_done = FALSE;
10319     int p;
10320
10321     if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10322         !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10323       continue;
10324
10325     for (p = 0; p < element_info[element].num_change_pages; p++)
10326     {
10327       struct ElementChangeInfo *change = &element_info[element].change_page[p];
10328
10329       if (change->can_change_or_has_action &&
10330           change->has_event[trigger_event] &&
10331           change->trigger_side & trigger_side &&
10332           change->trigger_player & trigger_player &&
10333           change->trigger_page & trigger_page_bits &&
10334           IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element))
10335       {
10336         change->actual_trigger_element = trigger_element;
10337         change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10338         change->actual_trigger_player_bits = trigger_player;
10339         change->actual_trigger_side = trigger_side;
10340         change->actual_trigger_ce_value = CustomValue[trigger_x][trigger_y];
10341         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10342
10343         if ((change->can_change && !change_done) || change->has_action)
10344         {
10345           int x, y;
10346
10347           SCAN_PLAYFIELD(x, y)
10348           {
10349             if (Feld[x][y] == element)
10350             {
10351               if (change->can_change && !change_done)
10352               {
10353                 /* if element already changed in this frame, not only prevent
10354                    another element change (checked in ChangeElement()), but
10355                    also prevent additional element actions for this element */
10356
10357                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10358                     !level.use_action_after_change_bug)
10359                   continue;
10360
10361                 ChangeDelay[x][y] = 1;
10362                 ChangeEvent[x][y] = trigger_event;
10363
10364                 HandleElementChange(x, y, p);
10365               }
10366               else if (change->has_action)
10367               {
10368                 /* if element already changed in this frame, not only prevent
10369                    another element change (checked in ChangeElement()), but
10370                    also prevent additional element actions for this element */
10371
10372                 if (ChangeCount[x][y] >= game.max_num_changes_per_frame &&
10373                     !level.use_action_after_change_bug)
10374                   continue;
10375
10376                 ExecuteCustomElementAction(x, y, element, p);
10377                 PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10378               }
10379             }
10380           }
10381
10382           if (change->can_change)
10383           {
10384             change_done = TRUE;
10385             change_done_any = TRUE;
10386           }
10387         }
10388       }
10389     }
10390   }
10391
10392   RECURSION_LOOP_DETECTION_END();
10393
10394   return change_done_any;
10395 }
10396
10397 static boolean CheckElementChangeExt(int x, int y,
10398                                      int element,
10399                                      int trigger_element,
10400                                      int trigger_event,
10401                                      int trigger_player,
10402                                      int trigger_side)
10403 {
10404   boolean change_done = FALSE;
10405   int p;
10406
10407   if (!CAN_CHANGE_OR_HAS_ACTION(element) ||
10408       !HAS_ANY_CHANGE_EVENT(element, trigger_event))
10409     return FALSE;
10410
10411   if (Feld[x][y] == EL_BLOCKED)
10412   {
10413     Blocked2Moving(x, y, &x, &y);
10414     element = Feld[x][y];
10415   }
10416
10417   /* check if element has already changed or is about to change after moving */
10418   if ((game.engine_version < VERSION_IDENT(3,2,0,7) &&
10419        Feld[x][y] != element) ||
10420
10421       (game.engine_version >= VERSION_IDENT(3,2,0,7) &&
10422        (ChangeCount[x][y] >= game.max_num_changes_per_frame ||
10423         ChangePage[x][y] != -1)))
10424     return FALSE;
10425
10426   RECURSION_LOOP_DETECTION_START(trigger_element, FALSE);
10427
10428   for (p = 0; p < element_info[element].num_change_pages; p++)
10429   {
10430     struct ElementChangeInfo *change = &element_info[element].change_page[p];
10431
10432     /* check trigger element for all events where the element that is checked
10433        for changing interacts with a directly adjacent element -- this is
10434        different to element changes that affect other elements to change on the
10435        whole playfield (which is handeld by CheckTriggeredElementChangeExt()) */
10436     boolean check_trigger_element =
10437       (trigger_event == CE_TOUCHING_X ||
10438        trigger_event == CE_HITTING_X ||
10439        trigger_event == CE_HIT_BY_X ||
10440        trigger_event == CE_DIGGING_X); /* this one was forgotten until 3.2.3 */
10441
10442     if (change->can_change_or_has_action &&
10443         change->has_event[trigger_event] &&
10444         change->trigger_side & trigger_side &&
10445         change->trigger_player & trigger_player &&
10446         (!check_trigger_element ||
10447          IS_EQUAL_OR_IN_GROUP(trigger_element, change->trigger_element)))
10448     {
10449       change->actual_trigger_element = trigger_element;
10450       change->actual_trigger_player = GET_PLAYER_FROM_BITS(trigger_player);
10451       change->actual_trigger_player_bits = trigger_player;
10452       change->actual_trigger_side = trigger_side;
10453       change->actual_trigger_ce_value = CustomValue[x][y];
10454       change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10455
10456       /* special case: trigger element not at (x,y) position for some events */
10457       if (check_trigger_element)
10458       {
10459         static struct
10460         {
10461           int dx, dy;
10462         } move_xy[] =
10463           {
10464             {  0,  0 },
10465             { -1,  0 },
10466             { +1,  0 },
10467             {  0,  0 },
10468             {  0, -1 },
10469             {  0,  0 }, { 0, 0 }, { 0, 0 },
10470             {  0, +1 }
10471           };
10472
10473         int xx = x + move_xy[MV_DIR_OPPOSITE(trigger_side)].dx;
10474         int yy = y + move_xy[MV_DIR_OPPOSITE(trigger_side)].dy;
10475
10476         change->actual_trigger_ce_value = CustomValue[xx][yy];
10477         change->actual_trigger_ce_score = GET_CE_SCORE(trigger_element);
10478       }
10479
10480       if (change->can_change && !change_done)
10481       {
10482         ChangeDelay[x][y] = 1;
10483         ChangeEvent[x][y] = trigger_event;
10484
10485         HandleElementChange(x, y, p);
10486
10487         change_done = TRUE;
10488       }
10489       else if (change->has_action)
10490       {
10491         ExecuteCustomElementAction(x, y, element, p);
10492         PlayLevelSoundElementAction(x, y, element, ACTION_PAGE_1 + p);
10493       }
10494     }
10495   }
10496
10497   RECURSION_LOOP_DETECTION_END();
10498
10499   return change_done;
10500 }
10501
10502 static void PlayPlayerSound(struct PlayerInfo *player)
10503 {
10504   int jx = player->jx, jy = player->jy;
10505   int sound_element = player->artwork_element;
10506   int last_action = player->last_action_waiting;
10507   int action = player->action_waiting;
10508
10509   if (player->is_waiting)
10510   {
10511     if (action != last_action)
10512       PlayLevelSoundElementAction(jx, jy, sound_element, action);
10513     else
10514       PlayLevelSoundElementActionIfLoop(jx, jy, sound_element, action);
10515   }
10516   else
10517   {
10518     if (action != last_action)
10519       StopSound(element_info[sound_element].sound[last_action]);
10520
10521     if (last_action == ACTION_SLEEPING)
10522       PlayLevelSoundElementAction(jx, jy, sound_element, ACTION_AWAKENING);
10523   }
10524 }
10525
10526 static void PlayAllPlayersSound()
10527 {
10528   int i;
10529
10530   for (i = 0; i < MAX_PLAYERS; i++)
10531     if (stored_player[i].active)
10532       PlayPlayerSound(&stored_player[i]);
10533 }
10534
10535 static void SetPlayerWaiting(struct PlayerInfo *player, boolean is_waiting)
10536 {
10537   boolean last_waiting = player->is_waiting;
10538   int move_dir = player->MovDir;
10539
10540   player->dir_waiting = move_dir;
10541   player->last_action_waiting = player->action_waiting;
10542
10543   if (is_waiting)
10544   {
10545     if (!last_waiting)          /* not waiting -> waiting */
10546     {
10547       player->is_waiting = TRUE;
10548
10549       player->frame_counter_bored =
10550         FrameCounter +
10551         game.player_boring_delay_fixed +
10552         GetSimpleRandom(game.player_boring_delay_random);
10553       player->frame_counter_sleeping =
10554         FrameCounter +
10555         game.player_sleeping_delay_fixed +
10556         GetSimpleRandom(game.player_sleeping_delay_random);
10557
10558       InitPlayerGfxAnimation(player, ACTION_WAITING, move_dir);
10559     }
10560
10561     if (game.player_sleeping_delay_fixed +
10562         game.player_sleeping_delay_random > 0 &&
10563         player->anim_delay_counter == 0 &&
10564         player->post_delay_counter == 0 &&
10565         FrameCounter >= player->frame_counter_sleeping)
10566       player->is_sleeping = TRUE;
10567     else if (game.player_boring_delay_fixed +
10568              game.player_boring_delay_random > 0 &&
10569              FrameCounter >= player->frame_counter_bored)
10570       player->is_bored = TRUE;
10571
10572     player->action_waiting = (player->is_sleeping ? ACTION_SLEEPING :
10573                               player->is_bored ? ACTION_BORING :
10574                               ACTION_WAITING);
10575
10576     if (player->is_sleeping && player->use_murphy)
10577     {
10578       /* special case for sleeping Murphy when leaning against non-free tile */
10579
10580       if (!IN_LEV_FIELD(player->jx - 1, player->jy) ||
10581           (Feld[player->jx - 1][player->jy] != EL_EMPTY &&
10582            !IS_MOVING(player->jx - 1, player->jy)))
10583         move_dir = MV_LEFT;
10584       else if (!IN_LEV_FIELD(player->jx + 1, player->jy) ||
10585                (Feld[player->jx + 1][player->jy] != EL_EMPTY &&
10586                 !IS_MOVING(player->jx + 1, player->jy)))
10587         move_dir = MV_RIGHT;
10588       else
10589         player->is_sleeping = FALSE;
10590
10591       player->dir_waiting = move_dir;
10592     }
10593
10594     if (player->is_sleeping)
10595     {
10596       if (player->num_special_action_sleeping > 0)
10597       {
10598         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10599         {
10600           int last_special_action = player->special_action_sleeping;
10601           int num_special_action = player->num_special_action_sleeping;
10602           int special_action =
10603             (last_special_action == ACTION_DEFAULT ? ACTION_SLEEPING_1 :
10604              last_special_action == ACTION_SLEEPING ? ACTION_SLEEPING :
10605              last_special_action < ACTION_SLEEPING_1 + num_special_action - 1 ?
10606              last_special_action + 1 : ACTION_SLEEPING);
10607           int special_graphic =
10608             el_act_dir2img(player->artwork_element, special_action, move_dir);
10609
10610           player->anim_delay_counter =
10611             graphic_info[special_graphic].anim_delay_fixed +
10612             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10613           player->post_delay_counter =
10614             graphic_info[special_graphic].post_delay_fixed +
10615             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10616
10617           player->special_action_sleeping = special_action;
10618         }
10619
10620         if (player->anim_delay_counter > 0)
10621         {
10622           player->action_waiting = player->special_action_sleeping;
10623           player->anim_delay_counter--;
10624         }
10625         else if (player->post_delay_counter > 0)
10626         {
10627           player->post_delay_counter--;
10628         }
10629       }
10630     }
10631     else if (player->is_bored)
10632     {
10633       if (player->num_special_action_bored > 0)
10634       {
10635         if (player->anim_delay_counter == 0 && player->post_delay_counter == 0)
10636         {
10637           int special_action =
10638             ACTION_BORING_1 + GetSimpleRandom(player->num_special_action_bored);
10639           int special_graphic =
10640             el_act_dir2img(player->artwork_element, special_action, move_dir);
10641
10642           player->anim_delay_counter =
10643             graphic_info[special_graphic].anim_delay_fixed +
10644             GetSimpleRandom(graphic_info[special_graphic].anim_delay_random);
10645           player->post_delay_counter =
10646             graphic_info[special_graphic].post_delay_fixed +
10647             GetSimpleRandom(graphic_info[special_graphic].post_delay_random);
10648
10649           player->special_action_bored = special_action;
10650         }
10651
10652         if (player->anim_delay_counter > 0)
10653         {
10654           player->action_waiting = player->special_action_bored;
10655           player->anim_delay_counter--;
10656         }
10657         else if (player->post_delay_counter > 0)
10658         {
10659           player->post_delay_counter--;
10660         }
10661       }
10662     }
10663   }
10664   else if (last_waiting)        /* waiting -> not waiting */
10665   {
10666     player->is_waiting = FALSE;
10667     player->is_bored = FALSE;
10668     player->is_sleeping = FALSE;
10669
10670     player->frame_counter_bored = -1;
10671     player->frame_counter_sleeping = -1;
10672
10673     player->anim_delay_counter = 0;
10674     player->post_delay_counter = 0;
10675
10676     player->dir_waiting = player->MovDir;
10677     player->action_waiting = ACTION_DEFAULT;
10678
10679     player->special_action_bored = ACTION_DEFAULT;
10680     player->special_action_sleeping = ACTION_DEFAULT;
10681   }
10682 }
10683
10684 static void CheckSaveEngineSnapshot(struct PlayerInfo *player)
10685 {
10686   static boolean player_was_moving = FALSE;
10687   static boolean player_was_snapping = FALSE;
10688   static boolean player_was_dropping = FALSE;
10689
10690   if ((!player->is_moving  && player_was_moving) ||
10691       (player->MovPos == 0 && player_was_moving) ||
10692       (player->is_snapping && !player_was_snapping) ||
10693       (player->is_dropping && !player_was_dropping))
10694   {
10695     if (!SaveEngineSnapshotToList())
10696       return;
10697
10698     player_was_moving = FALSE;
10699     player_was_snapping = TRUE;
10700     player_was_dropping = TRUE;
10701   }
10702   else
10703   {
10704     if (player->is_moving)
10705       player_was_moving = TRUE;
10706
10707     if (!player->is_snapping)
10708       player_was_snapping = FALSE;
10709
10710     if (!player->is_dropping)
10711       player_was_dropping = FALSE;
10712   }
10713 }
10714
10715 static void CheckSingleStepMode(struct PlayerInfo *player)
10716 {
10717   if (tape.single_step && tape.recording && !tape.pausing)
10718   {
10719     /* as it is called "single step mode", just return to pause mode when the
10720        player stopped moving after one tile (or never starts moving at all) */
10721     if (!player->is_moving && !player->is_pushing)
10722     {
10723       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
10724       SnapField(player, 0, 0);                  /* stop snapping */
10725     }
10726   }
10727
10728   CheckSaveEngineSnapshot(player);
10729 }
10730
10731 static byte PlayerActions(struct PlayerInfo *player, byte player_action)
10732 {
10733   int left      = player_action & JOY_LEFT;
10734   int right     = player_action & JOY_RIGHT;
10735   int up        = player_action & JOY_UP;
10736   int down      = player_action & JOY_DOWN;
10737   int button1   = player_action & JOY_BUTTON_1;
10738   int button2   = player_action & JOY_BUTTON_2;
10739   int dx        = (left ? -1 : right ? 1 : 0);
10740   int dy        = (up   ? -1 : down  ? 1 : 0);
10741
10742   if (!player->active || tape.pausing)
10743     return 0;
10744
10745   if (player_action)
10746   {
10747     if (button1)
10748       SnapField(player, dx, dy);
10749     else
10750     {
10751       if (button2)
10752         DropElement(player);
10753
10754       MovePlayer(player, dx, dy);
10755     }
10756
10757     CheckSingleStepMode(player);
10758
10759     SetPlayerWaiting(player, FALSE);
10760
10761     return player_action;
10762   }
10763   else
10764   {
10765     /* no actions for this player (no input at player's configured device) */
10766
10767     DigField(player, 0, 0, 0, 0, 0, 0, DF_NO_PUSH);
10768     SnapField(player, 0, 0);
10769     CheckGravityMovementWhenNotMoving(player);
10770
10771     if (player->MovPos == 0)
10772       SetPlayerWaiting(player, TRUE);
10773
10774     if (player->MovPos == 0)    /* needed for tape.playing */
10775       player->is_moving = FALSE;
10776
10777     player->is_dropping = FALSE;
10778     player->is_dropping_pressed = FALSE;
10779     player->drop_pressed_delay = 0;
10780
10781     CheckSingleStepMode(player);
10782
10783     return 0;
10784   }
10785 }
10786
10787 static void CheckLevelTime()
10788 {
10789   int i;
10790
10791   /* !!! SAME CODE AS IN "GameActions()" -- FIX THIS !!! */
10792   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10793   {
10794     if (level.native_em_level->lev->home == 0)  /* all players at home */
10795     {
10796       PlayerWins(local_player);
10797
10798       AllPlayersGone = TRUE;
10799
10800       level.native_em_level->lev->home = -1;
10801     }
10802
10803     if (level.native_em_level->ply[0]->alive == 0 &&
10804         level.native_em_level->ply[1]->alive == 0 &&
10805         level.native_em_level->ply[2]->alive == 0 &&
10806         level.native_em_level->ply[3]->alive == 0)      /* all dead */
10807       AllPlayersGone = TRUE;
10808   }
10809   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
10810   {
10811     if (game_sp.LevelSolved &&
10812         !game_sp.GameOver)                              /* game won */
10813     {
10814       PlayerWins(local_player);
10815
10816       game_sp.GameOver = TRUE;
10817
10818       AllPlayersGone = TRUE;
10819     }
10820
10821     if (game_sp.GameOver)                               /* game lost */
10822       AllPlayersGone = TRUE;
10823   }
10824
10825   if (TimeFrames >= FRAMES_PER_SECOND)
10826   {
10827     TimeFrames = 0;
10828     TapeTime++;
10829
10830     for (i = 0; i < MAX_PLAYERS; i++)
10831     {
10832       struct PlayerInfo *player = &stored_player[i];
10833
10834       if (SHIELD_ON(player))
10835       {
10836         player->shield_normal_time_left--;
10837
10838         if (player->shield_deadly_time_left > 0)
10839           player->shield_deadly_time_left--;
10840       }
10841     }
10842
10843     if (!local_player->LevelSolved && !level.use_step_counter)
10844     {
10845       TimePlayed++;
10846
10847       if (TimeLeft > 0)
10848       {
10849         TimeLeft--;
10850
10851         if (TimeLeft <= 10 && setup.time_limit)
10852           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
10853
10854         /* this does not make sense: game_panel_controls[GAME_PANEL_TIME].value
10855            is reset from other values in UpdateGameDoorValues() -- FIX THIS */
10856
10857         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
10858
10859         if (!TimeLeft && setup.time_limit)
10860         {
10861           if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
10862             level.native_em_level->lev->killed_out_of_time = TRUE;
10863           else
10864             for (i = 0; i < MAX_PLAYERS; i++)
10865               KillPlayer(&stored_player[i]);
10866         }
10867       }
10868       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
10869       {
10870         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
10871       }
10872
10873       level.native_em_level->lev->time =
10874         (game.no_time_limit ? TimePlayed : TimeLeft);
10875     }
10876
10877     if (tape.recording || tape.playing)
10878       DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
10879   }
10880
10881   if (tape.recording || tape.playing)
10882     DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
10883
10884   UpdateAndDisplayGameControlValues();
10885 }
10886
10887 void AdvanceFrameAndPlayerCounters(int player_nr)
10888 {
10889   int i;
10890
10891   /* advance frame counters (global frame counter and time frame counter) */
10892   FrameCounter++;
10893   TimeFrames++;
10894
10895   /* advance player counters (counters for move delay, move animation etc.) */
10896   for (i = 0; i < MAX_PLAYERS; i++)
10897   {
10898     boolean advance_player_counters = (player_nr == -1 || player_nr == i);
10899     int move_delay_value = stored_player[i].move_delay_value;
10900     int move_frames = MOVE_DELAY_NORMAL_SPEED / move_delay_value;
10901
10902     if (!advance_player_counters)       /* not all players may be affected */
10903       continue;
10904
10905     if (move_frames == 0)       /* less than one move per game frame */
10906     {
10907       int stepsize = TILEX / move_delay_value;
10908       int delay = move_delay_value / MOVE_DELAY_NORMAL_SPEED;
10909       int count = (stored_player[i].is_moving ?
10910                    ABS(stored_player[i].MovPos) / stepsize : FrameCounter);
10911
10912       if (count % delay == 0)
10913         move_frames = 1;
10914     }
10915
10916     stored_player[i].Frame += move_frames;
10917
10918     if (stored_player[i].MovPos != 0)
10919       stored_player[i].StepFrame += move_frames;
10920
10921     if (stored_player[i].move_delay > 0)
10922       stored_player[i].move_delay--;
10923
10924     /* due to bugs in previous versions, counter must count up, not down */
10925     if (stored_player[i].push_delay != -1)
10926       stored_player[i].push_delay++;
10927
10928     if (stored_player[i].drop_delay > 0)
10929       stored_player[i].drop_delay--;
10930
10931     if (stored_player[i].is_dropping_pressed)
10932       stored_player[i].drop_pressed_delay++;
10933   }
10934 }
10935
10936 void StartGameActions(boolean init_network_game, boolean record_tape,
10937                       int random_seed)
10938 {
10939   unsigned int new_random_seed = InitRND(random_seed);
10940
10941   if (record_tape)
10942     TapeStartRecording(new_random_seed);
10943
10944 #if defined(NETWORK_AVALIABLE)
10945   if (init_network_game)
10946   {
10947     SendToServer_StartPlaying();
10948
10949     return;
10950   }
10951 #endif
10952
10953   InitGame();
10954 }
10955
10956 void GameActions()
10957 {
10958   static unsigned int game_frame_delay = 0;
10959   unsigned int game_frame_delay_value;
10960   byte *recorded_player_action;
10961   byte summarized_player_action = 0;
10962   byte tape_action[MAX_PLAYERS];
10963   int i;
10964
10965   for (i = 0; i < MAX_PLAYERS; i++)
10966   {
10967     struct PlayerInfo *player = &stored_player[i];
10968
10969     // allow engine snapshot if movement attempt was stopped
10970     if ((game.snapshot.last_action[i] & KEY_MOTION) != 0 &&
10971         (player->action & KEY_MOTION) == 0)
10972       game.snapshot.changed_action = TRUE;
10973
10974     // allow engine snapshot in case of snapping/dropping attempt
10975     if ((game.snapshot.last_action[i] & KEY_BUTTON) == 0 &&
10976         (player->action & KEY_BUTTON) != 0)
10977       game.snapshot.changed_action = TRUE;
10978
10979     game.snapshot.last_action[i] = player->action;
10980   }
10981
10982   /* detect endless loops, caused by custom element programming */
10983   if (recursion_loop_detected && recursion_loop_depth == 0)
10984   {
10985     char *message = getStringCat3("Internal Error! Element ",
10986                                   EL_NAME(recursion_loop_element),
10987                                   " caused endless loop! Quit the game?");
10988
10989     Error(ERR_WARN, "element '%s' caused endless loop in game engine",
10990           EL_NAME(recursion_loop_element));
10991
10992     RequestQuitGameExt(FALSE, level_editor_test_game, message);
10993
10994     recursion_loop_detected = FALSE;    /* if game should be continued */
10995
10996     free(message);
10997
10998     return;
10999   }
11000
11001   if (game.restart_level)
11002     StartGameActions(options.network, setup.autorecord, level.random_seed);
11003
11004   /* !!! SAME CODE AS IN "CheckLevelTime()" -- FIX THIS !!! */
11005   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11006   {
11007     if (level.native_em_level->lev->home == 0)  /* all players at home */
11008     {
11009       PlayerWins(local_player);
11010
11011       AllPlayersGone = TRUE;
11012
11013       level.native_em_level->lev->home = -1;
11014     }
11015
11016     if (level.native_em_level->ply[0]->alive == 0 &&
11017         level.native_em_level->ply[1]->alive == 0 &&
11018         level.native_em_level->ply[2]->alive == 0 &&
11019         level.native_em_level->ply[3]->alive == 0)      /* all dead */
11020       AllPlayersGone = TRUE;
11021   }
11022   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11023   {
11024     if (game_sp.LevelSolved &&
11025         !game_sp.GameOver)                              /* game won */
11026     {
11027       PlayerWins(local_player);
11028
11029       game_sp.GameOver = TRUE;
11030
11031       AllPlayersGone = TRUE;
11032     }
11033
11034     if (game_sp.GameOver)                               /* game lost */
11035       AllPlayersGone = TRUE;
11036   }
11037
11038   if (local_player->LevelSolved && !local_player->LevelSolved_GameEnd)
11039     GameWon();
11040
11041   if (AllPlayersGone && !TAPE_IS_STOPPED(tape))
11042     TapeStop();
11043
11044   if (game_status != GAME_MODE_PLAYING)         /* status might have changed */
11045     return;
11046
11047   game_frame_delay_value =
11048     (tape.playing && tape.fast_forward ? FfwdFrameDelay : GameFrameDelay);
11049
11050   if (tape.playing && tape.warp_forward && !tape.pausing)
11051     game_frame_delay_value = 0;
11052
11053   /* ---------- main game synchronization point ---------- */
11054
11055   WaitUntilDelayReached(&game_frame_delay, game_frame_delay_value);
11056
11057   if (network_playing && !network_player_action_received)
11058   {
11059     /* try to get network player actions in time */
11060
11061 #if defined(NETWORK_AVALIABLE)
11062     /* last chance to get network player actions without main loop delay */
11063     HandleNetworking();
11064 #endif
11065
11066     /* game was quit by network peer */
11067     if (game_status != GAME_MODE_PLAYING)
11068       return;
11069
11070     if (!network_player_action_received)
11071       return;           /* failed to get network player actions in time */
11072
11073     /* do not yet reset "network_player_action_received" (for tape.pausing) */
11074   }
11075
11076   if (tape.pausing)
11077     return;
11078
11079   /* at this point we know that we really continue executing the game */
11080
11081   network_player_action_received = FALSE;
11082
11083   /* when playing tape, read previously recorded player input from tape data */
11084   recorded_player_action = (tape.playing ? TapePlayAction() : NULL);
11085
11086   /* TapePlayAction() may return NULL when toggling to "pause before death" */
11087   if (tape.pausing)
11088     return;
11089
11090   if (tape.set_centered_player)
11091   {
11092     game.centered_player_nr_next = tape.centered_player_nr_next;
11093     game.set_centered_player = TRUE;
11094   }
11095
11096   for (i = 0; i < MAX_PLAYERS; i++)
11097   {
11098     summarized_player_action |= stored_player[i].action;
11099
11100     if (!network_playing && (game.team_mode || tape.playing))
11101       stored_player[i].effective_action = stored_player[i].action;
11102   }
11103
11104 #if defined(NETWORK_AVALIABLE)
11105   if (network_playing)
11106     SendToServer_MovePlayer(summarized_player_action);
11107 #endif
11108
11109   if (!options.network && !game.team_mode)
11110     local_player->effective_action = summarized_player_action;
11111
11112   if (tape.recording &&
11113       setup.team_mode &&
11114       setup.input_on_focus &&
11115       game.centered_player_nr != -1)
11116   {
11117     for (i = 0; i < MAX_PLAYERS; i++)
11118       stored_player[i].effective_action =
11119         (i == game.centered_player_nr ? summarized_player_action : 0);
11120   }
11121
11122   if (recorded_player_action != NULL)
11123     for (i = 0; i < MAX_PLAYERS; i++)
11124       stored_player[i].effective_action = recorded_player_action[i];
11125
11126   for (i = 0; i < MAX_PLAYERS; i++)
11127   {
11128     tape_action[i] = stored_player[i].effective_action;
11129
11130     /* (this may happen in the RND game engine if a player was not present on
11131        the playfield on level start, but appeared later from a custom element */
11132     if (setup.team_mode &&
11133         tape.recording &&
11134         tape_action[i] &&
11135         !tape.player_participates[i])
11136       tape.player_participates[i] = TRUE;
11137   }
11138
11139   /* only record actions from input devices, but not programmed actions */
11140   if (tape.recording)
11141     TapeRecordAction(tape_action);
11142
11143 #if USE_NEW_PLAYER_ASSIGNMENTS
11144   // !!! also map player actions in single player mode !!!
11145   // if (game.team_mode)
11146   {
11147     byte mapped_action[MAX_PLAYERS];
11148
11149 #if DEBUG_PLAYER_ACTIONS
11150     printf(":::");
11151     for (i = 0; i < MAX_PLAYERS; i++)
11152       printf(" %d, ", stored_player[i].effective_action);
11153 #endif
11154
11155     for (i = 0; i < MAX_PLAYERS; i++)
11156       mapped_action[i] = stored_player[map_player_action[i]].effective_action;
11157
11158     for (i = 0; i < MAX_PLAYERS; i++)
11159       stored_player[i].effective_action = mapped_action[i];
11160
11161 #if DEBUG_PLAYER_ACTIONS
11162     printf(" =>");
11163     for (i = 0; i < MAX_PLAYERS; i++)
11164       printf(" %d, ", stored_player[i].effective_action);
11165     printf("\n");
11166 #endif
11167   }
11168 #if DEBUG_PLAYER_ACTIONS
11169   else
11170   {
11171     printf(":::");
11172     for (i = 0; i < MAX_PLAYERS; i++)
11173       printf(" %d, ", stored_player[i].effective_action);
11174     printf("\n");
11175   }
11176 #endif
11177 #endif
11178
11179   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
11180   {
11181     GameActions_EM_Main();
11182   }
11183   else if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
11184   {
11185     GameActions_SP_Main();
11186   }
11187   else
11188   {
11189     GameActions_RND();
11190   }
11191 }
11192
11193 void GameActions_EM_Main()
11194 {
11195   byte effective_action[MAX_PLAYERS];
11196   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11197   int i;
11198
11199   for (i = 0; i < MAX_PLAYERS; i++)
11200     effective_action[i] = stored_player[i].effective_action;
11201
11202   GameActions_EM(effective_action, warp_mode);
11203
11204   CheckLevelTime();
11205
11206   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11207 }
11208
11209 void GameActions_SP_Main()
11210 {
11211   byte effective_action[MAX_PLAYERS];
11212   boolean warp_mode = (tape.playing && tape.warp_forward && !tape.pausing);
11213   int i;
11214
11215   for (i = 0; i < MAX_PLAYERS; i++)
11216     effective_action[i] = stored_player[i].effective_action;
11217
11218   GameActions_SP(effective_action, warp_mode);
11219
11220   CheckLevelTime();
11221
11222   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11223 }
11224
11225 void GameActions_RND()
11226 {
11227   int magic_wall_x = 0, magic_wall_y = 0;
11228   int i, x, y, element, graphic;
11229
11230   InitPlayfieldScanModeVars();
11231
11232   if (game.engine_version >= VERSION_IDENT(3,2,0,7))
11233   {
11234     SCAN_PLAYFIELD(x, y)
11235     {
11236       ChangeCount[x][y] = 0;
11237       ChangeEvent[x][y] = -1;
11238     }
11239   }
11240
11241   if (game.set_centered_player)
11242   {
11243     boolean all_players_fit_to_screen = checkIfAllPlayersFitToScreen_RND();
11244
11245     /* switching to "all players" only possible if all players fit to screen */
11246     if (game.centered_player_nr_next == -1 && !all_players_fit_to_screen)
11247     {
11248       game.centered_player_nr_next = game.centered_player_nr;
11249       game.set_centered_player = FALSE;
11250     }
11251
11252     /* do not switch focus to non-existing (or non-active) player */
11253     if (game.centered_player_nr_next >= 0 &&
11254         !stored_player[game.centered_player_nr_next].active)
11255     {
11256       game.centered_player_nr_next = game.centered_player_nr;
11257       game.set_centered_player = FALSE;
11258     }
11259   }
11260
11261   if (game.set_centered_player &&
11262       ScreenMovPos == 0)        /* screen currently aligned at tile position */
11263   {
11264     int sx, sy;
11265
11266     if (game.centered_player_nr_next == -1)
11267     {
11268       setScreenCenteredToAllPlayers(&sx, &sy);
11269     }
11270     else
11271     {
11272       sx = stored_player[game.centered_player_nr_next].jx;
11273       sy = stored_player[game.centered_player_nr_next].jy;
11274     }
11275
11276     game.centered_player_nr = game.centered_player_nr_next;
11277     game.set_centered_player = FALSE;
11278
11279     DrawRelocateScreen(0, 0, sx, sy, MV_NONE, TRUE, setup.quick_switch);
11280     DrawGameDoorValues();
11281   }
11282
11283   for (i = 0; i < MAX_PLAYERS; i++)
11284   {
11285     int actual_player_action = stored_player[i].effective_action;
11286
11287 #if 1
11288     /* !!! THIS BREAKS THE FOLLOWING TAPES: !!!
11289        - rnd_equinox_tetrachloride 048
11290        - rnd_equinox_tetrachloride_ii 096
11291        - rnd_emanuel_schmieg 002
11292        - doctor_sloan_ww 001, 020
11293     */
11294     if (stored_player[i].MovPos == 0)
11295       CheckGravityMovement(&stored_player[i]);
11296 #endif
11297
11298     /* overwrite programmed action with tape action */
11299     if (stored_player[i].programmed_action)
11300       actual_player_action = stored_player[i].programmed_action;
11301
11302     PlayerActions(&stored_player[i], actual_player_action);
11303
11304     ScrollPlayer(&stored_player[i], SCROLL_GO_ON);
11305   }
11306
11307   ScrollScreen(NULL, SCROLL_GO_ON);
11308
11309   /* for backwards compatibility, the following code emulates a fixed bug that
11310      occured when pushing elements (causing elements that just made their last
11311      pushing step to already (if possible) make their first falling step in the
11312      same game frame, which is bad); this code is also needed to use the famous
11313      "spring push bug" which is used in older levels and might be wanted to be
11314      used also in newer levels, but in this case the buggy pushing code is only
11315      affecting the "spring" element and no other elements */
11316
11317   if (game.engine_version < VERSION_IDENT(2,2,0,7) || level.use_spring_bug)
11318   {
11319     for (i = 0; i < MAX_PLAYERS; i++)
11320     {
11321       struct PlayerInfo *player = &stored_player[i];
11322       int x = player->jx;
11323       int y = player->jy;
11324
11325       if (player->active && player->is_pushing && player->is_moving &&
11326           IS_MOVING(x, y) &&
11327           (game.engine_version < VERSION_IDENT(2,2,0,7) ||
11328            Feld[x][y] == EL_SPRING))
11329       {
11330         ContinueMoving(x, y);
11331
11332         /* continue moving after pushing (this is actually a bug) */
11333         if (!IS_MOVING(x, y))
11334           Stop[x][y] = FALSE;
11335       }
11336     }
11337   }
11338
11339   SCAN_PLAYFIELD(x, y)
11340   {
11341     ChangeCount[x][y] = 0;
11342     ChangeEvent[x][y] = -1;
11343
11344     /* this must be handled before main playfield loop */
11345     if (Feld[x][y] == EL_PLAYER_IS_LEAVING)
11346     {
11347       MovDelay[x][y]--;
11348       if (MovDelay[x][y] <= 0)
11349         RemoveField(x, y);
11350     }
11351
11352     if (Feld[x][y] == EL_ELEMENT_SNAPPING)
11353     {
11354       MovDelay[x][y]--;
11355       if (MovDelay[x][y] <= 0)
11356       {
11357         RemoveField(x, y);
11358         TEST_DrawLevelField(x, y);
11359
11360         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
11361       }
11362     }
11363
11364 #if DEBUG
11365     if (ChangePage[x][y] != -1 && ChangeDelay[x][y] != 1)
11366     {
11367       printf("GameActions(): x = %d, y = %d: ChangePage != -1\n", x, y);
11368       printf("GameActions(): This should never happen!\n");
11369
11370       ChangePage[x][y] = -1;
11371     }
11372 #endif
11373
11374     Stop[x][y] = FALSE;
11375     if (WasJustMoving[x][y] > 0)
11376       WasJustMoving[x][y]--;
11377     if (WasJustFalling[x][y] > 0)
11378       WasJustFalling[x][y]--;
11379     if (CheckCollision[x][y] > 0)
11380       CheckCollision[x][y]--;
11381     if (CheckImpact[x][y] > 0)
11382       CheckImpact[x][y]--;
11383
11384     GfxFrame[x][y]++;
11385
11386     /* reset finished pushing action (not done in ContinueMoving() to allow
11387        continuous pushing animation for elements with zero push delay) */
11388     if (GfxAction[x][y] == ACTION_PUSHING && !IS_MOVING(x, y))
11389     {
11390       ResetGfxAnimation(x, y);
11391       TEST_DrawLevelField(x, y);
11392     }
11393
11394 #if DEBUG
11395     if (IS_BLOCKED(x, y))
11396     {
11397       int oldx, oldy;
11398
11399       Blocked2Moving(x, y, &oldx, &oldy);
11400       if (!IS_MOVING(oldx, oldy))
11401       {
11402         printf("GameActions(): (BLOCKED => MOVING) context corrupted!\n");
11403         printf("GameActions(): BLOCKED: x = %d, y = %d\n", x, y);
11404         printf("GameActions(): !MOVING: oldx = %d, oldy = %d\n", oldx, oldy);
11405         printf("GameActions(): This should never happen!\n");
11406       }
11407     }
11408 #endif
11409   }
11410
11411   SCAN_PLAYFIELD(x, y)
11412   {
11413     element = Feld[x][y];
11414     graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11415
11416     ResetGfxFrame(x, y, TRUE);
11417
11418     if (ANIM_MODE(graphic) == ANIM_RANDOM &&
11419         IS_NEXT_FRAME(GfxFrame[x][y], graphic))
11420       ResetRandomAnimationValue(x, y);
11421
11422     SetRandomAnimationValue(x, y);
11423
11424     PlayLevelSoundActionIfLoop(x, y, GfxAction[x][y]);
11425
11426     if (IS_INACTIVE(element))
11427     {
11428       if (IS_ANIMATED(graphic))
11429         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11430
11431       continue;
11432     }
11433
11434     /* this may take place after moving, so 'element' may have changed */
11435     if (IS_CHANGING(x, y) &&
11436         (game.engine_version < VERSION_IDENT(3,0,7,1) || !Stop[x][y]))
11437     {
11438       int page = element_info[element].event_page_nr[CE_DELAY];
11439
11440       HandleElementChange(x, y, page);
11441
11442       element = Feld[x][y];
11443       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11444     }
11445
11446     if (!IS_MOVING(x, y) && (CAN_FALL(element) || CAN_MOVE(element)))
11447     {
11448       StartMoving(x, y);
11449
11450       element = Feld[x][y];
11451       graphic = el_act_dir2img(element, GfxAction[x][y], GfxDir[x][y]);
11452
11453       if (IS_ANIMATED(graphic) &&
11454           !IS_MOVING(x, y) &&
11455           !Stop[x][y])
11456         DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11457
11458       if (IS_GEM(element) || element == EL_SP_INFOTRON)
11459         TEST_DrawTwinkleOnField(x, y);
11460     }
11461     else if ((element == EL_ACID ||
11462               element == EL_EXIT_OPEN ||
11463               element == EL_EM_EXIT_OPEN ||
11464               element == EL_SP_EXIT_OPEN ||
11465               element == EL_STEEL_EXIT_OPEN ||
11466               element == EL_EM_STEEL_EXIT_OPEN ||
11467               element == EL_SP_TERMINAL ||
11468               element == EL_SP_TERMINAL_ACTIVE ||
11469               element == EL_EXTRA_TIME ||
11470               element == EL_SHIELD_NORMAL ||
11471               element == EL_SHIELD_DEADLY) &&
11472              IS_ANIMATED(graphic))
11473       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11474     else if (IS_MOVING(x, y))
11475       ContinueMoving(x, y);
11476     else if (IS_ACTIVE_BOMB(element))
11477       CheckDynamite(x, y);
11478     else if (element == EL_AMOEBA_GROWING)
11479       AmoebeWaechst(x, y);
11480     else if (element == EL_AMOEBA_SHRINKING)
11481       AmoebaDisappearing(x, y);
11482
11483 #if !USE_NEW_AMOEBA_CODE
11484     else if (IS_AMOEBALIVE(element))
11485       AmoebeAbleger(x, y);
11486 #endif
11487
11488     else if (element == EL_GAME_OF_LIFE || element == EL_BIOMAZE)
11489       Life(x, y);
11490     else if (element == EL_EXIT_CLOSED)
11491       CheckExit(x, y);
11492     else if (element == EL_EM_EXIT_CLOSED)
11493       CheckExitEM(x, y);
11494     else if (element == EL_STEEL_EXIT_CLOSED)
11495       CheckExitSteel(x, y);
11496     else if (element == EL_EM_STEEL_EXIT_CLOSED)
11497       CheckExitSteelEM(x, y);
11498     else if (element == EL_SP_EXIT_CLOSED)
11499       CheckExitSP(x, y);
11500     else if (element == EL_EXPANDABLE_WALL_GROWING ||
11501              element == EL_EXPANDABLE_STEELWALL_GROWING)
11502       MauerWaechst(x, y);
11503     else if (element == EL_EXPANDABLE_WALL ||
11504              element == EL_EXPANDABLE_WALL_HORIZONTAL ||
11505              element == EL_EXPANDABLE_WALL_VERTICAL ||
11506              element == EL_EXPANDABLE_WALL_ANY ||
11507              element == EL_BD_EXPANDABLE_WALL)
11508       MauerAbleger(x, y);
11509     else if (element == EL_EXPANDABLE_STEELWALL_HORIZONTAL ||
11510              element == EL_EXPANDABLE_STEELWALL_VERTICAL ||
11511              element == EL_EXPANDABLE_STEELWALL_ANY)
11512       MauerAblegerStahl(x, y);
11513     else if (element == EL_FLAMES)
11514       CheckForDragon(x, y);
11515     else if (element == EL_EXPLOSION)
11516       ; /* drawing of correct explosion animation is handled separately */
11517     else if (element == EL_ELEMENT_SNAPPING ||
11518              element == EL_DIAGONAL_SHRINKING ||
11519              element == EL_DIAGONAL_GROWING)
11520     {
11521       graphic = el_act_dir2img(GfxElement[x][y], GfxAction[x][y],GfxDir[x][y]);
11522
11523       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11524     }
11525     else if (IS_ANIMATED(graphic) && !IS_CHANGING(x, y))
11526       DrawLevelGraphicAnimationIfNeeded(x, y, graphic);
11527
11528     if (IS_BELT_ACTIVE(element))
11529       PlayLevelSoundAction(x, y, ACTION_ACTIVE);
11530
11531     if (game.magic_wall_active)
11532     {
11533       int jx = local_player->jx, jy = local_player->jy;
11534
11535       /* play the element sound at the position nearest to the player */
11536       if ((element == EL_MAGIC_WALL_FULL ||
11537            element == EL_MAGIC_WALL_ACTIVE ||
11538            element == EL_MAGIC_WALL_EMPTYING ||
11539            element == EL_BD_MAGIC_WALL_FULL ||
11540            element == EL_BD_MAGIC_WALL_ACTIVE ||
11541            element == EL_BD_MAGIC_WALL_EMPTYING ||
11542            element == EL_DC_MAGIC_WALL_FULL ||
11543            element == EL_DC_MAGIC_WALL_ACTIVE ||
11544            element == EL_DC_MAGIC_WALL_EMPTYING) &&
11545           ABS(x-jx) + ABS(y-jy) < ABS(magic_wall_x-jx) + ABS(magic_wall_y-jy))
11546       {
11547         magic_wall_x = x;
11548         magic_wall_y = y;
11549       }
11550     }
11551   }
11552
11553 #if USE_NEW_AMOEBA_CODE
11554   /* new experimental amoeba growth stuff */
11555   if (!(FrameCounter % 8))
11556   {
11557     static unsigned int random = 1684108901;
11558
11559     for (i = 0; i < level.amoeba_speed * 28 / 8; i++)
11560     {
11561       x = RND(lev_fieldx);
11562       y = RND(lev_fieldy);
11563       element = Feld[x][y];
11564
11565       if (!IS_PLAYER(x,y) &&
11566           (element == EL_EMPTY ||
11567            CAN_GROW_INTO(element) ||
11568            element == EL_QUICKSAND_EMPTY ||
11569            element == EL_QUICKSAND_FAST_EMPTY ||
11570            element == EL_ACID_SPLASH_LEFT ||
11571            element == EL_ACID_SPLASH_RIGHT))
11572       {
11573         if ((IN_LEV_FIELD(x, y-1) && Feld[x][y-1] == EL_AMOEBA_WET) ||
11574             (IN_LEV_FIELD(x-1, y) && Feld[x-1][y] == EL_AMOEBA_WET) ||
11575             (IN_LEV_FIELD(x+1, y) && Feld[x+1][y] == EL_AMOEBA_WET) ||
11576             (IN_LEV_FIELD(x, y+1) && Feld[x][y+1] == EL_AMOEBA_WET))
11577           Feld[x][y] = EL_AMOEBA_DROP;
11578       }
11579
11580       random = random * 129 + 1;
11581     }
11582   }
11583 #endif
11584
11585   game.explosions_delayed = FALSE;
11586
11587   SCAN_PLAYFIELD(x, y)
11588   {
11589     element = Feld[x][y];
11590
11591     if (ExplodeField[x][y])
11592       Explode(x, y, EX_PHASE_START, ExplodeField[x][y]);
11593     else if (element == EL_EXPLOSION)
11594       Explode(x, y, ExplodePhase[x][y], EX_TYPE_NORMAL);
11595
11596     ExplodeField[x][y] = EX_TYPE_NONE;
11597   }
11598
11599   game.explosions_delayed = TRUE;
11600
11601   if (game.magic_wall_active)
11602   {
11603     if (!(game.magic_wall_time_left % 4))
11604     {
11605       int element = Feld[magic_wall_x][magic_wall_y];
11606
11607       if (element == EL_BD_MAGIC_WALL_FULL ||
11608           element == EL_BD_MAGIC_WALL_ACTIVE ||
11609           element == EL_BD_MAGIC_WALL_EMPTYING)
11610         PlayLevelSound(magic_wall_x, magic_wall_y, SND_BD_MAGIC_WALL_ACTIVE);
11611       else if (element == EL_DC_MAGIC_WALL_FULL ||
11612                element == EL_DC_MAGIC_WALL_ACTIVE ||
11613                element == EL_DC_MAGIC_WALL_EMPTYING)
11614         PlayLevelSound(magic_wall_x, magic_wall_y, SND_DC_MAGIC_WALL_ACTIVE);
11615       else
11616         PlayLevelSound(magic_wall_x, magic_wall_y, SND_MAGIC_WALL_ACTIVE);
11617     }
11618
11619     if (game.magic_wall_time_left > 0)
11620     {
11621       game.magic_wall_time_left--;
11622
11623       if (!game.magic_wall_time_left)
11624       {
11625         SCAN_PLAYFIELD(x, y)
11626         {
11627           element = Feld[x][y];
11628
11629           if (element == EL_MAGIC_WALL_ACTIVE ||
11630               element == EL_MAGIC_WALL_FULL)
11631           {
11632             Feld[x][y] = EL_MAGIC_WALL_DEAD;
11633             TEST_DrawLevelField(x, y);
11634           }
11635           else if (element == EL_BD_MAGIC_WALL_ACTIVE ||
11636                    element == EL_BD_MAGIC_WALL_FULL)
11637           {
11638             Feld[x][y] = EL_BD_MAGIC_WALL_DEAD;
11639             TEST_DrawLevelField(x, y);
11640           }
11641           else if (element == EL_DC_MAGIC_WALL_ACTIVE ||
11642                    element == EL_DC_MAGIC_WALL_FULL)
11643           {
11644             Feld[x][y] = EL_DC_MAGIC_WALL_DEAD;
11645             TEST_DrawLevelField(x, y);
11646           }
11647         }
11648
11649         game.magic_wall_active = FALSE;
11650       }
11651     }
11652   }
11653
11654   if (game.light_time_left > 0)
11655   {
11656     game.light_time_left--;
11657
11658     if (game.light_time_left == 0)
11659       RedrawAllLightSwitchesAndInvisibleElements();
11660   }
11661
11662   if (game.timegate_time_left > 0)
11663   {
11664     game.timegate_time_left--;
11665
11666     if (game.timegate_time_left == 0)
11667       CloseAllOpenTimegates();
11668   }
11669
11670   if (game.lenses_time_left > 0)
11671   {
11672     game.lenses_time_left--;
11673
11674     if (game.lenses_time_left == 0)
11675       RedrawAllInvisibleElementsForLenses();
11676   }
11677
11678   if (game.magnify_time_left > 0)
11679   {
11680     game.magnify_time_left--;
11681
11682     if (game.magnify_time_left == 0)
11683       RedrawAllInvisibleElementsForMagnifier();
11684   }
11685
11686   for (i = 0; i < MAX_PLAYERS; i++)
11687   {
11688     struct PlayerInfo *player = &stored_player[i];
11689
11690     if (SHIELD_ON(player))
11691     {
11692       if (player->shield_deadly_time_left)
11693         PlayLevelSound(player->jx, player->jy, SND_SHIELD_DEADLY_ACTIVE);
11694       else if (player->shield_normal_time_left)
11695         PlayLevelSound(player->jx, player->jy, SND_SHIELD_NORMAL_ACTIVE);
11696     }
11697   }
11698
11699 #if USE_DELAYED_GFX_REDRAW
11700   SCAN_PLAYFIELD(x, y)
11701   {
11702     if (GfxRedraw[x][y] != GFX_REDRAW_NONE)
11703     {
11704       /* !!! PROBLEM: THIS REDRAWS THE PLAYFIELD _AFTER_ THE SCAN, BUT TILES
11705          !!! MAY HAVE CHANGED AFTER BEING DRAWN DURING PLAYFIELD SCAN !!! */
11706
11707       if (GfxRedraw[x][y] & GFX_REDRAW_TILE)
11708         DrawLevelField(x, y);
11709
11710       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED)
11711         DrawLevelFieldCrumbled(x, y);
11712
11713       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_CRUMBLED_NEIGHBOURS)
11714         DrawLevelFieldCrumbledNeighbours(x, y);
11715
11716       if (GfxRedraw[x][y] & GFX_REDRAW_TILE_TWINKLED)
11717         DrawTwinkleOnField(x, y);
11718     }
11719
11720     GfxRedraw[x][y] = GFX_REDRAW_NONE;
11721   }
11722 #endif
11723
11724   CheckLevelTime();
11725
11726   DrawAllPlayers();
11727   PlayAllPlayersSound();
11728
11729   if (options.debug)                    /* calculate frames per second */
11730   {
11731     static unsigned int fps_counter = 0;
11732     static int fps_frames = 0;
11733     unsigned int fps_delay_ms = Counter() - fps_counter;
11734
11735     fps_frames++;
11736
11737     if (fps_delay_ms >= 500)    /* calculate fps every 0.5 seconds */
11738     {
11739       global.frames_per_second = 1000 * (float)fps_frames / fps_delay_ms;
11740
11741       fps_frames = 0;
11742       fps_counter = Counter();
11743     }
11744
11745     redraw_mask |= REDRAW_FPS;
11746   }
11747
11748   AdvanceFrameAndPlayerCounters(-1);    /* advance counters for all players */
11749
11750   if (local_player->show_envelope != 0 && local_player->MovPos == 0)
11751   {
11752     ShowEnvelope(local_player->show_envelope - EL_ENVELOPE_1);
11753
11754     local_player->show_envelope = 0;
11755   }
11756
11757   /* use random number generator in every frame to make it less predictable */
11758   if (game.engine_version >= VERSION_IDENT(3,1,1,0))
11759     RND(1);
11760 }
11761
11762 static boolean AllPlayersInSight(struct PlayerInfo *player, int x, int y)
11763 {
11764   int min_x = x, min_y = y, max_x = x, max_y = y;
11765   int i;
11766
11767   for (i = 0; i < MAX_PLAYERS; i++)
11768   {
11769     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11770
11771     if (!stored_player[i].active || &stored_player[i] == player)
11772       continue;
11773
11774     min_x = MIN(min_x, jx);
11775     min_y = MIN(min_y, jy);
11776     max_x = MAX(max_x, jx);
11777     max_y = MAX(max_y, jy);
11778   }
11779
11780   return (max_x - min_x < SCR_FIELDX && max_y - min_y < SCR_FIELDY);
11781 }
11782
11783 static boolean AllPlayersInVisibleScreen()
11784 {
11785   int i;
11786
11787   for (i = 0; i < MAX_PLAYERS; i++)
11788   {
11789     int jx = stored_player[i].jx, jy = stored_player[i].jy;
11790
11791     if (!stored_player[i].active)
11792       continue;
11793
11794     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
11795       return FALSE;
11796   }
11797
11798   return TRUE;
11799 }
11800
11801 void ScrollLevel(int dx, int dy)
11802 {
11803   int softscroll_offset = (setup.soft_scrolling ? 2 * TILEX_VAR : 0);
11804   int x, y;
11805
11806   BlitBitmap(drawto_field, drawto_field,
11807              FX + TILEX_VAR * (dx == -1) - softscroll_offset,
11808              FY + TILEY_VAR * (dy == -1) - softscroll_offset,
11809              SXSIZE - TILEX_VAR * (dx != 0) + 2 * softscroll_offset,
11810              SYSIZE - TILEY_VAR * (dy != 0) + 2 * softscroll_offset,
11811              FX + TILEX_VAR * (dx == 1) - softscroll_offset,
11812              FY + TILEY_VAR * (dy == 1) - softscroll_offset);
11813
11814   if (dx != 0)
11815   {
11816     x = (dx == 1 ? BX1 : BX2);
11817     for (y = BY1; y <= BY2; y++)
11818       DrawScreenField(x, y);
11819   }
11820
11821   if (dy != 0)
11822   {
11823     y = (dy == 1 ? BY1 : BY2);
11824     for (x = BX1; x <= BX2; x++)
11825       DrawScreenField(x, y);
11826   }
11827
11828   redraw_mask |= REDRAW_FIELD;
11829 }
11830
11831 static boolean canFallDown(struct PlayerInfo *player)
11832 {
11833   int jx = player->jx, jy = player->jy;
11834
11835   return (IN_LEV_FIELD(jx, jy + 1) &&
11836           (IS_FREE(jx, jy + 1) ||
11837            (Feld[jx][jy + 1] == EL_ACID && player->can_fall_into_acid)) &&
11838           IS_WALKABLE_FROM(Feld[jx][jy], MV_DOWN) &&
11839           !IS_WALKABLE_INSIDE(Feld[jx][jy]));
11840 }
11841
11842 static boolean canPassField(int x, int y, int move_dir)
11843 {
11844   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11845   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11846   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11847   int nextx = x + dx;
11848   int nexty = y + dy;
11849   int element = Feld[x][y];
11850
11851   return (IS_PASSABLE_FROM(element, opposite_dir) &&
11852           !CAN_MOVE(element) &&
11853           IN_LEV_FIELD(nextx, nexty) && !IS_PLAYER(nextx, nexty) &&
11854           IS_WALKABLE_FROM(Feld[nextx][nexty], move_dir) &&
11855           (level.can_pass_to_walkable || IS_FREE(nextx, nexty)));
11856 }
11857
11858 static boolean canMoveToValidFieldWithGravity(int x, int y, int move_dir)
11859 {
11860   int opposite_dir = MV_DIR_OPPOSITE(move_dir);
11861   int dx = (move_dir & MV_LEFT ? -1 : move_dir & MV_RIGHT ? +1 : 0);
11862   int dy = (move_dir & MV_UP   ? -1 : move_dir & MV_DOWN  ? +1 : 0);
11863   int newx = x + dx;
11864   int newy = y + dy;
11865
11866   return (IN_LEV_FIELD(newx, newy) && !IS_FREE_OR_PLAYER(newx, newy) &&
11867           IS_GRAVITY_REACHABLE(Feld[newx][newy]) &&
11868           (IS_DIGGABLE(Feld[newx][newy]) ||
11869            IS_WALKABLE_FROM(Feld[newx][newy], opposite_dir) ||
11870            canPassField(newx, newy, move_dir)));
11871 }
11872
11873 static void CheckGravityMovement(struct PlayerInfo *player)
11874 {
11875   if (player->gravity && !player->programmed_action)
11876   {
11877     int move_dir_horizontal = player->effective_action & MV_HORIZONTAL;
11878     int move_dir_vertical   = player->effective_action & MV_VERTICAL;
11879     boolean player_is_snapping = (player->effective_action & JOY_BUTTON_1);
11880     int jx = player->jx, jy = player->jy;
11881     boolean player_is_moving_to_valid_field =
11882       (!player_is_snapping &&
11883        (canMoveToValidFieldWithGravity(jx, jy, move_dir_horizontal) ||
11884         canMoveToValidFieldWithGravity(jx, jy, move_dir_vertical)));
11885     boolean player_can_fall_down = canFallDown(player);
11886
11887     if (player_can_fall_down &&
11888         !player_is_moving_to_valid_field)
11889       player->programmed_action = MV_DOWN;
11890   }
11891 }
11892
11893 static void CheckGravityMovementWhenNotMoving(struct PlayerInfo *player)
11894 {
11895   return CheckGravityMovement(player);
11896
11897   if (player->gravity && !player->programmed_action)
11898   {
11899     int jx = player->jx, jy = player->jy;
11900     boolean field_under_player_is_free =
11901       (IN_LEV_FIELD(jx, jy + 1) && IS_FREE(jx, jy + 1));
11902     boolean player_is_standing_on_valid_field =
11903       (IS_WALKABLE_INSIDE(Feld[jx][jy]) ||
11904        (IS_WALKABLE(Feld[jx][jy]) &&
11905         !(element_info[Feld[jx][jy]].access_direction & MV_DOWN)));
11906
11907     if (field_under_player_is_free && !player_is_standing_on_valid_field)
11908       player->programmed_action = MV_DOWN;
11909   }
11910 }
11911
11912 /*
11913   MovePlayerOneStep()
11914   -----------------------------------------------------------------------------
11915   dx, dy:               direction (non-diagonal) to try to move the player to
11916   real_dx, real_dy:     direction as read from input device (can be diagonal)
11917 */
11918
11919 boolean MovePlayerOneStep(struct PlayerInfo *player,
11920                           int dx, int dy, int real_dx, int real_dy)
11921 {
11922   int jx = player->jx, jy = player->jy;
11923   int new_jx = jx + dx, new_jy = jy + dy;
11924   int can_move;
11925   boolean player_can_move = !player->cannot_move;
11926
11927   if (!player->active || (!dx && !dy))
11928     return MP_NO_ACTION;
11929
11930   player->MovDir = (dx < 0 ? MV_LEFT :
11931                     dx > 0 ? MV_RIGHT :
11932                     dy < 0 ? MV_UP :
11933                     dy > 0 ? MV_DOWN :  MV_NONE);
11934
11935   if (!IN_LEV_FIELD(new_jx, new_jy))
11936     return MP_NO_ACTION;
11937
11938   if (!player_can_move)
11939   {
11940     if (player->MovPos == 0)
11941     {
11942       player->is_moving = FALSE;
11943       player->is_digging = FALSE;
11944       player->is_collecting = FALSE;
11945       player->is_snapping = FALSE;
11946       player->is_pushing = FALSE;
11947     }
11948   }
11949
11950   if (!options.network && game.centered_player_nr == -1 &&
11951       !AllPlayersInSight(player, new_jx, new_jy))
11952     return MP_NO_ACTION;
11953
11954   can_move = DigField(player, jx, jy, new_jx, new_jy, real_dx,real_dy, DF_DIG);
11955   if (can_move != MP_MOVING)
11956     return can_move;
11957
11958   /* check if DigField() has caused relocation of the player */
11959   if (player->jx != jx || player->jy != jy)
11960     return MP_NO_ACTION;        /* <-- !!! CHECK THIS [-> MP_ACTION ?] !!! */
11961
11962   StorePlayer[jx][jy] = 0;
11963   player->last_jx = jx;
11964   player->last_jy = jy;
11965   player->jx = new_jx;
11966   player->jy = new_jy;
11967   StorePlayer[new_jx][new_jy] = player->element_nr;
11968
11969   if (player->move_delay_value_next != -1)
11970   {
11971     player->move_delay_value = player->move_delay_value_next;
11972     player->move_delay_value_next = -1;
11973   }
11974
11975   player->MovPos =
11976     (dx > 0 || dy > 0 ? -1 : 1) * (TILEX - TILEX / player->move_delay_value);
11977
11978   player->step_counter++;
11979
11980   PlayerVisit[jx][jy] = FrameCounter;
11981
11982   player->is_moving = TRUE;
11983
11984 #if 1
11985   /* should better be called in MovePlayer(), but this breaks some tapes */
11986   ScrollPlayer(player, SCROLL_INIT);
11987 #endif
11988
11989   return MP_MOVING;
11990 }
11991
11992 boolean MovePlayer(struct PlayerInfo *player, int dx, int dy)
11993 {
11994   int jx = player->jx, jy = player->jy;
11995   int old_jx = jx, old_jy = jy;
11996   int moved = MP_NO_ACTION;
11997
11998   if (!player->active)
11999     return FALSE;
12000
12001   if (!dx && !dy)
12002   {
12003     if (player->MovPos == 0)
12004     {
12005       player->is_moving = FALSE;
12006       player->is_digging = FALSE;
12007       player->is_collecting = FALSE;
12008       player->is_snapping = FALSE;
12009       player->is_pushing = FALSE;
12010     }
12011
12012     return FALSE;
12013   }
12014
12015   if (player->move_delay > 0)
12016     return FALSE;
12017
12018   player->move_delay = -1;              /* set to "uninitialized" value */
12019
12020   /* store if player is automatically moved to next field */
12021   player->is_auto_moving = (player->programmed_action != MV_NONE);
12022
12023   /* remove the last programmed player action */
12024   player->programmed_action = 0;
12025
12026   if (player->MovPos)
12027   {
12028     /* should only happen if pre-1.2 tape recordings are played */
12029     /* this is only for backward compatibility */
12030
12031     int original_move_delay_value = player->move_delay_value;
12032
12033 #if DEBUG
12034     printf("THIS SHOULD ONLY HAPPEN WITH PRE-1.2 LEVEL TAPES. [%d]\n",
12035            tape.counter);
12036 #endif
12037
12038     /* scroll remaining steps with finest movement resolution */
12039     player->move_delay_value = MOVE_DELAY_NORMAL_SPEED;
12040
12041     while (player->MovPos)
12042     {
12043       ScrollPlayer(player, SCROLL_GO_ON);
12044       ScrollScreen(NULL, SCROLL_GO_ON);
12045
12046       AdvanceFrameAndPlayerCounters(player->index_nr);
12047
12048       DrawAllPlayers();
12049       BackToFront();
12050     }
12051
12052     player->move_delay_value = original_move_delay_value;
12053   }
12054
12055   player->is_active = FALSE;
12056
12057   if (player->last_move_dir & MV_HORIZONTAL)
12058   {
12059     if (!(moved |= MovePlayerOneStep(player, 0, dy, dx, dy)))
12060       moved |= MovePlayerOneStep(player, dx, 0, dx, dy);
12061   }
12062   else
12063   {
12064     if (!(moved |= MovePlayerOneStep(player, dx, 0, dx, dy)))
12065       moved |= MovePlayerOneStep(player, 0, dy, dx, dy);
12066   }
12067
12068   if (!moved && !player->is_active)
12069   {
12070     player->is_moving = FALSE;
12071     player->is_digging = FALSE;
12072     player->is_collecting = FALSE;
12073     player->is_snapping = FALSE;
12074     player->is_pushing = FALSE;
12075   }
12076
12077   jx = player->jx;
12078   jy = player->jy;
12079
12080   if (moved & MP_MOVING && !ScreenMovPos &&
12081       (player->index_nr == game.centered_player_nr ||
12082        game.centered_player_nr == -1))
12083   {
12084     int old_scroll_x = scroll_x, old_scroll_y = scroll_y;
12085     int offset = game.scroll_delay_value;
12086
12087     if (!IN_VIS_FIELD(SCREENX(jx), SCREENY(jy)))
12088     {
12089       /* actual player has left the screen -- scroll in that direction */
12090       if (jx != old_jx)         /* player has moved horizontally */
12091         scroll_x += (jx - old_jx);
12092       else                      /* player has moved vertically */
12093         scroll_y += (jy - old_jy);
12094     }
12095     else
12096     {
12097       if (jx != old_jx)         /* player has moved horizontally */
12098       {
12099         if ((player->MovDir == MV_LEFT  && scroll_x > jx - MIDPOSX + offset) ||
12100             (player->MovDir == MV_RIGHT && scroll_x < jx - MIDPOSX - offset))
12101           scroll_x = jx-MIDPOSX + (scroll_x < jx-MIDPOSX ? -offset : +offset);
12102
12103         /* don't scroll over playfield boundaries */
12104         if (scroll_x < SBX_Left || scroll_x > SBX_Right)
12105           scroll_x = (scroll_x < SBX_Left ? SBX_Left : SBX_Right);
12106
12107         /* don't scroll more than one field at a time */
12108         scroll_x = old_scroll_x + SIGN(scroll_x - old_scroll_x);
12109
12110         /* don't scroll against the player's moving direction */
12111         if ((player->MovDir == MV_LEFT  && scroll_x > old_scroll_x) ||
12112             (player->MovDir == MV_RIGHT && scroll_x < old_scroll_x))
12113           scroll_x = old_scroll_x;
12114       }
12115       else                      /* player has moved vertically */
12116       {
12117         if ((player->MovDir == MV_UP   && scroll_y > jy - MIDPOSY + offset) ||
12118             (player->MovDir == MV_DOWN && scroll_y < jy - MIDPOSY - offset))
12119           scroll_y = jy-MIDPOSY + (scroll_y < jy-MIDPOSY ? -offset : +offset);
12120
12121         /* don't scroll over playfield boundaries */
12122         if (scroll_y < SBY_Upper || scroll_y > SBY_Lower)
12123           scroll_y = (scroll_y < SBY_Upper ? SBY_Upper : SBY_Lower);
12124
12125         /* don't scroll more than one field at a time */
12126         scroll_y = old_scroll_y + SIGN(scroll_y - old_scroll_y);
12127
12128         /* don't scroll against the player's moving direction */
12129         if ((player->MovDir == MV_UP   && scroll_y > old_scroll_y) ||
12130             (player->MovDir == MV_DOWN && scroll_y < old_scroll_y))
12131           scroll_y = old_scroll_y;
12132       }
12133     }
12134
12135     if (scroll_x != old_scroll_x || scroll_y != old_scroll_y)
12136     {
12137       if (!options.network && game.centered_player_nr == -1 &&
12138           !AllPlayersInVisibleScreen())
12139       {
12140         scroll_x = old_scroll_x;
12141         scroll_y = old_scroll_y;
12142       }
12143       else
12144       {
12145         ScrollScreen(player, SCROLL_INIT);
12146         ScrollLevel(old_scroll_x - scroll_x, old_scroll_y - scroll_y);
12147       }
12148     }
12149   }
12150
12151   player->StepFrame = 0;
12152
12153   if (moved & MP_MOVING)
12154   {
12155     if (old_jx != jx && old_jy == jy)
12156       player->MovDir = (old_jx < jx ? MV_RIGHT : MV_LEFT);
12157     else if (old_jx == jx && old_jy != jy)
12158       player->MovDir = (old_jy < jy ? MV_DOWN : MV_UP);
12159
12160     TEST_DrawLevelField(jx, jy);        /* for "crumbled sand" */
12161
12162     player->last_move_dir = player->MovDir;
12163     player->is_moving = TRUE;
12164     player->is_snapping = FALSE;
12165     player->is_switching = FALSE;
12166     player->is_dropping = FALSE;
12167     player->is_dropping_pressed = FALSE;
12168     player->drop_pressed_delay = 0;
12169
12170 #if 0
12171     /* should better be called here than above, but this breaks some tapes */
12172     ScrollPlayer(player, SCROLL_INIT);
12173 #endif
12174   }
12175   else
12176   {
12177     CheckGravityMovementWhenNotMoving(player);
12178
12179     player->is_moving = FALSE;
12180
12181     /* at this point, the player is allowed to move, but cannot move right now
12182        (e.g. because of something blocking the way) -- ensure that the player
12183        is also allowed to move in the next frame (in old versions before 3.1.1,
12184        the player was forced to wait again for eight frames before next try) */
12185
12186     if (game.engine_version >= VERSION_IDENT(3,1,1,0))
12187       player->move_delay = 0;   /* allow direct movement in the next frame */
12188   }
12189
12190   if (player->move_delay == -1)         /* not yet initialized by DigField() */
12191     player->move_delay = player->move_delay_value;
12192
12193   if (game.engine_version < VERSION_IDENT(3,0,7,0))
12194   {
12195     TestIfPlayerTouchesBadThing(jx, jy);
12196     TestIfPlayerTouchesCustomElement(jx, jy);
12197   }
12198
12199   if (!player->active)
12200     RemovePlayer(player);
12201
12202   return moved;
12203 }
12204
12205 void ScrollPlayer(struct PlayerInfo *player, int mode)
12206 {
12207   int jx = player->jx, jy = player->jy;
12208   int last_jx = player->last_jx, last_jy = player->last_jy;
12209   int move_stepsize = TILEX / player->move_delay_value;
12210
12211   if (!player->active)
12212     return;
12213
12214   if (player->MovPos == 0 && mode == SCROLL_GO_ON)      /* player not moving */
12215     return;
12216
12217   if (mode == SCROLL_INIT)
12218   {
12219     player->actual_frame_counter = FrameCounter;
12220     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12221
12222     if ((player->block_last_field || player->block_delay_adjustment > 0) &&
12223         Feld[last_jx][last_jy] == EL_EMPTY)
12224     {
12225       int last_field_block_delay = 0;   /* start with no blocking at all */
12226       int block_delay_adjustment = player->block_delay_adjustment;
12227
12228       /* if player blocks last field, add delay for exactly one move */
12229       if (player->block_last_field)
12230       {
12231         last_field_block_delay += player->move_delay_value;
12232
12233         /* when blocking enabled, prevent moving up despite gravity */
12234         if (player->gravity && player->MovDir == MV_UP)
12235           block_delay_adjustment = -1;
12236       }
12237
12238       /* add block delay adjustment (also possible when not blocking) */
12239       last_field_block_delay += block_delay_adjustment;
12240
12241       Feld[last_jx][last_jy] = EL_PLAYER_IS_LEAVING;
12242       MovDelay[last_jx][last_jy] = last_field_block_delay + 1;
12243     }
12244
12245     if (player->MovPos != 0)    /* player has not yet reached destination */
12246       return;
12247   }
12248   else if (!FrameReached(&player->actual_frame_counter, 1))
12249     return;
12250
12251   if (player->MovPos != 0)
12252   {
12253     player->MovPos += (player->MovPos > 0 ? -1 : 1) * move_stepsize;
12254     player->GfxPos = move_stepsize * (player->MovPos / move_stepsize);
12255
12256     /* before DrawPlayer() to draw correct player graphic for this case */
12257     if (player->MovPos == 0)
12258       CheckGravityMovement(player);
12259   }
12260
12261   if (player->MovPos == 0)      /* player reached destination field */
12262   {
12263     if (player->move_delay_reset_counter > 0)
12264     {
12265       player->move_delay_reset_counter--;
12266
12267       if (player->move_delay_reset_counter == 0)
12268       {
12269         /* continue with normal speed after quickly moving through gate */
12270         HALVE_PLAYER_SPEED(player);
12271
12272         /* be able to make the next move without delay */
12273         player->move_delay = 0;
12274       }
12275     }
12276
12277     player->last_jx = jx;
12278     player->last_jy = jy;
12279
12280     if (Feld[jx][jy] == EL_EXIT_OPEN ||
12281         Feld[jx][jy] == EL_EM_EXIT_OPEN ||
12282         Feld[jx][jy] == EL_EM_EXIT_OPENING ||
12283         Feld[jx][jy] == EL_STEEL_EXIT_OPEN ||
12284         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPEN ||
12285         Feld[jx][jy] == EL_EM_STEEL_EXIT_OPENING ||
12286         Feld[jx][jy] == EL_SP_EXIT_OPEN ||
12287         Feld[jx][jy] == EL_SP_EXIT_OPENING)     /* <-- special case */
12288     {
12289       DrawPlayer(player);       /* needed here only to cleanup last field */
12290       RemovePlayer(player);
12291
12292       if (local_player->friends_still_needed == 0 ||
12293           IS_SP_ELEMENT(Feld[jx][jy]))
12294         PlayerWins(player);
12295     }
12296
12297     /* this breaks one level: "machine", level 000 */
12298     {
12299       int move_direction = player->MovDir;
12300       int enter_side = MV_DIR_OPPOSITE(move_direction);
12301       int leave_side = move_direction;
12302       int old_jx = last_jx;
12303       int old_jy = last_jy;
12304       int old_element = Feld[old_jx][old_jy];
12305       int new_element = Feld[jx][jy];
12306
12307       if (IS_CUSTOM_ELEMENT(old_element))
12308         CheckElementChangeByPlayer(old_jx, old_jy, old_element,
12309                                    CE_LEFT_BY_PLAYER,
12310                                    player->index_bit, leave_side);
12311
12312       CheckTriggeredElementChangeByPlayer(old_jx, old_jy, old_element,
12313                                           CE_PLAYER_LEAVES_X,
12314                                           player->index_bit, leave_side);
12315
12316       if (IS_CUSTOM_ELEMENT(new_element))
12317         CheckElementChangeByPlayer(jx, jy, new_element, CE_ENTERED_BY_PLAYER,
12318                                    player->index_bit, enter_side);
12319
12320       CheckTriggeredElementChangeByPlayer(jx, jy, new_element,
12321                                           CE_PLAYER_ENTERS_X,
12322                                           player->index_bit, enter_side);
12323
12324       CheckTriggeredElementChangeBySide(jx, jy, player->initial_element,
12325                                         CE_MOVE_OF_X, move_direction);
12326     }
12327
12328     if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12329     {
12330       TestIfPlayerTouchesBadThing(jx, jy);
12331       TestIfPlayerTouchesCustomElement(jx, jy);
12332
12333       /* needed because pushed element has not yet reached its destination,
12334          so it would trigger a change event at its previous field location */
12335       if (!player->is_pushing)
12336         TestIfElementTouchesCustomElement(jx, jy);      /* for empty space */
12337
12338       if (!player->active)
12339         RemovePlayer(player);
12340     }
12341
12342     if (!local_player->LevelSolved && level.use_step_counter)
12343     {
12344       int i;
12345
12346       TimePlayed++;
12347
12348       if (TimeLeft > 0)
12349       {
12350         TimeLeft--;
12351
12352         if (TimeLeft <= 10 && setup.time_limit)
12353           PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
12354
12355         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
12356
12357         DisplayGameControlValues();
12358
12359         if (!TimeLeft && setup.time_limit)
12360           for (i = 0; i < MAX_PLAYERS; i++)
12361             KillPlayer(&stored_player[i]);
12362       }
12363       else if (game.no_time_limit && !AllPlayersGone) /* level w/o time limit */
12364       {
12365         game_panel_controls[GAME_PANEL_TIME].value = TimePlayed;
12366
12367         DisplayGameControlValues();
12368       }
12369     }
12370
12371     if (tape.single_step && tape.recording && !tape.pausing &&
12372         !player->programmed_action)
12373       TapeTogglePause(TAPE_TOGGLE_AUTOMATIC);
12374
12375     if (!player->programmed_action)
12376       CheckSaveEngineSnapshot(player);
12377   }
12378 }
12379
12380 void ScrollScreen(struct PlayerInfo *player, int mode)
12381 {
12382   static unsigned int screen_frame_counter = 0;
12383
12384   if (mode == SCROLL_INIT)
12385   {
12386     /* set scrolling step size according to actual player's moving speed */
12387     ScrollStepSize = TILEX / player->move_delay_value;
12388
12389     screen_frame_counter = FrameCounter;
12390     ScreenMovDir = player->MovDir;
12391     ScreenMovPos = player->MovPos;
12392     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12393     return;
12394   }
12395   else if (!FrameReached(&screen_frame_counter, 1))
12396     return;
12397
12398   if (ScreenMovPos)
12399   {
12400     ScreenMovPos += (ScreenMovPos > 0 ? -1 : 1) * ScrollStepSize;
12401     ScreenGfxPos = ScrollStepSize * (ScreenMovPos / ScrollStepSize);
12402     redraw_mask |= REDRAW_FIELD;
12403   }
12404   else
12405     ScreenMovDir = MV_NONE;
12406 }
12407
12408 void TestIfPlayerTouchesCustomElement(int x, int y)
12409 {
12410   static int xy[4][2] =
12411   {
12412     { 0, -1 },
12413     { -1, 0 },
12414     { +1, 0 },
12415     { 0, +1 }
12416   };
12417   static int trigger_sides[4][2] =
12418   {
12419     /* center side       border side */
12420     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12421     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12422     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12423     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12424   };
12425   static int touch_dir[4] =
12426   {
12427     MV_LEFT | MV_RIGHT,
12428     MV_UP   | MV_DOWN,
12429     MV_UP   | MV_DOWN,
12430     MV_LEFT | MV_RIGHT
12431   };
12432   int center_element = Feld[x][y];      /* should always be non-moving! */
12433   int i;
12434
12435   for (i = 0; i < NUM_DIRECTIONS; i++)
12436   {
12437     int xx = x + xy[i][0];
12438     int yy = y + xy[i][1];
12439     int center_side = trigger_sides[i][0];
12440     int border_side = trigger_sides[i][1];
12441     int border_element;
12442
12443     if (!IN_LEV_FIELD(xx, yy))
12444       continue;
12445
12446     if (IS_PLAYER(x, y))                /* player found at center element */
12447     {
12448       struct PlayerInfo *player = PLAYERINFO(x, y);
12449
12450       if (game.engine_version < VERSION_IDENT(3,0,7,0))
12451         border_element = Feld[xx][yy];          /* may be moving! */
12452       else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12453         border_element = Feld[xx][yy];
12454       else if (MovDir[xx][yy] & touch_dir[i])   /* elements are touching */
12455         border_element = MovingOrBlocked2Element(xx, yy);
12456       else
12457         continue;               /* center and border element do not touch */
12458
12459       CheckElementChangeByPlayer(xx, yy, border_element, CE_TOUCHED_BY_PLAYER,
12460                                  player->index_bit, border_side);
12461       CheckTriggeredElementChangeByPlayer(xx, yy, border_element,
12462                                           CE_PLAYER_TOUCHES_X,
12463                                           player->index_bit, border_side);
12464
12465       {
12466         /* use player element that is initially defined in the level playfield,
12467            not the player element that corresponds to the runtime player number
12468            (example: a level that contains EL_PLAYER_3 as the only player would
12469            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12470         int player_element = PLAYERINFO(x, y)->initial_element;
12471
12472         CheckElementChangeBySide(xx, yy, border_element, player_element,
12473                                  CE_TOUCHING_X, border_side);
12474       }
12475     }
12476     else if (IS_PLAYER(xx, yy))         /* player found at border element */
12477     {
12478       struct PlayerInfo *player = PLAYERINFO(xx, yy);
12479
12480       if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12481       {
12482         if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12483           continue;             /* center and border element do not touch */
12484       }
12485
12486       CheckElementChangeByPlayer(x, y, center_element, CE_TOUCHED_BY_PLAYER,
12487                                  player->index_bit, center_side);
12488       CheckTriggeredElementChangeByPlayer(x, y, center_element,
12489                                           CE_PLAYER_TOUCHES_X,
12490                                           player->index_bit, center_side);
12491
12492       {
12493         /* use player element that is initially defined in the level playfield,
12494            not the player element that corresponds to the runtime player number
12495            (example: a level that contains EL_PLAYER_3 as the only player would
12496            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12497         int player_element = PLAYERINFO(xx, yy)->initial_element;
12498
12499         CheckElementChangeBySide(x, y, center_element, player_element,
12500                                  CE_TOUCHING_X, center_side);
12501       }
12502
12503       break;
12504     }
12505   }
12506 }
12507
12508 void TestIfElementTouchesCustomElement(int x, int y)
12509 {
12510   static int xy[4][2] =
12511   {
12512     { 0, -1 },
12513     { -1, 0 },
12514     { +1, 0 },
12515     { 0, +1 }
12516   };
12517   static int trigger_sides[4][2] =
12518   {
12519     /* center side      border side */
12520     { CH_SIDE_TOP,      CH_SIDE_BOTTOM  },      /* check top    */
12521     { CH_SIDE_LEFT,     CH_SIDE_RIGHT   },      /* check left   */
12522     { CH_SIDE_RIGHT,    CH_SIDE_LEFT    },      /* check right  */
12523     { CH_SIDE_BOTTOM,   CH_SIDE_TOP     }       /* check bottom */
12524   };
12525   static int touch_dir[4] =
12526   {
12527     MV_LEFT | MV_RIGHT,
12528     MV_UP   | MV_DOWN,
12529     MV_UP   | MV_DOWN,
12530     MV_LEFT | MV_RIGHT
12531   };
12532   boolean change_center_element = FALSE;
12533   int center_element = Feld[x][y];      /* should always be non-moving! */
12534   int border_element_old[NUM_DIRECTIONS];
12535   int i;
12536
12537   for (i = 0; i < NUM_DIRECTIONS; i++)
12538   {
12539     int xx = x + xy[i][0];
12540     int yy = y + xy[i][1];
12541     int border_element;
12542
12543     border_element_old[i] = -1;
12544
12545     if (!IN_LEV_FIELD(xx, yy))
12546       continue;
12547
12548     if (game.engine_version < VERSION_IDENT(3,0,7,0))
12549       border_element = Feld[xx][yy];    /* may be moving! */
12550     else if (!IS_MOVING(xx, yy) && !IS_BLOCKED(xx, yy))
12551       border_element = Feld[xx][yy];
12552     else if (MovDir[xx][yy] & touch_dir[i])     /* elements are touching */
12553       border_element = MovingOrBlocked2Element(xx, yy);
12554     else
12555       continue;                 /* center and border element do not touch */
12556
12557     border_element_old[i] = border_element;
12558   }
12559
12560   for (i = 0; i < NUM_DIRECTIONS; i++)
12561   {
12562     int xx = x + xy[i][0];
12563     int yy = y + xy[i][1];
12564     int center_side = trigger_sides[i][0];
12565     int border_element = border_element_old[i];
12566
12567     if (border_element == -1)
12568       continue;
12569
12570     /* check for change of border element */
12571     CheckElementChangeBySide(xx, yy, border_element, center_element,
12572                              CE_TOUCHING_X, center_side);
12573
12574     /* (center element cannot be player, so we dont have to check this here) */
12575   }
12576
12577   for (i = 0; i < NUM_DIRECTIONS; i++)
12578   {
12579     int xx = x + xy[i][0];
12580     int yy = y + xy[i][1];
12581     int border_side = trigger_sides[i][1];
12582     int border_element = border_element_old[i];
12583
12584     if (border_element == -1)
12585       continue;
12586
12587     /* check for change of center element (but change it only once) */
12588     if (!change_center_element)
12589       change_center_element =
12590         CheckElementChangeBySide(x, y, center_element, border_element,
12591                                  CE_TOUCHING_X, border_side);
12592
12593     if (IS_PLAYER(xx, yy))
12594     {
12595       /* use player element that is initially defined in the level playfield,
12596          not the player element that corresponds to the runtime player number
12597          (example: a level that contains EL_PLAYER_3 as the only player would
12598          incorrectly give EL_PLAYER_1 for "player->element_nr") */
12599       int player_element = PLAYERINFO(xx, yy)->initial_element;
12600
12601       CheckElementChangeBySide(x, y, center_element, player_element,
12602                                CE_TOUCHING_X, border_side);
12603     }
12604   }
12605 }
12606
12607 void TestIfElementHitsCustomElement(int x, int y, int direction)
12608 {
12609   int dx = (direction == MV_LEFT ? -1 : direction == MV_RIGHT ? +1 : 0);
12610   int dy = (direction == MV_UP   ? -1 : direction == MV_DOWN  ? +1 : 0);
12611   int hitx = x + dx, hity = y + dy;
12612   int hitting_element = Feld[x][y];
12613   int touched_element;
12614
12615   if (IN_LEV_FIELD(hitx, hity) && IS_FREE(hitx, hity))
12616     return;
12617
12618   touched_element = (IN_LEV_FIELD(hitx, hity) ?
12619                      MovingOrBlocked2Element(hitx, hity) : EL_STEELWALL);
12620
12621   if (IN_LEV_FIELD(hitx, hity))
12622   {
12623     int opposite_direction = MV_DIR_OPPOSITE(direction);
12624     int hitting_side = direction;
12625     int touched_side = opposite_direction;
12626     boolean object_hit = (!IS_MOVING(hitx, hity) ||
12627                           MovDir[hitx][hity] != direction ||
12628                           ABS(MovPos[hitx][hity]) <= TILEY / 2);
12629
12630     object_hit = TRUE;
12631
12632     if (object_hit)
12633     {
12634       CheckElementChangeBySide(x, y, hitting_element, touched_element,
12635                                CE_HITTING_X, touched_side);
12636
12637       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12638                                CE_HIT_BY_X, hitting_side);
12639
12640       CheckElementChangeBySide(hitx, hity, touched_element, hitting_element,
12641                                CE_HIT_BY_SOMETHING, opposite_direction);
12642
12643       if (IS_PLAYER(hitx, hity))
12644       {
12645         /* use player element that is initially defined in the level playfield,
12646            not the player element that corresponds to the runtime player number
12647            (example: a level that contains EL_PLAYER_3 as the only player would
12648            incorrectly give EL_PLAYER_1 for "player->element_nr") */
12649         int player_element = PLAYERINFO(hitx, hity)->initial_element;
12650
12651         CheckElementChangeBySide(x, y, hitting_element, player_element,
12652                                  CE_HITTING_X, touched_side);
12653       }
12654     }
12655   }
12656
12657   /* "hitting something" is also true when hitting the playfield border */
12658   CheckElementChangeBySide(x, y, hitting_element, touched_element,
12659                            CE_HITTING_SOMETHING, direction);
12660 }
12661
12662 void TestIfGoodThingHitsBadThing(int good_x, int good_y, int good_move_dir)
12663 {
12664   int i, kill_x = -1, kill_y = -1;
12665
12666   int bad_element = -1;
12667   static int test_xy[4][2] =
12668   {
12669     { 0, -1 },
12670     { -1, 0 },
12671     { +1, 0 },
12672     { 0, +1 }
12673   };
12674   static int test_dir[4] =
12675   {
12676     MV_UP,
12677     MV_LEFT,
12678     MV_RIGHT,
12679     MV_DOWN
12680   };
12681
12682   for (i = 0; i < NUM_DIRECTIONS; i++)
12683   {
12684     int test_x, test_y, test_move_dir, test_element;
12685
12686     test_x = good_x + test_xy[i][0];
12687     test_y = good_y + test_xy[i][1];
12688
12689     if (!IN_LEV_FIELD(test_x, test_y))
12690       continue;
12691
12692     test_move_dir =
12693       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12694
12695     test_element = MovingOrBlocked2ElementIfNotLeaving(test_x, test_y);
12696
12697     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12698        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12699     */
12700     if ((DONT_RUN_INTO(test_element) && good_move_dir == test_dir[i]) ||
12701         (DONT_TOUCH(test_element)    && test_move_dir != test_dir[i]))
12702     {
12703       kill_x = test_x;
12704       kill_y = test_y;
12705       bad_element = test_element;
12706
12707       break;
12708     }
12709   }
12710
12711   if (kill_x != -1 || kill_y != -1)
12712   {
12713     if (IS_PLAYER(good_x, good_y))
12714     {
12715       struct PlayerInfo *player = PLAYERINFO(good_x, good_y);
12716
12717       if (player->shield_deadly_time_left > 0 &&
12718           !IS_INDESTRUCTIBLE(bad_element))
12719         Bang(kill_x, kill_y);
12720       else if (!PLAYER_ENEMY_PROTECTED(good_x, good_y))
12721         KillPlayer(player);
12722     }
12723     else
12724       Bang(good_x, good_y);
12725   }
12726 }
12727
12728 void TestIfBadThingHitsGoodThing(int bad_x, int bad_y, int bad_move_dir)
12729 {
12730   int i, kill_x = -1, kill_y = -1;
12731   int bad_element = Feld[bad_x][bad_y];
12732   static int test_xy[4][2] =
12733   {
12734     { 0, -1 },
12735     { -1, 0 },
12736     { +1, 0 },
12737     { 0, +1 }
12738   };
12739   static int touch_dir[4] =
12740   {
12741     MV_LEFT | MV_RIGHT,
12742     MV_UP   | MV_DOWN,
12743     MV_UP   | MV_DOWN,
12744     MV_LEFT | MV_RIGHT
12745   };
12746   static int test_dir[4] =
12747   {
12748     MV_UP,
12749     MV_LEFT,
12750     MV_RIGHT,
12751     MV_DOWN
12752   };
12753
12754   if (bad_element == EL_EXPLOSION)      /* skip just exploding bad things */
12755     return;
12756
12757   for (i = 0; i < NUM_DIRECTIONS; i++)
12758   {
12759     int test_x, test_y, test_move_dir, test_element;
12760
12761     test_x = bad_x + test_xy[i][0];
12762     test_y = bad_y + test_xy[i][1];
12763
12764     if (!IN_LEV_FIELD(test_x, test_y))
12765       continue;
12766
12767     test_move_dir =
12768       (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12769
12770     test_element = Feld[test_x][test_y];
12771
12772     /* 1st case: good thing is moving towards DONT_RUN_INTO style bad thing;
12773        2nd case: DONT_TOUCH style bad thing does not move away from good thing
12774     */
12775     if ((DONT_RUN_INTO(bad_element) &&  bad_move_dir == test_dir[i]) ||
12776         (DONT_TOUCH(bad_element)    && test_move_dir != test_dir[i]))
12777     {
12778       /* good thing is player or penguin that does not move away */
12779       if (IS_PLAYER(test_x, test_y))
12780       {
12781         struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12782
12783         if (bad_element == EL_ROBOT && player->is_moving)
12784           continue;     /* robot does not kill player if he is moving */
12785
12786         if (game.engine_version >= VERSION_IDENT(3,0,7,0))
12787         {
12788           if (player->MovPos != 0 && !(player->MovDir & touch_dir[i]))
12789             continue;           /* center and border element do not touch */
12790         }
12791
12792         kill_x = test_x;
12793         kill_y = test_y;
12794
12795         break;
12796       }
12797       else if (test_element == EL_PENGUIN)
12798       {
12799         kill_x = test_x;
12800         kill_y = test_y;
12801
12802         break;
12803       }
12804     }
12805   }
12806
12807   if (kill_x != -1 || kill_y != -1)
12808   {
12809     if (IS_PLAYER(kill_x, kill_y))
12810     {
12811       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12812
12813       if (player->shield_deadly_time_left > 0 &&
12814           !IS_INDESTRUCTIBLE(bad_element))
12815         Bang(bad_x, bad_y);
12816       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12817         KillPlayer(player);
12818     }
12819     else
12820       Bang(kill_x, kill_y);
12821   }
12822 }
12823
12824 void TestIfGoodThingGetsHitByBadThing(int bad_x, int bad_y, int bad_move_dir)
12825 {
12826   int bad_element = Feld[bad_x][bad_y];
12827   int dx = (bad_move_dir == MV_LEFT ? -1 : bad_move_dir == MV_RIGHT ? +1 : 0);
12828   int dy = (bad_move_dir == MV_UP   ? -1 : bad_move_dir == MV_DOWN  ? +1 : 0);
12829   int test_x = bad_x + dx, test_y = bad_y + dy;
12830   int test_move_dir, test_element;
12831   int kill_x = -1, kill_y = -1;
12832
12833   if (!IN_LEV_FIELD(test_x, test_y))
12834     return;
12835
12836   test_move_dir =
12837     (IS_MOVING(test_x, test_y) ? MovDir[test_x][test_y] : MV_NONE);
12838
12839   test_element = Feld[test_x][test_y];
12840
12841   if (test_move_dir != bad_move_dir)
12842   {
12843     /* good thing can be player or penguin that does not move away */
12844     if (IS_PLAYER(test_x, test_y))
12845     {
12846       struct PlayerInfo *player = PLAYERINFO(test_x, test_y);
12847
12848       /* (note: in comparison to DONT_RUN_TO and DONT_TOUCH, also handle the
12849          player as being hit when he is moving towards the bad thing, because
12850          the "get hit by" condition would be lost after the player stops) */
12851       if (player->MovPos != 0 && player->MovDir == bad_move_dir)
12852         return;         /* player moves away from bad thing */
12853
12854       kill_x = test_x;
12855       kill_y = test_y;
12856     }
12857     else if (test_element == EL_PENGUIN)
12858     {
12859       kill_x = test_x;
12860       kill_y = test_y;
12861     }
12862   }
12863
12864   if (kill_x != -1 || kill_y != -1)
12865   {
12866     if (IS_PLAYER(kill_x, kill_y))
12867     {
12868       struct PlayerInfo *player = PLAYERINFO(kill_x, kill_y);
12869
12870       if (player->shield_deadly_time_left > 0 &&
12871           !IS_INDESTRUCTIBLE(bad_element))
12872         Bang(bad_x, bad_y);
12873       else if (!PLAYER_ENEMY_PROTECTED(kill_x, kill_y))
12874         KillPlayer(player);
12875     }
12876     else
12877       Bang(kill_x, kill_y);
12878   }
12879 }
12880
12881 void TestIfPlayerTouchesBadThing(int x, int y)
12882 {
12883   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12884 }
12885
12886 void TestIfPlayerRunsIntoBadThing(int x, int y, int move_dir)
12887 {
12888   TestIfGoodThingHitsBadThing(x, y, move_dir);
12889 }
12890
12891 void TestIfBadThingTouchesPlayer(int x, int y)
12892 {
12893   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12894 }
12895
12896 void TestIfBadThingRunsIntoPlayer(int x, int y, int move_dir)
12897 {
12898   TestIfBadThingHitsGoodThing(x, y, move_dir);
12899 }
12900
12901 void TestIfFriendTouchesBadThing(int x, int y)
12902 {
12903   TestIfGoodThingHitsBadThing(x, y, MV_NONE);
12904 }
12905
12906 void TestIfBadThingTouchesFriend(int x, int y)
12907 {
12908   TestIfBadThingHitsGoodThing(x, y, MV_NONE);
12909 }
12910
12911 void TestIfBadThingTouchesOtherBadThing(int bad_x, int bad_y)
12912 {
12913   int i, kill_x = bad_x, kill_y = bad_y;
12914   static int xy[4][2] =
12915   {
12916     { 0, -1 },
12917     { -1, 0 },
12918     { +1, 0 },
12919     { 0, +1 }
12920   };
12921
12922   for (i = 0; i < NUM_DIRECTIONS; i++)
12923   {
12924     int x, y, element;
12925
12926     x = bad_x + xy[i][0];
12927     y = bad_y + xy[i][1];
12928     if (!IN_LEV_FIELD(x, y))
12929       continue;
12930
12931     element = Feld[x][y];
12932     if (IS_AMOEBOID(element) || element == EL_GAME_OF_LIFE ||
12933         element == EL_AMOEBA_GROWING || element == EL_AMOEBA_DROP)
12934     {
12935       kill_x = x;
12936       kill_y = y;
12937       break;
12938     }
12939   }
12940
12941   if (kill_x != bad_x || kill_y != bad_y)
12942     Bang(bad_x, bad_y);
12943 }
12944
12945 void KillPlayer(struct PlayerInfo *player)
12946 {
12947   int jx = player->jx, jy = player->jy;
12948
12949   if (!player->active)
12950     return;
12951
12952 #if 0
12953   printf("::: 0: killed == %d, active == %d, reanimated == %d\n",
12954          player->killed, player->active, player->reanimated);
12955 #endif
12956
12957   /* the following code was introduced to prevent an infinite loop when calling
12958      -> Bang()
12959      -> CheckTriggeredElementChangeExt()
12960      -> ExecuteCustomElementAction()
12961      -> KillPlayer()
12962      -> (infinitely repeating the above sequence of function calls)
12963      which occurs when killing the player while having a CE with the setting
12964      "kill player X when explosion of <player X>"; the solution using a new
12965      field "player->killed" was chosen for backwards compatibility, although
12966      clever use of the fields "player->active" etc. would probably also work */
12967 #if 1
12968   if (player->killed)
12969     return;
12970 #endif
12971
12972   player->killed = TRUE;
12973
12974   /* remove accessible field at the player's position */
12975   Feld[jx][jy] = EL_EMPTY;
12976
12977   /* deactivate shield (else Bang()/Explode() would not work right) */
12978   player->shield_normal_time_left = 0;
12979   player->shield_deadly_time_left = 0;
12980
12981 #if 0
12982   printf("::: 1: killed == %d, active == %d, reanimated == %d\n",
12983          player->killed, player->active, player->reanimated);
12984 #endif
12985
12986   Bang(jx, jy);
12987
12988 #if 0
12989   printf("::: 2: killed == %d, active == %d, reanimated == %d\n",
12990          player->killed, player->active, player->reanimated);
12991 #endif
12992
12993   if (player->reanimated)       /* killed player may have been reanimated */
12994     player->killed = player->reanimated = FALSE;
12995   else
12996     BuryPlayer(player);
12997 }
12998
12999 static void KillPlayerUnlessEnemyProtected(int x, int y)
13000 {
13001   if (!PLAYER_ENEMY_PROTECTED(x, y))
13002     KillPlayer(PLAYERINFO(x, y));
13003 }
13004
13005 static void KillPlayerUnlessExplosionProtected(int x, int y)
13006 {
13007   if (!PLAYER_EXPLOSION_PROTECTED(x, y))
13008     KillPlayer(PLAYERINFO(x, y));
13009 }
13010
13011 void BuryPlayer(struct PlayerInfo *player)
13012 {
13013   int jx = player->jx, jy = player->jy;
13014
13015   if (!player->active)
13016     return;
13017
13018   PlayLevelSoundElementAction(jx, jy, player->artwork_element, ACTION_DYING);
13019   PlayLevelSound(jx, jy, SND_GAME_LOSING);
13020
13021   player->GameOver = TRUE;
13022   RemovePlayer(player);
13023 }
13024
13025 void RemovePlayer(struct PlayerInfo *player)
13026 {
13027   int jx = player->jx, jy = player->jy;
13028   int i, found = FALSE;
13029
13030   player->present = FALSE;
13031   player->active = FALSE;
13032
13033   if (!ExplodeField[jx][jy])
13034     StorePlayer[jx][jy] = 0;
13035
13036   if (player->is_moving)
13037     TEST_DrawLevelField(player->last_jx, player->last_jy);
13038
13039   for (i = 0; i < MAX_PLAYERS; i++)
13040     if (stored_player[i].active)
13041       found = TRUE;
13042
13043   if (!found)
13044     AllPlayersGone = TRUE;
13045
13046   ExitX = ZX = jx;
13047   ExitY = ZY = jy;
13048 }
13049
13050 static void setFieldForSnapping(int x, int y, int element, int direction)
13051 {
13052   struct ElementInfo *ei = &element_info[element];
13053   int direction_bit = MV_DIR_TO_BIT(direction);
13054   int graphic_snapping = ei->direction_graphic[ACTION_SNAPPING][direction_bit];
13055   int action = (graphic_snapping != IMG_EMPTY_SPACE ? ACTION_SNAPPING :
13056                 IS_DIGGABLE(element) ? ACTION_DIGGING : ACTION_COLLECTING);
13057
13058   Feld[x][y] = EL_ELEMENT_SNAPPING;
13059   MovDelay[x][y] = MOVE_DELAY_NORMAL_SPEED + 1 - 1;
13060
13061   ResetGfxAnimation(x, y);
13062
13063   GfxElement[x][y] = element;
13064   GfxAction[x][y] = action;
13065   GfxDir[x][y] = direction;
13066   GfxFrame[x][y] = -1;
13067 }
13068
13069 /*
13070   =============================================================================
13071   checkDiagonalPushing()
13072   -----------------------------------------------------------------------------
13073   check if diagonal input device direction results in pushing of object
13074   (by checking if the alternative direction is walkable, diggable, ...)
13075   =============================================================================
13076 */
13077
13078 static boolean checkDiagonalPushing(struct PlayerInfo *player,
13079                                     int x, int y, int real_dx, int real_dy)
13080 {
13081   int jx, jy, dx, dy, xx, yy;
13082
13083   if (real_dx == 0 || real_dy == 0)     /* no diagonal direction => push */
13084     return TRUE;
13085
13086   /* diagonal direction: check alternative direction */
13087   jx = player->jx;
13088   jy = player->jy;
13089   dx = x - jx;
13090   dy = y - jy;
13091   xx = jx + (dx == 0 ? real_dx : 0);
13092   yy = jy + (dy == 0 ? real_dy : 0);
13093
13094   return (!IN_LEV_FIELD(xx, yy) || IS_SOLID_FOR_PUSHING(Feld[xx][yy]));
13095 }
13096
13097 /*
13098   =============================================================================
13099   DigField()
13100   -----------------------------------------------------------------------------
13101   x, y:                 field next to player (non-diagonal) to try to dig to
13102   real_dx, real_dy:     direction as read from input device (can be diagonal)
13103   =============================================================================
13104 */
13105
13106 static int DigField(struct PlayerInfo *player,
13107                     int oldx, int oldy, int x, int y,
13108                     int real_dx, int real_dy, int mode)
13109 {
13110   boolean is_player = (IS_PLAYER(oldx, oldy) || mode != DF_DIG);
13111   boolean player_was_pushing = player->is_pushing;
13112   boolean player_can_move = (!player->cannot_move && mode != DF_SNAP);
13113   boolean player_can_move_or_snap = (!player->cannot_move || mode == DF_SNAP);
13114   int jx = oldx, jy = oldy;
13115   int dx = x - jx, dy = y - jy;
13116   int nextx = x + dx, nexty = y + dy;
13117   int move_direction = (dx == -1 ? MV_LEFT  :
13118                         dx == +1 ? MV_RIGHT :
13119                         dy == -1 ? MV_UP    :
13120                         dy == +1 ? MV_DOWN  : MV_NONE);
13121   int opposite_direction = MV_DIR_OPPOSITE(move_direction);
13122   int dig_side = MV_DIR_OPPOSITE(move_direction);
13123   int old_element = Feld[jx][jy];
13124   int element = MovingOrBlocked2ElementIfNotLeaving(x, y);
13125   int collect_count;
13126
13127   if (is_player)                /* function can also be called by EL_PENGUIN */
13128   {
13129     if (player->MovPos == 0)
13130     {
13131       player->is_digging = FALSE;
13132       player->is_collecting = FALSE;
13133     }
13134
13135     if (player->MovPos == 0)    /* last pushing move finished */
13136       player->is_pushing = FALSE;
13137
13138     if (mode == DF_NO_PUSH)     /* player just stopped pushing */
13139     {
13140       player->is_switching = FALSE;
13141       player->push_delay = -1;
13142
13143       return MP_NO_ACTION;
13144     }
13145   }
13146
13147   if (IS_TUBE(Back[jx][jy]) && game.engine_version >= VERSION_IDENT(2,2,0,0))
13148     old_element = Back[jx][jy];
13149
13150   /* in case of element dropped at player position, check background */
13151   else if (Back[jx][jy] != EL_EMPTY &&
13152            game.engine_version >= VERSION_IDENT(2,2,0,0))
13153     old_element = Back[jx][jy];
13154
13155   if (IS_WALKABLE(old_element) && !ACCESS_FROM(old_element, move_direction))
13156     return MP_NO_ACTION;        /* field has no opening in this direction */
13157
13158   if (IS_PASSABLE(old_element) && !ACCESS_FROM(old_element,opposite_direction))
13159     return MP_NO_ACTION;        /* field has no opening in this direction */
13160
13161   if (player_can_move && element == EL_ACID && move_direction == MV_DOWN)
13162   {
13163     SplashAcid(x, y);
13164
13165     Feld[jx][jy] = player->artwork_element;
13166     InitMovingField(jx, jy, MV_DOWN);
13167     Store[jx][jy] = EL_ACID;
13168     ContinueMoving(jx, jy);
13169     BuryPlayer(player);
13170
13171     return MP_DONT_RUN_INTO;
13172   }
13173
13174   if (player_can_move && DONT_RUN_INTO(element))
13175   {
13176     TestIfPlayerRunsIntoBadThing(jx, jy, player->MovDir);
13177
13178     return MP_DONT_RUN_INTO;
13179   }
13180
13181   if (IS_MOVING(x, y) || IS_PLAYER(x, y))
13182     return MP_NO_ACTION;
13183
13184   collect_count = element_info[element].collect_count_initial;
13185
13186   if (!is_player && !IS_COLLECTIBLE(element))   /* penguin cannot collect it */
13187     return MP_NO_ACTION;
13188
13189   if (game.engine_version < VERSION_IDENT(2,2,0,0))
13190     player_can_move = player_can_move_or_snap;
13191
13192   if (mode == DF_SNAP && !IS_SNAPPABLE(element) &&
13193       game.engine_version >= VERSION_IDENT(2,2,0,0))
13194   {
13195     CheckElementChangeByPlayer(x, y, element, CE_SNAPPED_BY_PLAYER,
13196                                player->index_bit, dig_side);
13197     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13198                                         player->index_bit, dig_side);
13199
13200     if (element == EL_DC_LANDMINE)
13201       Bang(x, y);
13202
13203     if (Feld[x][y] != element)          /* field changed by snapping */
13204       return MP_ACTION;
13205
13206     return MP_NO_ACTION;
13207   }
13208
13209   if (player->gravity && is_player && !player->is_auto_moving &&
13210       canFallDown(player) && move_direction != MV_DOWN &&
13211       !canMoveToValidFieldWithGravity(jx, jy, move_direction))
13212     return MP_NO_ACTION;        /* player cannot walk here due to gravity */
13213
13214   if (player_can_move &&
13215       IS_WALKABLE(element) && ACCESS_FROM(element, opposite_direction))
13216   {
13217     int sound_element = SND_ELEMENT(element);
13218     int sound_action = ACTION_WALKING;
13219
13220     if (IS_RND_GATE(element))
13221     {
13222       if (!player->key[RND_GATE_NR(element)])
13223         return MP_NO_ACTION;
13224     }
13225     else if (IS_RND_GATE_GRAY(element))
13226     {
13227       if (!player->key[RND_GATE_GRAY_NR(element)])
13228         return MP_NO_ACTION;
13229     }
13230     else if (IS_RND_GATE_GRAY_ACTIVE(element))
13231     {
13232       if (!player->key[RND_GATE_GRAY_ACTIVE_NR(element)])
13233         return MP_NO_ACTION;
13234     }
13235     else if (element == EL_EXIT_OPEN ||
13236              element == EL_EM_EXIT_OPEN ||
13237              element == EL_EM_EXIT_OPENING ||
13238              element == EL_STEEL_EXIT_OPEN ||
13239              element == EL_EM_STEEL_EXIT_OPEN ||
13240              element == EL_EM_STEEL_EXIT_OPENING ||
13241              element == EL_SP_EXIT_OPEN ||
13242              element == EL_SP_EXIT_OPENING)
13243     {
13244       sound_action = ACTION_PASSING;    /* player is passing exit */
13245     }
13246     else if (element == EL_EMPTY)
13247     {
13248       sound_action = ACTION_MOVING;             /* nothing to walk on */
13249     }
13250
13251     /* play sound from background or player, whatever is available */
13252     if (element_info[sound_element].sound[sound_action] != SND_UNDEFINED)
13253       PlayLevelSoundElementAction(x, y, sound_element, sound_action);
13254     else
13255       PlayLevelSoundElementAction(x, y, player->artwork_element, sound_action);
13256   }
13257   else if (player_can_move &&
13258            IS_PASSABLE(element) && canPassField(x, y, move_direction))
13259   {
13260     if (!ACCESS_FROM(element, opposite_direction))
13261       return MP_NO_ACTION;      /* field not accessible from this direction */
13262
13263     if (CAN_MOVE(element))      /* only fixed elements can be passed! */
13264       return MP_NO_ACTION;
13265
13266     if (IS_EM_GATE(element))
13267     {
13268       if (!player->key[EM_GATE_NR(element)])
13269         return MP_NO_ACTION;
13270     }
13271     else if (IS_EM_GATE_GRAY(element))
13272     {
13273       if (!player->key[EM_GATE_GRAY_NR(element)])
13274         return MP_NO_ACTION;
13275     }
13276     else if (IS_EM_GATE_GRAY_ACTIVE(element))
13277     {
13278       if (!player->key[EM_GATE_GRAY_ACTIVE_NR(element)])
13279         return MP_NO_ACTION;
13280     }
13281     else if (IS_EMC_GATE(element))
13282     {
13283       if (!player->key[EMC_GATE_NR(element)])
13284         return MP_NO_ACTION;
13285     }
13286     else if (IS_EMC_GATE_GRAY(element))
13287     {
13288       if (!player->key[EMC_GATE_GRAY_NR(element)])
13289         return MP_NO_ACTION;
13290     }
13291     else if (IS_EMC_GATE_GRAY_ACTIVE(element))
13292     {
13293       if (!player->key[EMC_GATE_GRAY_ACTIVE_NR(element)])
13294         return MP_NO_ACTION;
13295     }
13296     else if (element == EL_DC_GATE_WHITE ||
13297              element == EL_DC_GATE_WHITE_GRAY ||
13298              element == EL_DC_GATE_WHITE_GRAY_ACTIVE)
13299     {
13300       if (player->num_white_keys == 0)
13301         return MP_NO_ACTION;
13302
13303       player->num_white_keys--;
13304     }
13305     else if (IS_SP_PORT(element))
13306     {
13307       if (element == EL_SP_GRAVITY_PORT_LEFT ||
13308           element == EL_SP_GRAVITY_PORT_RIGHT ||
13309           element == EL_SP_GRAVITY_PORT_UP ||
13310           element == EL_SP_GRAVITY_PORT_DOWN)
13311         player->gravity = !player->gravity;
13312       else if (element == EL_SP_GRAVITY_ON_PORT_LEFT ||
13313                element == EL_SP_GRAVITY_ON_PORT_RIGHT ||
13314                element == EL_SP_GRAVITY_ON_PORT_UP ||
13315                element == EL_SP_GRAVITY_ON_PORT_DOWN)
13316         player->gravity = TRUE;
13317       else if (element == EL_SP_GRAVITY_OFF_PORT_LEFT ||
13318                element == EL_SP_GRAVITY_OFF_PORT_RIGHT ||
13319                element == EL_SP_GRAVITY_OFF_PORT_UP ||
13320                element == EL_SP_GRAVITY_OFF_PORT_DOWN)
13321         player->gravity = FALSE;
13322     }
13323
13324     /* automatically move to the next field with double speed */
13325     player->programmed_action = move_direction;
13326
13327     if (player->move_delay_reset_counter == 0)
13328     {
13329       player->move_delay_reset_counter = 2;     /* two double speed steps */
13330
13331       DOUBLE_PLAYER_SPEED(player);
13332     }
13333
13334     PlayLevelSoundAction(x, y, ACTION_PASSING);
13335   }
13336   else if (player_can_move_or_snap && IS_DIGGABLE(element))
13337   {
13338     RemoveField(x, y);
13339
13340     if (mode != DF_SNAP)
13341     {
13342       GfxElement[x][y] = GFX_ELEMENT(element);
13343       player->is_digging = TRUE;
13344     }
13345
13346     PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
13347
13348     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_DIGS_X,
13349                                         player->index_bit, dig_side);
13350
13351     if (mode == DF_SNAP)
13352     {
13353       if (level.block_snap_field)
13354         setFieldForSnapping(x, y, element, move_direction);
13355       else
13356         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13357
13358       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13359                                           player->index_bit, dig_side);
13360     }
13361   }
13362   else if (player_can_move_or_snap && IS_COLLECTIBLE(element))
13363   {
13364     RemoveField(x, y);
13365
13366     if (is_player && mode != DF_SNAP)
13367     {
13368       GfxElement[x][y] = element;
13369       player->is_collecting = TRUE;
13370     }
13371
13372     if (element == EL_SPEED_PILL)
13373     {
13374       player->move_delay_value = MOVE_DELAY_HIGH_SPEED;
13375     }
13376     else if (element == EL_EXTRA_TIME && level.time > 0)
13377     {
13378       TimeLeft += level.extra_time;
13379
13380       game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13381
13382       DisplayGameControlValues();
13383     }
13384     else if (element == EL_SHIELD_NORMAL || element == EL_SHIELD_DEADLY)
13385     {
13386       player->shield_normal_time_left += level.shield_normal_time;
13387       if (element == EL_SHIELD_DEADLY)
13388         player->shield_deadly_time_left += level.shield_deadly_time;
13389     }
13390     else if (element == EL_DYNAMITE ||
13391              element == EL_EM_DYNAMITE ||
13392              element == EL_SP_DISK_RED)
13393     {
13394       if (player->inventory_size < MAX_INVENTORY_SIZE)
13395         player->inventory_element[player->inventory_size++] = element;
13396
13397       DrawGameDoorValues();
13398     }
13399     else if (element == EL_DYNABOMB_INCREASE_NUMBER)
13400     {
13401       player->dynabomb_count++;
13402       player->dynabombs_left++;
13403     }
13404     else if (element == EL_DYNABOMB_INCREASE_SIZE)
13405     {
13406       player->dynabomb_size++;
13407     }
13408     else if (element == EL_DYNABOMB_INCREASE_POWER)
13409     {
13410       player->dynabomb_xl = TRUE;
13411     }
13412     else if (IS_KEY(element))
13413     {
13414       player->key[KEY_NR(element)] = TRUE;
13415
13416       DrawGameDoorValues();
13417     }
13418     else if (element == EL_DC_KEY_WHITE)
13419     {
13420       player->num_white_keys++;
13421
13422       /* display white keys? */
13423       /* DrawGameDoorValues(); */
13424     }
13425     else if (IS_ENVELOPE(element))
13426     {
13427       player->show_envelope = element;
13428     }
13429     else if (element == EL_EMC_LENSES)
13430     {
13431       game.lenses_time_left = level.lenses_time * FRAMES_PER_SECOND;
13432
13433       RedrawAllInvisibleElementsForLenses();
13434     }
13435     else if (element == EL_EMC_MAGNIFIER)
13436     {
13437       game.magnify_time_left = level.magnify_time * FRAMES_PER_SECOND;
13438
13439       RedrawAllInvisibleElementsForMagnifier();
13440     }
13441     else if (IS_DROPPABLE(element) ||
13442              IS_THROWABLE(element))     /* can be collected and dropped */
13443     {
13444       int i;
13445
13446       if (collect_count == 0)
13447         player->inventory_infinite_element = element;
13448       else
13449         for (i = 0; i < collect_count; i++)
13450           if (player->inventory_size < MAX_INVENTORY_SIZE)
13451             player->inventory_element[player->inventory_size++] = element;
13452
13453       DrawGameDoorValues();
13454     }
13455     else if (collect_count > 0)
13456     {
13457       local_player->gems_still_needed -= collect_count;
13458       if (local_player->gems_still_needed < 0)
13459         local_player->gems_still_needed = 0;
13460
13461       game_panel_controls[GAME_PANEL_GEMS].value = local_player->gems_still_needed;
13462
13463       DisplayGameControlValues();
13464     }
13465
13466     RaiseScoreElement(element);
13467     PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
13468
13469     if (is_player)
13470       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_COLLECTS_X,
13471                                           player->index_bit, dig_side);
13472
13473     if (mode == DF_SNAP)
13474     {
13475       if (level.block_snap_field)
13476         setFieldForSnapping(x, y, element, move_direction);
13477       else
13478         TestIfElementTouchesCustomElement(x, y);        /* for empty space */
13479
13480       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SNAPS_X,
13481                                           player->index_bit, dig_side);
13482     }
13483   }
13484   else if (player_can_move_or_snap && IS_PUSHABLE(element))
13485   {
13486     if (mode == DF_SNAP && element != EL_BD_ROCK)
13487       return MP_NO_ACTION;
13488
13489     if (CAN_FALL(element) && dy)
13490       return MP_NO_ACTION;
13491
13492     if (CAN_FALL(element) && IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1) &&
13493         !(element == EL_SPRING && level.use_spring_bug))
13494       return MP_NO_ACTION;
13495
13496     if (CAN_MOVE(element) && GET_MAX_MOVE_DELAY(element) == 0 &&
13497         ((move_direction & MV_VERTICAL &&
13498           ((element_info[element].move_pattern & MV_LEFT &&
13499             IN_LEV_FIELD(x - 1, y) && IS_FREE(x - 1, y)) ||
13500            (element_info[element].move_pattern & MV_RIGHT &&
13501             IN_LEV_FIELD(x + 1, y) && IS_FREE(x + 1, y)))) ||
13502          (move_direction & MV_HORIZONTAL &&
13503           ((element_info[element].move_pattern & MV_UP &&
13504             IN_LEV_FIELD(x, y - 1) && IS_FREE(x, y - 1)) ||
13505            (element_info[element].move_pattern & MV_DOWN &&
13506             IN_LEV_FIELD(x, y + 1) && IS_FREE(x, y + 1))))))
13507       return MP_NO_ACTION;
13508
13509     /* do not push elements already moving away faster than player */
13510     if (CAN_MOVE(element) && MovDir[x][y] == move_direction &&
13511         ABS(getElementMoveStepsize(x, y)) > MOVE_STEPSIZE_NORMAL)
13512       return MP_NO_ACTION;
13513
13514     if (game.engine_version >= VERSION_IDENT(3,1,0,0))
13515     {
13516       if (player->push_delay_value == -1 || !player_was_pushing)
13517         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13518     }
13519     else if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13520     {
13521       if (player->push_delay_value == -1)
13522         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13523     }
13524     else if (game.engine_version >= VERSION_IDENT(2,2,0,7))
13525     {
13526       if (!player->is_pushing)
13527         player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13528     }
13529
13530     player->is_pushing = TRUE;
13531     player->is_active = TRUE;
13532
13533     if (!(IN_LEV_FIELD(nextx, nexty) &&
13534           (IS_FREE(nextx, nexty) ||
13535            (IS_SB_ELEMENT(element) &&
13536             Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY) ||
13537            (IS_CUSTOM_ELEMENT(element) &&
13538             CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty)))))
13539       return MP_NO_ACTION;
13540
13541     if (!checkDiagonalPushing(player, x, y, real_dx, real_dy))
13542       return MP_NO_ACTION;
13543
13544     if (player->push_delay == -1)       /* new pushing; restart delay */
13545       player->push_delay = 0;
13546
13547     if (player->push_delay < player->push_delay_value &&
13548         !(tape.playing && tape.file_version < FILE_VERSION_2_0) &&
13549         element != EL_SPRING && element != EL_BALLOON)
13550     {
13551       /* make sure that there is no move delay before next try to push */
13552       if (game.engine_version >= VERSION_IDENT(3,0,7,1))
13553         player->move_delay = 0;
13554
13555       return MP_NO_ACTION;
13556     }
13557
13558     if (IS_CUSTOM_ELEMENT(element) &&
13559         CUSTOM_ELEMENT_CAN_ENTER_FIELD(element, nextx, nexty))
13560     {
13561       if (!DigFieldByCE(nextx, nexty, element))
13562         return MP_NO_ACTION;
13563     }
13564
13565     if (IS_SB_ELEMENT(element))
13566     {
13567       if (element == EL_SOKOBAN_FIELD_FULL)
13568       {
13569         Back[x][y] = EL_SOKOBAN_FIELD_EMPTY;
13570         local_player->sokobanfields_still_needed++;
13571       }
13572
13573       if (Feld[nextx][nexty] == EL_SOKOBAN_FIELD_EMPTY)
13574       {
13575         Back[nextx][nexty] = EL_SOKOBAN_FIELD_EMPTY;
13576         local_player->sokobanfields_still_needed--;
13577       }
13578
13579       Feld[x][y] = EL_SOKOBAN_OBJECT;
13580
13581       if (Back[x][y] == Back[nextx][nexty])
13582         PlayLevelSoundAction(x, y, ACTION_PUSHING);
13583       else if (Back[x][y] != 0)
13584         PlayLevelSoundElementAction(x, y, EL_SOKOBAN_FIELD_FULL,
13585                                     ACTION_EMPTYING);
13586       else
13587         PlayLevelSoundElementAction(nextx, nexty, EL_SOKOBAN_FIELD_EMPTY,
13588                                     ACTION_FILLING);
13589
13590       if (local_player->sokobanfields_still_needed == 0 &&
13591           (game.emulation == EMU_SOKOBAN || level.auto_exit_sokoban))
13592       {
13593         PlayerWins(player);
13594
13595         PlayLevelSound(x, y, SND_GAME_SOKOBAN_SOLVING);
13596       }
13597     }
13598     else
13599       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
13600
13601     InitMovingField(x, y, move_direction);
13602     GfxAction[x][y] = ACTION_PUSHING;
13603
13604     if (mode == DF_SNAP)
13605       ContinueMoving(x, y);
13606     else
13607       MovPos[x][y] = (dx != 0 ? dx : dy);
13608
13609     Pushed[x][y] = TRUE;
13610     Pushed[nextx][nexty] = TRUE;
13611
13612     if (game.engine_version < VERSION_IDENT(2,2,0,7))
13613       player->push_delay_value = GET_NEW_PUSH_DELAY(element);
13614     else
13615       player->push_delay_value = -1;    /* get new value later */
13616
13617     /* check for element change _after_ element has been pushed */
13618     if (game.use_change_when_pushing_bug)
13619     {
13620       CheckElementChangeByPlayer(x, y, element, CE_PUSHED_BY_PLAYER,
13621                                  player->index_bit, dig_side);
13622       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PUSHES_X,
13623                                           player->index_bit, dig_side);
13624     }
13625   }
13626   else if (IS_SWITCHABLE(element))
13627   {
13628     if (PLAYER_SWITCHING(player, x, y))
13629     {
13630       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13631                                           player->index_bit, dig_side);
13632
13633       return MP_ACTION;
13634     }
13635
13636     player->is_switching = TRUE;
13637     player->switch_x = x;
13638     player->switch_y = y;
13639
13640     PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
13641
13642     if (element == EL_ROBOT_WHEEL)
13643     {
13644       Feld[x][y] = EL_ROBOT_WHEEL_ACTIVE;
13645       ZX = x;
13646       ZY = y;
13647
13648       game.robot_wheel_active = TRUE;
13649
13650       TEST_DrawLevelField(x, y);
13651     }
13652     else if (element == EL_SP_TERMINAL)
13653     {
13654       int xx, yy;
13655
13656       SCAN_PLAYFIELD(xx, yy)
13657       {
13658         if (Feld[xx][yy] == EL_SP_DISK_YELLOW)
13659           Bang(xx, yy);
13660         else if (Feld[xx][yy] == EL_SP_TERMINAL)
13661           Feld[xx][yy] = EL_SP_TERMINAL_ACTIVE;
13662       }
13663     }
13664     else if (IS_BELT_SWITCH(element))
13665     {
13666       ToggleBeltSwitch(x, y);
13667     }
13668     else if (element == EL_SWITCHGATE_SWITCH_UP ||
13669              element == EL_SWITCHGATE_SWITCH_DOWN ||
13670              element == EL_DC_SWITCHGATE_SWITCH_UP ||
13671              element == EL_DC_SWITCHGATE_SWITCH_DOWN)
13672     {
13673       ToggleSwitchgateSwitch(x, y);
13674     }
13675     else if (element == EL_LIGHT_SWITCH ||
13676              element == EL_LIGHT_SWITCH_ACTIVE)
13677     {
13678       ToggleLightSwitch(x, y);
13679     }
13680     else if (element == EL_TIMEGATE_SWITCH ||
13681              element == EL_DC_TIMEGATE_SWITCH)
13682     {
13683       ActivateTimegateSwitch(x, y);
13684     }
13685     else if (element == EL_BALLOON_SWITCH_LEFT  ||
13686              element == EL_BALLOON_SWITCH_RIGHT ||
13687              element == EL_BALLOON_SWITCH_UP    ||
13688              element == EL_BALLOON_SWITCH_DOWN  ||
13689              element == EL_BALLOON_SWITCH_NONE  ||
13690              element == EL_BALLOON_SWITCH_ANY)
13691     {
13692       game.wind_direction = (element == EL_BALLOON_SWITCH_LEFT  ? MV_LEFT  :
13693                              element == EL_BALLOON_SWITCH_RIGHT ? MV_RIGHT :
13694                              element == EL_BALLOON_SWITCH_UP    ? MV_UP    :
13695                              element == EL_BALLOON_SWITCH_DOWN  ? MV_DOWN  :
13696                              element == EL_BALLOON_SWITCH_NONE  ? MV_NONE  :
13697                              move_direction);
13698     }
13699     else if (element == EL_LAMP)
13700     {
13701       Feld[x][y] = EL_LAMP_ACTIVE;
13702       local_player->lights_still_needed--;
13703
13704       ResetGfxAnimation(x, y);
13705       TEST_DrawLevelField(x, y);
13706     }
13707     else if (element == EL_TIME_ORB_FULL)
13708     {
13709       Feld[x][y] = EL_TIME_ORB_EMPTY;
13710
13711       if (level.time > 0 || level.use_time_orb_bug)
13712       {
13713         TimeLeft += level.time_orb_time;
13714         game.no_time_limit = FALSE;
13715
13716         game_panel_controls[GAME_PANEL_TIME].value = TimeLeft;
13717
13718         DisplayGameControlValues();
13719       }
13720
13721       ResetGfxAnimation(x, y);
13722       TEST_DrawLevelField(x, y);
13723     }
13724     else if (element == EL_EMC_MAGIC_BALL_SWITCH ||
13725              element == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13726     {
13727       int xx, yy;
13728
13729       game.ball_state = !game.ball_state;
13730
13731       SCAN_PLAYFIELD(xx, yy)
13732       {
13733         int e = Feld[xx][yy];
13734
13735         if (game.ball_state)
13736         {
13737           if (e == EL_EMC_MAGIC_BALL)
13738             CreateField(xx, yy, EL_EMC_MAGIC_BALL_ACTIVE);
13739           else if (e == EL_EMC_MAGIC_BALL_SWITCH)
13740             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH_ACTIVE);
13741         }
13742         else
13743         {
13744           if (e == EL_EMC_MAGIC_BALL_ACTIVE)
13745             CreateField(xx, yy, EL_EMC_MAGIC_BALL);
13746           else if (e == EL_EMC_MAGIC_BALL_SWITCH_ACTIVE)
13747             CreateField(xx, yy, EL_EMC_MAGIC_BALL_SWITCH);
13748         }
13749       }
13750     }
13751
13752     CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13753                                         player->index_bit, dig_side);
13754
13755     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13756                                         player->index_bit, dig_side);
13757
13758     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13759                                         player->index_bit, dig_side);
13760
13761     return MP_ACTION;
13762   }
13763   else
13764   {
13765     if (!PLAYER_SWITCHING(player, x, y))
13766     {
13767       player->is_switching = TRUE;
13768       player->switch_x = x;
13769       player->switch_y = y;
13770
13771       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED,
13772                                  player->index_bit, dig_side);
13773       CheckTriggeredElementChangeByPlayer(x, y, element, CE_SWITCH_OF_X,
13774                                           player->index_bit, dig_side);
13775
13776       CheckElementChangeByPlayer(x, y, element, CE_SWITCHED_BY_PLAYER,
13777                                  player->index_bit, dig_side);
13778       CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_SWITCHES_X,
13779                                           player->index_bit, dig_side);
13780     }
13781
13782     CheckElementChangeByPlayer(x, y, element, CE_PRESSED_BY_PLAYER,
13783                                player->index_bit, dig_side);
13784     CheckTriggeredElementChangeByPlayer(x, y, element, CE_PLAYER_PRESSES_X,
13785                                         player->index_bit, dig_side);
13786
13787     return MP_NO_ACTION;
13788   }
13789
13790   player->push_delay = -1;
13791
13792   if (is_player)                /* function can also be called by EL_PENGUIN */
13793   {
13794     if (Feld[x][y] != element)          /* really digged/collected something */
13795     {
13796       player->is_collecting = !player->is_digging;
13797       player->is_active = TRUE;
13798     }
13799   }
13800
13801   return MP_MOVING;
13802 }
13803
13804 static boolean DigFieldByCE(int x, int y, int digging_element)
13805 {
13806   int element = Feld[x][y];
13807
13808   if (!IS_FREE(x, y))
13809   {
13810     int action = (IS_DIGGABLE(element) ? ACTION_DIGGING :
13811                   IS_COLLECTIBLE(element) ? ACTION_COLLECTING :
13812                   ACTION_BREAKING);
13813
13814     /* no element can dig solid indestructible elements */
13815     if (IS_INDESTRUCTIBLE(element) &&
13816         !IS_DIGGABLE(element) &&
13817         !IS_COLLECTIBLE(element))
13818       return FALSE;
13819
13820     if (AmoebaNr[x][y] &&
13821         (element == EL_AMOEBA_FULL ||
13822          element == EL_BD_AMOEBA ||
13823          element == EL_AMOEBA_GROWING))
13824     {
13825       AmoebaCnt[AmoebaNr[x][y]]--;
13826       AmoebaCnt2[AmoebaNr[x][y]]--;
13827     }
13828
13829     if (IS_MOVING(x, y))
13830       RemoveMovingField(x, y);
13831     else
13832     {
13833       RemoveField(x, y);
13834       TEST_DrawLevelField(x, y);
13835     }
13836
13837     /* if digged element was about to explode, prevent the explosion */
13838     ExplodeField[x][y] = EX_TYPE_NONE;
13839
13840     PlayLevelSoundAction(x, y, action);
13841   }
13842
13843   Store[x][y] = EL_EMPTY;
13844
13845   /* this makes it possible to leave the removed element again */
13846   if (IS_EQUAL_OR_IN_GROUP(element, MOVE_ENTER_EL(digging_element)))
13847     Store[x][y] = element;
13848
13849   return TRUE;
13850 }
13851
13852 static boolean SnapField(struct PlayerInfo *player, int dx, int dy)
13853 {
13854   int jx = player->jx, jy = player->jy;
13855   int x = jx + dx, y = jy + dy;
13856   int snap_direction = (dx == -1 ? MV_LEFT  :
13857                         dx == +1 ? MV_RIGHT :
13858                         dy == -1 ? MV_UP    :
13859                         dy == +1 ? MV_DOWN  : MV_NONE);
13860   boolean can_continue_snapping = (level.continuous_snapping &&
13861                                    WasJustFalling[x][y] < CHECK_DELAY_FALLING);
13862
13863   if (player->MovPos != 0 && game.engine_version >= VERSION_IDENT(2,2,0,0))
13864     return FALSE;
13865
13866   if (!player->active || !IN_LEV_FIELD(x, y))
13867     return FALSE;
13868
13869   if (dx && dy)
13870     return FALSE;
13871
13872   if (!dx && !dy)
13873   {
13874     if (player->MovPos == 0)
13875       player->is_pushing = FALSE;
13876
13877     player->is_snapping = FALSE;
13878
13879     if (player->MovPos == 0)
13880     {
13881       player->is_moving = FALSE;
13882       player->is_digging = FALSE;
13883       player->is_collecting = FALSE;
13884     }
13885
13886     return FALSE;
13887   }
13888
13889   /* prevent snapping with already pressed snap key when not allowed */
13890   if (player->is_snapping && !can_continue_snapping)
13891     return FALSE;
13892
13893   player->MovDir = snap_direction;
13894
13895   if (player->MovPos == 0)
13896   {
13897     player->is_moving = FALSE;
13898     player->is_digging = FALSE;
13899     player->is_collecting = FALSE;
13900   }
13901
13902   player->is_dropping = FALSE;
13903   player->is_dropping_pressed = FALSE;
13904   player->drop_pressed_delay = 0;
13905
13906   if (DigField(player, jx, jy, x, y, 0, 0, DF_SNAP) == MP_NO_ACTION)
13907     return FALSE;
13908
13909   player->is_snapping = TRUE;
13910   player->is_active = TRUE;
13911
13912   if (player->MovPos == 0)
13913   {
13914     player->is_moving = FALSE;
13915     player->is_digging = FALSE;
13916     player->is_collecting = FALSE;
13917   }
13918
13919   if (player->MovPos != 0)      /* prevent graphic bugs in versions < 2.2.0 */
13920     TEST_DrawLevelField(player->last_jx, player->last_jy);
13921
13922   TEST_DrawLevelField(x, y);
13923
13924   return TRUE;
13925 }
13926
13927 static boolean DropElement(struct PlayerInfo *player)
13928 {
13929   int old_element, new_element;
13930   int dropx = player->jx, dropy = player->jy;
13931   int drop_direction = player->MovDir;
13932   int drop_side = drop_direction;
13933   int drop_element = get_next_dropped_element(player);
13934
13935   player->is_dropping_pressed = TRUE;
13936
13937   /* do not drop an element on top of another element; when holding drop key
13938      pressed without moving, dropped element must move away before the next
13939      element can be dropped (this is especially important if the next element
13940      is dynamite, which can be placed on background for historical reasons) */
13941   if (PLAYER_DROPPING(player, dropx, dropy) && Feld[dropx][dropy] != EL_EMPTY)
13942     return MP_ACTION;
13943
13944   if (IS_THROWABLE(drop_element))
13945   {
13946     dropx += GET_DX_FROM_DIR(drop_direction);
13947     dropy += GET_DY_FROM_DIR(drop_direction);
13948
13949     if (!IN_LEV_FIELD(dropx, dropy))
13950       return FALSE;
13951   }
13952
13953   old_element = Feld[dropx][dropy];     /* old element at dropping position */
13954   new_element = drop_element;           /* default: no change when dropping */
13955
13956   /* check if player is active, not moving and ready to drop */
13957   if (!player->active || player->MovPos || player->drop_delay > 0)
13958     return FALSE;
13959
13960   /* check if player has anything that can be dropped */
13961   if (new_element == EL_UNDEFINED)
13962     return FALSE;
13963
13964   /* check if drop key was pressed long enough for EM style dynamite */
13965   if (new_element == EL_EM_DYNAMITE && player->drop_pressed_delay < 40)
13966     return FALSE;
13967
13968   /* check if anything can be dropped at the current position */
13969   if (IS_ACTIVE_BOMB(old_element) || old_element == EL_EXPLOSION)
13970     return FALSE;
13971
13972   /* collected custom elements can only be dropped on empty fields */
13973   if (IS_CUSTOM_ELEMENT(new_element) && old_element != EL_EMPTY)
13974     return FALSE;
13975
13976   if (old_element != EL_EMPTY)
13977     Back[dropx][dropy] = old_element;   /* store old element on this field */
13978
13979   ResetGfxAnimation(dropx, dropy);
13980   ResetRandomAnimationValue(dropx, dropy);
13981
13982   if (player->inventory_size > 0 ||
13983       player->inventory_infinite_element != EL_UNDEFINED)
13984   {
13985     if (player->inventory_size > 0)
13986     {
13987       player->inventory_size--;
13988
13989       DrawGameDoorValues();
13990
13991       if (new_element == EL_DYNAMITE)
13992         new_element = EL_DYNAMITE_ACTIVE;
13993       else if (new_element == EL_EM_DYNAMITE)
13994         new_element = EL_EM_DYNAMITE_ACTIVE;
13995       else if (new_element == EL_SP_DISK_RED)
13996         new_element = EL_SP_DISK_RED_ACTIVE;
13997     }
13998
13999     Feld[dropx][dropy] = new_element;
14000
14001     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14002       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14003                           el2img(Feld[dropx][dropy]), 0);
14004
14005     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14006
14007     /* needed if previous element just changed to "empty" in the last frame */
14008     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14009
14010     CheckElementChangeByPlayer(dropx, dropy, new_element, CE_DROPPED_BY_PLAYER,
14011                                player->index_bit, drop_side);
14012     CheckTriggeredElementChangeByPlayer(dropx, dropy, new_element,
14013                                         CE_PLAYER_DROPS_X,
14014                                         player->index_bit, drop_side);
14015
14016     TestIfElementTouchesCustomElement(dropx, dropy);
14017   }
14018   else          /* player is dropping a dyna bomb */
14019   {
14020     player->dynabombs_left--;
14021
14022     Feld[dropx][dropy] = new_element;
14023
14024     if (IN_SCR_FIELD(SCREENX(dropx), SCREENY(dropy)))
14025       DrawGraphicThruMask(SCREENX(dropx), SCREENY(dropy),
14026                           el2img(Feld[dropx][dropy]), 0);
14027
14028     PlayLevelSoundAction(dropx, dropy, ACTION_DROPPING);
14029   }
14030
14031   if (Feld[dropx][dropy] == new_element) /* uninitialized unless CE change */
14032     InitField_WithBug1(dropx, dropy, FALSE);
14033
14034   new_element = Feld[dropx][dropy];     /* element might have changed */
14035
14036   if (IS_CUSTOM_ELEMENT(new_element) && CAN_MOVE(new_element) &&
14037       element_info[new_element].move_pattern == MV_WHEN_DROPPED)
14038   {
14039     if (element_info[new_element].move_direction_initial == MV_START_AUTOMATIC)
14040       MovDir[dropx][dropy] = drop_direction;
14041
14042     ChangeCount[dropx][dropy] = 0;      /* allow at least one more change */
14043
14044     /* do not cause impact style collision by dropping elements that can fall */
14045     CheckCollision[dropx][dropy] = CHECK_DELAY_COLLISION;
14046   }
14047
14048   player->drop_delay = GET_NEW_DROP_DELAY(drop_element);
14049   player->is_dropping = TRUE;
14050
14051   player->drop_pressed_delay = 0;
14052   player->is_dropping_pressed = FALSE;
14053
14054   player->drop_x = dropx;
14055   player->drop_y = dropy;
14056
14057   return TRUE;
14058 }
14059
14060 /* ------------------------------------------------------------------------- */
14061 /* game sound playing functions                                              */
14062 /* ------------------------------------------------------------------------- */
14063
14064 static int *loop_sound_frame = NULL;
14065 static int *loop_sound_volume = NULL;
14066
14067 void InitPlayLevelSound()
14068 {
14069   int num_sounds = getSoundListSize();
14070
14071   checked_free(loop_sound_frame);
14072   checked_free(loop_sound_volume);
14073
14074   loop_sound_frame  = checked_calloc(num_sounds * sizeof(int));
14075   loop_sound_volume = checked_calloc(num_sounds * sizeof(int));
14076 }
14077
14078 static void PlayLevelSound(int x, int y, int nr)
14079 {
14080   int sx = SCREENX(x), sy = SCREENY(y);
14081   int volume, stereo_position;
14082   int max_distance = 8;
14083   int type = (IS_LOOP_SOUND(nr) ? SND_CTRL_PLAY_LOOP : SND_CTRL_PLAY_SOUND);
14084
14085   if ((!setup.sound_simple && !IS_LOOP_SOUND(nr)) ||
14086       (!setup.sound_loops && IS_LOOP_SOUND(nr)))
14087     return;
14088
14089   if (!IN_LEV_FIELD(x, y) ||
14090       sx < -max_distance || sx >= SCR_FIELDX + max_distance ||
14091       sy < -max_distance || sy >= SCR_FIELDY + max_distance)
14092     return;
14093
14094   volume = SOUND_MAX_VOLUME;
14095
14096   if (!IN_SCR_FIELD(sx, sy))
14097   {
14098     int dx = ABS(sx - SCR_FIELDX / 2) - SCR_FIELDX / 2;
14099     int dy = ABS(sy - SCR_FIELDY / 2) - SCR_FIELDY / 2;
14100
14101     volume -= volume * (dx > dy ? dx : dy) / max_distance;
14102   }
14103
14104   stereo_position = (SOUND_MAX_LEFT +
14105                      (sx + max_distance) * SOUND_MAX_LEFT2RIGHT /
14106                      (SCR_FIELDX + 2 * max_distance));
14107
14108   if (IS_LOOP_SOUND(nr))
14109   {
14110     /* This assures that quieter loop sounds do not overwrite louder ones,
14111        while restarting sound volume comparison with each new game frame. */
14112
14113     if (loop_sound_volume[nr] > volume && loop_sound_frame[nr] == FrameCounter)
14114       return;
14115
14116     loop_sound_volume[nr] = volume;
14117     loop_sound_frame[nr] = FrameCounter;
14118   }
14119
14120   PlaySoundExt(nr, volume, stereo_position, type);
14121 }
14122
14123 static void PlayLevelSoundNearest(int x, int y, int sound_action)
14124 {
14125   PlayLevelSound(x < LEVELX(BX1) ? LEVELX(BX1) :
14126                  x > LEVELX(BX2) ? LEVELX(BX2) : x,
14127                  y < LEVELY(BY1) ? LEVELY(BY1) :
14128                  y > LEVELY(BY2) ? LEVELY(BY2) : y,
14129                  sound_action);
14130 }
14131
14132 static void PlayLevelSoundAction(int x, int y, int action)
14133 {
14134   PlayLevelSoundElementAction(x, y, Feld[x][y], action);
14135 }
14136
14137 static void PlayLevelSoundElementAction(int x, int y, int element, int action)
14138 {
14139   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14140
14141   if (sound_effect != SND_UNDEFINED)
14142     PlayLevelSound(x, y, sound_effect);
14143 }
14144
14145 static void PlayLevelSoundElementActionIfLoop(int x, int y, int element,
14146                                               int action)
14147 {
14148   int sound_effect = element_info[SND_ELEMENT(element)].sound[action];
14149
14150   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14151     PlayLevelSound(x, y, sound_effect);
14152 }
14153
14154 static void PlayLevelSoundActionIfLoop(int x, int y, int action)
14155 {
14156   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14157
14158   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14159     PlayLevelSound(x, y, sound_effect);
14160 }
14161
14162 static void StopLevelSoundActionIfLoop(int x, int y, int action)
14163 {
14164   int sound_effect = element_info[SND_ELEMENT(Feld[x][y])].sound[action];
14165
14166   if (sound_effect != SND_UNDEFINED && IS_LOOP_SOUND(sound_effect))
14167     StopSound(sound_effect);
14168 }
14169
14170 static void PlayLevelMusic()
14171 {
14172   if (levelset.music[level_nr] != MUS_UNDEFINED)
14173     PlayMusic(levelset.music[level_nr]);        /* from config file */
14174   else
14175     PlayMusic(MAP_NOCONF_MUSIC(level_nr));      /* from music dir */
14176 }
14177
14178 void PlayLevelSound_EM(int xx, int yy, int element_em, int sample)
14179 {
14180   int element = (element_em > -1 ? map_element_EM_to_RND(element_em) : 0);
14181   int offset = (BorderElement == EL_STEELWALL ? 1 : 0);
14182   int x = xx - 1 - offset;
14183   int y = yy - 1 - offset;
14184
14185   switch (sample)
14186   {
14187     case SAMPLE_blank:
14188       PlayLevelSoundElementAction(x, y, element, ACTION_WALKING);
14189       break;
14190
14191     case SAMPLE_roll:
14192       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14193       break;
14194
14195     case SAMPLE_stone:
14196       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14197       break;
14198
14199     case SAMPLE_nut:
14200       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14201       break;
14202
14203     case SAMPLE_crack:
14204       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14205       break;
14206
14207     case SAMPLE_bug:
14208       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14209       break;
14210
14211     case SAMPLE_tank:
14212       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14213       break;
14214
14215     case SAMPLE_android_clone:
14216       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14217       break;
14218
14219     case SAMPLE_android_move:
14220       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14221       break;
14222
14223     case SAMPLE_spring:
14224       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14225       break;
14226
14227     case SAMPLE_slurp:
14228       PlayLevelSoundElementAction(x, y, element, ACTION_EATING);
14229       break;
14230
14231     case SAMPLE_eater:
14232       PlayLevelSoundElementAction(x, y, element, ACTION_WAITING);
14233       break;
14234
14235     case SAMPLE_eater_eat:
14236       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14237       break;
14238
14239     case SAMPLE_alien:
14240       PlayLevelSoundElementAction(x, y, element, ACTION_MOVING);
14241       break;
14242
14243     case SAMPLE_collect:
14244       PlayLevelSoundElementAction(x, y, element, ACTION_COLLECTING);
14245       break;
14246
14247     case SAMPLE_diamond:
14248       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14249       break;
14250
14251     case SAMPLE_squash:
14252       /* !!! CHECK THIS !!! */
14253 #if 1
14254       PlayLevelSoundElementAction(x, y, element, ACTION_BREAKING);
14255 #else
14256       PlayLevelSoundElementAction(x, y, element, ACTION_SMASHED_BY_ROCK);
14257 #endif
14258       break;
14259
14260     case SAMPLE_wonderfall:
14261       PlayLevelSoundElementAction(x, y, element, ACTION_FILLING);
14262       break;
14263
14264     case SAMPLE_drip:
14265       PlayLevelSoundElementAction(x, y, element, ACTION_IMPACT);
14266       break;
14267
14268     case SAMPLE_push:
14269       PlayLevelSoundElementAction(x, y, element, ACTION_PUSHING);
14270       break;
14271
14272     case SAMPLE_dirt:
14273       PlayLevelSoundElementAction(x, y, element, ACTION_DIGGING);
14274       break;
14275
14276     case SAMPLE_acid:
14277       PlayLevelSoundElementAction(x, y, element, ACTION_SPLASHING);
14278       break;
14279
14280     case SAMPLE_ball:
14281       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14282       break;
14283
14284     case SAMPLE_grow:
14285       PlayLevelSoundElementAction(x, y, element, ACTION_GROWING);
14286       break;
14287
14288     case SAMPLE_wonder:
14289       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14290       break;
14291
14292     case SAMPLE_door:
14293       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14294       break;
14295
14296     case SAMPLE_exit_open:
14297       PlayLevelSoundElementAction(x, y, element, ACTION_OPENING);
14298       break;
14299
14300     case SAMPLE_exit_leave:
14301       PlayLevelSoundElementAction(x, y, element, ACTION_PASSING);
14302       break;
14303
14304     case SAMPLE_dynamite:
14305       PlayLevelSoundElementAction(x, y, element, ACTION_DROPPING);
14306       break;
14307
14308     case SAMPLE_tick:
14309       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14310       break;
14311
14312     case SAMPLE_press:
14313       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVATING);
14314       break;
14315
14316     case SAMPLE_wheel:
14317       PlayLevelSoundElementAction(x, y, element, ACTION_ACTIVE);
14318       break;
14319
14320     case SAMPLE_boom:
14321       PlayLevelSoundElementAction(x, y, element, ACTION_EXPLODING);
14322       break;
14323
14324     case SAMPLE_die:
14325       PlayLevelSoundElementAction(x, y, element, ACTION_DYING);
14326       break;
14327
14328     case SAMPLE_time:
14329       PlaySound(SND_GAME_RUNNING_OUT_OF_TIME);
14330       break;
14331
14332     default:
14333       PlayLevelSoundElementAction(x, y, element, ACTION_DEFAULT);
14334       break;
14335   }
14336 }
14337
14338 void PlayLevelSound_SP(int xx, int yy, int element_sp, int action_sp)
14339 {
14340   int element = map_element_SP_to_RND(element_sp);
14341   int action = map_action_SP_to_RND(action_sp);
14342   int offset = (setup.sp_show_border_elements ? 0 : 1);
14343   int x = xx - offset;
14344   int y = yy - offset;
14345
14346   PlayLevelSoundElementAction(x, y, element, action);
14347 }
14348
14349 void RaiseScore(int value)
14350 {
14351   local_player->score += value;
14352
14353   game_panel_controls[GAME_PANEL_SCORE].value = local_player->score;
14354
14355   DisplayGameControlValues();
14356 }
14357
14358 void RaiseScoreElement(int element)
14359 {
14360   switch (element)
14361   {
14362     case EL_EMERALD:
14363     case EL_BD_DIAMOND:
14364     case EL_EMERALD_YELLOW:
14365     case EL_EMERALD_RED:
14366     case EL_EMERALD_PURPLE:
14367     case EL_SP_INFOTRON:
14368       RaiseScore(level.score[SC_EMERALD]);
14369       break;
14370     case EL_DIAMOND:
14371       RaiseScore(level.score[SC_DIAMOND]);
14372       break;
14373     case EL_CRYSTAL:
14374       RaiseScore(level.score[SC_CRYSTAL]);
14375       break;
14376     case EL_PEARL:
14377       RaiseScore(level.score[SC_PEARL]);
14378       break;
14379     case EL_BUG:
14380     case EL_BD_BUTTERFLY:
14381     case EL_SP_ELECTRON:
14382       RaiseScore(level.score[SC_BUG]);
14383       break;
14384     case EL_SPACESHIP:
14385     case EL_BD_FIREFLY:
14386     case EL_SP_SNIKSNAK:
14387       RaiseScore(level.score[SC_SPACESHIP]);
14388       break;
14389     case EL_YAMYAM:
14390     case EL_DARK_YAMYAM:
14391       RaiseScore(level.score[SC_YAMYAM]);
14392       break;
14393     case EL_ROBOT:
14394       RaiseScore(level.score[SC_ROBOT]);
14395       break;
14396     case EL_PACMAN:
14397       RaiseScore(level.score[SC_PACMAN]);
14398       break;
14399     case EL_NUT:
14400       RaiseScore(level.score[SC_NUT]);
14401       break;
14402     case EL_DYNAMITE:
14403     case EL_EM_DYNAMITE:
14404     case EL_SP_DISK_RED:
14405     case EL_DYNABOMB_INCREASE_NUMBER:
14406     case EL_DYNABOMB_INCREASE_SIZE:
14407     case EL_DYNABOMB_INCREASE_POWER:
14408       RaiseScore(level.score[SC_DYNAMITE]);
14409       break;
14410     case EL_SHIELD_NORMAL:
14411     case EL_SHIELD_DEADLY:
14412       RaiseScore(level.score[SC_SHIELD]);
14413       break;
14414     case EL_EXTRA_TIME:
14415       RaiseScore(level.extra_time_score);
14416       break;
14417     case EL_KEY_1:
14418     case EL_KEY_2:
14419     case EL_KEY_3:
14420     case EL_KEY_4:
14421     case EL_EM_KEY_1:
14422     case EL_EM_KEY_2:
14423     case EL_EM_KEY_3:
14424     case EL_EM_KEY_4:
14425     case EL_EMC_KEY_5:
14426     case EL_EMC_KEY_6:
14427     case EL_EMC_KEY_7:
14428     case EL_EMC_KEY_8:
14429     case EL_DC_KEY_WHITE:
14430       RaiseScore(level.score[SC_KEY]);
14431       break;
14432     default:
14433       RaiseScore(element_info[element].collect_score);
14434       break;
14435   }
14436 }
14437
14438 void RequestQuitGameExt(boolean skip_request, boolean quick_quit, char *message)
14439 {
14440   if (skip_request || Request(message, REQ_ASK | REQ_STAY_CLOSED))
14441   {
14442     /* closing door required in case of envelope style request dialogs */
14443     if (!skip_request)
14444       CloseDoor(DOOR_CLOSE_1);
14445
14446 #if defined(NETWORK_AVALIABLE)
14447     if (options.network)
14448       SendToServer_StopPlaying(NETWORK_STOP_BY_PLAYER);
14449     else
14450 #endif
14451     {
14452       if (quick_quit)
14453       {
14454         FadeSkipNextFadeIn();
14455
14456         game_status = GAME_MODE_MAIN;
14457
14458         DrawAndFadeInMainMenu(REDRAW_FIELD);
14459       }
14460       else
14461       {
14462         game_status = GAME_MODE_MAIN;
14463
14464         DrawAndFadeInMainMenu(REDRAW_FIELD);
14465       }
14466     }
14467   }
14468   else          /* continue playing the game */
14469   {
14470     if (tape.playing && tape.deactivate_display)
14471       TapeDeactivateDisplayOff(TRUE);
14472
14473     OpenDoor(DOOR_OPEN_1 | DOOR_COPY_BACK);
14474
14475     if (tape.playing && tape.deactivate_display)
14476       TapeDeactivateDisplayOn();
14477   }
14478 }
14479
14480 void RequestQuitGame(boolean ask_if_really_quit)
14481 {
14482   boolean quick_quit = (!ask_if_really_quit || level_editor_test_game);
14483   boolean skip_request = AllPlayersGone || quick_quit;
14484
14485   RequestQuitGameExt(skip_request, quick_quit,
14486                      "Do you really want to quit the game?");
14487 }
14488
14489
14490 /* ------------------------------------------------------------------------- */
14491 /* random generator functions                                                */
14492 /* ------------------------------------------------------------------------- */
14493
14494 unsigned int InitEngineRandom_RND(int seed)
14495 {
14496   game.num_random_calls = 0;
14497
14498   return InitEngineRandom(seed);
14499 }
14500
14501 unsigned int RND(int max)
14502 {
14503   if (max > 0)
14504   {
14505     game.num_random_calls++;
14506
14507     return GetEngineRandom(max);
14508   }
14509
14510   return 0;
14511 }
14512
14513
14514 /* ------------------------------------------------------------------------- */
14515 /* game engine snapshot handling functions                                   */
14516 /* ------------------------------------------------------------------------- */
14517
14518 struct EngineSnapshotInfo
14519 {
14520   /* runtime values for custom element collect score */
14521   int collect_score[NUM_CUSTOM_ELEMENTS];
14522
14523   /* runtime values for group element choice position */
14524   int choice_pos[NUM_GROUP_ELEMENTS];
14525
14526   /* runtime values for belt position animations */
14527   int belt_graphic[4][NUM_BELT_PARTS];
14528   int belt_anim_mode[4][NUM_BELT_PARTS];
14529 };
14530
14531 static struct EngineSnapshotInfo engine_snapshot_rnd;
14532 static char *snapshot_level_identifier = NULL;
14533 static int snapshot_level_nr = -1;
14534
14535 static void SaveEngineSnapshotValues_RND()
14536 {
14537   static int belt_base_active_element[4] =
14538   {
14539     EL_CONVEYOR_BELT_1_LEFT_ACTIVE,
14540     EL_CONVEYOR_BELT_2_LEFT_ACTIVE,
14541     EL_CONVEYOR_BELT_3_LEFT_ACTIVE,
14542     EL_CONVEYOR_BELT_4_LEFT_ACTIVE
14543   };
14544   int i, j;
14545
14546   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14547   {
14548     int element = EL_CUSTOM_START + i;
14549
14550     engine_snapshot_rnd.collect_score[i] = element_info[element].collect_score;
14551   }
14552
14553   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14554   {
14555     int element = EL_GROUP_START + i;
14556
14557     engine_snapshot_rnd.choice_pos[i] = element_info[element].group->choice_pos;
14558   }
14559
14560   for (i = 0; i < 4; i++)
14561   {
14562     for (j = 0; j < NUM_BELT_PARTS; j++)
14563     {
14564       int element = belt_base_active_element[i] + j;
14565       int graphic = el2img(element);
14566       int anim_mode = graphic_info[graphic].anim_mode;
14567
14568       engine_snapshot_rnd.belt_graphic[i][j] = graphic;
14569       engine_snapshot_rnd.belt_anim_mode[i][j] = anim_mode;
14570     }
14571   }
14572 }
14573
14574 static void LoadEngineSnapshotValues_RND()
14575 {
14576   unsigned int num_random_calls = game.num_random_calls;
14577   int i, j;
14578
14579   for (i = 0; i < NUM_CUSTOM_ELEMENTS; i++)
14580   {
14581     int element = EL_CUSTOM_START + i;
14582
14583     element_info[element].collect_score = engine_snapshot_rnd.collect_score[i];
14584   }
14585
14586   for (i = 0; i < NUM_GROUP_ELEMENTS; i++)
14587   {
14588     int element = EL_GROUP_START + i;
14589
14590     element_info[element].group->choice_pos = engine_snapshot_rnd.choice_pos[i];
14591   }
14592
14593   for (i = 0; i < 4; i++)
14594   {
14595     for (j = 0; j < NUM_BELT_PARTS; j++)
14596     {
14597       int graphic = engine_snapshot_rnd.belt_graphic[i][j];
14598       int anim_mode = engine_snapshot_rnd.belt_anim_mode[i][j];
14599
14600       graphic_info[graphic].anim_mode = anim_mode;
14601     }
14602   }
14603
14604   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14605   {
14606     InitRND(tape.random_seed);
14607     for (i = 0; i < num_random_calls; i++)
14608       RND(1);
14609   }
14610
14611   if (game.num_random_calls != num_random_calls)
14612   {
14613     Error(ERR_INFO, "number of random calls out of sync");
14614     Error(ERR_INFO, "number of random calls should be %d", num_random_calls);
14615     Error(ERR_INFO, "number of random calls is %d", game.num_random_calls);
14616     Error(ERR_EXIT, "this should not happen -- please debug");
14617   }
14618 }
14619
14620 void FreeEngineSnapshotSingle()
14621 {
14622   FreeSnapshotSingle();
14623
14624   setString(&snapshot_level_identifier, NULL);
14625   snapshot_level_nr = -1;
14626 }
14627
14628 void FreeEngineSnapshotList()
14629 {
14630   FreeSnapshotList();
14631 }
14632
14633 ListNode *SaveEngineSnapshotBuffers()
14634 {
14635   ListNode *buffers = NULL;
14636
14637   /* copy some special values to a structure better suited for the snapshot */
14638
14639   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14640     SaveEngineSnapshotValues_RND();
14641   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14642     SaveEngineSnapshotValues_EM();
14643   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14644     SaveEngineSnapshotValues_SP(&buffers);
14645
14646   /* save values stored in special snapshot structure */
14647
14648   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14649     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_rnd));
14650   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14651     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_em));
14652   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14653     SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(engine_snapshot_sp));
14654
14655   /* save further RND engine values */
14656
14657   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(stored_player));
14658   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(game));
14659   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(tape));
14660
14661   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZX));
14662   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ZY));
14663   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitX));
14664   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExitY));
14665
14666   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(FrameCounter));
14667   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeFrames));
14668   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimePlayed));
14669   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TimeLeft));
14670   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(TapeTime));
14671
14672   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovDir));
14673   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenMovPos));
14674   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScreenGfxPos));
14675
14676   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ScrollStepSize));
14677
14678   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AllPlayersGone));
14679
14680   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt));
14681   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaCnt2));
14682
14683   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Feld));
14684   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovPos));
14685   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDir));
14686   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(MovDelay));
14687   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeDelay));
14688   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangePage));
14689   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CustomValue));
14690   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store));
14691   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Store2));
14692   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(StorePlayer));
14693   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Back));
14694   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(AmoebaNr));
14695   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustMoving));
14696   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(WasJustFalling));
14697   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckCollision));
14698   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(CheckImpact));
14699   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Stop));
14700   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(Pushed));
14701
14702   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeCount));
14703   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ChangeEvent));
14704
14705   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodePhase));
14706   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeDelay));
14707   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(ExplodeField));
14708
14709   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(RunnerVisit));
14710   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(PlayerVisit));
14711
14712   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxFrame));
14713   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxRandom));
14714   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxElement));
14715   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxAction));
14716   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(GfxDir));
14717
14718   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_x));
14719   SaveSnapshotBuffer(&buffers, ARGS_ADDRESS_AND_SIZEOF(scroll_y));
14720
14721   /* save level identification information */
14722
14723   setString(&snapshot_level_identifier, leveldir_current->identifier);
14724   snapshot_level_nr = level_nr;
14725
14726 #if 0
14727   ListNode *node = engine_snapshot_list_rnd;
14728   int num_bytes = 0;
14729
14730   while (node != NULL)
14731   {
14732     num_bytes += ((struct EngineSnapshotNodeInfo *)node->content)->size;
14733
14734     node = node->next;
14735   }
14736
14737   printf("::: size of engine snapshot: %d bytes\n", num_bytes);
14738 #endif
14739
14740   return buffers;
14741 }
14742
14743 void SaveEngineSnapshotSingle()
14744 {
14745   ListNode *buffers = SaveEngineSnapshotBuffers();
14746
14747   /* finally save all snapshot buffers to single snapshot */
14748   SaveSnapshotSingle(buffers);
14749 }
14750
14751 boolean SaveEngineSnapshotToList()
14752 {
14753   boolean save_snapshot =
14754     (FrameCounter == 0 ||
14755      (game.snapshot.mode == SNAPSHOT_MODE_STEP) ||
14756      (game.snapshot.mode == SNAPSHOT_MODE_MOVE &&
14757       game.snapshot.changed_action));
14758
14759   game.snapshot.changed_action = FALSE;
14760
14761   if (!save_snapshot)
14762     return FALSE;
14763
14764   ListNode *buffers = SaveEngineSnapshotBuffers();
14765
14766   /* finally save all snapshot buffers to snapshot list */
14767   SaveSnapshotToList(buffers);
14768
14769   return TRUE;
14770 }
14771
14772 void LoadEngineSnapshotValues()
14773 {
14774   /* restore special values from snapshot structure */
14775
14776   if (level.game_engine_type == GAME_ENGINE_TYPE_RND)
14777     LoadEngineSnapshotValues_RND();
14778   if (level.game_engine_type == GAME_ENGINE_TYPE_EM)
14779     LoadEngineSnapshotValues_EM();
14780   if (level.game_engine_type == GAME_ENGINE_TYPE_SP)
14781     LoadEngineSnapshotValues_SP();
14782 }
14783
14784 void LoadEngineSnapshotSingle()
14785 {
14786   LoadSnapshotSingle();
14787
14788   LoadEngineSnapshotValues();
14789 }
14790
14791 void LoadEngineSnapshot_Undo(int steps)
14792 {
14793   LoadSnapshotFromList_Older(steps);
14794
14795   LoadEngineSnapshotValues();
14796 }
14797
14798 void LoadEngineSnapshot_Redo(int steps)
14799 {
14800   LoadSnapshotFromList_Newer(steps);
14801
14802   LoadEngineSnapshotValues();
14803 }
14804
14805 boolean CheckEngineSnapshot()
14806 {
14807   return (strEqual(snapshot_level_identifier, leveldir_current->identifier) &&
14808           snapshot_level_nr == level_nr);
14809 }
14810
14811
14812 /* ---------- new game button stuff ---------------------------------------- */
14813
14814 static struct
14815 {
14816   int graphic;
14817   struct XY *pos;
14818   int gadget_id;
14819   char *infotext;
14820 } gamebutton_info[NUM_GAME_BUTTONS] =
14821 {
14822   {
14823     IMG_GAME_BUTTON_GFX_STOP,           &game.button.stop,
14824     GAME_CTRL_ID_STOP,                  "stop game"
14825   },
14826   {
14827     IMG_GAME_BUTTON_GFX_PAUSE,          &game.button.pause,
14828     GAME_CTRL_ID_PAUSE,                 "pause game"
14829   },
14830   {
14831     IMG_GAME_BUTTON_GFX_PLAY,           &game.button.play,
14832     GAME_CTRL_ID_PLAY,                  "play game"
14833   },
14834   {
14835     IMG_GAME_BUTTON_GFX_UNDO,           &game.button.undo,
14836     GAME_CTRL_ID_UNDO,                  "undo step"
14837   },
14838   {
14839     IMG_GAME_BUTTON_GFX_REDO,           &game.button.redo,
14840     GAME_CTRL_ID_REDO,                  "redo step"
14841   },
14842   {
14843     IMG_GAME_BUTTON_GFX_SAVE,           &game.button.save,
14844     GAME_CTRL_ID_SAVE,                  "save game"
14845   },
14846   {
14847     IMG_GAME_BUTTON_GFX_LOAD,           &game.button.load,
14848     GAME_CTRL_ID_LOAD,                  "load game"
14849   },
14850   {
14851     IMG_GAME_BUTTON_GFX_SOUND_MUSIC,    &game.button.sound_music,
14852     SOUND_CTRL_ID_MUSIC,                "background music on/off"
14853   },
14854   {
14855     IMG_GAME_BUTTON_GFX_SOUND_LOOPS,    &game.button.sound_loops,
14856     SOUND_CTRL_ID_LOOPS,                "sound loops on/off"
14857   },
14858   {
14859     IMG_GAME_BUTTON_GFX_SOUND_SIMPLE,   &game.button.sound_simple,
14860     SOUND_CTRL_ID_SIMPLE,               "normal sounds on/off"
14861   }
14862 };
14863
14864 void CreateGameButtons()
14865 {
14866   int i;
14867
14868   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14869   {
14870     struct GraphicInfo *gfx = &graphic_info[gamebutton_info[i].graphic];
14871     struct XY *pos = gamebutton_info[i].pos;
14872     struct GadgetInfo *gi;
14873     int button_type;
14874     boolean checked;
14875     unsigned int event_mask;
14876     int base_x = (tape.show_game_buttons ? VX : DX);
14877     int base_y = (tape.show_game_buttons ? VY : DY);
14878     int gd_x   = gfx->src_x;
14879     int gd_y   = gfx->src_y;
14880     int gd_xp  = gfx->src_x + gfx->pressed_xoffset;
14881     int gd_yp  = gfx->src_y + gfx->pressed_yoffset;
14882     int gd_xa  = gfx->src_x + gfx->active_xoffset;
14883     int gd_ya  = gfx->src_y + gfx->active_yoffset;
14884     int gd_xap = gfx->src_x + gfx->active_xoffset + gfx->pressed_xoffset;
14885     int gd_yap = gfx->src_y + gfx->active_yoffset + gfx->pressed_yoffset;
14886     int id = i;
14887
14888     if (gfx->bitmap == NULL)
14889     {
14890       game_gadget[id] = NULL;
14891
14892       continue;
14893     }
14894
14895     if (id == GAME_CTRL_ID_STOP ||
14896         id == GAME_CTRL_ID_PAUSE ||
14897         id == GAME_CTRL_ID_PLAY ||
14898         id == GAME_CTRL_ID_SAVE ||
14899         id == GAME_CTRL_ID_LOAD)
14900     {
14901       button_type = GD_TYPE_NORMAL_BUTTON;
14902       checked = FALSE;
14903       event_mask = GD_EVENT_RELEASED;
14904     }
14905     else if (id == GAME_CTRL_ID_UNDO ||
14906              id == GAME_CTRL_ID_REDO)
14907     {
14908       button_type = GD_TYPE_NORMAL_BUTTON;
14909       checked = FALSE;
14910       event_mask = GD_EVENT_PRESSED | GD_EVENT_REPEATED;
14911     }
14912     else
14913     {
14914       button_type = GD_TYPE_CHECK_BUTTON;
14915       checked =
14916         ((id == SOUND_CTRL_ID_MUSIC && setup.sound_music) ||
14917          (id == SOUND_CTRL_ID_LOOPS && setup.sound_loops) ||
14918          (id == SOUND_CTRL_ID_SIMPLE && setup.sound_simple) ? TRUE : FALSE);
14919       event_mask = GD_EVENT_PRESSED;
14920     }
14921
14922     gi = CreateGadget(GDI_CUSTOM_ID, id,
14923                       GDI_INFO_TEXT, gamebutton_info[i].infotext,
14924                       GDI_X, base_x + GDI_ACTIVE_POS(pos->x),
14925                       GDI_Y, base_y + GDI_ACTIVE_POS(pos->y),
14926                       GDI_WIDTH, gfx->width,
14927                       GDI_HEIGHT, gfx->height,
14928                       GDI_TYPE, button_type,
14929                       GDI_STATE, GD_BUTTON_UNPRESSED,
14930                       GDI_CHECKED, checked,
14931                       GDI_DESIGN_UNPRESSED, gfx->bitmap, gd_x, gd_y,
14932                       GDI_DESIGN_PRESSED, gfx->bitmap, gd_xp, gd_yp,
14933                       GDI_ALT_DESIGN_UNPRESSED, gfx->bitmap, gd_xa, gd_ya,
14934                       GDI_ALT_DESIGN_PRESSED, gfx->bitmap, gd_xap, gd_yap,
14935                       GDI_DIRECT_DRAW, FALSE,
14936                       GDI_EVENT_MASK, event_mask,
14937                       GDI_CALLBACK_ACTION, HandleGameButtons,
14938                       GDI_END);
14939
14940     if (gi == NULL)
14941       Error(ERR_EXIT, "cannot create gadget");
14942
14943     game_gadget[id] = gi;
14944   }
14945 }
14946
14947 void FreeGameButtons()
14948 {
14949   int i;
14950
14951   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14952     FreeGadget(game_gadget[i]);
14953 }
14954
14955 void MapStopPlayButtons()
14956 {
14957   UnmapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14958   UnmapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14959
14960   MapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14961   MapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14962 }
14963
14964 void MapUndoRedoButtons()
14965 {
14966   UnmapGadget(game_gadget[GAME_CTRL_ID_STOP]);
14967   UnmapGadget(game_gadget[GAME_CTRL_ID_PLAY]);
14968
14969   MapGadget(game_gadget[GAME_CTRL_ID_UNDO]);
14970   MapGadget(game_gadget[GAME_CTRL_ID_REDO]);
14971 }
14972
14973 void MapGameButtons()
14974 {
14975   int i;
14976
14977   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14978     if (i != GAME_CTRL_ID_UNDO &&
14979         i != GAME_CTRL_ID_REDO)
14980       MapGadget(game_gadget[i]);
14981 }
14982
14983 void UnmapGameButtons()
14984 {
14985   int i;
14986
14987   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14988     UnmapGadget(game_gadget[i]);
14989 }
14990
14991 void RedrawGameButtons()
14992 {
14993   int i;
14994
14995   for (i = 0; i < NUM_GAME_BUTTONS; i++)
14996     RedrawGadget(game_gadget[i]);
14997
14998   // RedrawGadget() may have set REDRAW_ALL if buttons are defined off-area
14999   redraw_mask &= ~REDRAW_ALL;
15000 }
15001
15002 void GameUndoRedoExt()
15003 {
15004   ClearPlayerAction();
15005
15006   tape.pausing = TRUE;
15007
15008   RedrawPlayfield();
15009   UpdateAndDisplayGameControlValues();
15010
15011   DrawCompleteVideoDisplay();
15012   DrawVideoDisplay(VIDEO_STATE_TIME_ON, TapeTime);
15013   DrawVideoDisplay(VIDEO_STATE_FRAME_ON, FrameCounter);
15014
15015   BackToFront();
15016 }
15017
15018 void GameUndo(int steps)
15019 {
15020   if (!CheckEngineSnapshot())
15021     return;
15022
15023   LoadEngineSnapshot_Undo(steps);
15024
15025   GameUndoRedoExt();
15026 }
15027
15028 void GameRedo(int steps)
15029 {
15030   if (!CheckEngineSnapshot())
15031     return;
15032
15033   LoadEngineSnapshot_Redo(steps);
15034
15035   GameUndoRedoExt();
15036 }
15037
15038 static void HandleGameButtonsExt(int id, int button)
15039 {
15040   int steps = BUTTON_STEPSIZE(button);
15041   boolean handle_game_buttons =
15042     (game_status == GAME_MODE_PLAYING ||
15043      (game_status == GAME_MODE_MAIN && tape.show_game_buttons));
15044
15045   if (!handle_game_buttons)
15046     return;
15047
15048   switch (id)
15049   {
15050     case GAME_CTRL_ID_STOP:
15051       if (game_status == GAME_MODE_MAIN)
15052         break;
15053
15054       if (tape.playing)
15055         TapeStop();
15056       else
15057         RequestQuitGame(TRUE);
15058
15059       break;
15060
15061     case GAME_CTRL_ID_PAUSE:
15062       if (options.network && game_status == GAME_MODE_PLAYING)
15063       {
15064 #if defined(NETWORK_AVALIABLE)
15065         if (tape.pausing)
15066           SendToServer_ContinuePlaying();
15067         else
15068           SendToServer_PausePlaying();
15069 #endif
15070       }
15071       else
15072         TapeTogglePause(TAPE_TOGGLE_MANUAL);
15073       break;
15074
15075     case GAME_CTRL_ID_PLAY:
15076       if (game_status == GAME_MODE_MAIN)
15077       {
15078         StartGameActions(options.network, setup.autorecord, level.random_seed);
15079       }
15080       else if (tape.pausing)
15081       {
15082 #if defined(NETWORK_AVALIABLE)
15083         if (options.network)
15084           SendToServer_ContinuePlaying();
15085         else
15086 #endif
15087         {
15088           tape.pausing = FALSE;
15089           DrawVideoDisplay(VIDEO_STATE_PAUSE_OFF, 0);
15090         }
15091       }
15092       break;
15093
15094     case GAME_CTRL_ID_UNDO:
15095       GameUndo(steps);
15096       break;
15097
15098     case GAME_CTRL_ID_REDO:
15099       GameRedo(steps);
15100       break;
15101
15102     case GAME_CTRL_ID_SAVE:
15103       TapeQuickSave();
15104       break;
15105
15106     case GAME_CTRL_ID_LOAD:
15107       TapeQuickLoad();
15108       break;
15109
15110     case SOUND_CTRL_ID_MUSIC:
15111       if (setup.sound_music)
15112       { 
15113         setup.sound_music = FALSE;
15114
15115         FadeMusic();
15116       }
15117       else if (audio.music_available)
15118       { 
15119         setup.sound = setup.sound_music = TRUE;
15120
15121         SetAudioMode(setup.sound);
15122
15123         PlayLevelMusic();
15124       }
15125       break;
15126
15127     case SOUND_CTRL_ID_LOOPS:
15128       if (setup.sound_loops)
15129         setup.sound_loops = FALSE;
15130       else if (audio.loops_available)
15131       {
15132         setup.sound = setup.sound_loops = TRUE;
15133
15134         SetAudioMode(setup.sound);
15135       }
15136       break;
15137
15138     case SOUND_CTRL_ID_SIMPLE:
15139       if (setup.sound_simple)
15140         setup.sound_simple = FALSE;
15141       else if (audio.sound_available)
15142       {
15143         setup.sound = setup.sound_simple = TRUE;
15144
15145         SetAudioMode(setup.sound);
15146       }
15147       break;
15148
15149     default:
15150       break;
15151   }
15152 }
15153
15154 static void HandleGameButtons(struct GadgetInfo *gi)
15155 {
15156   HandleGameButtonsExt(gi->custom_id, gi->event.button);
15157 }
15158
15159 void HandleSoundButtonKeys(Key key)
15160 {
15161
15162   if (key == setup.shortcut.sound_simple)
15163     ClickOnGadget(game_gadget[SOUND_CTRL_ID_SIMPLE], MB_LEFTBUTTON);
15164   else if (key == setup.shortcut.sound_loops)
15165     ClickOnGadget(game_gadget[SOUND_CTRL_ID_LOOPS], MB_LEFTBUTTON);
15166   else if (key == setup.shortcut.sound_music)
15167     ClickOnGadget(game_gadget[SOUND_CTRL_ID_MUSIC], MB_LEFTBUTTON);
15168 }